diff options
Diffstat (limited to 'erts/emulator/beam/erl_map.c')
-rw-r--r-- | erts/emulator/beam/erl_map.c | 173 |
1 files changed, 113 insertions, 60 deletions
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 8a336765f9..337567406b 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2014-2022. All Rights Reserved. + * Copyright Ericsson AB 2014-2023. 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. @@ -151,6 +151,9 @@ void erts_init_map(void) { BIF_RETTYPE map_size_1(BIF_ALIST_1) { Sint size = erts_map_size(BIF_ARG_1); + + /* NOTE: The JIT has its own implementation of this BIF. */ + if (size < 0) { BIF_P->fvalue = BIF_ARG_1; BIF_ERROR(BIF_P, BADMAP); @@ -267,6 +270,7 @@ BIF_RETTYPE maps_get_2(BIF_ALIST_2) { } BIF_RETTYPE map_get_2(BIF_ALIST_2) { + /* NOTE: The JIT has its own implementation of this BIF. */ BIF_RET(maps_get_2(BIF_CALL_ARGS)); } @@ -433,7 +437,9 @@ static Eterm flatmap_from_validated_list(Process *p, Eterm list, Eterm fill_valu idx = size; - while(idx > 0 && (c = CMP_TERM(key,ks[idx-1])) < 0) { idx--; } + while(idx > 0 && (c = erts_cmp_flatmap_keys(key,ks[idx-1])) < 0) { + idx--; + } if (c == 0) { /* last compare was equal, @@ -700,15 +706,10 @@ from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, sys_memcpy((void *) hp, (void *) vs, n * sizeof(Eterm)); - if (fmpp) { - *fmpp = fmp; - return THE_NON_VALUE; - } - return make_flatmap(fmp); + *fmpp = fmp; + return THE_NON_VALUE; } else { - if (fmpp) { - *fmpp = NULL; - } + *fmpp = NULL; return erts_hashmap_from_ks_and_vs(factory, ks, vs, n); } } @@ -730,21 +731,6 @@ Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Ui return res; } -Eterm erts_map_from_sorted_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, - Uint n) -{ -#ifdef DEBUG - Uint i; - /* verify that key array contains unique and sorted keys... */ - for (i = 1; i < n; i++) { - ASSERT(CMP_TERM(ks[i-1], ks[i]) < 0); - } -#endif - - return from_ks_and_vs(factory, ks, vs, n, NULL); -} - - Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Uint n, Eterm key, Eterm value) { @@ -801,14 +787,7 @@ static Eterm hashmap_from_unsorted_array(ErtsHeapFactory* factory, Uint cx; Eterm res; - if (n == 0) { - Eterm *hp; - hp = erts_produce_heap(factory, 2, 0); - hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(0); - hp[1] = 0; - - return make_hashmap(hp); - } + ASSERT(n > 0); /* sort and compact array (remove non-unique entries) */ erts_qsort(hxns, n, sizeof(hxnode_t), hxnodecmp); @@ -1225,6 +1204,7 @@ BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) { } BIF_RETTYPE is_map_key_2(BIF_ALIST_2) { + /* NOTE: The JIT has its own implementation of this BIF. */ BIF_RET(maps_is_key_2(BIF_CALL_ARGS)); } @@ -1316,34 +1296,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); @@ -1352,7 +1329,7 @@ static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { vs2 = flatmap_get_values(mp2); while(i1 < n1 && i2 < n2) { - c = CMP_TERM(ks1[i1],ks2[i2]); + c = (ks1[i1] == ks2[i2]) ? 0 : erts_cmp_flatmap_keys(ks1[i1],ks2[i2]); if (c == 0) { /* use righthand side arguments map value, * but advance both maps */ @@ -1383,20 +1360,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 */ @@ -2201,7 +2200,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { ASSERT(n >= 0); /* copy map in order */ - while (n && ((c = CMP_TERM(*ks, key)) < 0)) { + while (n && ((c = erts_cmp_flatmap_keys(*ks, key)) < 0)) { *shp++ = *ks++; *hp++ = *vs++; n--; @@ -3200,7 +3199,7 @@ int erts_validate_and_sort_flatmap(flatmap_t* mp) for (ix = 1; ix < sz; ix++) { jx = ix; - while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + while( jx > 0 && (c = erts_cmp_flatmap_keys(ks[jx],ks[jx-1])) <= 0 ) { /* identical key -> error */ if (c == 0) return 0; @@ -3231,7 +3230,7 @@ void erts_usort_flatmap(flatmap_t* mp) for (ix = 1; ix < sz; ix++) { jx = ix; - while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + while( jx > 0 && (c = erts_cmp_flatmap_keys(ks[jx],ks[jx-1])) <= 0 ) { /* identical key -> remove it */ if (c == 0) { sys_memmove(ks+jx-1,ks+jx,(sz-ix)*sizeof(Eterm)); @@ -3656,6 +3655,60 @@ BIF_RETTYPE erts_internal_map_next_3(BIF_ALIST_3) { BIF_ERROR(BIF_P, BADARG); } + /* Handle an ordered iterator. */ + if (type == iterator && (is_list(path) || is_nil(path))) { +#ifdef DEBUG +#define ORDERED_ITER_FACTOR 200 +#else +#define ORDERED_ITER_FACTOR 32 +#endif + int orig_elems = MAX(1, ERTS_BIF_REDS_LEFT(BIF_P) / ORDERED_ITER_FACTOR); + int elems = orig_elems; + Uint needed = 4 * elems + 2; + Eterm *hp = HAlloc(BIF_P, needed); + Eterm *hp_end = hp + needed; + Eterm result = am_none; + Eterm *patch_ptr = &result; + + while (is_list(path) && elems > 0) { + Eterm *lst = list_val(path); + Eterm key = CAR(lst); + Eterm res = make_tuple(hp); + const Eterm *value = erts_maps_get(key, map); + if (!value) { + ordered_badarg: + HRelease(BIF_P, hp_end, hp); + BIF_ERROR(BIF_P, BADARG); + } + hp[0] = make_arityval(3); + hp[1] = key; + hp[2] = *value; + *patch_ptr = res; + patch_ptr = &hp[3]; + hp += 4; + path = CDR(lst); + elems--; + } + + if (is_list(path)) { + Eterm next = CONS(hp, path, map); + hp += 2; + ASSERT(hp == hp_end); + *patch_ptr = next; + BUMP_ALL_REDS(BIF_P); + ASSERT(is_tuple(result)); + BIF_RET(result); + } else if (is_nil(path)) { + HRelease(BIF_P, hp_end, hp); + *patch_ptr = am_none; + BUMP_REDS(BIF_P, ORDERED_ITER_FACTOR * (orig_elems - elems)); + ASSERT(result == am_none || is_tuple(result)); + BIF_RET(result); + } else { + goto ordered_badarg; + } + } + if (is_flatmap(map)) { Uint n; Eterm *ks,*vs, res, *hp; |