diff options
Diffstat (limited to 'erts/emulator/beam/external.c')
-rw-r--r-- | erts/emulator/beam/external.c | 1053 |
1 files changed, 352 insertions, 701 deletions
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 67584e38ec..a95e098c59 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2021. All Rights Reserved. + * Copyright Ericsson AB 1996-2022. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ #include "erl_map.h" #include "erl_proc_sig_queue.h" #include "erl_trace.h" +#include "erl_global_literals.h" #define PASS_THROUGH 'p' @@ -65,19 +66,6 @@ #define ERTS_MAX_TINY_CREATION (3) #define is_tiny_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_TINY_CREATION) -/* - * When 0 is used as creation, the real creation - * is unknown. Creation 0 on data will be changed to current - * creation of the node which it belongs to when it enters - * that node. - * This typically happens when a remote pid is created with - * list_to_pid/1 and then sent to the remote node. This behavior - * has the undesirable effect that a pid can be passed between nodes, - * and as a result of that not being equal to itself (the pid that - * comes back isn't equal to the original pid). - * - */ - #undef ERTS_DEBUG_USE_DIST_SEP #ifdef DEBUG # if 0 @@ -123,12 +111,9 @@ static ErtsExtSzRes encode_size_struct_int(TTBSizeContext*, ErtsAtomCacheMap *ac static Export binary_to_term_trap_export; static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1); static Sint transcode_dist_obuf(ErtsDistOutputBuf*, DistEntry*, Uint64 dflags, Sint reds); -static byte *hopefull_bit_binary(TTBEncodeContext* ctx, byte **epp, Binary *pb_val, Eterm pb_term, - byte *bytes, byte bitoffs, byte bitsize, Uint sz); -static void hopefull_export(TTBEncodeContext* ctx, byte **epp, Export* exp, Uint32 dflags, - struct erl_off_heap_header** off_heap); static void store_in_vec(TTBEncodeContext *ctx, byte *ep, Binary *ohbin, Eterm ohpb, byte *ohp, Uint ohsz); +static Uint32 calc_iovec_fun_size(SysIOVec* iov, Uint32 fun_high_ix, byte* size_p); void erts_init_external(void) { erts_init_trap_export(&term_to_binary_trap_export, @@ -230,7 +215,6 @@ insert_acache_map(ErtsAtomCacheMap *acmp, Eterm atom, Uint64 dflags) if (acmp && acmp->sz < ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES) { int ix; ASSERT(acmp->hdr_sz < 0); - ASSERT(dflags & DFLAG_UTF8_ATOMS); ix = atom2cix(atom); if (acmp->cache[ix].iix < 0) { acmp->cache[ix].iix = acmp->sz; @@ -268,7 +252,6 @@ erts_finalize_atom_cache_map(ErtsAtomCacheMap *acmp, Uint64 dflags) int i; int sz = 0; int min_sz; - ASSERT(dflags & DFLAG_UTF8_ATOMS); ASSERT(acmp->hdr_sz < 0); /* Make sure cache update instructions fit */ min_sz = (2+4)*acmp->sz; @@ -438,7 +421,6 @@ Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf* ob, int long_atoms; Uint64 seq_id = 0, frag_id = 0; register byte *ep = ob->eiov->iov[1].iov_base; - ASSERT(dflags & DFLAG_UTF8_ATOMS); /* * The buffer can have different layouts at this point depending on @@ -852,9 +834,6 @@ erts_prepare_dist_ext(ErtsDistExternal *edep, ASSERT(dep); erts_de_rlock(dep); - ASSERT(dep->dflags & DFLAG_UTF8_ATOMS); - - if ((dep->state != ERTS_DE_STATE_CONNECTED && dep->state != ERTS_DE_STATE_PENDING) || dep->connection_id != conn_id) { @@ -1612,8 +1591,7 @@ typedef struct { ErtsHeapFactory factory; int remaining_n; char* remaining_bytes; - ErtsWStack flat_maps; - ErtsPStack hamt_array; + ErtsPStack map_array; } B2TDecodeContext; typedef struct { @@ -1812,9 +1790,9 @@ static void b2t_destroy_context(B2TContext* context) case B2TDecodeTuple: case B2TDecodeString: case B2TDecodeBinary: - if (context->u.dc.hamt_array.pstart) { - erts_free(context->u.dc.hamt_array.alloc_type, - context->u.dc.hamt_array.pstart); + if (context->u.dc.map_array.pstart) { + erts_free(context->u.dc.map_array.alloc_type, + context->u.dc.map_array.pstart); } break; default:; @@ -1969,8 +1947,7 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Eterm bin, B2TContext *ctx) ctx->u.dc.res = (Eterm) (UWord) NULL; ctx->u.dc.next = &ctx->u.dc.res; erts_factory_proc_prealloc_init(&ctx->u.dc.factory, p, ctx->heap_size); - ctx->u.dc.flat_maps.wstart = NULL; - ctx->u.dc.hamt_array.pstart = NULL; + ctx->u.dc.map_array.pstart = NULL; ctx->state = B2TDecode; /*fall through*/ case B2TDecode: @@ -2955,9 +2932,8 @@ static ERTS_INLINE int dec_is_this_node(Eterm sysname, Uint32 creation) { return (sysname == INTERNAL_LOCAL_SYSNAME || - (sysname == erts_this_node->sysname - && (creation == erts_this_node->creation - || creation == ORIG_CREATION))); + (sysname == erts_this_node->sysname && + creation == erts_this_node->creation)); } @@ -3152,9 +3128,20 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, break; case ENC_PATCH_FUN_SIZE: { - byte* size_p = (byte *) obj; - Sint32 sz = ep - size_p; - put_int32(sz, size_p); + byte* size_p = (byte *) obj; + Sint32 fun_sz; + + if (use_iov && !ErtsInArea(size_p, ctx->cptr, ep - ctx->cptr)) { + ASSERT(ctx->vlen > 0); + fun_sz = (ep - ctx->cptr) + + calc_iovec_fun_size(ctx->iov, ctx->vlen-1, size_p); + } + else { + /* No iovec encoding or still in same iovec buffer as start + * of fun. Easy to calculate fun size. */ + fun_sz = ep - size_p; + } + put_int32(fun_sz, size_p); } goto outer_loop; case ENC_BIN_COPY: { @@ -3387,8 +3374,6 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, ? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj)); Uint32 creation = ref_creation(obj); - ASSERT(dflags & DFLAG_EXTENDED_REFERENCES); - erts_magic_ref_save_bin(obj); *ep++ = NEWER_REFERENCE_EXT; @@ -3662,72 +3647,23 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, data_dst = ep; ep += j; } - } else if (dflags & DFLAG_BIT_BINARIES) { - /* Bit-level binary. */ - if (dflags & DFLAG_PENDING_CONNECT) { - ASSERT(ctx); - j = off_heap_bytesize; - if (!j) { - pb_val = NULL; - pb_term = THE_NON_VALUE; - j = binary_size(obj); - } - data_dst = hopefull_bit_binary(ctx, &ep, pb_val, pb_term, - bytes, bitoffs, bitsize, j); - if (!data_dst) - break; /* off heap binary referred... */ - ASSERT(!off_heap_bytesize); - off_heap_tail = 0; - /* - * Trailing bits already written by hopefull_bit_binary(); - * now go copy all whole octets... - */ - bitsize = 0; - } - else { - *ep++ = BIT_BINARY_EXT; - j = binary_size(obj); - put_int32((j+1), ep); - ep += 4; - *ep++ = bitsize; - if (off_heap_bytesize) { - /* trailing bits */ - ep[0] = 0; - copy_binary_to_buffer(ep, 0, bytes + j, 0, bitsize); - off_heap_tail = 1; - } - else { - ep[j] = 0; /* Zero unused bits at end of binary */ - data_dst = ep; - ep += j + 1; - } - } } else { - /* - * Bit-level binary, but the receiver doesn't support it. - * Build a tuple instead. - */ - *ep++ = SMALL_TUPLE_EXT; - *ep++ = 2; - *ep++ = BINARY_EXT; - j = binary_size(obj); - put_int32((j+1), ep); - ep += 4; - + /* Bit-level binary. */ + *ep++ = BIT_BINARY_EXT; + j = binary_size(obj); + put_int32((j+1), ep); + ep += 4; + *ep++ = bitsize; if (off_heap_bytesize) { /* trailing bits */ ep[0] = 0; copy_binary_to_buffer(ep, 0, bytes + j, 0, bitsize); - ep[1] = SMALL_INTEGER_EXT; - ep[2] = bitsize; - off_heap_tail = 3; + off_heap_tail = 1; } else { - ep[j] = 0; /* Zero unused bits at end of binary */ + ep[j] = 0; /* Zero unused bits at end of binary */ data_dst = ep; - ep += j+1; - *ep++ = SMALL_INTEGER_EXT; - *ep++ = bitsize; + ep += j + 1; } } if (off_heap_bytesize) { @@ -3745,63 +3681,49 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, } } break; - case EXPORT_DEF: - { - Export* exp = *((Export **) (export_val(obj) + 1)); - if (dflags & DFLAG_PENDING_CONNECT) { - ASSERT(ctx); - hopefull_export(ctx, &ep, exp, dflags, off_heap); - } - else if ((dflags & DFLAG_EXPORT_PTR_TAG) != 0) { - *ep++ = EXPORT_EXT; - ep = enc_atom(acmp, exp->info.mfa.module, ep, dflags); - ep = enc_atom(acmp, exp->info.mfa.function, ep, dflags); - ep = enc_term(acmp, make_small(exp->info.mfa.arity), - ep, dflags, off_heap); - } else { - /* Tag, arity */ - *ep++ = SMALL_TUPLE_EXT; - put_int8(2, ep); - ep += 1; + case FUN_DEF: + { + ErlFunThing* funp = (ErlFunThing *) fun_val(obj); - /* Module name */ - ep = enc_atom(acmp, exp->info.mfa.module, ep, dflags); + if (is_local_fun(funp)) { + ErlFunEntry* fe = funp->entry.fun; + int ei; - /* Function name */ - ep = enc_atom(acmp, exp->info.mfa.function, ep, dflags); - } - break; - } - break; - case FUN_DEF: - { - ErlFunThing* funp = (ErlFunThing *) fun_val(obj); - int ei; - - ASSERT(dflags & DFLAG_NEW_FUN_TAGS); - *ep++ = NEW_FUN_EXT; - WSTACK_PUSH2(s, ENC_PATCH_FUN_SIZE, - (UWord) ep); /* Position for patching in size */ - ep += 4; - *ep = funp->arity; - ep += 1; - sys_memcpy(ep, funp->fe->uniq, 16); - ep += 16; - put_int32(funp->fe->index, ep); - ep += 4; - put_int32(funp->num_free, ep); - ep += 4; - ep = enc_atom(acmp, funp->fe->module, ep, dflags); - ep = enc_term(acmp, make_small(funp->fe->old_index), ep, dflags, off_heap); - ep = enc_term(acmp, make_small(funp->fe->old_uniq), ep, dflags, off_heap); - ep = enc_pid(acmp, funp->creator, ep, dflags); - - for (ei = funp->num_free-1; ei >= 0; ei--) { - WSTACK_PUSH2(s, ENC_TERM, (UWord) funp->env[ei]); - } - } - break; - } + *ep++ = NEW_FUN_EXT; + WSTACK_PUSH2(s, ENC_PATCH_FUN_SIZE, + (UWord) ep); /* Position for patching in size */ + ep += 4; + *ep = funp->arity; + ep += 1; + sys_memcpy(ep, fe->uniq, 16); + ep += 16; + put_int32(fe->index, ep); + ep += 4; + put_int32((Uint32)funp->num_free, ep); + ep += 4; + ep = enc_atom(acmp, fe->module, ep, dflags); + ep = enc_term(acmp, make_small(fe->old_index), ep, dflags, off_heap); + ep = enc_term(acmp, make_small(fe->old_uniq), ep, dflags, off_heap); + ep = enc_pid(acmp, funp->creator, ep, dflags); + + for (ei = funp->num_free-1; ei >= 0; ei--) { + WSTACK_PUSH2(s, ENC_TERM, (UWord) funp->env[ei]); + } + } else { + Export *exp = funp->entry.exp; + + ASSERT(is_external_fun(funp) && funp->next == NULL); + + *ep++ = EXPORT_EXT; + ep = enc_atom(acmp, exp->info.mfa.module, ep, dflags); + ep = enc_atom(acmp, exp->info.mfa.function, ep, dflags); + ep = enc_term(acmp, make_small(exp->info.mfa.arity), + ep, dflags, off_heap); + + } + } + break; + } } DESTROY_WSTACK(s); if (ctx) { @@ -3939,123 +3861,6 @@ store_in_vec(TTBEncodeContext *ctx, } } -static byte * -begin_hopefull_data(TTBEncodeContext *ctx, byte *ep) -{ - store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0); - ASSERT(ERTS_NO_HIX == get_uint32(ctx->hopefull_ixp)); - put_int32(ctx->vlen, ctx->hopefull_ixp); - ctx->hopefull_ixp = ep; - put_int32(ERTS_NO_HIX, ep); - ep += 4; - ctx->cptr = ep; - return ep; -} - -static byte * -end_hopefull_data(TTBEncodeContext *ctx, byte *ep, Uint fallback_size) -{ - Uint sz; - store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0); - /* - * Reserve extra room for fallback if needed. The four - * bytes used for hopefull index can be used for - * fallback encoding... - */ - sz = ep - ctx->hopefull_ixp; - if (fallback_size > sz) { - ep += fallback_size - sz; - ctx->cptr = ep; - } - return ep; -} - -static byte * -hopefull_bit_binary(TTBEncodeContext* ctx, byte **epp, Binary *pb_val, Eterm pb_term, - byte *bytes, byte bitoffs, byte bitsize, Uint sz) -{ - byte *octets, *ep = *epp; - - ctx->hopefull_flags |= DFLAG_BIT_BINARIES; - - /* - * The fallback: - * - * SMALL_TUPLE_EXT - 1 byte - * 2 - 1 byte - * BINARY_EXT - 1 byte - * whole octet size ('sz') - 4 byte - * whole octets - 'sz' bytes - * trailing bits - 1 byte - * SMALL_INTEGER_EXT - 1 byte - * bitsize - 1 byte - */ - - /* bit binary prelude in one hopefull data element */ - ep = begin_hopefull_data(ctx, ep); - *ep++ = BIT_BINARY_EXT; - put_int32((sz+1), ep); - ep += 4; - *ep++ = bitsize; - ep = end_hopefull_data(ctx, ep, 1+1+1+4); - - /* All whole octets... */ - if (pb_val) { - octets = NULL; - store_in_vec(ctx, ep, pb_val, pb_term, bytes, sz); - } - else { - /* ... will be copied here afterwards */ - octets = ep; - ep += sz; - } - - /* copy trailing bits into new hopefull data element */ - ep = begin_hopefull_data(ctx, ep); - *ep = 0; /* Clear the bit in the byte */ - - copy_binary_to_buffer(ep, 0, bytes + sz, bitoffs, bitsize); - ep++; - - ep = end_hopefull_data(ctx, ep, 1+1+1); - *epp = ep; - - return octets; -} - -static void -hopefull_export(TTBEncodeContext* ctx, byte **epp, Export* exp, Uint32 dflags, - struct erl_off_heap_header** off_heap) -{ - Uint fallback_sz; - byte *ep = *epp, *mod_start; - - /* - * The fallback: - * - * SMALL_TUPLE_EXT - 1 byte - * 2 - 1 byte - * module atom... - M bytes - * function atom... - F bytes - */ - - ctx->hopefull_flags |= DFLAG_EXPORT_PTR_TAG; - - ep = begin_hopefull_data(ctx, ep); - - *ep++ = EXPORT_EXT; - mod_start = ep; - ep = enc_atom(NULL, exp->info.mfa.module, ep, dflags); - ep = enc_atom(NULL, exp->info.mfa.function, ep, dflags); - fallback_sz = 2 + (ep - mod_start); - ep = enc_term(NULL, make_small(exp->info.mfa.arity), - ep, dflags, off_heap); - - ep = end_hopefull_data(ctx, ep, fallback_sz); - - *epp = ep; -} - /** @brief Is it a list of bytes not longer than MAX_STRING_LEN? * @param lenp out: string length or number of list cells traversed * @return true/false @@ -4086,11 +3891,14 @@ is_external_string(Eterm list, Uint* lenp) } -struct dec_term_hamt +struct dec_term_map { - Eterm* objp; /* write result here */ - Uint size; /* nr of leafs */ - Eterm* leaf_array; + Eterm* objp; /* hashmap: write result here, flatmap: NULL */ + Uint size; /* hashmap: nr of leafs, flatmap: unused */ + union { + Eterm* leaf_array; /* hashmap */ + flatmap_t* flatmap; + } u; }; @@ -4105,12 +3913,11 @@ dec_term(ErtsDistExternal *edep, B2TContext* ctx, int ets_decode) { -#define PSTACK_TYPE struct dec_term_hamt - PSTACK_DECLARE(hamt_array, 5); +#define PSTACK_TYPE struct dec_term_map + PSTACK_DECLARE(map_array, 10); int n; ErtsAtomEncoding char_enc; register Eterm* hp; /* Please don't take the address of hp */ - DECLARE_WSTACK(flat_maps); /* for preprocessing of small maps */ Eterm* next; SWord reds; #ifdef DEBUG @@ -4196,13 +4003,9 @@ dec_term(ErtsDistExternal *edep, return NULL; } } - PSTACK_CHANGE_ALLOCATOR(hamt_array, ERTS_ALC_T_SAVED_ESTACK); - WSTACK_CHANGE_ALLOCATOR(flat_maps, ERTS_ALC_T_SAVED_ESTACK); - if (ctx->u.dc.hamt_array.pstart) { - PSTACK_RESTORE(hamt_array, &ctx->u.dc.hamt_array); - } - if (ctx->u.dc.flat_maps.wstart) { - WSTACK_RESTORE(flat_maps, &ctx->u.dc.flat_maps); + PSTACK_CHANGE_ALLOCATOR(map_array, ERTS_ALC_T_SAVED_ESTACK); + if (ctx->u.dc.map_array.pstart) { + PSTACK_RESTORE(map_array, &ctx->u.dc.map_array); } } else { @@ -4327,10 +4130,18 @@ dec_term_atom_common: case LARGE_TUPLE_EXT: n = get_int32(ep); ep += 4; + if (n == 0) { + *objp = ERTS_GLOBAL_LIT_EMPTY_TUPLE; + break; + } goto tuple_loop; case SMALL_TUPLE_EXT: n = get_int8(ep); ep++; + if (n == 0) { + *objp = ERTS_GLOBAL_LIT_EMPTY_TUPLE; + break; + } tuple_loop: *objp = make_tuple(hp); *hp++ = make_arityval(n); @@ -4355,37 +4166,39 @@ dec_term_atom_common: *objp = NIL; break; case LIST_EXT: - n = get_int32(ep); + { + Uint32 nu = get_uint32(ep); ep += 4; - if (n == 0) { + if (nu == 0) { next = objp; break; } *objp = make_list(hp); - hp += 2 * n; + hp += 2 * (Uint) nu; objp = hp - 2; objp[0] = (Eterm) (objp+1); objp[1] = (Eterm) next; next = objp; objp -= 2; - n--; + nu--; if (ctx) { - if (reds < n) { + if ((Uint) reds < nu) { ASSERT(reds > 0); ctx->state = B2TDecodeList; - ctx->u.dc.remaining_n = n - reds; - n = reds; + ctx->u.dc.remaining_n = nu - reds; + nu = reds; } - reds -= n; + reds -= nu; } - while (n > 0) { + while (nu > 0) { objp[0] = (Eterm) next; objp[1] = make_list(next); next = objp; objp -= 2; - n--; + nu--; } break; + } case STRING_EXT: n = get_int16(ep); ep += 2; @@ -4665,19 +4478,21 @@ dec_term_atom_common: } case BINARY_EXT: { - n = get_int32(ep); + Uint32 nu = get_uint32(ep); ep += 4; - if ((unsigned)n <= ERL_ONHEAP_BIN_LIMIT) { + ASSERT(IS_BINARY_SIZE_OK(nu)); + + if (nu <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hb = (ErlHeapBin *) hp; - hb->thing_word = header_heap_bin(n); - hb->size = n; - hp += heap_bin_size(n); - sys_memcpy(hb->data, ep, n); + hb->thing_word = header_heap_bin(nu); + hb->size = nu; + hp += heap_bin_size(nu); + sys_memcpy(hb->data, ep, nu); *objp = make_binary(hb); } else if (edep && edep->data && edep->data->binp && - n > (edep->data->binp->orig_size / 4)) { + nu > (edep->data->binp->orig_size / 4)) { /* If we decode a refc binary from a distribution data entry we know that it is a refc binary to begin with so we just increment it and use the reference. This @@ -4689,37 +4504,39 @@ dec_term_atom_common: Binary* bptr = edep->data->binp; erts_refc_inc(&bptr->intern.refc, 1); pb->thing_word = HEADER_PROC_BIN; - pb->size = n; + pb->size = nu; pb->next = factory->off_heap->first; factory->off_heap->first = (struct erl_off_heap_header*)pb; pb->val = bptr; pb->bytes = (byte*) ep; ERTS_ASSERT((byte*)(bptr->orig_bytes) < ep && - ep+n <= (byte*)(bptr->orig_bytes+bptr->orig_size)); + ep+nu <= (byte*)(bptr->orig_bytes+bptr->orig_size)); pb->flags = 0; OH_OVERHEAD(factory->off_heap, pb->size / sizeof(Eterm)); hp += PROC_BIN_SIZE; *objp = make_binary(pb); } else { - Binary* dbin = erts_bin_nrml_alloc(n); + Binary* dbin; + + dbin = erts_bin_nrml_alloc(nu); *objp = erts_build_proc_bin(factory->off_heap, hp, dbin); hp += PROC_BIN_SIZE; if (ctx) { - int n_limit = reds * B2T_MEMCPY_FACTOR; - if (n > n_limit) { + unsigned int n_limit = reds * B2T_MEMCPY_FACTOR; + if (nu > n_limit) { ctx->state = B2TDecodeBinary; - ctx->u.dc.remaining_n = n - n_limit; + ctx->u.dc.remaining_n = nu - n_limit; ctx->u.dc.remaining_bytes = dbin->orig_bytes + n_limit; - n = n_limit; + nu = n_limit; reds = 0; } else - reds -= n / B2T_MEMCPY_FACTOR; + reds -= nu / B2T_MEMCPY_FACTOR; } - sys_memcpy(dbin->orig_bytes, ep, n); + sys_memcpy(dbin->orig_bytes, ep, nu); } - ep += n; + ep += nu; break; } case BIT_BINARY_EXT: @@ -4727,51 +4544,53 @@ dec_term_atom_common: Eterm bin; ErlSubBin* sb; Uint bitsize; + Uint32 nu = get_uint32(ep); + + ASSERT(IS_BINARY_SIZE_OK(nu)); - n = get_int32(ep); bitsize = ep[4]; - if (((bitsize==0) != (n==0)) || bitsize > 8) + if (((bitsize==0) != (nu==0)) || bitsize > 8) goto error; ep += 5; - if ((unsigned)n <= ERL_ONHEAP_BIN_LIMIT) { + if (nu <= ERL_ONHEAP_BIN_LIMIT) { ErlHeapBin* hb = (ErlHeapBin *) hp; - hb->thing_word = header_heap_bin(n); - hb->size = n; - sys_memcpy(hb->data, ep, n); + hb->thing_word = header_heap_bin(nu); + hb->size = nu; + sys_memcpy(hb->data, ep, nu); bin = make_binary(hb); - hp += heap_bin_size(n); - ep += n; + hp += heap_bin_size(nu); + ep += nu; } else { - Binary* dbin = erts_bin_nrml_alloc(n); - Uint n_copy = n; + Binary* dbin = erts_bin_nrml_alloc(nu); + Uint n_copy = nu; bin = erts_build_proc_bin(factory->off_heap, hp, dbin); hp += PROC_BIN_SIZE; if (ctx) { int n_limit = reds * B2T_MEMCPY_FACTOR; - if (n > n_limit) { + if (nu > n_limit) { ctx->state = B2TDecodeBinary; - ctx->u.dc.remaining_n = n - n_limit; + ctx->u.dc.remaining_n = nu - n_limit; ctx->u.dc.remaining_bytes = dbin->orig_bytes + n_limit; n_copy = n_limit; reds = 0; } else { - reds -= n / B2T_MEMCPY_FACTOR; + reds -= nu / B2T_MEMCPY_FACTOR; } } sys_memcpy(dbin->orig_bytes, ep, n_copy); ep += n_copy; } - if (bitsize == 8 || n == 0) { + if (bitsize == 8 || nu == 0) { *objp = bin; } else { sb = (ErlSubBin *)hp; sb->thing_word = HEADER_SUB_BIN; sb->orig = bin; - sb->size = n - 1; + sb->size = nu - 1; sb->bitsize = bitsize; sb->bitoffs = 0; sb->offs = 0; @@ -4781,56 +4600,63 @@ dec_term_atom_common: } break; } - case EXPORT_EXT: - { - Eterm mod; - Eterm name; - Eterm temp; - Sint arity; - - if ((ep = dec_atom(edep, ep, &mod)) == NULL) { - goto error; - } - if ((ep = dec_atom(edep, ep, &name)) == NULL) { - goto error; - } - factory->hp = hp; - ep = dec_term(edep, factory, ep, &temp, NULL, 0); - hp = factory->hp; - if (ep == NULL) { - goto error; - } - if (!is_small(temp)) { - goto error; - } - arity = signed_val(temp); - if (arity < 0) { - goto error; - } - if (edep && (edep->flags & ERTS_DIST_EXT_BTT_SAFE)) { - if (!erts_active_export_entry(mod, name, arity)) - goto error; + case EXPORT_EXT: + { + ErlFunThing *funp; + Export *export; + Eterm mod; + Eterm name; + Eterm temp; + Sint arity; + + if ((ep = dec_atom(edep, ep, &mod)) == NULL) { + goto error; } - *objp = make_export(hp); - *hp++ = HEADER_EXPORT; - *hp++ = (Eterm) erts_export_get_or_make_stub(mod, name, arity); - break; - } - break; + if ((ep = dec_atom(edep, ep, &name)) == NULL) { + goto error; + } + factory->hp = hp; + ep = dec_term(edep, factory, ep, &temp, NULL, 0); + if (ep == NULL) { + goto error; + } + if (!is_small(temp)) { + goto error; + } + arity = signed_val(temp); + if (arity < 0) { + goto error; + } + if (edep && (edep->flags & ERTS_DIST_EXT_BTT_SAFE)) { + if (!erts_active_export_entry(mod, name, arity)) { + goto error; + } + } + + export = erts_export_get_or_make_stub(mod, name, arity); + funp = erts_new_export_fun_thing(&factory->hp, export, arity); + hp = factory->hp; + *objp = make_fun(funp); + } + break; case MAP_EXT: { Uint32 size,n; Eterm *kptr,*vptr; Eterm keys; + struct dec_term_map* map = PSTACK_PUSH(map_array); size = get_int32(ep); ep += 4; if (size <= MAP_SMALL_MAP_LIMIT) { flatmap_t *mp; - - keys = make_tuple(hp); - *hp++ = make_arityval(size); - hp += size; + if (size == 0) { + keys = ERTS_GLOBAL_LIT_EMPTY_TUPLE; + } else { + keys = make_tuple(hp); + *hp++ = make_arityval(size); + hp += size; + } kptr = hp - 1; mp = (flatmap_t*)hp; @@ -4842,7 +4668,9 @@ dec_term_atom_common: * vptr, last word for values */ - WSTACK_PUSH(flat_maps, (UWord)mp); + map->objp = NULL; + map->u.flatmap = mp; + mp->thing_word = MAP_HEADER_FLATMAP; mp->size = size; mp->keys = keys; @@ -4857,11 +4685,10 @@ dec_term_atom_common: } } else { /* Make hamt */ - struct dec_term_hamt* hamt = PSTACK_PUSH(hamt_array); - - hamt->objp = objp; - hamt->size = size; - hamt->leaf_array = hp; + ASSERT(objp != NULL); + map->objp = objp; + map->size = size; + map->u.leaf_array = hp; for (n = size; n; n--) { CDR(hp) = (Eterm) next; @@ -4929,8 +4756,9 @@ dec_term_atom_common: funp->next = factory->off_heap->first; factory->off_heap->first = (struct erl_off_heap_header*)funp; - funp->fe = erts_put_fun_entry2(module, old_uniq, old_index, - uniq, index, arity); + funp->entry.fun = erts_put_fun_entry2(module, old_uniq, + old_index, uniq, + index, arity); funp->arity = arity; hp = factory->hp; @@ -5022,11 +4850,8 @@ dec_term_atom_common: ctx->u.dc.ep = ep; ctx->u.dc.next = next; ctx->u.dc.factory.hp = hp; - if (!WSTACK_ISEMPTY(flat_maps)) { - WSTACK_SAVE(flat_maps, &ctx->u.dc.flat_maps); - } - if (!PSTACK_IS_EMPTY(hamt_array)) { - PSTACK_SAVE(hamt_array, &ctx->u.dc.hamt_array); + if (!PSTACK_IS_EMPTY(map_array)) { + PSTACK_SAVE(map_array, &ctx->u.dc.map_array); } ctx->reds = 0; return NULL; @@ -5043,36 +4868,45 @@ dec_term_atom_common: factory->hp = hp; /* * From here on factory may produce (more) heap fragments + * and we don't use local variable 'hp' anymore. */ - if (!PSTACK_IS_EMPTY(hamt_array)) { - do { - struct dec_term_hamt* hamt = PSTACK_TOP(hamt_array); - - *hamt->objp = erts_hashmap_from_array(factory, - hamt->leaf_array, - hamt->size, - 1); - if (is_non_value(*hamt->objp)) - goto error_hamt; - - (void) PSTACK_POP(hamt_array); - } while (!PSTACK_IS_EMPTY(hamt_array)); - } - - /* Iterate through all the (flat)maps and check for validity and sort keys - * - done here for when we know it is complete. + /* + * Iterate through all the maps and for + * + hashmaps: hash keys and generate all inner hamt nodes + * + flatmaps: check for duplicate keys and sort keys if needed + * + * We do this at the end because the size of the preallocated heap is only + * guaranteed to include everything except hamt nodes. If unlucky with hash + * collisions the factory may need to create new heap fragments. + * + * As maps may include each other as keys, it's important the iteration + * below is done bottom-up. Sub maps are completed before potential + * container maps. */ + if (!PSTACK_IS_EMPTY(map_array)) { + do { + struct dec_term_map* map = PSTACK_TOP(map_array); + + if (map->objp) { + *map->objp = erts_hashmap_from_array(factory, + map->u.leaf_array, + map->size, + 1); + if (is_non_value(*map->objp)) + goto error_map_fixup; + } + else { + if (!erts_validate_and_sort_flatmap(map->u.flatmap)) + goto error_map_fixup; + } - while(!WSTACK_ISEMPTY(flat_maps)) { - next = (Eterm *)WSTACK_POP(flat_maps); - if (!erts_validate_and_sort_flatmap((flatmap_t*)next)) - goto error; + (void) PSTACK_POP(map_array); + } while (!PSTACK_IS_EMPTY(map_array)); } - /* Now that no more errors can occur, the stacks can be destroyed safely. */ - PSTACK_DESTROY(hamt_array); - WSTACK_DESTROY(flat_maps); + /* Now that no more errors can occur, the stack can be destroyed safely. */ + PSTACK_DESTROY(map_array); ASSERT((Eterm*)*dbg_resultp != NULL); @@ -5096,15 +4930,13 @@ error: } else ASSERT(!factory->hp || factory->hp == hp); -error_hamt: +error_map_fixup: erts_factory_undo(factory); - PSTACK_DESTROY(hamt_array); + PSTACK_DESTROY(map_array); if (ctx) { ctx->state = B2TDecodeFail; ctx->reds = reds; } - WSTACK_DESTROY(flat_maps); - return NULL; } @@ -5233,7 +5065,6 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, break; case EXTERNAL_REF_DEF: case REF_DEF: - ASSERT(dflags & DFLAG_EXTENDED_REFERENCES); i = ref_no_numbers(obj); result += (1 + 2 + encode_size_struct2(acmp, ref_node_name(obj), dflags) + 4 + 4*i); @@ -5389,21 +5220,12 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, + 4 /* size */); trailing_result = 0; } - else if (dflags & DFLAG_BIT_BINARIES) { + else { result += (1 /* BIT_BINARY_EXT */ + 4 /* size */ + 1 /* trailing bitsize */); trailing_result = 1 /* trailing bits */; } - else { - /* sigh... */ - result += (1 /* SMALL_TUPLE_EXT */ - + 1 /* 2 tuple size */ - + 1 /* BINARY_EXT */ - + 4 /* binary size */); - trailing_result = (1 /* SMALL_INTEGER_EXT */ - + 1 /* bitsize */); - } csz = result - ctx->last_result; ctx->last_result = result; result += trailing_result; @@ -5414,110 +5236,54 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, /* potentially multiple elements for binary */ vlen += bin_size/MAX_SYSIOVEC_IOVLEN; ctx->extra_size += bin_size; - - if (dflags & DFLAG_PENDING_CONNECT) { - ASSERT(dflags & DFLAG_BIT_BINARIES); - ASSERT(ctx); - vlen += 2; /* for hopefull prolog and epilog */ - result += (4 /* for hopefull prolog (see below) */ - + 4); /* for hopefull epilog (see below) */ - ctx->last_result = result; - } break; } } } if (bitsize == 0) { - result += (1 /* BIT_BINARY_EXT */ - + 4 /* size */ - + bin_size); - } - else if (dflags & DFLAG_PENDING_CONNECT) { - /* This is the odd case when we have an un-aligned bit-string - during a pending connect. */ - Uint csz; - ASSERT(dflags & DFLAG_BIT_BINARIES); - ASSERT(ctx); - csz = result - ctx->last_result; - /* potentially multiple elements leading up to binary */ - vlen += (csz + MAX_SYSIOVEC_IOVLEN - 1)/MAX_SYSIOVEC_IOVLEN; - - vlen++; /* hopefull prolog */ - /* - * Size for hopefull prolog is max of - * - fallback: 1 + 1 + 1 + 4 - * - hopfull index + bit binary prolog: 4 + 1 + 4 + 1 - */ - result += 4 + 1 + 4 + 1; - /* potentially multiple elements for binary */ - vlen += bin_size/MAX_SYSIOVEC_IOVLEN + 1; - result += bin_size; - vlen++; /* hopefull epiolog */ - /* - * Size for hopefull epiolog is max of - * - fallback: 1 + 1 + 1 - * - hopfull index + bit binary epilog: 4 + 1 - */ - result += 4 + 1; - ctx->last_result = result; - } - else if (dflags & DFLAG_BIT_BINARIES) { - result += 1 + 4 + 1 + bin_size + 1; + result += (1 /* BINARY_EXT */ + + 4); /* size */ } else { - /* Sigh... */ - result += 1 + 1 + 1 + 4 + bin_size + 1 + 1 + 1; + result += (1 /* BIT_BINARY_EXT */ + + 4 /* size */ + + 1 /* bits */ + + 1); /* trailing bits */ } + result += bin_size; break; } - case FUN_DEF: - { - ErlFunThing* funp = (ErlFunThing *) fun_val(obj); - - ASSERT(dflags & DFLAG_NEW_FUN_TAGS); - result += 20+1+1+4; /* New ID + Tag */ - result += 4; /* Length field (number of free variables */ - result += encode_size_struct2(acmp, funp->creator, dflags); - result += encode_size_struct2(acmp, funp->fe->module, dflags); - result += 2 * (1+4); /* Index, Uniq */ - if (funp->num_free > 1) { - WSTACK_PUSH2(s, (UWord) (funp->env + 1), - (UWord) TERM_ARRAY_OP(funp->num_free-1)); - } - if (funp->num_free != 0) { - obj = funp->env[0]; - continue; /* big loop */ - } - break; - } + case FUN_DEF: + { + ErlFunThing *funp = (ErlFunThing *) fun_val(obj); + + if (is_local_fun(funp)) { + result += 20+1+1+4; /* New ID + Tag */ + result += 4; /* Length field (number of free variables */ + result += encode_size_struct2(acmp, funp->creator, dflags); + result += encode_size_struct2(acmp, funp->entry.fun->module, dflags); + result += 2 * (1+4); /* Index, Uniq */ + if (funp->num_free > 1) { + WSTACK_PUSH2(s, (UWord) (funp->env + 1), + (UWord) TERM_ARRAY_OP(funp->num_free-1)); + } + if (funp->num_free != 0) { + obj = funp->env[0]; + continue; /* big loop */ + } + } else { + Export* ep = funp->entry.exp; - case EXPORT_DEF: - { - Export* ep = *((Export **) (export_val(obj) + 1)); - Uint tmp_result = result; - result += 1; - result += encode_size_struct2(acmp, ep->info.mfa.module, dflags); - result += encode_size_struct2(acmp, ep->info.mfa.function, dflags); - result += encode_size_struct2(acmp, make_small(ep->info.mfa.arity), dflags); - if (dflags & DFLAG_PENDING_CONNECT) { - Uint csz; - ASSERT(ctx); + ASSERT(is_external_fun(funp) && funp->next == NULL); - /* - * Fallback is 1 + 1 + Module size + Function size, that is, - * the hopefull index + hopefull encoding is larger... - */ - ASSERT(dflags & DFLAG_EXPORT_PTR_TAG); - csz = tmp_result - ctx->last_result; - /* potentially multiple elements leading up to hopefull entry */ - vlen += (csz/MAX_SYSIOVEC_IOVLEN + 1 - + 1); /* hopefull entry */ - result += 4; /* hopefull index */ - ctx->last_result = result; + result += 1; + result += encode_size_struct2(acmp, ep->info.mfa.module, dflags); + result += encode_size_struct2(acmp, ep->info.mfa.function, dflags); + result += encode_size_struct2(acmp, make_small(ep->info.mfa.arity), dflags); } - } - break; + break; + } default: erts_exit(ERTS_ERROR_EXIT,"Internal data structure error (in encode_size_struct_int) %x\n", @@ -5540,7 +5306,6 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, obj = CAR(cons); } break; - case TERM_ARRAY_OP(1): obj = *(Eterm*)WSTACK_POP(s); break; @@ -5577,11 +5342,21 @@ static Sint decoded_size(const byte *ep, const byte* endp, int internal_tags, B2TContext* ctx) { Sint heap_size; - int terms; int atom_extra_skip; Uint n; SWord reds; + /* Keep track of the current number of sub terms remaining to be decoded. + * + * We limit the number of sub terms to 2^32-1, even on 64-bit + * machines, because a term that has many sub-terms must be truly + * ginormous and is proably a mistake. + * + * This means that a map with 2^31 or more elements cannot be decoded, + * even on a 64-bit machine. + */ + Uint32 terms; + if (ctx) { reds = ctx->reds; if (ctx->u.sc.ep) { @@ -5620,9 +5395,11 @@ init_done: if ((sz) > endp-ep) { goto error; } \ } while (0) +/* Increment the number of terms that remain to decode + * and check for the term counter wrapping around. */ #define ADDTERMS(n) \ do { \ - int before = terms; \ + Uint32 before = terms; \ terms += (n); \ if (terms < before) goto error; \ } while (0) @@ -5707,7 +5484,7 @@ init_done: case_PID: /* In case it is an external pid */ heap_size += EXTERNAL_PID_HEAP_SIZE; - terms++; + ADDTERMS(1); break; case V4_PORT_EXT: atom_extra_skip = 12; @@ -5720,7 +5497,7 @@ init_done: case_PORT: /* In case it is an external port */ heap_size += EXTERNAL_PORT_HEAP_SIZE; - terms++; + ADDTERMS(1); break; case NEWER_REFERENCE_EXT: atom_extra_skip = 4; @@ -5745,7 +5522,7 @@ init_done: #else heap_size += EXTERNAL_THING_HEAD_SIZE + id_words; #endif - terms++; + ADDTERMS(1); break; } case REFERENCE_EXT: @@ -5760,20 +5537,23 @@ init_done: CHKSIZE(4); n = get_uint32(ep); ep += 4; - ADDTERMS(n); - terms++; + CHKSIZE(n); /* Fail faster if the binary is too short. */ + /* Count terms in two operations to avoid overflow. */ + ADDTERMS(n); + ADDTERMS(1); heap_size += 2 * n; break; case SMALL_TUPLE_EXT: CHKSIZE(1); n = *ep++; - terms += n; + ADDTERMS(n); heap_size += n + 1; break; case LARGE_TUPLE_EXT: CHKSIZE(4); n = get_uint32(ep); ep += 4; + CHKSIZE(n); /* Fail faster if the binary is too short. */ ADDTERMS(n); heap_size += n + 1; break; @@ -5781,19 +5561,25 @@ init_done: CHKSIZE(4); n = get_uint32(ep); ep += 4; - ADDTERMS(2*n); if (n <= MAP_SMALL_MAP_LIMIT) { heap_size += 3 + n + 1 + n; - } else { -#if !defined(ARCH_64) - if ((n >> 30) != 0) { - /* Can't possibly fit in memory. */ - goto error; - } +#if defined(ARCH_64) + } else if ((n >> 31) != 0) { + /* Avoid overflow by limiting the number of elements in + * a map to 2^31-1 (about 2 billions). */ + goto error; +#else + } else if ((n >> 30) != 0) { + /* Can't possibly fit in memory on 32-bit machine. */ + goto error; #endif - CHKSIZE(2*n); /* Conservative size check */ + } else { + CHKSIZE(2*(Uint)n); /* Fail faster if the binary is too short. */ heap_size += HASHMAP_ESTIMATED_HEAP_SIZE(n); } + /* Count terms in two operations to avoid overflow. */ + ADDTERMS(n); + ADDTERMS(n); break; case STRING_EXT: CHKSIZE(2); @@ -5812,6 +5598,10 @@ init_done: case BINARY_EXT: CHKSIZE(4); n = get_uint32(ep); +#if defined(ARCH_32) + if (!IS_BINARY_SIZE_OK(n)) + goto error; +#endif SKIP2(n, 4); if (n <= ERL_ONHEAP_BIN_LIMIT) { heap_size += heap_bin_size(n); @@ -5823,6 +5613,10 @@ init_done: { CHKSIZE(5); n = get_uint32(ep); +#if defined(ARCH_32) + if (!IS_BINARY_SIZE_OK(n)) + goto error; +#endif SKIP2(n, 5); if (n <= ERL_ONHEAP_BIN_LIMIT) { heap_size += heap_bin_size(n) + ERL_SUB_BIN_SIZE; @@ -5832,17 +5626,18 @@ init_done: } break; case EXPORT_EXT: - terms += 3; - heap_size += 2; + ADDTERMS(3); + heap_size += ERL_FUN_SIZE; break; case NEW_FUN_EXT: { unsigned num_free; - Uint total_size; CHKSIZE(1+16+4+4); - total_size = get_uint32(ep); - CHKSIZE(total_size); + /* Ignore faulty Size field as we have bugs since OTP 23.0 + * that can encode it too large if fun contains EXPORT_EXT or + * BIT_BINARY_EXT hopefully encoded for pending connection. + */ ep += 1+16+4+4; CHKSIZE(4); num_free = get_uint32(ep); @@ -5850,7 +5645,7 @@ init_done: if (num_free > MAX_ARG) { goto error; } - terms += 4 + num_free; + ADDTERMS(4 + num_free); heap_size += ERL_FUN_SIZE + num_free; break; } @@ -5888,7 +5683,7 @@ init_done: } terms--; - if (ctx && --reds <= 0 && terms > 0) { + if (ctx && --reds <= 0 && terms != 0) { ctx->u.sc.heap_size = heap_size; ctx->u.sc.terms = terms; ctx->u.sc.ep = ep; @@ -5896,9 +5691,8 @@ init_done: ctx->reds = 0; return 0; } - }while (terms > 0); + } while (terms != 0); - /* 'terms' may be non-zero if it has wrapped around */ if (terms == 0) { if (ctx) { ctx->state = B2TDecodeInit; @@ -5977,6 +5771,29 @@ transcode_decode_state_destroy(ErtsTranscodeDecodeState *state) erts_free(ERTS_ALC_T_TMP, state->hp); } +static Uint32 +calc_iovec_fun_size(SysIOVec* iov, Uint32 fun_high_ix, byte* size_p) +{ + Sint32 ix; + Uint32 fun_size = 0; + + ASSERT(size_p[-1] == NEW_FUN_EXT); + + /* + * Search backwards for start of fun while adding up its total byte size. + */ + for (ix = fun_high_ix; ix >= 0; ix--) { + fun_size += iov[ix].iov_len; + + if (ErtsInArea(size_p, iov[ix].iov_base, iov[ix].iov_len)) { + fun_size -= (size_p - (byte*)iov[ix].iov_base); + break; + } + } + ERTS_ASSERT(ix >= 0); + return fun_size; +} + static Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, DistEntry* dep, @@ -5986,10 +5803,8 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, ErlIOVec* eiov = ob->eiov; SysIOVec* iov = eiov->iov; byte *hdr; - Uint64 hopefull_flags; - Uint32 hopefull_ix, payload_ix; + Uint32 payload_ix; Sint start_r, r; - Uint new_len; byte *ep; if (reds < 0) @@ -6055,11 +5870,9 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, return reds; } + /* Currently, the hopefull flags and IX are not used. */ hdr++; - hopefull_flags = get_int64(hdr); - hdr += 8; - hopefull_ix = get_int32(hdr); if ((~dflags & DFLAG_SPAWN) && ep[0] == SMALL_TUPLE_EXT @@ -6222,8 +6035,7 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, #endif /* Send unlink ack to local sender... */ - erts_proc_sig_send_dist_unlink_ack(NULL, dep, - dep->connection_id, + erts_proc_sig_send_dist_unlink_ack(dep, dep->connection_id, remote, local, id); transcode_decode_state_destroy(&tds); @@ -6237,167 +6049,6 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, start_r = r = reds*ERTS_TRANSCODE_REDS_FACT; - if (~dflags & hopefull_flags) { - - while (hopefull_ix != ERTS_NO_HIX) { - Uint32 new_hopefull_ix; - - if (r <= 0) { /* yield... */ - /* save current hopefull_ix... */ - ep = (byte *) iov[1].iov_base; - ep += 5; - put_int32(hopefull_ix, ep); - return -1; - } - - /* Read next hopefull index */ - ep = (byte *) iov[hopefull_ix].iov_base; - ep -= 4; - new_hopefull_ix = get_int32(ep); - ASSERT(new_hopefull_ix == ERTS_NO_HIX - || (hopefull_ix < new_hopefull_ix - && new_hopefull_ix < eiov->vsize)); - - ep = (byte *) iov[hopefull_ix].iov_base; - switch (*ep) { - - case EXPORT_EXT: { - byte *start_ep, *end_ep; - Eterm module, function; - if (!(hopefull_flags & DFLAG_EXPORT_PTR_TAG)) - break; - /* Read original encoding... */ - ep++; - start_ep = ep; - ep = (byte*)dec_atom(NULL, ep, &module); - ASSERT(ep && is_atom(module)); - ep = (byte*)dec_atom(NULL, ep, &function); - ASSERT(ep && is_atom(function)); - end_ep = ep; - ASSERT(*ep == SMALL_INTEGER_EXT - || *ep == INTEGER_EXT - || *ep == SMALL_BIG_EXT - || *ep == LARGE_BIG_EXT); - - /* - * module and function atoms are encoded - * between start_ep and end_ep. Prepend a - * 2-tuple tag before the atoms and - * remove arity at end. - */ - - /* write fallback */ - - ep = start_ep; - ep--; - put_int8(2, ep); - ep--; - *ep = SMALL_TUPLE_EXT; - - iov[hopefull_ix].iov_base = ep; - - /* Update iov sizes... */ - new_len = end_ep - ep; - eiov->size -= iov[hopefull_ix].iov_len; - eiov->size += new_len; - iov[hopefull_ix].iov_len = new_len; - r--; - break; - } - - case BIT_BINARY_EXT: { - Uint bin_sz; - byte bitsize, epilog_byte; - ASSERT(hopefull_ix != ERTS_NO_HIX); - if (!(hopefull_flags & DFLAG_BIT_BINARIES)) { - /* skip to epilog... */ - hopefull_ix = new_hopefull_ix; - ep = (byte *) iov[hopefull_ix].iov_base; - ep -= 4; - new_hopefull_ix = get_int32(ep); - ASSERT(new_hopefull_ix == ERTS_NO_HIX - || (hopefull_ix < new_hopefull_ix - && new_hopefull_ix < eiov->vsize)); - break; - } - - /* read original encoded prolog... */ - ep++; - bin_sz = get_uint32(ep); - ep += 4; - bitsize = *ep++; - - /* write fallback prolog... */ - iov[hopefull_ix].iov_base = &((byte*)iov[hopefull_ix].iov_base)[-4]; - ep = (byte *) iov[hopefull_ix].iov_base; - - *ep++ = SMALL_TUPLE_EXT; - *ep++ = 2; - *ep++ = BINARY_EXT; - put_int32(bin_sz, ep); - ep += 4; - - /* Update iov sizes... */ - new_len = ep - (byte *) iov[hopefull_ix].iov_base; - eiov->size -= iov[hopefull_ix].iov_len; - eiov->size += new_len; - iov[hopefull_ix].iov_len = new_len; - r--; -#ifdef DEBUG - /* - * The binary data between the prolog and the - * epilog should be of size 'bin_sz - 1' and - * exists in the iov elements between prolog - * and epilog... - */ - { - Uint ix, debug_bin_sz = 0; - for (ix = hopefull_ix+1; ix < new_hopefull_ix; ix++) - debug_bin_sz += iov[ix].iov_len; - ASSERT(debug_bin_sz == bin_sz - 1); - } -#endif - /* jump to epilog... */ - hopefull_ix = new_hopefull_ix; - ep = (byte *) iov[hopefull_ix].iov_base; - - /* read original encoded epilog... */ - epilog_byte = *ep; - - ASSERT(1 == iov[hopefull_ix].iov_len); - - iov[hopefull_ix].iov_base = &((byte*)iov[hopefull_ix].iov_base)[-4]; - ep = (byte *) iov[hopefull_ix].iov_base; - new_hopefull_ix = get_int32(ep); - ASSERT(new_hopefull_ix == ERTS_NO_HIX - || (hopefull_ix < new_hopefull_ix - && new_hopefull_ix < eiov->vsize)); - - /* write fallback epilog... */ - - *ep++ = epilog_byte; - *ep++ = SMALL_INTEGER_EXT; - *ep++ = bitsize; - - /* Update iov sizes... */ - new_len = ep - (byte *) iov[hopefull_ix].iov_base; - eiov->size -= iov[hopefull_ix].iov_len; - eiov->size += new_len; - iov[hopefull_ix].iov_len = new_len; - r--; - break; - } - - default: - ERTS_INTERNAL_ERROR("Unexpected external tag"); - break; - } - - hopefull_ix = new_hopefull_ix; - r--; - } - } - /* * Replace hopefull data header with actual header... */ |