@@ -254,7 +254,6 @@ static inline bool has_key_at(term tuple, size_t key_index)
254
254
255
255
static EtsErrorCode insert (struct EtsTable * ets_table , term tuple , Context * ctx )
256
256
{
257
- assert (term_is_tuple (tuple ));
258
257
size_t key_index = ets_table -> key_index ;
259
258
if (UNLIKELY (!has_key_at (tuple , key_index ))) {
260
259
return EtsBadEntry ;
@@ -264,7 +263,8 @@ static EtsErrorCode insert(struct EtsTable *ets_table, term tuple, Context *ctx)
264
263
struct HNode * node = NULL ;
265
264
if (is_duplicate_bag ) {
266
265
term key = term_get_tuple_element (tuple , key_index );
267
- term old_tuples = ets_hashtable_lookup (ets_table -> hashtable , key , key_index , ctx -> global );
266
+ term old_tuples = ets_hashtable_lookup (ets_table -> hashtable , key , ctx -> global );
267
+ assert (term_is_list (old_tuples ));
268
268
bool is_new = term_is_nil (old_tuples );
269
269
node = ets_hashtable_new_node_from_list (is_new ? term_nil () : old_tuples , tuple , key_index );
270
270
} else {
@@ -350,20 +350,53 @@ EtsErrorCode ets_insert(term name_or_ref, term entry, Context *ctx)
350
350
return result ;
351
351
}
352
352
353
- static EtsErrorCode lookup_maybe_gc (struct EtsTable * ets_table , term key , term * ret , Context * ctx , int num_roots , term * roots )
353
+ static EtsErrorCode lookup_maybe_gc (struct EtsTable * ets_table , term key , size_t index , term * ret , Context * ctx , int num_roots , term * roots )
354
354
{
355
- term res = ets_hashtable_lookup (ets_table -> hashtable , key , ets_table -> key_index , ctx -> global );
355
+ bool is_duplicate_bag = ets_table -> table_type == EtsTableDuplicateBag ;
356
+ bool lookup_element = index != ETS_NO_INDEX ;
357
+ term ets_entry = ets_hashtable_lookup (ets_table -> hashtable , key , ctx -> global );
356
358
357
- if (term_is_nil (res )) {
359
+ if (term_is_nil (ets_entry )) {
358
360
* ret = term_nil ();
359
- } else {
361
+ } else if (is_duplicate_bag ) {
362
+ assert (term_is_list (ets_entry ));
363
+ // for tuple list and it reversed version - we don't want to copy terms in the loop
364
+ int _proper ;
365
+ size_t n = term_list_length (ets_entry , & _proper );
366
+ size_t size = LIST_SIZE (n , 1 ) + memory_estimate_usage (ets_entry );
367
+ if (UNLIKELY (memory_ensure_free_with_roots (ctx , size , num_roots , roots , MEMORY_CAN_SHRINK ))) {
368
+ return EtsAllocationFailure ;
369
+ }
370
+ term tuples = memory_copy_term_tree (& ctx -> heap , ets_entry );
371
+ // lookup returns in insertion order
372
+ // TODO: store it in correct order?
373
+ term reversed = term_nil ();
374
+ while (term_is_nonempty_list (tuples )) {
375
+ term elem = term_get_list_head (tuples );
376
+ if (lookup_element ) {
377
+ term tuple = elem ;
378
+ if (UNLIKELY (!has_key_at (tuple , index ))) {
379
+ return EtsBadPosition ;
380
+ }
381
+ elem = term_get_tuple_element (tuple , index );
382
+ }
383
+ reversed = term_list_prepend (elem , reversed , & ctx -> heap );
384
+ tuples = term_get_list_tail (tuples );
385
+ }
360
386
361
- size_t size = (size_t ) memory_estimate_usage (res );
362
- // allocate [object]
387
+ * ret = reversed ;
388
+ } else {
389
+ if (lookup_element ) {
390
+ if (UNLIKELY (!has_key_at (ets_entry , index ))) {
391
+ return EtsBadPosition ;
392
+ }
393
+ ets_entry = term_get_tuple_element (ets_entry , index );
394
+ }
395
+ size_t size = (size_t ) memory_estimate_usage (ets_entry );
363
396
if (UNLIKELY (memory_ensure_free_with_roots (ctx , size + CONS_SIZE , num_roots , roots , MEMORY_CAN_SHRINK ) != MEMORY_GC_OK )) {
364
397
return EtsAllocationFailure ;
365
398
}
366
- term new_res = memory_copy_term_tree (& ctx -> heap , res );
399
+ term new_res = memory_copy_term_tree (& ctx -> heap , ets_entry );
367
400
* ret = term_list_prepend (new_res , term_nil (), & ctx -> heap );
368
401
}
369
402
@@ -377,7 +410,7 @@ EtsErrorCode ets_lookup_maybe_gc(term name_or_ref, term key, term *ret, Context
377
410
return EtsBadAccess ;
378
411
}
379
412
380
- EtsErrorCode result = lookup_maybe_gc (ets_table , key , ret , ctx , 0 , NULL );
413
+ EtsErrorCode result = lookup_maybe_gc (ets_table , key , ETS_NO_INDEX , ret , ctx , 0 , NULL );
381
414
SMP_UNLOCK (ets_table );
382
415
383
416
return result ;
@@ -389,25 +422,19 @@ EtsErrorCode ets_lookup_element_maybe_gc(term name_or_ref, term key, size_t key_
389
422
if (IS_NULL_PTR (ets_table )) {
390
423
return EtsBadAccess ;
391
424
}
425
+ bool is_duplicate_bag = ets_table -> table_type == EtsTableDuplicateBag ;
392
426
393
- term entry = ets_hashtable_lookup (ets_table -> hashtable , key , ets_table -> key_index , ctx -> global );
394
- if (UNLIKELY (!term_is_tuple (entry ))) {
395
- SMP_UNLOCK (ets_table );
396
- return EtsEntryNotFound ;
397
- }
398
-
399
- if (UNLIKELY (!has_key_at (entry , key_index ))) {
427
+ term entry ;
428
+ EtsErrorCode result = lookup_maybe_gc (ets_table , key , key_index , & entry , ctx , 0 , NULL );
429
+ if (result != EtsOk ) {
400
430
SMP_UNLOCK (ets_table );
401
- return EtsBadPosition ;
431
+ return result ;
402
432
}
403
-
404
- term t = term_get_tuple_element (entry , key_index );
405
- size_t size = (size_t ) memory_estimate_usage (t );
406
- if (UNLIKELY (memory_ensure_free_opt (ctx , size , MEMORY_CAN_SHRINK ) != MEMORY_GC_OK )) {
433
+ if (term_is_nil (entry )) {
407
434
SMP_UNLOCK (ets_table );
408
- return EtsAllocationFailure ;
435
+ return EtsEntryNotFound ;
409
436
}
410
- * ret = memory_copy_term_tree ( & ctx -> heap , t );
437
+ * ret = is_duplicate_bag ? entry : term_get_list_head ( entry );
411
438
SMP_UNLOCK (ets_table );
412
439
413
440
return EtsOk ;
@@ -438,7 +465,7 @@ EtsErrorCode ets_delete(term name_or_ref, term key, term *ret, Context *ctx)
438
465
return EtsBadAccess ;
439
466
}
440
467
441
- bool _found = ets_hashtable_remove (ets_table -> hashtable , key , ets_table -> key_index , ctx -> global );
468
+ bool _found = ets_hashtable_remove (ets_table -> hashtable , key , ctx -> global );
442
469
UNUSED (_found );
443
470
SMP_UNLOCK (ets_table );
444
471
@@ -503,7 +530,7 @@ EtsErrorCode ets_update_counter_maybe_gc(term ref, term key, term operation, ter
503
530
term roots [] = { key , operation , safe_default_value };
504
531
505
532
term list ;
506
- EtsErrorCode result = lookup_maybe_gc (ets_table , key , & list , ctx , 3 , roots );
533
+ EtsErrorCode result = lookup_maybe_gc (ets_table , key , ETS_NO_INDEX , & list , ctx , 3 , roots );
507
534
if (UNLIKELY (result != EtsOk )) {
508
535
SMP_UNLOCK (ets_table );
509
536
return result ;
0 commit comments