diff options
Diffstat (limited to 'erts/emulator/beam/erl_map.c')
| -rw-r--r-- | erts/emulator/beam/erl_map.c | 65 |
1 files changed, 42 insertions, 23 deletions
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 7009a7f105..e7452f4e8c 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1334,34 +1334,31 @@ BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { BIF_ERROR(BIF_P, BADMAP); } -static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { +static Eterm flatmap_merge(Process *p, Eterm map1, Eterm map2) { Eterm *hp,*thp; - Eterm tup; Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2; flatmap_t *mp1,*mp2,*mp_new; Uint n,n1,n2,i1,i2,need,unused_size=0; Sint c = 0; - mp1 = (flatmap_t*)flatmap_val(nodeA); - mp2 = (flatmap_t*)flatmap_val(nodeB); + mp1 = (flatmap_t*)flatmap_val(map1); + mp2 = (flatmap_t*)flatmap_val(map2); n1 = flatmap_get_size(mp1); n2 = flatmap_get_size(mp2); - if (n1 == 0) return nodeB; - if (n2 == 0) return nodeA; + if (n1 == 0) return map2; + if (n2 == 0) return map1; need = MAP_HEADER_FLATMAP_SZ + 1 + 2 * (n1 + n2); hp = HAlloc(p, need); - thp = hp; - tup = make_tuple(thp); - ks = hp + 1; hp += 1 + n1 + n2; mp_new = (flatmap_t*)hp; hp += MAP_HEADER_FLATMAP_SZ; vs = hp; hp += n1 + n2; + thp = hp; + ks = hp + 1; hp += 1 + n1 + n2; mp_new->thing_word = MAP_HEADER_FLATMAP; - mp_new->size = 0; - mp_new->keys = tup; + mp_new->keys = make_tuple(thp); i1 = 0; i2 = 0; ks1 = flatmap_get_keys(mp1); @@ -1401,20 +1398,42 @@ static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { i2++; } - if (unused_size) { - /* the key tuple is embedded in the heap, write a bignum to clear it. - * - * release values as normal since they are on the top of the heap - * size = n1 + n1 - unused_size - */ - - *ks = make_pos_bignum_header(unused_size - 1); - HRelease(p, vs + unused_size, vs); - } - n = n1 + n2 - unused_size; - *thp = make_arityval(n); mp_new->size = n; + *thp = make_arityval(n); + + if (unused_size ) { + Eterm* hp_release; + + if (n == n2) { + /* Reuse entire map2 */ + if (n == n1 + && erts_is_literal(mp1->keys, boxed_val(mp1->keys)) + && !erts_is_literal(mp2->keys, boxed_val(mp2->keys))) { + /* + * We want map2, but map1 has a nice literal key tuple. + * Solution: MUTATE HEAP to get both. + */ + ASSERT(eq(mp1->keys, mp2->keys)); + mp2->keys = mp1->keys; + } + HRelease(p, hp, (Eterm *)mp_new); + return map2; + } + else if (n == n1) { + /* Reuse key tuple of map1 */ + mp_new->keys = mp1->keys; + /* Release key tuple and unused values */ + hp_release = thp - unused_size; + } + else { + /* Unused values are embedded in the heap, write bignum to clear them */ + *vs = make_pos_bignum_header(unused_size - 1); + /* Release unused keys */ + hp_release = ks; + } + HRelease(p, hp, hp_release); + } /* Reshape map to a hashmap if the map exceeds the limit */ |
