Created
April 5, 2015 20:23
-
-
Save jlouis/7ed6e3c83077dd842519 to your computer and use it in GitHub Desktop.
Coverage of erl_map.c with 1000 tests run.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-: 0:Source:beam/erl_map.c | |
-: 0:Graph:obj/x86_64-unknown-linux-gnu/gcov/smp/erl_map.gcno | |
-: 0:Data:obj/x86_64-unknown-linux-gnu/gcov/smp/erl_map.gcda | |
-: 0:Runs:4 | |
-: 0:Programs:1 | |
-: 1:/* | |
-: 2: * %CopyrightBegin% | |
-: 3: * | |
-: 4: * Copyright Ericsson AB 2014. All Rights Reserved. | |
-: 5: * | |
-: 6: * The contents of this file are subject to the Erlang Public License, | |
-: 7: * Version 1.1, (the "License"); you may not use this file except in | |
-: 8: * compliance with the License. You should have received a copy of the | |
-: 9: * Erlang Public License along with this software. If not, it can be | |
-: 10: * retrieved online at http://www.erlang.org/. | |
-: 11: * | |
-: 12: * Software distributed under the License is distributed on an "AS IS" | |
-: 13: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | |
-: 14: * the License for the specific language governing rights and limitations | |
-: 15: * under the License. | |
-: 16: * | |
-: 17: * %CopyrightEnd% | |
-: 18: * | |
-: 19: * hashmaps are an adaption of Rich Hickeys Persistent HashMaps | |
-: 20: * which were an adaption of Phil Bagwells - Hash Array Mapped Tries | |
-: 21: * | |
-: 22: * Author: Björn-Egil Dahlberg | |
-: 23: */ | |
-: 24: | |
-: 25:#ifdef HAVE_CONFIG_H | |
-: 26:# include "config.h" | |
-: 27:#endif | |
-: 28: | |
-: 29:#include "sys.h" | |
-: 30:#include "erl_vm.h" | |
-: 31:#include "global.h" | |
-: 32:#include "erl_process.h" | |
-: 33:#include "error.h" | |
-: 34:#include "bif.h" | |
-: 35: | |
-: 36:#include "erl_map.h" | |
-: 37: | |
-: 38:/* BIFs | |
-: 39: * | |
-: 40: * DONE: | |
-: 41: * - erlang:is_map/1 | |
-: 42: * - erlang:map_size/1 | |
-: 43: * | |
-: 44: * - maps:find/2 | |
-: 45: * - maps:from_list/1 | |
-: 46: * - maps:get/2 | |
-: 47: * - maps:is_key/2 | |
-: 48: * - maps:keys/1 | |
-: 49: * - maps:merge/2 | |
-: 50: * - maps:new/0 | |
-: 51: * - maps:put/3 | |
-: 52: * - maps:remove/2 | |
-: 53: * - maps:to_list/1 | |
-: 54: * - maps:update/3 | |
-: 55: * - maps:values/1 | |
-: 56: * | |
-: 57: * TODO: | |
-: 58: * - maps:foldl/3 | |
-: 59: * - maps:foldr/3 | |
-: 60: * - maps:map/3 | |
-: 61: * - maps:size/1 | |
-: 62: * - maps:without/2 | |
-: 63: * | |
-: 64: * DEBUG: for sharing calculation | |
-: 65: * - erts_internal:map_to_tuple_keys/1 | |
-: 66: */ | |
-: 67: | |
-: 68:#ifndef DECL_AM | |
-: 69:#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) | |
-: 70:#endif | |
-: 71: | |
-: 72:/* for hashmap_from_list/1 */ | |
-: 73:typedef struct { | |
-: 74: Uint32 hx; | |
-: 75: Uint32 skip; | |
-: 76: Uint i; | |
-: 77: Eterm val; | |
-: 78:} hxnode_t; | |
-: 79: | |
-: 80: | |
-: 81:static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB); | |
-: 82:static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args); | |
-: 83:static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB); | |
-: 84:static Eterm hashmap_to_list(Process *p, Eterm map); | |
-: 85:static Eterm hashmap_keys(Process *p, Eterm map); | |
-: 86:static Eterm hashmap_values(Process *p, Eterm map); | |
-: 87:static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm node); | |
-: 88:static Eterm flatmap_from_validated_list(Process *p, Eterm list, Uint size); | |
-: 89:static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size); | |
-: 90:static Eterm hashmap_from_unsorted_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int reject_dupkeys); | |
-: 91:static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, int is_root); | |
-: 92:static Eterm hashmap_from_chunked_array(ErtsHeapFactory*, hxnode_t *hxns, Uint n, Uint size, int is_root); | |
-: 93:static Eterm hashmap_info(Process *p, Eterm node); | |
-: 94:static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]); | |
-: 95:static int hxnodecmp(hxnode_t* a, hxnode_t* b); | |
-: 96:static int hxnodecmpkey(hxnode_t* a, hxnode_t* b); | |
-: 97: | |
-: 98:/* erlang:map_size/1 | |
-: 99: * the corresponding instruction is implemented in: | |
-: 100: * beam/erl_bif_guard.c | |
-: 101: */ | |
-: 102: | |
#####: 103:BIF_RETTYPE map_size_1(BIF_ALIST_1) { | |
#####: 104: if (is_flatmap(BIF_ARG_1)) { | |
-: 105: Eterm *hp; | |
#####: 106: Uint hsz = 0; | |
#####: 107: flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1); | |
#####: 108: Uint n = flatmap_get_size(mp); | |
-: 109: | |
#####: 110: erts_bld_uint(NULL, &hsz, n); | |
#####: 111: hp = HAlloc(BIF_P, hsz); | |
#####: 112: BIF_RET(erts_bld_uint(&hp, NULL, n)); | |
#####: 113: } else if (is_hashmap(BIF_ARG_1)) { | |
-: 114: Eterm *head, *hp, res; | |
#####: 115: Uint size, hsz=0; | |
-: 116: | |
#####: 117: head = hashmap_val(BIF_ARG_1); | |
#####: 118: size = head[1]; | |
#####: 119: (void) erts_bld_uint(NULL, &hsz, size); | |
#####: 120: hp = HAlloc(BIF_P, hsz); | |
#####: 121: res = erts_bld_uint(&hp, NULL, size); | |
#####: 122: BIF_RET(res); | |
-: 123: } | |
-: 124: | |
#####: 125: BIF_ERROR(BIF_P, BADARG); | |
-: 126:} | |
-: 127: | |
-: 128:/* maps:to_list/1 */ | |
-: 129: | |
853583: 130:BIF_RETTYPE maps_to_list_1(BIF_ALIST_1) { | |
853583: 131: if (is_flatmap(BIF_ARG_1)) { | |
-: 132: Uint n; | |
-: 133: Eterm* hp; | |
-: 134: Eterm *ks,*vs, res, tup; | |
851565: 135: flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1); | |
-: 136: | |
851565: 137: ks = flatmap_get_keys(mp); | |
851565: 138: vs = flatmap_get_values(mp); | |
851565: 139: n = flatmap_get_size(mp); | |
851565: 140: hp = HAlloc(BIF_P, (2 + 3) * n); | |
851565: 141: res = NIL; | |
-: 142: | |
7121251: 143: while(n--) { | |
5418121: 144: tup = TUPLE2(hp, ks[n], vs[n]); hp += 3; | |
5418121: 145: res = CONS(hp, tup, res); hp += 2; | |
-: 146: } | |
-: 147: | |
851565: 148: BIF_RET(res); | |
2018: 149: } else if (is_hashmap(BIF_ARG_1)) { | |
2018: 150: return hashmap_to_list(BIF_P, BIF_ARG_1); | |
-: 151: } | |
-: 152: | |
#####: 153: BIF_ERROR(BIF_P, BADARG); | |
-: 154:} | |
-: 155: | |
-: 156:/* maps:find/2 | |
-: 157: * return value if key *matches* a key in the map | |
-: 158: */ | |
-: 159: | |
-: 160:const Eterm * | |
-: 161:#if HALFWORD_HEAP | |
-: 162:erts_maps_get_rel(Eterm key, Eterm map, Eterm *map_base) | |
-: 163:#else | |
6703: 164:erts_maps_get(Eterm key, Eterm map) | |
-: 165:#endif | |
-: 166:{ | |
-: 167: Uint32 hx; | |
6703: 168: if (is_flatmap_rel(map, map_base)) { | |
-: 169: Eterm *ks, *vs; | |
-: 170: flatmap_t *mp; | |
-: 171: Uint n, i; | |
-: 172: | |
4101: 173: mp = (flatmap_t *)flatmap_val_rel(map, map_base); | |
4101: 174: n = flatmap_get_size(mp); | |
-: 175: | |
4101: 176: if (n == 0) { | |
184: 177: return NULL; | |
-: 178: } | |
-: 179: | |
3917: 180: ks = (Eterm *)tuple_val_rel(mp->keys, map_base) + 1; | |
3917: 181: vs = flatmap_get_values(mp); | |
-: 182: | |
3917: 183: if (is_immed(key)) { | |
15482: 184: for (i = 0; i < n; i++) { | |
15241: 185: if (ks[i] == key) { | |
3230: 186: return &vs[i]; | |
-: 187: } | |
-: 188: } | |
-: 189: } | |
-: 190: | |
7764: 191: for (i = 0; i < n; i++) { | |
7451: 192: if (eq_rel(ks[i], map_base, key, NULL)) { | |
374: 193: return &vs[i]; | |
-: 194: } | |
-: 195: } | |
313: 196: return NULL; | |
-: 197: } | |
-: 198: ASSERT(is_hashmap_rel(map, map_base)); | |
2602: 199: hx = hashmap_make_hash(key); | |
-: 200: | |
2602: 201: return erts_hashmap_get_rel(hx, key, map, map_base); | |
-: 202:} | |
-: 203: | |
4358: 204:BIF_RETTYPE maps_find_2(BIF_ALIST_2) { | |
4358: 205: if (is_map(BIF_ARG_2)) { | |
-: 206: Eterm *hp, res; | |
-: 207: const Eterm *value; | |
-: 208: | |
4358: 209: value = erts_maps_get(BIF_ARG_1, BIF_ARG_2); | |
4358: 210: if (value) { | |
3861: 211: hp = HAlloc(BIF_P, 3); | |
3861: 212: res = make_tuple(hp); | |
3861: 213: *hp++ = make_arityval(2); | |
3861: 214: *hp++ = am_ok; | |
3861: 215: *hp++ = *value; | |
3861: 216: BIF_RET(res); | |
-: 217: } | |
497: 218: BIF_RET(am_error); | |
-: 219: } | |
#####: 220: BIF_ERROR(BIF_P, BADARG); | |
-: 221:} | |
-: 222: | |
-: 223:/* maps:get/2 | |
-: 224: * return value if key *matches* a key in the map | |
-: 225: * exception bad_key if none matches | |
-: 226: */ | |
-: 227: | |
1187: 228:BIF_RETTYPE maps_get_2(BIF_ALIST_2) { | |
1187: 229: if (is_map(BIF_ARG_2)) { | |
-: 230: Eterm *hp; | |
-: 231: Eterm error; | |
-: 232: const Eterm *value; | |
-: 233: char *s_error; | |
-: 234: | |
1187: 235: value = erts_maps_get(BIF_ARG_1, BIF_ARG_2); | |
1187: 236: if (value) { | |
960: 237: BIF_RET(*value); | |
-: 238: } | |
-: 239: | |
227: 240: s_error = "bad_key"; | |
227: 241: error = am_atom_put(s_error, sys_strlen(s_error)); | |
-: 242: | |
227: 243: hp = HAlloc(BIF_P, 3); | |
227: 244: BIF_P->fvalue = TUPLE2(hp, error, BIF_ARG_1); | |
227: 245: BIF_ERROR(BIF_P, EXC_ERROR_2); | |
-: 246: } | |
#####: 247: BIF_ERROR(BIF_P, BADARG); | |
-: 248:} | |
-: 249: | |
-: 250:/* maps:from_list/1 | |
-: 251: * List may be unsorted [{K,V}] | |
-: 252: */ | |
-: 253: | |
163969: 254:BIF_RETTYPE maps_from_list_1(BIF_ALIST_1) { | |
163969: 255: Eterm item = BIF_ARG_1, res, *kv; | |
163969: 256: Uint size = 0; | |
163969: 257: if (is_list(item) || is_nil(item)) { | |
-: 258: | |
-: 259: /* Calculate size and check validity */ | |
-: 260: | |
1711551: 261: while(is_list(item)) { | |
1383613: 262: res = CAR(list_val(item)); | |
1383613: 263: if (is_not_tuple(res)) | |
-: 264: goto error; | |
-: 265: | |
1383613: 266: kv = tuple_val(res); | |
1383613: 267: if (*kv != make_arityval(2)) | |
#####: 268: goto error; | |
-: 269: | |
1383613: 270: size++; | |
1383613: 271: item = CDR(list_val(item)); | |
-: 272: } | |
-: 273: | |
163969: 274: if (is_not_nil(item)) | |
#####: 275: goto error; | |
-: 276: | |
163969: 277: if (size > MAP_SMALL_MAP_LIMIT) { | |
4794: 278: BIF_RET(hashmap_from_validated_list(BIF_P, BIF_ARG_1, size)); | |
-: 279: } else { | |
159175: 280: BIF_RET(flatmap_from_validated_list(BIF_P, BIF_ARG_1, size)); | |
-: 281: } | |
-: 282: } | |
-: 283: | |
-: 284:error: | |
-: 285: | |
#####: 286: BIF_ERROR(BIF_P, BADARG); | |
-: 287:} | |
-: 288: | |
159175: 289:static Eterm flatmap_from_validated_list(Process *p, Eterm list, Uint size) { | |
159175: 290: Eterm *kv, item = list; | |
-: 291: Eterm *hp, *thp,*vs, *ks, keys, res; | |
-: 292: flatmap_t *mp; | |
159175: 293: Uint unused_size = 0; | |
159175: 294: Sint c = 0; | |
159175: 295: Sint idx = 0; | |
-: 296: | |
-: 297: | |
159175: 298: hp = HAlloc(p, 3 + 1 + (2 * size)); | |
159175: 299: thp = hp; | |
159175: 300: keys = make_tuple(hp); | |
159175: 301: *hp++ = make_arityval(size); | |
159175: 302: ks = hp; | |
159175: 303: hp += size; | |
159175: 304: mp = (flatmap_t*)hp; | |
159175: 305: res = make_flatmap(mp); | |
159175: 306: hp += MAP_HEADER_FLATMAP_SZ; | |
159175: 307: vs = hp; | |
-: 308: | |
159175: 309: mp->thing_word = MAP_HEADER_FLATMAP; | |
159175: 310: mp->size = size; /* set later, might shrink*/ | |
159175: 311: mp->keys = keys; | |
-: 312: | |
159175: 313: if (size == 0) | |
13860: 314: return res; | |
-: 315: | |
-: 316: /* first entry */ | |
145315: 317: kv = tuple_val(CAR(list_val(item))); | |
145315: 318: ks[0] = kv[1]; | |
145315: 319: vs[0] = kv[2]; | |
145315: 320: size = 1; | |
145315: 321: item = CDR(list_val(item)); | |
-: 322: | |
-: 323: /* insert sort key/value pairs */ | |
1163476: 324: while(is_list(item)) { | |
-: 325: | |
872846: 326: kv = tuple_val(CAR(list_val(item))); | |
-: 327: | |
-: 328: /* compare ks backwards | |
-: 329: * idx represent word index to be written (hole position). | |
-: 330: * We cannot copy the elements when searching since we might | |
-: 331: * have an equal key. So we search for just the index first =( | |
-: 332: * | |
-: 333: * It is perhaps faster to move the values in the first pass. | |
-: 334: * Check for uniqueness during insert phase and then have a | |
-: 335: * second phace compacting the map if duplicates are found | |
-: 336: * during insert. .. or do someother sort .. shell-sort perhaps. | |
-: 337: */ | |
-: 338: | |
872846: 339: idx = size; | |
-: 340: | |
872846: 341: while(idx > 0 && (c = CMP_TERM(kv[1],ks[idx-1])) < 0) { idx--; } | |
-: 342: | |
872846: 343: if (c == 0) { | |
-: 344: /* last compare was equal, | |
-: 345: * i.e. we have to release memory | |
-: 346: * and overwrite that key/value | |
-: 347: */ | |
189: 348: ks[idx-1] = kv[1]; | |
189: 349: vs[idx-1] = kv[2]; | |
189: 350: unused_size++; | |
-: 351: } else { | |
872657: 352: Uint i = size; | |
1948147: 353: while(i > idx) { | |
202833: 354: ks[i] = ks[i-1]; | |
202833: 355: vs[i] = vs[i-1]; | |
202833: 356: i--; | |
-: 357: } | |
872657: 358: ks[idx] = kv[1]; | |
872657: 359: vs[idx] = kv[2]; | |
872657: 360: size++; | |
-: 361: } | |
872846: 362: item = CDR(list_val(item)); | |
-: 363: } | |
-: 364: | |
145315: 365: if (unused_size) { | |
-: 366: /* the key tuple is embedded in the heap | |
-: 367: * write a bignum to clear it. | |
-: 368: */ | |
-: 369: /* release values as normal since they are on the top of the heap */ | |
-: 370: | |
77: 371: ks[size] = make_pos_bignum_header(unused_size - 1); | |
77: 372: HRelease(p, vs + size + unused_size, vs + size); | |
-: 373: } | |
-: 374: | |
145315: 375: *thp = make_arityval(size); | |
145315: 376: mp->size = size; | |
145315: 377: return res; | |
-: 378:} | |
-: 379: | |
-: 380:#define swizzle32(D,S) \ | |
-: 381: do { \ | |
-: 382: (D) = ((S) & 0x0000000f) << 28 | ((S) & 0x000000f0) << 20 \ | |
-: 383: | ((S) & 0x00000f00) << 12 | ((S) & 0x0000f000) << 4 \ | |
-: 384: | ((S) & 0x000f0000) >> 4 | ((S) & 0x00f00000) >> 12 \ | |
-: 385: | ((S) & 0x0f000000) >> 20 | ((S) & 0xf0000000) >> 28; \ | |
-: 386: } while(0) | |
-: 387: | |
-: 388:#define maskval(V,L) (((V) >> ((7 - (L))*4)) & 0xf) | |
-: 389:#define cdepth(V1,V2) (hashmap_clz((V1) ^ (V2)) >> 2) | |
-: 390: | |
4794: 391:static Eterm hashmap_from_validated_list(Process *p, Eterm list, Uint size) { | |
4794: 392: Eterm item = list; | |
-: 393: Eterm *hp; | |
-: 394: Eterm *kv, res; | |
-: 395: Eterm tmp[2]; | |
-: 396: Uint32 sw, hx; | |
4794: 397: Uint ix = 0; | |
-: 398: hxnode_t *hxns; | |
-: 399: ErtsHeapFactory factory; | |
-: 400: | |
-: 401: ASSERT(size > 0); | |
-: 402: | |
4794: 403: hp = HAlloc(p, (2 * size)); | |
-: 404: | |
-: 405: /* create tmp hx values and leaf ptrs */ | |
4794: 406: hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, size * sizeof(hxnode_t)); | |
-: 407: | |
375040: 408: while(is_list(item)) { | |
365452: 409: res = CAR(list_val(item)); | |
365452: 410: kv = tuple_val(res); | |
365452: 411: hx = hashmap_restore_hash(tmp,0,kv[1]); | |
365452: 412: swizzle32(sw,hx); | |
365452: 413: hxns[ix].hx = sw; | |
365452: 414: hxns[ix].val = CONS(hp, kv[1], kv[2]); hp += 2; | |
365452: 415: hxns[ix].skip = 1; /* will be reassigned in from_array */ | |
365452: 416: hxns[ix].i = ix; | |
365452: 417: ix++; | |
365452: 418: item = CDR(list_val(item)); | |
-: 419: } | |
-: 420: | |
4794: 421: factory.p = p; | |
4794: 422: res = hashmap_from_unsorted_array(&factory, hxns, size, 0); | |
-: 423: | |
4794: 424: erts_free(ERTS_ALC_T_TMP, (void *) hxns); | |
-: 425: ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); | |
-: 426: | |
4794: 427: if (hashmap_size(res) <= MAP_SMALL_MAP_LIMIT) { | |
20: 428: DECLARE_WSTACK(wstack); | |
-: 429: Eterm *kv, *ks, *vs; | |
-: 430: flatmap_t *mp; | |
-: 431: Eterm keys; | |
20: 432: Uint n = hashmap_size(res); | |
-: 433: | |
-: 434: /* build flat structure */ | |
20: 435: hp = HAlloc(p, 3 + 1 + (2 * n)); | |
20: 436: keys = make_tuple(hp); | |
20: 437: *hp++ = make_arityval(n); | |
20: 438: ks = hp; | |
20: 439: hp += n; | |
20: 440: mp = (flatmap_t*)hp; | |
20: 441: hp += MAP_HEADER_FLATMAP_SZ; | |
20: 442: vs = hp; | |
-: 443: | |
20: 444: mp->thing_word = MAP_HEADER_FLATMAP; | |
20: 445: mp->size = n; | |
20: 446: mp->keys = keys; | |
-: 447: | |
20: 448: hashmap_iterator_init(&wstack, res, 0); | |
-: 449: | |
614: 450: while ((kv=hashmap_iterator_next(&wstack)) != NULL) { | |
574: 451: *ks++ = CAR(kv); | |
574: 452: *vs++ = CDR(kv); | |
-: 453: } | |
-: 454: | |
-: 455: /* it cannot have multiple keys */ | |
20: 456: erts_validate_and_sort_flatmap(mp); | |
-: 457: | |
20: 458: DESTROY_WSTACK(wstack); | |
20: 459: return make_flatmap(mp); | |
-: 460: } | |
-: 461: | |
4774: 462: return res; | |
-: 463:} | |
-: 464: | |
#####: 465:Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n, | |
-: 466: int reject_dupkeys) { | |
-: 467: Uint32 sw, hx; | |
-: 468: Uint ix; | |
-: 469: hxnode_t *hxns; | |
-: 470: Eterm res; | |
-: 471: | |
-: 472: /* create tmp hx values and leaf ptrs */ | |
#####: 473: hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t)); | |
-: 474: | |
#####: 475: for (ix = 0; ix < n; ix++) { | |
#####: 476: hx = hashmap_make_hash(*leafs); | |
#####: 477: swizzle32(sw,hx); | |
#####: 478: hxns[ix].hx = sw; | |
#####: 479: hxns[ix].val = make_list(leafs); | |
#####: 480: hxns[ix].skip = 1; | |
#####: 481: hxns[ix].i = ix; | |
#####: 482: leafs += 2; | |
-: 483: } | |
-: 484: | |
#####: 485: res = hashmap_from_unsorted_array(factory, hxns, n, reject_dupkeys); | |
-: 486: | |
#####: 487: erts_free(ERTS_ALC_T_TMP, (void *) hxns); | |
-: 488: | |
#####: 489: return res; | |
-: 490:} | |
-: 491: | |
-: 492: | |
267: 493:Eterm erts_hashmap_from_ks_and_vs_extra(Process *p, Eterm *ks, Eterm *vs, Uint n, | |
-: 494: Eterm key, Eterm value) { | |
-: 495: Uint32 sw, hx; | |
-: 496: Uint i,sz; | |
-: 497: hxnode_t *hxns; | |
-: 498: ErtsHeapFactory factory; | |
-: 499: Eterm *hp, res; | |
-: 500: | |
267: 501: sz = (key == THE_NON_VALUE) ? n : (n + 1); | |
-: 502: ASSERT(sz > MAP_SMALL_MAP_LIMIT); | |
267: 503: hp = HAlloc(p, 2 * sz); | |
-: 504: | |
-: 505: /* create tmp hx values and leaf ptrs */ | |
267: 506: hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, sz * sizeof(hxnode_t)); | |
-: 507: | |
8811: 508: for(i = 0; i < n; i++) { | |
8544: 509: hx = hashmap_make_hash(ks[i]); | |
8544: 510: swizzle32(sw,hx); | |
8544: 511: hxns[i].hx = sw; | |
8544: 512: hxns[i].val = CONS(hp, ks[i], vs[i]); hp += 2; | |
8544: 513: hxns[i].skip = 1; /* will be reassigned in from_array */ | |
8544: 514: hxns[i].i = i; | |
-: 515: } | |
-: 516: | |
267: 517: if (key != THE_NON_VALUE) { | |
267: 518: hx = hashmap_make_hash(key); | |
267: 519: swizzle32(sw,hx); | |
267: 520: hxns[i].hx = sw; | |
267: 521: hxns[i].val = CONS(hp, key, value); hp += 2; | |
267: 522: hxns[i].skip = 1; | |
267: 523: hxns[i].i = i; | |
-: 524: } | |
-: 525: | |
267: 526: factory.p = p; | |
267: 527: res = hashmap_from_unsorted_array(&factory, hxns, sz, 0); | |
-: 528: | |
267: 529: erts_free(ERTS_ALC_T_TMP, (void *) hxns); | |
-: 530: ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); | |
-: 531: | |
267: 532: return res; | |
-: 533:} | |
-: 534: | |
5061: 535:static Eterm hashmap_from_unsorted_array(ErtsHeapFactory* factory, | |
-: 536: hxnode_t *hxns, Uint n, | |
-: 537: int reject_dupkeys) { | |
5061: 538: Uint jx = 0, ix = 0, lx, cx; | |
-: 539: Eterm res; | |
-: 540: | |
5061: 541: if (n == 0) { | |
-: 542: Eterm *hp; | |
#####: 543: hp = erts_produce_heap(factory, 2, 0); | |
#####: 544: hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(0); | |
#####: 545: hp[1] = 0; | |
-: 546: | |
#####: 547: return make_hashmap(hp); | |
-: 548: } | |
-: 549: | |
-: 550: /* sort and compact array (remove non-unique entries) */ | |
5061: 551: qsort(hxns, n, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp); | |
-: 552: | |
5061: 553: ix = 0, cx = 0; | |
337101: 554: while(ix < n - 1) { | |
326979: 555: if (hxns[ix].hx == hxns[ix+1].hx) { | |
-: 556: | |
-: 557: /* find region of equal hash values */ | |
39034: 558: jx = ix + 1; | |
39034: 559: while(jx < n && hxns[ix].hx == hxns[jx].hx) { jx++; } | |
-: 560: /* find all correct keys from region | |
-: 561: * (last in list but now hash sorted so we check highest id instead) */ | |
-: 562: | |
-: 563: /* resort with keys instead of hash value within region */ | |
-: 564: | |
39034: 565: qsort(&hxns[ix], jx - ix, sizeof(hxnode_t), | |
-: 566: (int (*)(const void *, const void *)) hxnodecmpkey); | |
-: 567: | |
154644: 568: while(ix < jx) { | |
76576: 569: lx = ix; | |
234842: 570: while(ix < jx && EQ(CAR(list_val(hxns[ix].val)), CAR(list_val(hxns[lx].val)))) { | |
81690: 571: if (reject_dupkeys) | |
#####: 572: return THE_NON_VALUE; | |
-: 573: | |
81690: 574: if (hxns[ix].i > hxns[lx].i) { | |
5114: 575: lx = ix; | |
-: 576: } | |
81690: 577: ix++; | |
-: 578: } | |
76576: 579: hxns[cx].hx = hxns[lx].hx; | |
76576: 580: hxns[cx].val = hxns[lx].val; | |
76576: 581: cx++; | |
-: 582: } | |
39034: 583: ix = jx; | |
39034: 584: continue; | |
-: 585: } | |
287945: 586: if (ix > cx) { | |
10370: 587: hxns[cx].hx = hxns[ix].hx; | |
10370: 588: hxns[cx].val = hxns[ix].val; | |
-: 589: } | |
287945: 590: cx++; | |
287945: 591: ix++; | |
-: 592: } | |
-: 593: | |
5061: 594: if (ix < n) { | |
4628: 595: hxns[cx].hx = hxns[ix].hx; | |
4628: 596: hxns[cx].val = hxns[ix].val; | |
4628: 597: cx++; | |
-: 598: } | |
-: 599: | |
5061: 600: if (cx > 1) { | |
-: 601: /* recursive decompose array */ | |
5061: 602: res = hashmap_from_sorted_unique_array(factory, hxns, cx, 0); | |
-: 603: } else { | |
-: 604: Eterm *hp; | |
-: 605: | |
-: 606: /* we only have one item, either because n was 1 or | |
-: 607: * because we hade multiples of the same key. | |
-: 608: * | |
-: 609: * hash value has been swizzled, need to drag it down to get the | |
-: 610: * correct slot. */ | |
-: 611: | |
#####: 612: hp = erts_produce_heap(factory, HAMT_HEAD_BITMAP_SZ(1), 0); | |
#####: 613: hp[0] = MAP_HEADER_HAMT_HEAD_BITMAP(1 << ((hxns[0].hx >> 0x1c) & 0xf)); | |
#####: 614: hp[1] = 1; | |
#####: 615: hp[2] = hxns[0].val; | |
#####: 616: res = make_hashmap(hp); | |
-: 617: } | |
-: 618: | |
5061: 619: return res; | |
-: 620:} | |
-: 621: | |
42603: 622:static Eterm hashmap_from_sorted_unique_array(ErtsHeapFactory* factory, | |
-: 623: hxnode_t *hxns, Uint n, int lvl) { | |
42603: 624: Eterm res = NIL; | |
-: 625: Uint i,ix,jx,elems; | |
-: 626: Uint32 sw, hx; | |
-: 627: Eterm val; | |
-: 628: Eterm th[2]; | |
-: 629: hxnode_t *tmp; | |
-: 630: | |
-: 631: ASSERT(lvl < 32); | |
42603: 632: ix = 0; | |
42603: 633: elems = 1; | |
449717: 634: while (ix < n - 1) { | |
364511: 635: if (hxns[ix].hx == hxns[ix+1].hx) { | |
37542: 636: jx = ix + 1; | |
37542: 637: while (jx < n && hxns[ix].hx == hxns[jx].hx) { jx++; } | |
37542: 638: tmp = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, ((jx - ix)) * sizeof(hxnode_t)); | |
-: 639: | |
112626: 640: for(i = 0; i < jx - ix; i++) { | |
75084: 641: val = hxns[i + ix].val; | |
75084: 642: hx = hashmap_restore_hash(th, lvl + 8, CAR(list_val(val))); | |
75084: 643: swizzle32(sw,hx); | |
75084: 644: tmp[i].hx = sw; | |
75084: 645: tmp[i].val = val; | |
75084: 646: tmp[i].i = i; | |
75084: 647: tmp[i].skip = 1; | |
-: 648: } | |
-: 649: | |
37542: 650: qsort(tmp, jx - ix, sizeof(hxnode_t), (int (*)(const void *, const void *)) hxnodecmp); | |
-: 651: | |
37542: 652: hxns[ix].skip = jx - ix; | |
37542: 653: hxns[ix].val = hashmap_from_sorted_unique_array(factory, tmp, jx - ix, lvl + 8); | |
37542: 654: erts_free(ERTS_ALC_T_TMP, (void *) tmp); | |
37542: 655: ix = jx; | |
37542: 656: if (ix < n) { elems++; } | |
37542: 657: continue; | |
-: 658: } | |
326969: 659: hxns[ix].skip = 1; | |
326969: 660: elems++; | |
326969: 661: ix++; | |
-: 662: } | |
-: 663: | |
42603: 664: res = hashmap_from_chunked_array(factory, hxns, elems, n, !lvl); | |
-: 665: | |
-: 666: ERTS_FACTORY_HOLE_CHECK(factory); | |
-: 667: | |
42603: 668: return res; | |
-: 669:} | |
-: 670: | |
-: 671:#define HALLOC_EXTRA 200 | |
42603: 672:static Eterm hashmap_from_chunked_array(ErtsHeapFactory *factory, hxnode_t *hxns, Uint n, | |
-: 673: Uint size, int is_root) { | |
-: 674: Uint ix, d, dn, dc, slot, elems; | |
-: 675: Uint32 v, vp, vn, hdr; | |
-: 676: Uint bp, sz; | |
42603: 677: DECLARE_ESTACK(stack); | |
42603: 678: Eterm res = NIL, *hp = NULL, *nhp; | |
-: 679: | |
-: 680: ASSERT(n > 1); | |
-: 681: | |
-: 682: /* push initial nodes on the stack, | |
-: 683: * this is the starting depth */ | |
-: 684: | |
42603: 685: ix = 0; | |
42603: 686: d = 0; | |
42603: 687: vp = hxns[ix].hx; | |
42603: 688: v = hxns[ix + hxns[ix].skip].hx; | |
-: 689: | |
-: 690: ASSERT(vp > v); | |
42603: 691: slot = maskval(vp,d); | |
-: 692: | |
95019: 693: while(slot == maskval(v,d)) { | |
9813: 694: ESTACK_PUSH(stack, 1 << slot); | |
9813: 695: d++; | |
9813: 696: slot = maskval(vp,d); | |
-: 697: } | |
-: 698: | |
42603: 699: res = hxns[ix].val; | |
-: 700: | |
42603: 701: if (hxns[ix].skip > 1) { | |
412: 702: dc = 7; | |
-: 703: /* build collision nodes */ | |
3222: 704: while (dc > d) { | |
2398: 705: hp = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA); | |
2398: 706: hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(vp,dc)); | |
2398: 707: hp[1] = res; | |
2398: 708: res = make_hashmap(hp); | |
2398: 709: dc--; | |
-: 710: } | |
-: 711: } | |
-: 712: | |
42603: 713: ESTACK_PUSH2(stack,res,1 << slot); | |
-: 714: | |
-: 715: /* all of the other nodes .. */ | |
42603: 716: elems = n - 2; /* remove first and last elements */ | |
406691: 717: while(elems--) { | |
321485: 718: hdr = ESTACK_POP(stack); | |
321485: 719: ix = ix + hxns[ix].skip; | |
-: 720: | |
-: 721: /* determine if node or subtree should be built by looking | |
-: 722: * at the next value. */ | |
-: 723: | |
321485: 724: vn = hxns[ix + hxns[ix].skip].hx; | |
321485: 725: dn = cdepth(v,vn); | |
-: 726: ASSERT(v > vn); | |
-: 727: | |
321485: 728: res = hxns[ix].val; | |
-: 729: | |
321485: 730: if (hxns[ix].skip > 1) { | |
36707: 731: int wat = (d > dn) ? d : dn; | |
36707: 732: dc = 7; | |
-: 733: /* build collision nodes */ | |
281627: 734: while (dc > wat) { | |
208213: 735: hp = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA); | |
208213: 736: hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc)); | |
208213: 737: hp[1] = res; | |
208213: 738: res = make_hashmap(hp); | |
208213: 739: dc--; | |
-: 740: } | |
-: 741: } | |
-: 742: | |
-: 743: /* next depth is higher (implies collision) */ | |
321485: 744: if (d < dn) { | |
-: 745: /* hdr is the popped one initially */ | |
300143: 746: while(d < dn) { | |
106983: 747: slot = maskval(v, d); | |
106983: 748: bp = 1 << slot; | |
106983: 749: ESTACK_PUSH(stack, hdr | bp); | |
106983: 750: d++; | |
106983: 751: hdr = 0; /* clear hdr for all other collisions */ | |
-: 752: } | |
-: 753: | |
96580: 754: slot = maskval(v, d); | |
96580: 755: bp = 1 << slot; | |
-: 756: /* no more collisions */ | |
96580: 757: ESTACK_PUSH2(stack,res,bp); | |
224905: 758: } else if (d == dn) { | |
-: 759: /* no collisions at all */ | |
130871: 760: slot = maskval(v, d); | |
130871: 761: bp = 1 << slot; | |
130871: 762: ESTACK_PUSH2(stack,res,hdr | bp); | |
-: 763: } else { | |
-: 764: /* dn < n, we have a drop and we are done | |
-: 765: * build nodes and subtree */ | |
295769: 766: while (dn != d) { | |
107701: 767: slot = maskval(v, d); | |
107701: 768: bp = 1 << slot; | |
-: 769: /* OR bitposition before sz calculation to handle | |
-: 770: * redundant collisions */ | |
107701: 771: hdr |= bp; | |
107701: 772: sz = hashmap_bitcount(hdr); | |
107701: 773: hp = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA); | |
107701: 774: nhp = hp; | |
107701: 775: *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hdr); | |
107701: 776: *hp++ = res; sz--; | |
107701: 777: while (sz--) { *hp++ = ESTACK_POP(stack); } | |
-: 778: ASSERT((hp - nhp) < 18); | |
107701: 779: res = make_hashmap(nhp); | |
-: 780: | |
-: 781: /* we need to pop the next hdr and push if we don't need it */ | |
-: 782: | |
107701: 783: hdr = ESTACK_POP(stack); | |
107701: 784: d--; | |
-: 785: } | |
94034: 786: ESTACK_PUSH2(stack,res,hdr); | |
-: 787: } | |
-: 788: | |
321485: 789: vp = v; | |
321485: 790: v = vn; | |
321485: 791: d = dn; | |
-: 792: ERTS_FACTORY_HOLE_CHECK(factory); | |
-: 793: } | |
-: 794: | |
-: 795: /* v and vp are reused from above */ | |
42603: 796: dn = cdepth(vp,v); | |
42603: 797: ix = ix + hxns[ix].skip; | |
42603: 798: res = hxns[ix].val; | |
-: 799: | |
42603: 800: if (hxns[ix].skip > 1) { | |
423: 801: dc = 7; | |
-: 802: /* build collision nodes */ | |
3344: 803: while (dc > dn) { | |
2498: 804: hp = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(1), HALLOC_EXTRA); | |
2498: 805: hp[0] = MAP_HEADER_HAMT_NODE_BITMAP(1 << maskval(v,dc)); | |
2498: 806: hp[1] = res; | |
2498: 807: res = make_hashmap(hp); | |
2498: 808: dc--; | |
-: 809: } | |
-: 810: } | |
-: 811: | |
42603: 812: hdr = ESTACK_POP(stack); | |
-: 813: /* pop remaining subtree if any */ | |
94301: 814: while (dn) { | |
9095: 815: slot = maskval(v, dn); | |
9095: 816: bp = 1 << slot; | |
-: 817: /* OR bitposition before sz calculation to handle | |
-: 818: * redundant collisions */ | |
9095: 819: hdr |= bp; | |
9095: 820: sz = hashmap_bitcount(hdr); | |
9095: 821: hp = erts_produce_heap(factory, HAMT_NODE_BITMAP_SZ(sz), HALLOC_EXTRA); | |
9095: 822: nhp = hp; | |
9095: 823: *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hdr); | |
9095: 824: *hp++ = res; sz--; | |
-: 825: | |
9095: 826: while (sz--) { *hp++ = ESTACK_POP(stack); } | |
9095: 827: res = make_hashmap(nhp); | |
9095: 828: hdr = ESTACK_POP(stack); | |
9095: 829: dn--; | |
-: 830: } | |
-: 831: | |
-: 832: /* and finally the root .. */ | |
-: 833: | |
42603: 834: slot = maskval(v, dn); | |
42603: 835: bp = 1 << slot; | |
42603: 836: hdr |= bp; | |
42603: 837: sz = hashmap_bitcount(hdr); | |
42603: 838: hp = erts_produce_heap(factory, sz + /* hdr + item */ (is_root ? 2 : 1), 0); | |
42603: 839: nhp = hp; | |
-: 840: | |
42603: 841: if (is_root) { | |
5061: 842: *hp++ = (hdr == 0xffff) ? MAP_HEADER_HAMT_HEAD_ARRAY : MAP_HEADER_HAMT_HEAD_BITMAP(hdr); | |
5061: 843: *hp++ = size; | |
-: 844: } else { | |
37542: 845: *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hdr); | |
-: 846: } | |
-: 847: | |
42603: 848: *hp++ = res; sz--; | |
42603: 849: while (sz--) { *hp++ = ESTACK_POP(stack); } | |
-: 850: | |
42603: 851: res = make_hashmap(nhp); | |
-: 852: | |
-: 853: ASSERT(ESTACK_COUNT(stack) == 0); | |
42603: 854: DESTROY_ESTACK(stack); | |
-: 855: ERTS_FACTORY_HOLE_CHECK(factory); | |
42603: 856: return res; | |
-: 857:} | |
-: 858:#undef HALLOC_EXTRA | |
-: 859: | |
46116: 860:static int hxnodecmpkey(hxnode_t *a, hxnode_t *b) { | |
46116: 861: return CMP_TERM(CAR(list_val(a->val)), CAR(list_val(b->val))); | |
-: 862:} | |
-: 863: | |
1895662: 864:static int hxnodecmp(hxnode_t *a, hxnode_t *b) { | |
1895662: 865: if (a->hx < b->hx) | |
932429: 866: return 1; | |
963233: 867: else if (a->hx == b->hx) | |
45641: 868: return 0; | |
-: 869: else | |
917592: 870: return -1; | |
-: 871:} | |
-: 872: | |
-: 873:/* maps:is_key/2 */ | |
-: 874: | |
1158: 875:BIF_RETTYPE maps_is_key_2(BIF_ALIST_2) { | |
1158: 876: if (is_map(BIF_ARG_2)) { | |
1158: 877: BIF_RET(erts_maps_get(BIF_ARG_1, BIF_ARG_2) ? am_true : am_false); | |
-: 878: } | |
#####: 879: BIF_ERROR(BIF_P, BADARG); | |
-: 880:} | |
-: 881: | |
-: 882:/* maps:keys/1 */ | |
-: 883: | |
1174: 884:BIF_RETTYPE maps_keys_1(BIF_ALIST_1) { | |
1174: 885: if (is_flatmap(BIF_ARG_1)) { | |
565: 886: Eterm *hp, *ks, res = NIL; | |
-: 887: flatmap_t *mp; | |
-: 888: Uint n; | |
-: 889: | |
565: 890: mp = (flatmap_t*)flatmap_val(BIF_ARG_1); | |
565: 891: n = flatmap_get_size(mp); | |
-: 892: | |
565: 893: if (n == 0) | |
50: 894: BIF_RET(res); | |
-: 895: | |
515: 896: hp = HAlloc(BIF_P, (2 * n)); | |
515: 897: ks = flatmap_get_keys(mp); | |
-: 898: | |
6930: 899: while(n--) { | |
5900: 900: res = CONS(hp, ks[n], res); hp += 2; | |
-: 901: } | |
-: 902: | |
515: 903: BIF_RET(res); | |
609: 904: } else if (is_hashmap(BIF_ARG_1)) { | |
609: 905: BIF_RET(hashmap_keys(BIF_P, BIF_ARG_1)); | |
-: 906: } | |
#####: 907: BIF_ERROR(BIF_P, BADARG); | |
-: 908:} | |
-: 909:/* maps:merge/2 */ | |
-: 910: | |
74: 911:BIF_RETTYPE maps_merge_2(BIF_ALIST_2) { | |
74: 912: if (is_flatmap(BIF_ARG_1)) { | |
74: 913: if (is_flatmap(BIF_ARG_2)) { | |
74: 914: BIF_RET(flatmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2)); | |
#####: 915: } else if (is_hashmap(BIF_ARG_2)) { | |
-: 916: /* Will always become a tree */ | |
#####: 917: BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_1, BIF_ARG_2, 0)); | |
-: 918: } | |
#####: 919: } else if (is_hashmap(BIF_ARG_1)) { | |
#####: 920: if (is_hashmap(BIF_ARG_2)) { | |
#####: 921: BIF_RET(hashmap_merge(BIF_P, BIF_ARG_1, BIF_ARG_2)); | |
#####: 922: } else if (is_flatmap(BIF_ARG_2)) { | |
-: 923: /* Will always become a tree */ | |
#####: 924: BIF_RET(map_merge_mixed(BIF_P, BIF_ARG_2, BIF_ARG_1, 1)); | |
-: 925: } | |
-: 926: } | |
#####: 927: BIF_ERROR(BIF_P, BADARG); | |
-: 928:} | |
-: 929: | |
74: 930:static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { | |
-: 931: Eterm *hp,*thp; | |
-: 932: Eterm tup; | |
-: 933: Eterm *ks,*vs,*ks1,*vs1,*ks2,*vs2; | |
-: 934: flatmap_t *mp1,*mp2,*mp_new; | |
74: 935: Uint n,n1,n2,i1,i2,need,unused_size=0; | |
74: 936: Sint c = 0; | |
-: 937: | |
74: 938: mp1 = (flatmap_t*)flatmap_val(nodeA); | |
74: 939: mp2 = (flatmap_t*)flatmap_val(nodeB); | |
74: 940: n1 = flatmap_get_size(mp1); | |
74: 941: n2 = flatmap_get_size(mp2); | |
-: 942: | |
74: 943: need = MAP_HEADER_FLATMAP_SZ + 1 + 2 * (n1 + n2); | |
-: 944: | |
74: 945: hp = HAlloc(p, need); | |
74: 946: thp = hp; | |
74: 947: tup = make_tuple(thp); | |
74: 948: ks = hp + 1; hp += 1 + n1 + n2; | |
74: 949: mp_new = (flatmap_t*)hp; hp += MAP_HEADER_FLATMAP_SZ; | |
74: 950: vs = hp; hp += n1 + n2; | |
-: 951: | |
74: 952: mp_new->thing_word = MAP_HEADER_FLATMAP; | |
74: 953: mp_new->size = 0; | |
74: 954: mp_new->keys = tup; | |
-: 955: | |
74: 956: i1 = 0; i2 = 0; | |
74: 957: ks1 = flatmap_get_keys(mp1); | |
74: 958: vs1 = flatmap_get_values(mp1); | |
74: 959: ks2 = flatmap_get_keys(mp2); | |
74: 960: vs2 = flatmap_get_values(mp2); | |
-: 961: | |
544: 962: while(i1 < n1 && i2 < n2) { | |
396: 963: c = CMP_TERM(ks1[i1],ks2[i2]); | |
396: 964: if (c == 0) { | |
-: 965: /* use righthand side arguments map value, | |
-: 966: * but advance both maps */ | |
164: 967: *ks++ = ks2[i2]; | |
164: 968: *vs++ = vs2[i2]; | |
164: 969: i1++, i2++, unused_size++; | |
232: 970: } else if (c < 0) { | |
#####: 971: *ks++ = ks1[i1]; | |
#####: 972: *vs++ = vs1[i1]; | |
#####: 973: i1++; | |
-: 974: } else { | |
232: 975: *ks++ = ks2[i2]; | |
232: 976: *vs++ = vs2[i2]; | |
232: 977: i2++; | |
-: 978: } | |
-: 979: } | |
-: 980: | |
-: 981: /* copy remaining */ | |
148: 982: while (i1 < n1) { | |
#####: 983: *ks++ = ks1[i1]; | |
#####: 984: *vs++ = vs1[i1]; | |
#####: 985: i1++; | |
-: 986: } | |
-: 987: | |
148: 988: while (i2 < n2) { | |
#####: 989: *ks++ = ks2[i2]; | |
#####: 990: *vs++ = vs2[i2]; | |
#####: 991: i2++; | |
-: 992: } | |
-: 993: | |
74: 994: if (unused_size) { | |
-: 995: /* the key tuple is embedded in the heap, write a bignum to clear it. | |
-: 996: * | |
-: 997: * release values as normal since they are on the top of the heap | |
-: 998: * size = n1 + n1 - unused_size | |
-: 999: */ | |
-: 1000: | |
74: 1001: *ks = make_pos_bignum_header(unused_size - 1); | |
74: 1002: HRelease(p, vs + unused_size, vs); | |
-: 1003: } | |
-: 1004: | |
74: 1005: n = n1 + n2 - unused_size; | |
74: 1006: *thp = make_arityval(n); | |
74: 1007: mp_new->size = n; | |
-: 1008: | |
-: 1009: /* Reshape map to a hashmap if the map exceeds the limit */ | |
-: 1010: | |
74: 1011: if (n > MAP_SMALL_MAP_LIMIT) { | |
-: 1012: Uint32 hx,sw; | |
-: 1013: Uint i; | |
-: 1014: Eterm res; | |
-: 1015: hxnode_t *hxns; | |
-: 1016: ErtsHeapFactory factory; | |
-: 1017: | |
#####: 1018: ks = flatmap_get_keys(mp_new); | |
#####: 1019: vs = flatmap_get_values(mp_new); | |
-: 1020: | |
#####: 1021: hp = HAlloc(p, 2 * n); | |
-: 1022: | |
#####: 1023: hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP,n * sizeof(hxnode_t)); | |
-: 1024: | |
#####: 1025: for (i = 0; i < n; i++) { | |
#####: 1026: hx = hashmap_make_hash(ks[i]); | |
#####: 1027: swizzle32(sw,hx); | |
#####: 1028: hxns[i].hx = sw; | |
#####: 1029: hxns[i].val = CONS(hp, ks[i], vs[i]); hp += 2; | |
#####: 1030: hxns[i].skip = 1; | |
#####: 1031: hxns[i].i = i; | |
-: 1032: } | |
-: 1033: | |
#####: 1034: factory.p = p; | |
#####: 1035: res = hashmap_from_unsorted_array(&factory, hxns, n, 0); | |
-: 1036: | |
#####: 1037: erts_free(ERTS_ALC_T_TMP, (void *) hxns); | |
-: 1038: ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); | |
-: 1039: | |
#####: 1040: return res; | |
-: 1041: } | |
-: 1042: | |
74: 1043: return make_flatmap(mp_new); | |
-: 1044:} | |
-: 1045: | |
#####: 1046:static Eterm map_merge_mixed(Process *p, Eterm flat, Eterm tree, int swap_args) { | |
-: 1047: Eterm *ks, *vs, *hp, res; | |
-: 1048: flatmap_t *mp; | |
-: 1049: Uint n, i; | |
-: 1050: hxnode_t *hxns; | |
-: 1051: Uint32 sw, hx; | |
-: 1052: ErtsHeapFactory factory; | |
-: 1053: | |
-: 1054: /* convert flat to tree */ | |
-: 1055: | |
-: 1056: ASSERT(is_flatmap(flat)); | |
-: 1057: ASSERT(is_hashmap(tree)); | |
-: 1058: | |
#####: 1059: mp = (flatmap_t*)flatmap_val(flat); | |
#####: 1060: n = flatmap_get_size(mp); | |
-: 1061: | |
#####: 1062: ks = flatmap_get_keys(mp); | |
#####: 1063: vs = flatmap_get_values(mp); | |
-: 1064: | |
#####: 1065: hp = HAlloc(p, 2 * n); | |
-: 1066: | |
#####: 1067: hxns = (hxnode_t *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(hxnode_t)); | |
-: 1068: | |
#####: 1069: for (i = 0; i < n; i++) { | |
#####: 1070: hx = hashmap_make_hash(ks[i]); | |
#####: 1071: swizzle32(sw,hx); | |
#####: 1072: hxns[i].hx = sw; | |
#####: 1073: hxns[i].val = CONS(hp, ks[i], vs[i]); hp += 2; | |
#####: 1074: hxns[i].skip = 1; | |
#####: 1075: hxns[i].i = i; | |
-: 1076: } | |
-: 1077: | |
#####: 1078: factory.p = p; | |
#####: 1079: res = hashmap_from_unsorted_array(&factory, hxns, n, 0); | |
-: 1080: | |
#####: 1081: erts_free(ERTS_ALC_T_TMP, (void *) hxns); | |
-: 1082: ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); | |
-: 1083: | |
#####: 1084: return swap_args ? hashmap_merge(p, tree, res) : hashmap_merge(p, res, tree); | |
-: 1085:} | |
-: 1086: | |
-: 1087:#define HALLOC_EXTRA 200 | |
-: 1088: | |
#####: 1089:static Eterm hashmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { | |
-: 1090:#define PSTACK_TYPE struct HashmapMergePStackType | |
-: 1091: struct HashmapMergePStackType { | |
-: 1092: Eterm *srcA, *srcB; | |
-: 1093: Uint32 abm, bbm, rbm; /* node bitmaps */ | |
-: 1094: int keepA; | |
-: 1095: int ix; | |
-: 1096: Eterm array[16]; | |
-: 1097: }; | |
#####: 1098: PSTACK_DECLARE(s, 4); | |
#####: 1099: struct HashmapMergePStackType* sp = PSTACK_PUSH(s); | |
-: 1100: Eterm *hp, *nhp; | |
-: 1101: Eterm hdrA, hdrB; | |
-: 1102: Eterm th[2]; | |
-: 1103: Uint32 ahx, bhx; | |
-: 1104: Uint size; /* total key-value counter */ | |
#####: 1105: int keepA = 0; | |
#####: 1106: unsigned lvl = 0; | |
#####: 1107: Eterm res = THE_NON_VALUE; | |
-: 1108: | |
-: 1109: /* | |
-: 1110: * Strategy: Do depth-first traversal of both trees (at the same time) | |
-: 1111: * and merge each pair of nodes. | |
-: 1112: */ | |
-: 1113: | |
-: 1114: { | |
#####: 1115: hashmap_head_t* a = (hashmap_head_t*) hashmap_val(nodeA); | |
#####: 1116: hashmap_head_t* b = (hashmap_head_t*) hashmap_val(nodeB); | |
#####: 1117: size = a->size + b->size; | |
-: 1118: } | |
-: 1119: | |
-: 1120:recurse: | |
-: 1121: | |
#####: 1122: if (primary_tag(nodeA) == TAG_PRIMARY_BOXED && | |
#####: 1123: primary_tag(nodeB) == TAG_PRIMARY_LIST) { | |
-: 1124: /* Avoid implementing this combination by switching places */ | |
#####: 1125: Eterm tmp = nodeA; | |
#####: 1126: nodeA = nodeB; | |
#####: 1127: nodeB = tmp; | |
#####: 1128: keepA = !keepA; | |
-: 1129: } | |
-: 1130: | |
#####: 1131: switch (primary_tag(nodeA)) { | |
-: 1132: case TAG_PRIMARY_LIST: { | |
#####: 1133: sp->srcA = list_val(nodeA); | |
#####: 1134: switch (primary_tag(nodeB)) { | |
-: 1135: case TAG_PRIMARY_LIST: { /* LEAF + LEAF */ | |
#####: 1136: sp->srcB = list_val(nodeB); | |
-: 1137: | |
#####: 1138: if (EQ(CAR(sp->srcA), CAR(sp->srcB))) { | |
#####: 1139: --size; | |
#####: 1140: res = keepA ? nodeA : nodeB; | |
-: 1141: } else { | |
#####: 1142: ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA)); | |
#####: 1143: bhx = hashmap_restore_hash(th, lvl, CAR(sp->srcB)); | |
#####: 1144: sp->abm = 1 << hashmap_index(ahx); | |
#####: 1145: sp->bbm = 1 << hashmap_index(bhx); | |
-: 1146: | |
#####: 1147: sp->srcA = &nodeA; | |
#####: 1148: sp->srcB = &nodeB; | |
-: 1149: } | |
#####: 1150: break; | |
-: 1151: } | |
-: 1152: case TAG_PRIMARY_BOXED: { /* LEAF + NODE */ | |
#####: 1153: sp->srcB = boxed_val(nodeB); | |
-: 1154: ASSERT(is_header(*sp->srcB)); | |
#####: 1155: hdrB = *sp->srcB++; | |
-: 1156: | |
#####: 1157: ahx = hashmap_restore_hash(th, lvl, CAR(sp->srcA)); | |
#####: 1158: sp->abm = 1 << hashmap_index(ahx); | |
#####: 1159: sp->srcA = &nodeA; | |
#####: 1160: switch(hdrB & _HEADER_MAP_SUBTAG_MASK) { | |
-: 1161: case HAMT_SUBTAG_HEAD_ARRAY: | |
#####: 1162: sp->srcB++; | |
#####: 1163: sp->bbm = 0xffff; | |
#####: 1164: break; | |
-: 1165: | |
#####: 1166: case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++; | |
-: 1167: case HAMT_SUBTAG_NODE_BITMAP: | |
#####: 1168: sp->bbm = MAP_HEADER_VAL(hdrB); | |
#####: 1169: break; | |
-: 1170: | |
-: 1171: default: | |
#####: 1172: erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); | |
-: 1173: break; | |
-: 1174: } | |
#####: 1175: break; | |
-: 1176: } | |
-: 1177: default: | |
#####: 1178: erl_exit(1, "bad primary tag %ld\r\n", nodeB); | |
-: 1179: } | |
#####: 1180: break; | |
-: 1181: } | |
-: 1182: case TAG_PRIMARY_BOXED: { /* NODE + NODE */ | |
#####: 1183: sp->srcA = boxed_val(nodeA); | |
#####: 1184: hdrA = *sp->srcA++; | |
-: 1185: ASSERT(is_header(hdrA)); | |
#####: 1186: switch (hdrA & _HEADER_MAP_SUBTAG_MASK) { | |
-: 1187: case HAMT_SUBTAG_HEAD_ARRAY: { | |
#####: 1188: sp->srcA++; | |
-: 1189: ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED); | |
#####: 1190: sp->abm = 0xffff; | |
#####: 1191: sp->srcB = boxed_val(nodeB); | |
#####: 1192: hdrB = *sp->srcB++; | |
-: 1193: ASSERT(is_header(hdrB)); | |
#####: 1194: switch (hdrB & _HEADER_MAP_SUBTAG_MASK) { | |
-: 1195: case HAMT_SUBTAG_HEAD_ARRAY: | |
#####: 1196: sp->srcB++; | |
#####: 1197: sp->bbm = 0xffff; | |
#####: 1198: break; | |
#####: 1199: case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++; | |
-: 1200: case HAMT_SUBTAG_NODE_BITMAP: | |
#####: 1201: sp->bbm = MAP_HEADER_VAL(hdrB); | |
#####: 1202: break; | |
-: 1203: default: | |
#####: 1204: erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); | |
-: 1205: } | |
#####: 1206: break; | |
-: 1207: } | |
#####: 1208: case HAMT_SUBTAG_HEAD_BITMAP: sp->srcA++; | |
-: 1209: case HAMT_SUBTAG_NODE_BITMAP: { | |
-: 1210: ASSERT(primary_tag(nodeB) == TAG_PRIMARY_BOXED); | |
#####: 1211: sp->abm = MAP_HEADER_VAL(hdrA); | |
#####: 1212: sp->srcB = boxed_val(nodeB); | |
#####: 1213: hdrB = *sp->srcB++; | |
-: 1214: ASSERT(is_header(hdrB)); | |
#####: 1215: switch (hdrB & _HEADER_MAP_SUBTAG_MASK) { | |
-: 1216: case HAMT_SUBTAG_HEAD_ARRAY: | |
#####: 1217: sp->srcB++; | |
#####: 1218: sp->bbm = 0xffff; | |
#####: 1219: break; | |
#####: 1220: case HAMT_SUBTAG_HEAD_BITMAP: sp->srcB++; | |
-: 1221: case HAMT_SUBTAG_NODE_BITMAP: | |
#####: 1222: sp->bbm = MAP_HEADER_VAL(hdrB); | |
#####: 1223: break; | |
-: 1224: | |
-: 1225: default: | |
#####: 1226: erl_exit(1, "bad header tag %ld\r\n", *sp->srcB & _HEADER_MAP_SUBTAG_MASK); | |
-: 1227: } | |
#####: 1228: break; | |
-: 1229: } | |
-: 1230: default: | |
#####: 1231: erl_exit(1, "bad primary tag %ld\r\n", nodeA); | |
-: 1232: } | |
#####: 1233: break; | |
-: 1234: } | |
-: 1235: default: | |
#####: 1236: erl_exit(1, "bad primary tag %ld\r\n", nodeA); | |
-: 1237: } | |
-: 1238: | |
-: 1239: for (;;) { | |
#####: 1240: if (is_value(res)) { /* We have a complete (sub-)tree or leaf */ | |
#####: 1241: if (lvl == 0) | |
#####: 1242: break; | |
-: 1243: | |
-: 1244: /* Pop from stack and continue build parent node */ | |
#####: 1245: lvl--; | |
#####: 1246: sp = PSTACK_POP(s); | |
#####: 1247: sp->array[sp->ix++] = res; | |
#####: 1248: res = THE_NON_VALUE; | |
#####: 1249: if (sp->rbm) { | |
#####: 1250: sp->srcA++; | |
#####: 1251: sp->srcB++; | |
#####: 1252: keepA = sp->keepA; | |
-: 1253: } | |
-: 1254: } else { /* Start build a node */ | |
#####: 1255: sp->ix = 0; | |
#####: 1256: sp->rbm = sp->abm | sp->bbm; | |
-: 1257: ASSERT(!(sp->rbm == 0 && lvl > 0)); | |
-: 1258: } | |
-: 1259: | |
#####: 1260: while (sp->rbm) { | |
#####: 1261: Uint32 next = sp->rbm & (sp->rbm-1); | |
#####: 1262: Uint32 bit = sp->rbm ^ next; | |
#####: 1263: sp->rbm = next; | |
#####: 1264: if (sp->abm & bit) { | |
#####: 1265: if (sp->bbm & bit) { | |
-: 1266: /* Bit clash. Push and resolve by recursive merge */ | |
#####: 1267: if (sp->rbm) { | |
#####: 1268: sp->keepA = keepA; | |
-: 1269: } | |
#####: 1270: nodeA = *sp->srcA; | |
#####: 1271: nodeB = *sp->srcB; | |
#####: 1272: lvl++; | |
#####: 1273: sp = PSTACK_PUSH(s); | |
#####: 1274: goto recurse; | |
-: 1275: } else { | |
#####: 1276: sp->array[sp->ix++] = *sp->srcA++; | |
-: 1277: } | |
-: 1278: } else { | |
-: 1279: ASSERT(sp->bbm & bit); | |
#####: 1280: sp->array[sp->ix++] = *sp->srcB++; | |
-: 1281: } | |
-: 1282: } | |
-: 1283: | |
-: 1284: ASSERT(sp->ix == hashmap_bitcount(sp->abm | sp->bbm)); | |
#####: 1285: if (lvl == 0) { | |
#####: 1286: nhp = HAllocX(p, HAMT_HEAD_BITMAP_SZ(sp->ix), HALLOC_EXTRA); | |
#####: 1287: hp = nhp; | |
#####: 1288: *hp++ = (sp->ix == 16 ? MAP_HEADER_HAMT_HEAD_ARRAY | |
#####: 1289: : MAP_HEADER_HAMT_HEAD_BITMAP(sp->abm | sp->bbm)); | |
#####: 1290: *hp++ = size; | |
-: 1291: } else { | |
#####: 1292: nhp = HAllocX(p, HAMT_NODE_BITMAP_SZ(sp->ix), HALLOC_EXTRA); | |
#####: 1293: hp = nhp; | |
#####: 1294: *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(sp->abm | sp->bbm); | |
-: 1295: } | |
#####: 1296: memcpy(hp, sp->array, sp->ix * sizeof(Eterm)); | |
#####: 1297: res = make_boxed(nhp); | |
#####: 1298: } | |
#####: 1299: PSTACK_DESTROY(s); | |
#####: 1300: return res; | |
-: 1301:} | |
-: 1302: | |
#####: 1303:static int hash_cmp(Uint32 ha, Uint32 hb) | |
-: 1304:{ | |
-: 1305: int i; | |
#####: 1306: for (i=0; i<8; i++) { | |
#####: 1307: int cmp = (int)(ha & 0xF) - (int)(hb & 0xF); | |
#####: 1308: if (cmp) | |
#####: 1309: return cmp; | |
#####: 1310: ha >>= 4; | |
#####: 1311: hb >>= 4; | |
-: 1312: } | |
#####: 1313: return 0; | |
-: 1314:} | |
-: 1315: | |
#####: 1316:int hashmap_key_hash_cmp(Eterm* ap, Eterm* bp) | |
-: 1317:{ | |
-: 1318: Eterm th[2]; | |
#####: 1319: unsigned lvl = 0; | |
-: 1320: | |
#####: 1321: if (ap && bp) { | |
-: 1322: ASSERT(CMP_TERM(CAR(ap), CAR(bp)) != 0); | |
-: 1323: for (;;) { | |
#####: 1324: Uint32 ha = hashmap_restore_hash(th, lvl, CAR(ap)); | |
#####: 1325: Uint32 hb = hashmap_restore_hash(th, lvl, CAR(bp)); | |
#####: 1326: int cmp = hash_cmp(ha, hb); | |
#####: 1327: if (cmp) | |
#####: 1328: return cmp; | |
#####: 1329: lvl += 8; | |
#####: 1330: } | |
-: 1331: } | |
#####: 1332: return ap ? -1 : 1; | |
-: 1333:} | |
-: 1334: | |
-: 1335:/* maps:new/0 */ | |
-: 1336: | |
#####: 1337:BIF_RETTYPE maps_new_0(BIF_ALIST_0) { | |
-: 1338: Eterm* hp; | |
-: 1339: Eterm tup; | |
-: 1340: flatmap_t *mp; | |
-: 1341: | |
#####: 1342: hp = HAlloc(BIF_P, (MAP_HEADER_FLATMAP_SZ + 1)); | |
#####: 1343: tup = make_tuple(hp); | |
#####: 1344: *hp++ = make_arityval(0); | |
-: 1345: | |
#####: 1346: mp = (flatmap_t*)hp; | |
#####: 1347: mp->thing_word = MAP_HEADER_FLATMAP; | |
#####: 1348: mp->size = 0; | |
#####: 1349: mp->keys = tup; | |
-: 1350: | |
#####: 1351: BIF_RET(make_flatmap(mp)); | |
-: 1352:} | |
-: 1353: | |
-: 1354:/* maps:put/3 */ | |
-: 1355: | |
32216: 1356:BIF_RETTYPE maps_put_3(BIF_ALIST_3) { | |
32216: 1357: if (is_map(BIF_ARG_3)) { | |
32216: 1358: BIF_RET(erts_maps_put(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3)); | |
-: 1359: } | |
#####: 1360: BIF_ERROR(BIF_P, BADARG); | |
-: 1361:} | |
-: 1362: | |
-: 1363:/* maps:remove/3 */ | |
-: 1364: | |
808: 1365:int erts_maps_remove(Process *p, Eterm key, Eterm map, Eterm *res) { | |
-: 1366: Uint32 hx; | |
808: 1367: if (is_flatmap(map)) { | |
-: 1368: Sint n; | |
-: 1369: Uint need; | |
-: 1370: Eterm *hp_start; | |
-: 1371: Eterm *thp, *mhp; | |
-: 1372: Eterm *ks, *vs, tup; | |
375: 1373: flatmap_t *mp = (flatmap_t*)flatmap_val(map); | |
-: 1374: | |
375: 1375: n = flatmap_get_size(mp); | |
-: 1376: | |
375: 1377: if (n == 0) { | |
34: 1378: *res = map; | |
34: 1379: return 1; | |
-: 1380: } | |
-: 1381: | |
341: 1382: ks = flatmap_get_keys(mp); | |
341: 1383: vs = flatmap_get_values(mp); | |
-: 1384: | |
-: 1385: /* Assume key exists. | |
-: 1386: * Release allocated if it didn't. | |
-: 1387: * Allocate key tuple first. | |
-: 1388: */ | |
-: 1389: | |
341: 1390: need = n + 1 - 1 + 3 + n - 1; /* tuple - 1 + map - 1 */ | |
341: 1391: hp_start = HAlloc(p, need); | |
341: 1392: thp = hp_start; | |
341: 1393: mhp = thp + n; /* offset with tuple heap size */ | |
-: 1394: | |
341: 1395: tup = make_tuple(thp); | |
341: 1396: *thp++ = make_arityval(n - 1); | |
-: 1397: | |
341: 1398: *res = make_flatmap(mhp); | |
341: 1399: *mhp++ = MAP_HEADER_FLATMAP; | |
341: 1400: *mhp++ = n - 1; | |
341: 1401: *mhp++ = tup; | |
-: 1402: | |
341: 1403: if (is_immed(key)) { | |
-: 1404: while (1) { | |
1399: 1405: if (*ks == key) { | |
205: 1406: goto found_key; | |
1194: 1407: } else if (--n) { | |
1153: 1408: *mhp++ = *vs++; | |
1153: 1409: *thp++ = *ks++; | |
-: 1410: } else | |
41: 1411: break; | |
1153: 1412: } | |
-: 1413: } else { | |
-: 1414: while(1) { | |
1058: 1415: if (EQ(*ks, key)) { | |
-: 1416: goto found_key; | |
979: 1417: } else if (--n) { | |
963: 1418: *mhp++ = *vs++; | |
963: 1419: *thp++ = *ks++; | |
-: 1420: } else | |
16: 1421: break; | |
963: 1422: } | |
-: 1423: } | |
-: 1424: | |
-: 1425: /* Not found, remove allocated memory | |
-: 1426: * and return previous map. | |
-: 1427: */ | |
57: 1428: HRelease(p, hp_start + need, hp_start); | |
-: 1429: | |
57: 1430: *res = map; | |
57: 1431: return 1; | |
-: 1432: | |
-: 1433:found_key: | |
-: 1434: /* Copy rest of keys and values */ | |
284: 1435: if (--n) { | |
220: 1436: sys_memcpy(mhp, vs+1, n*sizeof(Eterm)); | |
220: 1437: sys_memcpy(thp, ks+1, n*sizeof(Eterm)); | |
-: 1438: } | |
284: 1439: return 1; | |
-: 1440: } | |
-: 1441: ASSERT(is_hashmap(map)); | |
433: 1442: hx = hashmap_make_hash(key); | |
433: 1443: *res = hashmap_delete(p, hx, key, map); | |
433: 1444: return 1; | |
-: 1445:} | |
-: 1446: | |
808: 1447:BIF_RETTYPE maps_remove_2(BIF_ALIST_2) { | |
808: 1448: if (is_map(BIF_ARG_2)) { | |
-: 1449: Eterm res; | |
808: 1450: if (erts_maps_remove(BIF_P, BIF_ARG_1, BIF_ARG_2, &res)) { | |
808: 1451: BIF_RET(res); | |
-: 1452: } | |
-: 1453: } | |
#####: 1454: BIF_ERROR(BIF_P, BADARG); | |
-: 1455:} | |
-: 1456: | |
2324: 1457:int erts_maps_update(Process *p, Eterm key, Eterm value, Eterm map, Eterm *res) { | |
-: 1458: Uint32 hx; | |
2324: 1459: if (is_flatmap(map)) { | |
-: 1460: Sint n,i; | |
-: 1461: Eterm* hp,*shp; | |
-: 1462: Eterm *ks,*vs; | |
1069: 1463: flatmap_t *mp = (flatmap_t*)flatmap_val(map); | |
-: 1464: | |
1069: 1465: if ((n = flatmap_get_size(mp)) == 0) { | |
91: 1466: return 0; | |
-: 1467: } | |
-: 1468: | |
978: 1469: ks = flatmap_get_keys(mp); | |
978: 1470: vs = flatmap_get_values(mp); | |
-: 1471: | |
-: 1472: /* only allocate for values, | |
-: 1473: * assume key-tuple will be intact | |
-: 1474: */ | |
-: 1475: | |
978: 1476: hp = HAlloc(p, MAP_HEADER_FLATMAP_SZ + n); | |
978: 1477: shp = hp; | |
978: 1478: *hp++ = MAP_HEADER_FLATMAP; | |
978: 1479: *hp++ = n; | |
978: 1480: *hp++ = mp->keys; | |
-: 1481: | |
978: 1482: if (is_immed(key)) { | |
4714: 1483: for( i = 0; i < n; i ++) { | |
4590: 1484: if (ks[i] == key) { | |
611: 1485: goto found_key; | |
-: 1486: } else { | |
3979: 1487: *hp++ = *vs++; | |
-: 1488: } | |
-: 1489: } | |
-: 1490: } else { | |
2845: 1491: for( i = 0; i < n; i ++) { | |
2810: 1492: if (EQ(ks[i], key)) { | |
-: 1493: goto found_key; | |
-: 1494: } else { | |
2602: 1495: *hp++ = *vs++; | |
-: 1496: } | |
-: 1497: } | |
-: 1498: } | |
-: 1499: | |
159: 1500: HRelease(p, shp + MAP_HEADER_FLATMAP_SZ + n, shp); | |
159: 1501: return 0; | |
-: 1502: | |
-: 1503:found_key: | |
819: 1504: *hp++ = value; | |
819: 1505: vs++; | |
819: 1506: if (++i < n) | |
607: 1507: sys_memcpy(hp, vs, (n - i)*sizeof(Eterm)); | |
819: 1508: *res = make_flatmap(shp); | |
819: 1509: return 1; | |
-: 1510: } | |
-: 1511: | |
-: 1512: ASSERT(is_hashmap(map)); | |
1255: 1513: hx = hashmap_make_hash(key); | |
1255: 1514: *res = erts_hashmap_insert(p, hx, key, value, map, 1); | |
1255: 1515: if (is_value(*res)) | |
1044: 1516: return 1; | |
-: 1517: | |
211: 1518: return 0; | |
-: 1519:} | |
-: 1520: | |
32216: 1521:Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { | |
-: 1522: Uint32 hx; | |
-: 1523: Eterm res; | |
32216: 1524: if (is_flatmap(map)) { | |
-: 1525: Sint n,i; | |
15909: 1526: Sint c = 0; | |
-: 1527: Eterm* hp, *shp; | |
-: 1528: Eterm *ks, *vs, tup; | |
15909: 1529: flatmap_t *mp = (flatmap_t*)flatmap_val(map); | |
-: 1530: | |
15909: 1531: n = flatmap_get_size(mp); | |
-: 1532: | |
15909: 1533: if (n == 0) { | |
550: 1534: hp = HAlloc(p, MAP_HEADER_FLATMAP_SZ + 1 + 2); | |
550: 1535: tup = make_tuple(hp); | |
550: 1536: *hp++ = make_arityval(1); | |
550: 1537: *hp++ = key; | |
550: 1538: res = make_flatmap(hp); | |
550: 1539: *hp++ = MAP_HEADER_FLATMAP; | |
550: 1540: *hp++ = 1; | |
550: 1541: *hp++ = tup; | |
550: 1542: *hp++ = value; | |
-: 1543: | |
550: 1544: return res; | |
-: 1545: } | |
-: 1546: | |
15359: 1547: ks = flatmap_get_keys(mp); | |
15359: 1548: vs = flatmap_get_values(mp); | |
-: 1549: | |
-: 1550: /* only allocate for values, | |
-: 1551: * assume key-tuple will be intact | |
-: 1552: */ | |
-: 1553: | |
15359: 1554: hp = HAlloc(p, MAP_HEADER_FLATMAP_SZ + n); | |
15359: 1555: shp = hp; /* save hp, used if optimistic update fails */ | |
15359: 1556: res = make_flatmap(hp); | |
15359: 1557: *hp++ = MAP_HEADER_FLATMAP; | |
15359: 1558: *hp++ = n; | |
15359: 1559: *hp++ = mp->keys; | |
-: 1560: | |
15359: 1561: if (is_immed(key)) { | |
173931: 1562: for( i = 0; i < n; i ++) { | |
161947: 1563: if (ks[i] == key) { | |
4058: 1564: *hp++ = value; | |
4058: 1565: vs++; | |
4058: 1566: c = 1; | |
-: 1567: } else { | |
157889: 1568: *hp++ = *vs++; | |
-: 1569: } | |
-: 1570: } | |
-: 1571: } else { | |
55715: 1572: for( i = 0; i < n; i ++) { | |
52340: 1573: if (EQ(ks[i], key)) { | |
13: 1574: *hp++ = value; | |
13: 1575: vs++; | |
13: 1576: c = 1; | |
-: 1577: } else { | |
52327: 1578: *hp++ = *vs++; | |
-: 1579: } | |
-: 1580: } | |
-: 1581: } | |
-: 1582: | |
15359: 1583: if (c) | |
4071: 1584: return res; | |
-: 1585: | |
-: 1586: /* the map will grow */ | |
-: 1587: | |
11288: 1588: if (n >= MAP_SMALL_MAP_LIMIT) { | |
267: 1589: HRelease(p, shp + MAP_HEADER_FLATMAP_SZ + n, shp); | |
267: 1590: ks = flatmap_get_keys(mp); | |
267: 1591: vs = flatmap_get_values(mp); | |
-: 1592: | |
267: 1593: res = erts_hashmap_from_ks_and_vs_extra(p,ks,vs,n,key,value); | |
-: 1594: | |
267: 1595: return res; | |
-: 1596: } | |
-: 1597: | |
-: 1598: /* still a small map. need to make a new tuple, | |
-: 1599: * use old hp since it needs to be recreated anyway. */ | |
-: 1600: | |
11021: 1601: tup = make_tuple(shp); | |
11021: 1602: *shp++ = make_arityval(n+1); | |
-: 1603: | |
11021: 1604: hp = HAlloc(p, 3 + n + 1); | |
11021: 1605: res = make_flatmap(hp); | |
11021: 1606: *hp++ = MAP_HEADER_FLATMAP; | |
11021: 1607: *hp++ = n + 1; | |
11021: 1608: *hp++ = tup; | |
-: 1609: | |
11021: 1610: ks = flatmap_get_keys(mp); | |
11021: 1611: vs = flatmap_get_values(mp); | |
-: 1612: | |
-: 1613: ASSERT(n >= 0); | |
-: 1614: | |
-: 1615: /* copy map in order */ | |
102783: 1616: while (n && ((c = CMP_TERM(*ks, key)) < 0)) { | |
80741: 1617: *shp++ = *ks++; | |
80741: 1618: *hp++ = *vs++; | |
80741: 1619: n--; | |
-: 1620: } | |
-: 1621: | |
11021: 1622: *shp++ = key; | |
11021: 1623: *hp++ = value; | |
-: 1624: | |
-: 1625: ASSERT(n >= 0); | |
-: 1626: | |
100320: 1627: while(n--) { | |
78278: 1628: *shp++ = *ks++; | |
78278: 1629: *hp++ = *vs++; | |
-: 1630: } | |
-: 1631: /* we have one word remaining | |
-: 1632: * this will work out fine once we get the size word | |
-: 1633: * in the header. | |
-: 1634: */ | |
11021: 1635: *shp = make_pos_bignum_header(0); | |
11021: 1636: return res; | |
-: 1637: } | |
-: 1638: ASSERT(is_hashmap(map)); | |
-: 1639: | |
16307: 1640: hx = hashmap_make_hash(key); | |
16307: 1641: res = erts_hashmap_insert(p, hx, key, value, map, 0); | |
-: 1642: ASSERT(is_hashmap(res)); | |
-: 1643: | |
16307: 1644: return res; | |
-: 1645:} | |
-: 1646: | |
-: 1647:/* maps:update/3 */ | |
-: 1648: | |
2324: 1649:BIF_RETTYPE maps_update_3(BIF_ALIST_3) { | |
2324: 1650: if (is_map(BIF_ARG_3)) { | |
-: 1651: Eterm res; | |
2324: 1652: if (erts_maps_update(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &res)) { | |
1863: 1653: BIF_RET(res); | |
-: 1654: } | |
-: 1655: } | |
461: 1656: BIF_ERROR(BIF_P, BADARG); | |
-: 1657:} | |
-: 1658: | |
-: 1659: | |
-: 1660:/* maps:values/1 */ | |
-: 1661: | |
1151: 1662:BIF_RETTYPE maps_values_1(BIF_ALIST_1) { | |
1151: 1663: if (is_flatmap(BIF_ARG_1)) { | |
546: 1664: Eterm *hp, *vs, res = NIL; | |
-: 1665: flatmap_t *mp; | |
-: 1666: Uint n; | |
-: 1667: | |
546: 1668: mp = (flatmap_t*)flatmap_val(BIF_ARG_1); | |
546: 1669: n = flatmap_get_size(mp); | |
-: 1670: | |
546: 1671: if (n == 0) | |
45: 1672: BIF_RET(res); | |
-: 1673: | |
501: 1674: hp = HAlloc(BIF_P, (2 * n)); | |
501: 1675: vs = flatmap_get_values(mp); | |
-: 1676: | |
6607: 1677: while(n--) { | |
5605: 1678: res = CONS(hp, vs[n], res); hp += 2; | |
-: 1679: } | |
-: 1680: | |
501: 1681: BIF_RET(res); | |
605: 1682: } else if (is_hashmap(BIF_ARG_1)) { | |
605: 1683: BIF_RET(hashmap_values(BIF_P, BIF_ARG_1)); | |
-: 1684: } | |
#####: 1685: BIF_ERROR(BIF_P, BADARG); | |
-: 1686:} | |
-: 1687: | |
2018: 1688:static Eterm hashmap_to_list(Process *p, Eterm node) { | |
2018: 1689: DECLARE_WSTACK(stack); | |
-: 1690: Eterm *hp, *kv; | |
2018: 1691: Eterm res = NIL; | |
-: 1692: | |
2018: 1693: hp = HAlloc(p, hashmap_size(node) * (2 + 3)); | |
2018: 1694: hashmap_iterator_init(&stack, node, 0); | |
159369: 1695: while ((kv=hashmap_iterator_next(&stack)) != NULL) { | |
155333: 1696: Eterm tup = TUPLE2(hp, CAR(kv), CDR(kv)); | |
155333: 1697: hp += 3; | |
155333: 1698: res = CONS(hp, tup, res); | |
155333: 1699: hp += 2; | |
-: 1700: } | |
2018: 1701: DESTROY_WSTACK(stack); | |
2018: 1702: return res; | |
-: 1703:} | |
-: 1704: | |
3258: 1705:void hashmap_iterator_init(ErtsWStack* s, Eterm node, int reverse) { | |
3258: 1706: Eterm hdr = *hashmap_val(node); | |
-: 1707: Uint sz; | |
-: 1708: | |
3258: 1709: switch(hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 1710: case HAMT_SUBTAG_HEAD_ARRAY: | |
2218: 1711: sz = 16; | |
2218: 1712: break; | |
-: 1713: case HAMT_SUBTAG_HEAD_BITMAP: | |
1040: 1714: sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); | |
1040: 1715: break; | |
-: 1716: default: | |
#####: 1717: erl_exit(1, "bad header"); | |
-: 1718: } | |
-: 1719: | |
3258: 1720: WSTACK_PUSH3((*s), (UWord)THE_NON_VALUE, /* end marker */ | |
-: 1721: (UWord)(!reverse ? 0 : sz+1), | |
-: 1722: (UWord)node); | |
3258: 1723:} | |
-: 1724: | |
512497: 1725:Eterm* hashmap_iterator_next(ErtsWStack* s) { | |
-: 1726: Eterm node, *ptr, hdr; | |
-: 1727: Uint32 sz; | |
-: 1728: Uint idx; | |
-: 1729: | |
-: 1730: for (;;) { | |
-: 1731: ASSERT(!WSTACK_ISEMPTY((*s))); | |
512497: 1732: node = (Eterm) WSTACK_POP((*s)); | |
512497: 1733: if (is_non_value(node)) { | |
3258: 1734: return NULL; | |
-: 1735: } | |
509239: 1736: idx = (Uint) WSTACK_POP((*s)); | |
-: 1737: for (;;) { | |
-: 1738: ASSERT(is_boxed(node)); | |
765740: 1739: ptr = boxed_val(node); | |
765740: 1740: hdr = *ptr; | |
-: 1741: ASSERT(is_header(hdr)); | |
765740: 1742: switch(hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 1743: case HAMT_SUBTAG_HEAD_ARRAY: | |
37706: 1744: ptr++; | |
37706: 1745: sz = 16; | |
37706: 1746: break; | |
-: 1747: case HAMT_SUBTAG_HEAD_BITMAP: | |
15805: 1748: ptr++; | |
-: 1749: case HAMT_SUBTAG_NODE_BITMAP: | |
728034: 1750: sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); | |
-: 1751: ASSERT(sz < 17); | |
728034: 1752: break; | |
-: 1753: default: | |
#####: 1754: erl_exit(1, "bad header"); | |
-: 1755: } | |
-: 1756: | |
765740: 1757: idx++; | |
-: 1758: | |
765740: 1759: if (idx <= sz) { | |
505981: 1760: WSTACK_PUSH2((*s), (UWord)idx, (UWord)node); | |
-: 1761: | |
505981: 1762: if (is_list(ptr[idx])) { | |
249480: 1763: return list_val(ptr[idx]); | |
-: 1764: } | |
-: 1765: ASSERT(is_boxed(ptr[idx])); | |
256501: 1766: node = ptr[idx]; | |
256501: 1767: idx = 0; | |
-: 1768: } | |
-: 1769: else | |
259759: 1770: break; /* and pop parent node */ | |
256501: 1771: } | |
259759: 1772: } | |
-: 1773:} | |
-: 1774: | |
#####: 1775:Eterm* hashmap_iterator_prev(ErtsWStack* s) { | |
-: 1776: Eterm node, *ptr, hdr; | |
-: 1777: Uint32 sz; | |
-: 1778: Uint idx; | |
-: 1779: | |
-: 1780: for (;;) { | |
-: 1781: ASSERT(!WSTACK_ISEMPTY((*s))); | |
#####: 1782: node = (Eterm) WSTACK_POP((*s)); | |
#####: 1783: if (is_non_value(node)) { | |
#####: 1784: return NULL; | |
-: 1785: } | |
#####: 1786: idx = (Uint) WSTACK_POP((*s)); | |
-: 1787: for (;;) { | |
-: 1788: ASSERT(is_boxed(node)); | |
#####: 1789: ptr = boxed_val(node); | |
#####: 1790: hdr = *ptr; | |
-: 1791: ASSERT(is_header(hdr)); | |
#####: 1792: switch(hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 1793: case HAMT_SUBTAG_HEAD_ARRAY: | |
#####: 1794: ptr++; | |
#####: 1795: sz = 16; | |
#####: 1796: break; | |
-: 1797: case HAMT_SUBTAG_HEAD_BITMAP: | |
#####: 1798: ptr++; | |
-: 1799: case HAMT_SUBTAG_NODE_BITMAP: | |
#####: 1800: sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); | |
-: 1801: ASSERT(sz < 17); | |
#####: 1802: break; | |
-: 1803: default: | |
#####: 1804: erl_exit(1, "bad header"); | |
-: 1805: } | |
-: 1806: | |
#####: 1807: if (idx > sz) | |
#####: 1808: idx = sz; | |
-: 1809: else | |
#####: 1810: idx--; | |
-: 1811: | |
#####: 1812: if (idx >= 1) { | |
#####: 1813: WSTACK_PUSH2((*s), (UWord)idx, (UWord)node); | |
-: 1814: | |
#####: 1815: if (is_list(ptr[idx])) { | |
#####: 1816: return list_val(ptr[idx]); | |
-: 1817: } | |
-: 1818: ASSERT(is_boxed(ptr[idx])); | |
#####: 1819: node = ptr[idx]; | |
#####: 1820: idx = 17; | |
-: 1821: } | |
-: 1822: else | |
#####: 1823: break; /* and pop parent node */ | |
#####: 1824: } | |
#####: 1825: } | |
-: 1826:} | |
-: 1827: | |
-: 1828:const Eterm * | |
-: 1829:#if HALFWORD_HEAP | |
-: 1830:erts_hashmap_get_rel(Uint32 hx, Eterm key, Eterm node, Eterm *map_base) | |
-: 1831:#else | |
2602: 1832:erts_hashmap_get(Uint32 hx, Eterm key, Eterm node) | |
-: 1833:#endif | |
-: 1834:{ | |
-: 1835: Eterm *ptr, hdr, *res; | |
2602: 1836: Uint ix, lvl = 0; | |
-: 1837: Uint32 hval,bp; | |
-: 1838: DeclareTmpHeapNoproc(th,2); | |
-: 1839: UseTmpHeapNoproc(2); | |
-: 1840: | |
-: 1841: ASSERT(is_boxed(node)); | |
2602: 1842: ptr = boxed_val(node); | |
2602: 1843: hdr = *ptr; | |
-: 1844: ASSERT(is_header(hdr)); | |
-: 1845: ASSERT(is_hashmap_header_head(hdr)); | |
2602: 1846: ptr++; | |
-: 1847: | |
-: 1848: for (;;) { | |
8837: 1849: hval = MAP_HEADER_VAL(hdr); | |
8837: 1850: ix = hashmap_index(hx); | |
8837: 1851: if (hval != 0xffff) { | |
7082: 1852: bp = 1 << ix; | |
7082: 1853: if (!(bp & hval)) { | |
-: 1854: /* not occupied */ | |
200: 1855: res = NULL; | |
200: 1856: break; | |
-: 1857: } | |
6882: 1858: ix = hashmap_bitcount(hval & (bp - 1)); | |
-: 1859: } | |
8637: 1860: node = ptr[ix+1]; | |
-: 1861: | |
8637: 1862: if (is_list(node)) { /* LEAF NODE [K|V] */ | |
2402: 1863: ptr = list_val(node); | |
-: 1864: | |
2402: 1865: res = eq_rel(CAR(ptr), map_base, key, NULL) ? &(CDR(ptr)) : NULL; | |
2402: 1866: break; | |
-: 1867: } | |
-: 1868: | |
6235: 1869: hx = hashmap_shift_hash(th,hx,lvl,key); | |
-: 1870: | |
-: 1871: ASSERT(is_boxed(node)); | |
6235: 1872: ptr = boxed_val(node); | |
6235: 1873: hdr = *ptr; | |
-: 1874: ASSERT(is_header(hdr)); | |
-: 1875: ASSERT(!is_hashmap_header_head(hdr)); | |
6235: 1876: } | |
-: 1877: | |
-: 1878: UnUseTmpHeapNoproc(2); | |
2602: 1879: return res; | |
-: 1880:} | |
-: 1881: | |
17562: 1882:Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value, | |
-: 1883: Eterm map, int is_update) { | |
-: 1884: Uint size, upsz; | |
17562: 1885: Eterm *hp, res = THE_NON_VALUE; | |
17562: 1886: DECLARE_ESTACK(stack); | |
17562: 1887: if (erts_hashmap_insert_down(hx, key, map, &size, &upsz, &stack, is_update)) { | |
17351: 1888: hp = HAlloc(p, size); | |
17351: 1889: res = erts_hashmap_insert_up(hp, key, value, &upsz, &stack); | |
-: 1890: } | |
-: 1891: | |
17562: 1892: DESTROY_ESTACK(stack); | |
-: 1893: ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); | |
-: 1894: ERTS_HOLE_CHECK(p); | |
-: 1895: | |
17562: 1896: return res; | |
-: 1897:} | |
-: 1898: | |
-: 1899: | |
17562: 1900:int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz, | |
-: 1901: Uint *update_size, ErtsEStack *sp, int is_update) { | |
-: 1902: Eterm *ptr; | |
-: 1903: Eterm hdr, ckey; | |
-: 1904: Eterm th[2]; | |
-: 1905: Uint32 ix, cix, bp, hval, chx; | |
17562: 1906: Uint slot, lvl = 0, clvl; | |
17562: 1907: Uint size = 0, n = 0; | |
-: 1908: | |
17562: 1909: *update_size = 1; | |
-: 1910: | |
-: 1911: for (;;) { | |
70165: 1912: switch(primary_tag(node)) { | |
-: 1913: case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */ | |
10777: 1914: ptr = list_val(node); | |
10777: 1915: ckey = CAR(ptr); | |
10777: 1916: if (EQ(ckey, key)) { | |
6748: 1917: *update_size = 0; | |
6748: 1918: goto unroll; | |
-: 1919: } | |
4029: 1920: if (is_update) { | |
104: 1921: return 0; | |
-: 1922: } | |
3925: 1923: goto insert_subnodes; | |
-: 1924: case TAG_PRIMARY_BOXED: | |
59388: 1925: ptr = boxed_val(node); | |
59388: 1926: hdr = *ptr; | |
-: 1927: ASSERT(is_header(hdr)); | |
-: 1928: | |
59388: 1929: switch(hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 1930: case HAMT_SUBTAG_HEAD_ARRAY: | |
10187: 1931: ix = hashmap_index(hx); | |
10187: 1932: hx = hashmap_shift_hash(th,hx,lvl,key); | |
10187: 1933: size += HAMT_HEAD_ARRAY_SZ; | |
10187: 1934: ESTACK_PUSH2(*sp, ix, node); | |
10187: 1935: node = ptr[ix+2]; | |
10187: 1936: break; | |
-: 1937: case HAMT_SUBTAG_NODE_BITMAP: | |
41826: 1938: hval = MAP_HEADER_VAL(hdr); | |
41826: 1939: ix = hashmap_index(hx); | |
41826: 1940: bp = 1 << ix; | |
41826: 1941: slot = hashmap_bitcount(hval & (bp - 1)); | |
41826: 1942: n = hashmap_bitcount(hval); | |
-: 1943: | |
41826: 1944: ESTACK_PUSH4(*sp, n, bp, slot, node); | |
-: 1945: | |
-: 1946: /* occupied */ | |
41826: 1947: if (bp & hval) { | |
35476: 1948: hx = hashmap_shift_hash(th,hx,lvl,key); | |
35476: 1949: node = ptr[slot+1]; | |
-: 1950: ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17); | |
35476: 1951: size += HAMT_NODE_BITMAP_SZ(n); | |
35476: 1952: break; | |
-: 1953: } | |
-: 1954: /* not occupied */ | |
6350: 1955: if (is_update) { | |
102: 1956: return 0; | |
-: 1957: } | |
6248: 1958: size += HAMT_NODE_BITMAP_SZ(n+1); | |
6248: 1959: goto unroll; | |
-: 1960: case HAMT_SUBTAG_HEAD_BITMAP: | |
7375: 1961: hval = MAP_HEADER_VAL(hdr); | |
7375: 1962: ix = hashmap_index(hx); | |
7375: 1963: bp = 1 << ix; | |
7375: 1964: slot = hashmap_bitcount(hval & (bp - 1)); | |
7375: 1965: n = hashmap_bitcount(hval); | |
-: 1966: | |
7375: 1967: ESTACK_PUSH4(*sp, n, bp, slot, node); | |
-: 1968: | |
-: 1969: /* occupied */ | |
7375: 1970: if (bp & hval) { | |
6940: 1971: hx = hashmap_shift_hash(th,hx,lvl,key); | |
6940: 1972: node = ptr[slot+2]; | |
-: 1973: ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18); | |
6940: 1974: size += HAMT_HEAD_BITMAP_SZ(n); | |
6940: 1975: break; | |
-: 1976: } | |
-: 1977: /* not occupied */ | |
435: 1978: if (is_update) { | |
5: 1979: return 0; | |
-: 1980: } | |
430: 1981: size += HAMT_HEAD_BITMAP_SZ(n+1); | |
430: 1982: goto unroll; | |
-: 1983: default: | |
#####: 1984: erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); | |
-: 1985: break; | |
-: 1986: } | |
52603: 1987: break; | |
-: 1988: default: | |
#####: 1989: erl_exit(1, "bad primary tag %p\r\n", node); | |
-: 1990: break; | |
-: 1991: } | |
52603: 1992: } | |
-: 1993:insert_subnodes: | |
3925: 1994: clvl = lvl; | |
3925: 1995: chx = hashmap_restore_hash(th,clvl,ckey); | |
3925: 1996: size += HAMT_NODE_BITMAP_SZ(2); | |
3925: 1997: ix = hashmap_index(hx); | |
3925: 1998: cix = hashmap_index(chx); | |
-: 1999: | |
16889: 2000: while (cix == ix) { | |
9039: 2001: ESTACK_PUSH4(*sp, 0, 1 << ix, 0, MAP_HEADER_HAMT_NODE_BITMAP(0)); | |
9039: 2002: size += HAMT_NODE_BITMAP_SZ(1); | |
9039: 2003: hx = hashmap_shift_hash(th,hx,lvl,key); | |
9039: 2004: chx = hashmap_shift_hash(th,chx,clvl,ckey); | |
9039: 2005: ix = hashmap_index(hx); | |
9039: 2006: cix = hashmap_index(chx); | |
-: 2007: } | |
3925: 2008: ESTACK_PUSH3(*sp, cix, ix, node); | |
-: 2009: | |
-: 2010:unroll: | |
17351: 2011: *sz = size + /* res cons */ 2; | |
17351: 2012: return 1; | |
-: 2013:} | |
-: 2014: | |
17351: 2015:Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value, | |
-: 2016: Uint *update_size, ErtsEStack *sp) { | |
-: 2017: Eterm node, *ptr, hdr; | |
-: 2018: Eterm res; | |
17351: 2019: Eterm *nhp = NULL; | |
-: 2020: Uint32 ix, cix, bp, hval; | |
-: 2021: Uint slot, n; | |
-: 2022: /* Needed for halfword */ | |
-: 2023: DeclareTmpHeapNoproc(fake,1); | |
-: 2024: UseTmpHeapNoproc(1); | |
-: 2025: | |
17351: 2026: res = CONS(hp, key, value); hp += 2; | |
-: 2027: | |
-: 2028: do { | |
71928: 2029: node = ESTACK_POP(*sp); | |
71928: 2030: switch(primary_tag(node)) { | |
-: 2031: case TAG_PRIMARY_LIST: | |
3925: 2032: ix = (Uint32) ESTACK_POP(*sp); | |
3925: 2033: cix = (Uint32) ESTACK_POP(*sp); | |
-: 2034: | |
3925: 2035: nhp = hp; | |
3925: 2036: *hp++ = MAP_HEADER_HAMT_NODE_BITMAP((1 << ix) | (1 << cix)); | |
3925: 2037: if (ix < cix) { | |
1952: 2038: *hp++ = res; | |
1952: 2039: *hp++ = node; | |
-: 2040: } else { | |
1973: 2041: *hp++ = node; | |
1973: 2042: *hp++ = res; | |
-: 2043: } | |
3925: 2044: res = make_hashmap(nhp); | |
3925: 2045: break; | |
-: 2046: case TAG_PRIMARY_HEADER: | |
-: 2047: /* subnodes, fake it */ | |
9039: 2048: *fake = node; | |
9039: 2049: node = make_boxed(fake); | |
-: 2050: case TAG_PRIMARY_BOXED: | |
68003: 2051: ptr = boxed_val(node); | |
68003: 2052: hdr = *ptr; | |
-: 2053: ASSERT(is_header(hdr)); | |
-: 2054: | |
68003: 2055: switch(hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 2056: case HAMT_SUBTAG_HEAD_ARRAY: | |
10048: 2057: slot = (Uint) ESTACK_POP(*sp); | |
10048: 2058: nhp = hp; | |
10048: 2059: n = HAMT_HEAD_ARRAY_SZ - 2; | |
10048: 2060: *hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++; | |
10048: 2061: *hp++ = (*ptr++) + *update_size; | |
10048: 2062: while(n--) { *hp++ = *ptr++; } | |
10048: 2063: nhp[slot+2] = res; | |
10048: 2064: res = make_hashmap(nhp); | |
10048: 2065: break; | |
-: 2066: case HAMT_SUBTAG_NODE_BITMAP: | |
50652: 2067: slot = (Uint) ESTACK_POP(*sp); | |
50652: 2068: bp = (Uint32) ESTACK_POP(*sp); | |
50652: 2069: n = (Uint32) ESTACK_POP(*sp); | |
50652: 2070: hval = MAP_HEADER_VAL(hdr); | |
50652: 2071: nhp = hp; | |
50652: 2072: *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval | bp); ptr++; | |
-: 2073: | |
50652: 2074: n -= slot; | |
50652: 2075: while(slot--) { *hp++ = *ptr++; } | |
50652: 2076: *hp++ = res; | |
50652: 2077: if (hval & bp) { ptr++; n--; } | |
50652: 2078: while(n--) { *hp++ = *ptr++; } | |
-: 2079: | |
50652: 2080: res = make_hashmap(nhp); | |
50652: 2081: break; | |
-: 2082: case HAMT_SUBTAG_HEAD_BITMAP: | |
7303: 2083: slot = (Uint) ESTACK_POP(*sp); | |
7303: 2084: bp = (Uint32) ESTACK_POP(*sp); | |
7303: 2085: n = (Uint32) ESTACK_POP(*sp); | |
7303: 2086: hval = MAP_HEADER_VAL(hdr); | |
7303: 2087: nhp = hp; | |
7303: 2088: *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval | bp); ptr++; | |
7303: 2089: *hp++ = (*ptr++) + *update_size; | |
-: 2090: | |
7303: 2091: n -= slot; | |
7303: 2092: while(slot--) { *hp++ = *ptr++; } | |
7303: 2093: *hp++ = res; | |
7303: 2094: if (hval & bp) { ptr++; n--; } | |
7303: 2095: while(n--) { *hp++ = *ptr++; } | |
-: 2096: | |
7303: 2097: if ((hval | bp) == 0xffff) { | |
155: 2098: *nhp = MAP_HEADER_HAMT_HEAD_ARRAY; | |
-: 2099: } | |
7303: 2100: res = make_hashmap(nhp); | |
7303: 2101: break; | |
-: 2102: default: | |
#####: 2103: erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); | |
-: 2104: break; | |
-: 2105: } | |
68003: 2106: break; | |
-: 2107: default: | |
#####: 2108: erl_exit(1, "bad primary tag %x\r\n", primary_tag(node)); | |
-: 2109: break; | |
-: 2110: } | |
-: 2111: | |
71928: 2112: } while(!ESTACK_ISEMPTY(*sp)); | |
-: 2113: | |
-: 2114: UnUseTmpHeapNoproc(1); | |
17351: 2115: return res; | |
-: 2116:} | |
-: 2117: | |
609: 2118:static Eterm hashmap_keys(Process* p, Eterm node) { | |
609: 2119: DECLARE_WSTACK(stack); | |
-: 2120: hashmap_head_t* root; | |
-: 2121: Eterm *hp, *kv; | |
609: 2122: Eterm res = NIL; | |
-: 2123: | |
609: 2124: root = (hashmap_head_t*) boxed_val(node); | |
609: 2125: hp = HAlloc(p, root->size * 2); | |
609: 2126: hashmap_iterator_init(&stack, node, 0); | |
49154: 2127: while ((kv=hashmap_iterator_next(&stack)) != NULL) { | |
47936: 2128: res = CONS(hp, CAR(kv), res); | |
47936: 2129: hp += 2; | |
-: 2130: } | |
609: 2131: DESTROY_WSTACK(stack); | |
609: 2132: return res; | |
-: 2133:} | |
-: 2134: | |
605: 2135:static Eterm hashmap_values(Process* p, Eterm node) { | |
605: 2136: DECLARE_WSTACK(stack); | |
-: 2137: hashmap_head_t* root; | |
-: 2138: Eterm *hp, *kv; | |
605: 2139: Eterm res = NIL; | |
-: 2140: | |
605: 2141: root = (hashmap_head_t*) boxed_val(node); | |
605: 2142: hp = HAlloc(p, root->size * 2); | |
605: 2143: hashmap_iterator_init(&stack, node, 0); | |
46649: 2144: while ((kv=hashmap_iterator_next(&stack)) != NULL) { | |
45439: 2145: res = CONS(hp, CDR(kv), res); | |
45439: 2146: hp += 2; | |
-: 2147: } | |
605: 2148: DESTROY_WSTACK(stack); | |
605: 2149: return res; | |
-: 2150:} | |
-: 2151: | |
433: 2152:static Eterm hashmap_delete(Process *p, Uint32 hx, Eterm key, Eterm map) { | |
433: 2153: Eterm *hp = NULL, *nhp = NULL, *hp_end = NULL; | |
-: 2154: Eterm th[2]; | |
-: 2155: Eterm *ptr; | |
433: 2156: Eterm hdr, res = map, node = map; | |
-: 2157: Uint32 ix, bp, hval; | |
433: 2158: Uint slot, lvl = 0; | |
433: 2159: Uint size = 0, n = 0; | |
433: 2160: DECLARE_ESTACK(stack); | |
-: 2161: | |
-: 2162: for (;;) { | |
1858: 2163: switch(primary_tag(node)) { | |
-: 2164: case TAG_PRIMARY_LIST: | |
399: 2165: if (EQ(CAR(list_val(node)), key)) { | |
-: 2166: goto unroll; | |
-: 2167: } | |
44: 2168: goto not_found; | |
-: 2169: case TAG_PRIMARY_BOXED: | |
1459: 2170: ptr = boxed_val(node); | |
1459: 2171: hdr = *ptr; | |
-: 2172: ASSERT(is_header(hdr)); | |
-: 2173: | |
1459: 2174: switch(hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 2175: case HAMT_SUBTAG_HEAD_ARRAY: | |
284: 2176: ix = hashmap_index(hx); | |
284: 2177: hx = hashmap_shift_hash(th,hx,lvl,key); | |
284: 2178: size += HAMT_HEAD_ARRAY_SZ; | |
284: 2179: ESTACK_PUSH2(stack, ix, node); | |
284: 2180: node = ptr[ix+2]; | |
284: 2181: break; | |
-: 2182: case HAMT_SUBTAG_NODE_BITMAP: | |
1026: 2183: hval = MAP_HEADER_VAL(hdr); | |
1026: 2184: ix = hashmap_index(hx); | |
1026: 2185: bp = 1 << ix; | |
1026: 2186: slot = hashmap_bitcount(hval & (bp - 1)); | |
1026: 2187: n = hashmap_bitcount(hval); | |
-: 2188: | |
1026: 2189: ESTACK_PUSH4(stack, n, bp, slot, node); | |
-: 2190: | |
-: 2191: /* occupied */ | |
1026: 2192: if (bp & hval) { | |
993: 2193: hx = hashmap_shift_hash(th,hx,lvl,key); | |
993: 2194: node = ptr[slot+1]; | |
-: 2195: ASSERT(HAMT_NODE_BITMAP_SZ(n) <= 17); | |
993: 2196: size += HAMT_NODE_BITMAP_SZ(n); | |
993: 2197: break; | |
-: 2198: } | |
-: 2199: /* not occupied */ | |
33: 2200: goto not_found; | |
-: 2201: case HAMT_SUBTAG_HEAD_BITMAP: | |
149: 2202: hval = MAP_HEADER_VAL(hdr); | |
149: 2203: ix = hashmap_index(hx); | |
149: 2204: bp = 1 << ix; | |
149: 2205: slot = hashmap_bitcount(hval & (bp - 1)); | |
149: 2206: n = hashmap_bitcount(hval); | |
-: 2207: | |
149: 2208: ESTACK_PUSH4(stack, n, bp, slot, node); | |
-: 2209: | |
-: 2210: /* occupied */ | |
149: 2211: if (bp & hval) { | |
148: 2212: hx = hashmap_shift_hash(th,hx,lvl,key); | |
148: 2213: node = ptr[slot+2]; | |
-: 2214: ASSERT(HAMT_HEAD_BITMAP_SZ(n) <= 18); | |
148: 2215: size += HAMT_HEAD_BITMAP_SZ(n); | |
148: 2216: break; | |
-: 2217: } | |
-: 2218: /* not occupied */ | |
1: 2219: goto not_found; | |
-: 2220: default: | |
#####: 2221: erl_exit(1, "bad header tag %ld\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); | |
-: 2222: break; | |
-: 2223: } | |
1425: 2224: break; | |
-: 2225: default: | |
#####: 2226: erl_exit(1, "bad primary tag %p\r\n", node); | |
-: 2227: break; | |
-: 2228: } | |
1425: 2229: } | |
-: 2230: | |
-: 2231:unroll: | |
-: 2232: /* the size is bounded and atleast one less than the previous size */ | |
355: 2233: size -= 1; | |
355: 2234: n = hashmap_size(map) - 1; | |
-: 2235: | |
355: 2236: if (n <= MAP_SMALL_MAP_LIMIT) { | |
6: 2237: DECLARE_WSTACK(wstack); | |
-: 2238: Eterm *kv, *ks, *vs; | |
-: 2239: flatmap_t *mp; | |
-: 2240: Eterm keys; | |
-: 2241: | |
6: 2242: DESTROY_ESTACK(stack); | |
-: 2243: | |
-: 2244: /* build flat structure */ | |
6: 2245: hp = HAlloc(p, 3 + 1 + (2 * n)); | |
6: 2246: keys = make_tuple(hp); | |
6: 2247: *hp++ = make_arityval(n); | |
6: 2248: ks = hp; | |
6: 2249: hp += n; | |
6: 2250: mp = (flatmap_t*)hp; | |
6: 2251: hp += MAP_HEADER_FLATMAP_SZ; | |
6: 2252: vs = hp; | |
-: 2253: | |
6: 2254: mp->thing_word = MAP_HEADER_FLATMAP; | |
6: 2255: mp->size = n; | |
6: 2256: mp->keys = keys; | |
-: 2257: | |
6: 2258: hashmap_iterator_init(&wstack, map, 0); | |
-: 2259: | |
210: 2260: while ((kv=hashmap_iterator_next(&wstack)) != NULL) { | |
198: 2261: if (EQ(CAR(kv),key)) | |
6: 2262: continue; | |
192: 2263: *ks++ = CAR(kv); | |
192: 2264: *vs++ = CDR(kv); | |
-: 2265: } | |
-: 2266: | |
-: 2267: /* it cannot have multiple keys */ | |
6: 2268: erts_validate_and_sort_flatmap(mp); | |
-: 2269: | |
6: 2270: DESTROY_WSTACK(wstack); | |
6: 2271: return make_flatmap(mp); | |
-: 2272: } | |
-: 2273: | |
349: 2274: hp = HAlloc(p, size); | |
349: 2275: hp_end = hp + size; | |
349: 2276: res = THE_NON_VALUE; | |
-: 2277: | |
-: 2278: do { | |
1278: 2279: node = ESTACK_POP(stack); | |
-: 2280: | |
-: 2281: /* all nodes are things */ | |
1278: 2282: ptr = boxed_val(node); | |
1278: 2283: hdr = *ptr; | |
-: 2284: ASSERT(is_header(hdr)); | |
-: 2285: | |
1278: 2286: switch(hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 2287: case HAMT_SUBTAG_HEAD_ARRAY: | |
233: 2288: ix = (Uint) ESTACK_POP(stack); | |
233: 2289: nhp = hp; | |
233: 2290: if (res == THE_NON_VALUE) { | |
2: 2291: n = 16; | |
2: 2292: n -= ix; | |
2: 2293: *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(0xffff ^ (1 << ix)); ptr++; | |
2: 2294: *hp++ = (*ptr++) - 1; | |
2: 2295: while(ix--) { *hp++ = *ptr++; } | |
2: 2296: ptr++; n--; | |
2: 2297: while(n--) { *hp++ = *ptr++; } | |
2: 2298: res = make_hashmap(nhp); | |
-: 2299: } else { | |
231: 2300: n = 16; | |
231: 2301: *hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++; | |
231: 2302: *hp++ = (*ptr++) - 1; | |
231: 2303: while(n--) { *hp++ = *ptr++; } | |
231: 2304: nhp[ix+2] = res; | |
231: 2305: res = make_hashmap(nhp); | |
-: 2306: } | |
233: 2307: break; | |
-: 2308: case HAMT_SUBTAG_NODE_BITMAP: | |
929: 2309: slot = (Uint) ESTACK_POP(stack); | |
929: 2310: bp = (Uint32) ESTACK_POP(stack); | |
929: 2311: n = (Uint32) ESTACK_POP(stack); | |
929: 2312: nhp = hp; | |
-: 2313: | |
-: 2314: /* bitmap change matrix | |
-: 2315: * res | none leaf bitmap | |
-: 2316: * ---------------------------- | |
-: 2317: * n=1 | remove remove keep | |
-: 2318: * n=2 | other keep keep | |
-: 2319: * n>2 | shrink keep keep | |
-: 2320: * | |
-: 2321: * other: (remember, n is 2) | |
-: 2322: * shrink if the other bitmap value is a bitmap node | |
-: 2323: * remove if the other bitmap value is a leaf | |
-: 2324: * | |
-: 2325: * remove: | |
-: 2326: * this bitmap node is removed, res is moved up in tree (could be none) | |
-: 2327: * this is a special case of shrink | |
-: 2328: * | |
-: 2329: * keep: | |
-: 2330: * the current path index is still used down in the tree, need to keep it | |
-: 2331: * copy as usual with the updated res | |
-: 2332: * | |
-: 2333: * shrink: | |
-: 2334: * the current path index is no longer used down in the tree, remove it (shrink) | |
-: 2335: */ | |
929: 2336: if (res == THE_NON_VALUE) { | |
336: 2337: if (n == 1) { | |
#####: 2338: break; | |
336: 2339: } else if (n == 2) { | |
158: 2340: if (slot == 0) { | |
73: 2341: ix = 2; /* off by one 'cause hdr */ | |
-: 2342: } else { | |
85: 2343: ix = 1; /* off by one 'cause hdr */ | |
-: 2344: } | |
158: 2345: if (primary_tag(ptr[ix]) == TAG_PRIMARY_LIST) { | |
142: 2346: res = ptr[ix]; | |
-: 2347: } else { | |
16: 2348: hval = MAP_HEADER_VAL(hdr); | |
16: 2349: *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval ^ bp); | |
16: 2350: *hp++ = ptr[ix]; | |
16: 2351: res = make_hashmap(nhp); | |
-: 2352: } | |
-: 2353: } else { | |
-: 2354: /* n > 2 */ | |
178: 2355: hval = MAP_HEADER_VAL(hdr); | |
178: 2356: *hp++ = MAP_HEADER_HAMT_NODE_BITMAP(hval ^ bp); ptr++; | |
178: 2357: n -= slot; | |
178: 2358: while(slot--) { *hp++ = *ptr++; } | |
178: 2359: ptr++; n--; | |
178: 2360: while(n--) { *hp++ = *ptr++; } | |
178: 2361: res = make_hashmap(nhp); | |
-: 2362: } | |
593: 2363: } else if (primary_tag(res) == TAG_PRIMARY_LIST && n == 1) { | |
-: 2364: break; | |
-: 2365: } else { | |
-: 2366: /* res is bitmap or leaf && n > 1, keep */ | |
155: 2367: n -= slot; | |
155: 2368: *hp++ = *ptr++; | |
155: 2369: while(slot--) { *hp++ = *ptr++; } | |
155: 2370: *hp++ = res; | |
155: 2371: ptr++; n--; | |
155: 2372: while(n--) { *hp++ = *ptr++; } | |
155: 2373: res = make_hashmap(nhp); | |
-: 2374: } | |
491: 2375: break; | |
-: 2376: case HAMT_SUBTAG_HEAD_BITMAP: | |
116: 2377: slot = (Uint) ESTACK_POP(stack); | |
116: 2378: bp = (Uint32) ESTACK_POP(stack); | |
116: 2379: n = (Uint32) ESTACK_POP(stack); | |
116: 2380: nhp = hp; | |
-: 2381: | |
116: 2382: if (res != THE_NON_VALUE) { | |
105: 2383: *hp++ = *ptr++; | |
105: 2384: *hp++ = (*ptr++) - 1; | |
105: 2385: n -= slot; | |
105: 2386: while(slot--) { *hp++ = *ptr++; } | |
105: 2387: *hp++ = res; | |
105: 2388: ptr++; n--; | |
105: 2389: while(n--) { *hp++ = *ptr++; } | |
-: 2390: } else { | |
11: 2391: hval = MAP_HEADER_VAL(hdr); | |
11: 2392: *hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval ^ bp); ptr++; | |
11: 2393: *hp++ = (*ptr++) - 1; | |
11: 2394: n -= slot; | |
11: 2395: while(slot--) { *hp++ = *ptr++; } | |
11: 2396: ptr++; n--; | |
11: 2397: while(n--) { *hp++ = *ptr++; } | |
-: 2398: } | |
116: 2399: res = make_hashmap(nhp); | |
116: 2400: break; | |
-: 2401: default: | |
#####: 2402: erl_exit(1, "bad header tag %x\r\n", hdr & _HEADER_MAP_SUBTAG_MASK); | |
-: 2403: break; | |
-: 2404: } | |
1278: 2405: } while(!ESTACK_ISEMPTY(stack)); | |
349: 2406: HRelease(p, hp_end, hp); | |
-: 2407:not_found: | |
427: 2408: DESTROY_ESTACK(stack); | |
-: 2409: ERTS_VERIFY_UNUSED_TEMP_ALLOC(p); | |
-: 2410: ERTS_HOLE_CHECK(p); | |
427: 2411: return res; | |
-: 2412:} | |
-: 2413: | |
-: 2414: | |
49: 2415:int erts_validate_and_sort_flatmap(flatmap_t* mp) | |
-: 2416:{ | |
49: 2417: Eterm *ks = flatmap_get_keys(mp); | |
49: 2418: Eterm *vs = flatmap_get_values(mp); | |
49: 2419: Uint sz = flatmap_get_size(mp); | |
-: 2420: Uint ix,jx; | |
-: 2421: Eterm tmp; | |
-: 2422: Sint c; | |
-: 2423: | |
-: 2424: /* sort */ | |
-: 2425: | |
801: 2426: for (ix = 1; ix < sz; ix++) { | |
752: 2427: jx = ix; | |
6797: 2428: while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { | |
-: 2429: /* identical key -> error */ | |
5293: 2430: if (c == 0) return 0; | |
-: 2431: | |
5293: 2432: tmp = ks[jx]; | |
5293: 2433: ks[jx] = ks[jx - 1]; | |
5293: 2434: ks[jx - 1] = tmp; | |
-: 2435: | |
5293: 2436: tmp = vs[jx]; | |
5293: 2437: vs[jx] = vs[jx - 1]; | |
5293: 2438: vs[jx - 1] = tmp; | |
-: 2439: | |
5293: 2440: jx--; | |
-: 2441: } | |
-: 2442: } | |
49: 2443: return 1; | |
-: 2444:} | |
-: 2445: | |
-: 2446:/* Really rough estimate of sqrt(x) | |
-: 2447: * Guaranteed not to be less than sqrt(x) | |
-: 2448: */ | |
#####: 2449:static int int_sqrt_ceiling(Uint x) | |
-: 2450:{ | |
-: 2451: int n; | |
-: 2452: | |
#####: 2453: if (x <= 2) | |
#####: 2454: return x; | |
-: 2455: | |
#####: 2456: n = erts_fit_in_bits_uint(x-1); | |
#####: 2457: if (n & 1) { | |
-: 2458: /* Calc: sqrt(2^n) = 2^(n/2) * sqrt(2) ~= 2^(n/2) * 3 / 2 */ | |
#####: 2459: return (1 << (n/2 - 1)) * 3; | |
-: 2460: } | |
-: 2461: else { | |
-: 2462: /* Calc: sqrt(2^n) = 2^(n/2) */ | |
#####: 2463: return 1 << (n / 2); | |
-: 2464: } | |
-: 2465:} | |
-: 2466: | |
#####: 2467:Uint hashmap_over_estimated_heap_size(Uint k) | |
-: 2468:{ | |
-: 2469: /* k is nr of key-value pairs. | |
-: 2470: N(k) is expected nr of nodes in hamt. | |
-: 2471: | |
-: 2472: Observation: | |
-: 2473: For uniformly distributed hash values, average of N varies between | |
-: 2474: 0.3*k and 0.4*k (with a beautiful sine curve) | |
-: 2475: and standard deviation of N is about sqrt(k)/3. | |
-: 2476: | |
-: 2477: Assuming normal probability distribution, we overestimate nr of nodes | |
-: 2478: by 15 std.devs above the average, which gives a probability for overrun | |
-: 2479: less than 1.0e-49 (same magnitude as a git SHA1 collision). | |
-: 2480: */ | |
#####: 2481: Uint max_nodes = 2*k/5 + (15/3)*int_sqrt_ceiling(k); | |
#####: 2482: return (k*2 + /* leaf cons cells */ | |
-: 2483: k + /* leaf list terms */ | |
#####: 2484: max_nodes*2); /* headers + parent boxed terms */ | |
-: 2485:} | |
-: 2486: | |
-: 2487: | |
#####: 2488:BIF_RETTYPE erts_debug_map_info_1(BIF_ALIST_1) { | |
#####: 2489: if (is_hashmap(BIF_ARG_1)) { | |
#####: 2490: BIF_RET(hashmap_info(BIF_P,BIF_ARG_1)); | |
-: 2491: } | |
#####: 2492: BIF_ERROR(BIF_P, BADARG); | |
-: 2493:} | |
-: 2494: | |
-: 2495:/* | |
-: 2496: * erts_internal:map_to_tuple_keys/1 | |
-: 2497: * | |
-: 2498: * Used in erts_debug:size/1 | |
-: 2499: */ | |
-: 2500: | |
#####: 2501:BIF_RETTYPE erts_internal_map_to_tuple_keys_1(BIF_ALIST_1) { | |
#####: 2502: if (is_flatmap(BIF_ARG_1)) { | |
#####: 2503: flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1); | |
#####: 2504: BIF_RET(mp->keys); | |
-: 2505: } | |
#####: 2506: BIF_ERROR(BIF_P, BADARG); | |
-: 2507:} | |
-: 2508: | |
-: 2509:/* | |
-: 2510: * erts_internal:map_type/1 | |
-: 2511: * | |
-: 2512: * Used in erts_debug:size/1 | |
-: 2513: */ | |
-: 2514: | |
#####: 2515:BIF_RETTYPE erts_internal_map_type_1(BIF_ALIST_1) { | |
#####: 2516: DECL_AM(hashmap); | |
#####: 2517: DECL_AM(hashmap_node); | |
#####: 2518: DECL_AM(flatmap); | |
#####: 2519: if (is_flatmap(BIF_ARG_1)) { | |
#####: 2520: BIF_RET(AM_flatmap); | |
#####: 2521: } else if (is_hashmap(BIF_ARG_1)) { | |
#####: 2522: Eterm hdr = *(boxed_val(BIF_ARG_1)); | |
-: 2523: ASSERT(is_header(hdr)); | |
#####: 2524: switch (hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 2525: case HAMT_SUBTAG_HEAD_ARRAY: | |
-: 2526: case HAMT_SUBTAG_HEAD_BITMAP: | |
#####: 2527: BIF_RET(AM_hashmap); | |
-: 2528: case HAMT_SUBTAG_NODE_BITMAP: | |
#####: 2529: BIF_RET(AM_hashmap_node); | |
-: 2530: default: | |
#####: 2531: erl_exit(1, "bad header"); | |
-: 2532: } | |
-: 2533: } | |
#####: 2534: BIF_ERROR(BIF_P, BADARG); | |
-: 2535:} | |
-: 2536: | |
-: 2537:/* | |
-: 2538: * erts_internal:map_hashmap_children/1 | |
-: 2539: * | |
-: 2540: * Used in erts_debug:size/1 | |
-: 2541: */ | |
-: 2542: | |
#####: 2543:BIF_RETTYPE erts_internal_map_hashmap_children_1(BIF_ALIST_1) { | |
#####: 2544: if (is_hashmap(BIF_ARG_1)) { | |
#####: 2545: Eterm node = BIF_ARG_1; | |
#####: 2546: Eterm *ptr, hdr, *hp, res = NIL; | |
#####: 2547: Uint sz = 0; | |
#####: 2548: ptr = boxed_val(node); | |
#####: 2549: hdr = *ptr; | |
-: 2550: | |
-: 2551: ASSERT(is_header(hdr)); | |
-: 2552: | |
#####: 2553: switch(hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 2554: case HAMT_SUBTAG_NODE_BITMAP: | |
#####: 2555: sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); | |
#####: 2556: ptr += 1; | |
#####: 2557: break; | |
-: 2558: case HAMT_SUBTAG_HEAD_BITMAP: | |
#####: 2559: sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); | |
#####: 2560: ptr += 2; | |
#####: 2561: break; | |
-: 2562: case HAMT_SUBTAG_HEAD_ARRAY: | |
#####: 2563: sz = 16; | |
#####: 2564: ptr += 2; | |
#####: 2565: break; | |
-: 2566: default: | |
#####: 2567: erl_exit(1, "bad header\r\n"); | |
-: 2568: break; | |
-: 2569: } | |
-: 2570: ASSERT(sz < 17); | |
#####: 2571: hp = HAlloc(BIF_P, 2*sz); | |
#####: 2572: while(sz--) { res = CONS(hp, *ptr++, res); hp += 2; } | |
#####: 2573: BIF_RET(res); | |
-: 2574: } | |
#####: 2575: BIF_ERROR(BIF_P, BADARG); | |
-: 2576:} | |
-: 2577: | |
-: 2578: | |
#####: 2579:static Eterm hashmap_info(Process *p, Eterm node) { | |
-: 2580: Eterm *hp; | |
#####: 2581: Eterm res = NIL, info = NIL; | |
-: 2582: Eterm *ptr, tup, hdr; | |
-: 2583: Uint sz; | |
#####: 2584: DECL_AM(depth); | |
#####: 2585: DECL_AM(leafs); | |
#####: 2586: DECL_AM(bitmaps); | |
#####: 2587: DECL_AM(arrays); | |
#####: 2588: Uint nleaf=0, nbitmap=0, narray=0; | |
-: 2589: Uint bitmap_usage[16], leaf_usage[16]; | |
#####: 2590: Uint lvl = 0, clvl; | |
#####: 2591: DECLARE_ESTACK(stack); | |
-: 2592: | |
#####: 2593: for (sz = 0; sz < 16; sz++) { | |
#####: 2594: bitmap_usage[sz] = 0; | |
#####: 2595: leaf_usage[sz] = 0; | |
-: 2596: } | |
-: 2597: | |
#####: 2598: ptr = boxed_val(node); | |
#####: 2599: ESTACK_PUSH(stack, 0); | |
#####: 2600: ESTACK_PUSH(stack, node); | |
-: 2601: do { | |
#####: 2602: node = ESTACK_POP(stack); | |
#####: 2603: clvl = ESTACK_POP(stack); | |
#####: 2604: if (lvl < clvl) | |
#####: 2605: lvl = clvl; | |
#####: 2606: switch(primary_tag(node)) { | |
-: 2607: case TAG_PRIMARY_LIST: | |
#####: 2608: nleaf++; | |
#####: 2609: leaf_usage[clvl] += 1; | |
#####: 2610: break; | |
-: 2611: case TAG_PRIMARY_BOXED: | |
#####: 2612: ptr = boxed_val(node); | |
#####: 2613: hdr = *ptr; | |
-: 2614: ASSERT(is_header(hdr)); | |
#####: 2615: switch(hdr & _HEADER_MAP_SUBTAG_MASK) { | |
-: 2616: case HAMT_SUBTAG_NODE_BITMAP: | |
#####: 2617: nbitmap++; | |
#####: 2618: sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); | |
-: 2619: ASSERT(sz < 17); | |
#####: 2620: bitmap_usage[sz-1] += 1; | |
#####: 2621: while(sz--) { | |
#####: 2622: ESTACK_PUSH(stack, clvl + 1); | |
#####: 2623: ESTACK_PUSH(stack, ptr[sz+1]); | |
-: 2624: } | |
#####: 2625: break; | |
-: 2626: case HAMT_SUBTAG_HEAD_BITMAP: | |
#####: 2627: nbitmap++; | |
#####: 2628: sz = hashmap_bitcount(MAP_HEADER_VAL(hdr)); | |
#####: 2629: bitmap_usage[sz-1] += 1; | |
#####: 2630: while(sz--) { | |
#####: 2631: ESTACK_PUSH(stack, clvl + 1); | |
#####: 2632: ESTACK_PUSH(stack, ptr[sz+2]); | |
-: 2633: } | |
#####: 2634: break; | |
-: 2635: case HAMT_SUBTAG_HEAD_ARRAY: | |
#####: 2636: narray++; | |
#####: 2637: sz = 16; | |
#####: 2638: while(sz--) { | |
#####: 2639: ESTACK_PUSH(stack, clvl + 1); | |
#####: 2640: ESTACK_PUSH(stack, ptr[sz+2]); | |
-: 2641: } | |
#####: 2642: break; | |
-: 2643: default: | |
#####: 2644: erl_exit(1, "bad header\r\n"); | |
-: 2645: break; | |
-: 2646: } | |
-: 2647: } | |
#####: 2648: } while(!ESTACK_ISEMPTY(stack)); | |
-: 2649: | |
-: 2650: | |
-: 2651: /* size */ | |
#####: 2652: sz = 0; | |
#####: 2653: hashmap_bld_tuple_uint(NULL,&sz,16,leaf_usage); | |
#####: 2654: hashmap_bld_tuple_uint(NULL,&sz,16,bitmap_usage); | |
-: 2655: | |
-: 2656: /* alloc */ | |
#####: 2657: hp = HAlloc(p, 2+3 + 3*(2+4) + sz); | |
-: 2658: | |
#####: 2659: info = hashmap_bld_tuple_uint(&hp,NULL,16,leaf_usage); | |
#####: 2660: tup = TUPLE3(hp, AM_leafs, make_small(nleaf),info); hp += 4; | |
#####: 2661: res = CONS(hp, tup, res); hp += 2; | |
-: 2662: | |
#####: 2663: info = hashmap_bld_tuple_uint(&hp,NULL,16,bitmap_usage); | |
#####: 2664: tup = TUPLE3(hp, AM_bitmaps, make_small(nbitmap), info); hp += 4; | |
#####: 2665: res = CONS(hp, tup, res); hp += 2; | |
-: 2666: | |
#####: 2667: tup = TUPLE3(hp, AM_arrays, make_small(narray),NIL); hp += 4; | |
#####: 2668: res = CONS(hp, tup, res); hp += 2; | |
-: 2669: | |
#####: 2670: tup = TUPLE2(hp, AM_depth, make_small(lvl)); hp += 3; | |
#####: 2671: res = CONS(hp, tup, res); hp += 2; | |
-: 2672: | |
#####: 2673: DESTROY_ESTACK(stack); | |
-: 2674: ERTS_HOLE_CHECK(p); | |
#####: 2675: return res; | |
-: 2676:} | |
-: 2677: | |
#####: 2678:static Eterm hashmap_bld_tuple_uint(Uint **hpp, Uint *szp, Uint n, Uint nums[]) { | |
#####: 2679: Eterm res = THE_NON_VALUE; | |
#####: 2680: Eterm *ts = (Eterm *)erts_alloc(ERTS_ALC_T_TMP, n * sizeof(Eterm)); | |
-: 2681: Uint i; | |
-: 2682: | |
#####: 2683: for (i = 0; i < n; i++) { | |
#####: 2684: ts[i] = erts_bld_uint(hpp, szp, nums[i]); | |
-: 2685: } | |
#####: 2686: res = erts_bld_tuplev(hpp, szp, n, ts); | |
#####: 2687: erts_free(ERTS_ALC_T_TMP, (void *) ts); | |
#####: 2688: return res; | |
-: 2689:} | |
-: 2690: | |
-: 2691: | |
-: 2692:/* implementation of builtin emulations */ | |
-: 2693: | |
-: 2694:#if !ERTS_AT_LEAST_GCC_VSN__(3, 4, 0) | |
-: 2695:/* Count leading zeros emulation */ | |
-: 2696:Uint32 hashmap_clz(Uint32 x) { | |
-: 2697: Uint32 y; | |
-: 2698: int n = 32; | |
-: 2699: y = x >>16; if (y != 0) {n = n -16; x = y;} | |
-: 2700: y = x >> 8; if (y != 0) {n = n - 8; x = y;} | |
-: 2701: y = x >> 4; if (y != 0) {n = n - 4; x = y;} | |
-: 2702: y = x >> 2; if (y != 0) {n = n - 2; x = y;} | |
-: 2703: y = x >> 1; if (y != 0) return n - 2; | |
-: 2704: return n - x; | |
-: 2705:} | |
-: 2706: | |
-: 2707:const Uint32 SK5 = 0x55555555, SK3 = 0x33333333; | |
-: 2708:const Uint32 SKF0 = 0xF0F0F0F, SKFF = 0xFF00FF; | |
-: 2709: | |
-: 2710:/* CTPOP emulation */ | |
-: 2711:Uint32 hashmap_bitcount(Uint32 x) { | |
-: 2712: x -= ((x >> 1 ) & SK5); | |
-: 2713: x = (x & SK3 ) + ((x >> 2 ) & SK3 ); | |
-: 2714: x = (x & SKF0) + ((x >> 4 ) & SKF0); | |
-: 2715: x += x >> 8; | |
-: 2716: return (x + (x >> 16)) & 0x3F; | |
-: 2717:} | |
-: 2718:#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment