diff --git a/src/cereggii/atomic_dict/atomic_dict.c b/src/cereggii/atomic_dict/atomic_dict.c index 040af67f..fbf1541e 100644 --- a/src/cereggii/atomic_dict/atomic_dict.c +++ b/src/cereggii/atomic_dict/atomic_dict.c @@ -451,6 +451,7 @@ AtomicDict_GetItem(AtomicDict *self, PyObject *key) if (result.entry_p == NULL) { PyErr_SetObject(PyExc_KeyError, key); + result.entry.value = NULL; } Py_DECREF(meta); @@ -591,7 +592,13 @@ atomic_dict_get_empty_entry(AtomicDict *dk, atomic_dict_meta *meta, atomic_dict_ entry_loc->entry = NULL; } -void +typedef enum atomic_dict_robin_hood_result { + ok, + failed, + grow, +} atomic_dict_robin_hood_result; + +atomic_dict_robin_hood_result atomic_dict_robin_hood(atomic_dict_meta *meta, atomic_dict_node *nodes, atomic_dict_node to_insert, int distance_0_ix) { /* @@ -607,19 +614,22 @@ atomic_dict_robin_hood(atomic_dict_meta *meta, atomic_dict_node *nodes, atomic_d * 2. there is at least 1 empty node * */ - int nodes_in_two_words = 16 / (meta->node_size / 8); atomic_dict_node current = to_insert; atomic_dict_node temp; int probe = 0; int reservations = 0; + int cursor; beginning: - for (; probe < nodes_in_two_words; probe++) { - int cursor = distance_0_ix + probe + reservations; + for (; probe < meta->nodes_in_two_regions; probe++) { + if (probe >= (1 << meta->distance_size) - 1) { + return grow; + } + cursor = distance_0_ix + probe + reservations; if (nodes[cursor].node == 0) { current.distance = probe; nodes[cursor] = current; - return; + return ok; } if (atomic_dict_node_is_reservation(&nodes[cursor], meta)) { @@ -635,6 +645,7 @@ atomic_dict_robin_hood(atomic_dict_meta *meta, atomic_dict_node *nodes, atomic_d if (nodes[cursor].distance < probe) { current.distance = probe; + atomic_dict_compute_raw_node(¤t, meta); temp = nodes[cursor]; nodes[cursor] = current; current = temp; @@ -643,6 +654,7 @@ atomic_dict_robin_hood(atomic_dict_meta *meta, atomic_dict_node *nodes, atomic_d goto beginning; } } + return failed; } void @@ -712,10 +724,32 @@ atomic_dict_insert_entry(atomic_dict_meta *meta, atomic_dict_entry_loc *entry_lo for (int i = start; i < meta->nodes_in_two_regions; ++i) { if (read_buffer[i].node == 0) { - node.distance = i; atomic_dict_nodes_copy_buffers(read_buffer, temp); - atomic_dict_robin_hood(meta, temp, node, start); - if (atomic_dict_atomic_write_nodes_at(ix, i + 1 - start, &read_buffer[start], &temp[start], meta)) { + atomic_dict_robin_hood_result rh = atomic_dict_robin_hood(meta, temp, node, start); + // if (rh == grow) { + // atomic_dict_grow(); + // } + assert(rh == ok); + int begin_write = -1; + for (int j = start; j < meta->nodes_in_two_regions; ++j) { + atomic_dict_compute_raw_node(&temp[j], meta); + if (temp[j].node != read_buffer[j].node) { + begin_write = j; + break; + } + } + assert(begin_write != -1); + int end_write; + for (int j = begin_write + 1; j < meta->nodes_in_two_regions; ++j) { + atomic_dict_compute_raw_node(&temp[j], meta); + if (temp[j].node == read_buffer[j].node) { + end_write = j; + break; + } + } + assert(end_write > begin_write); + if (atomic_dict_atomic_write_nodes_at(ix + begin_write - start, end_write - begin_write, + &read_buffer[begin_write], &temp[begin_write], meta)) { goto done; } goto beginning; diff --git a/src/cereggii/atomic_dict/atomic_dict_node_ops.c b/src/cereggii/atomic_dict/atomic_dict_node_ops.c index a47cb676..ce7984d7 100644 --- a/src/cereggii/atomic_dict/atomic_dict_node_ops.c +++ b/src/cereggii/atomic_dict/atomic_dict_node_ops.c @@ -70,7 +70,7 @@ atomic_dict_read_2_nodes_at(unsigned long ix, atomic_dict_node *nodes, atomic_di node_region_big = meta->index[big]; } atomic_dict_parse_node_from_region(ix, node_region_little, &nodes[0], meta); - atomic_dict_parse_node_from_region(ix, node_region_big, &nodes[1], meta); + atomic_dict_parse_node_from_region(ix + 1, node_region_big, &nodes[1], meta); } void @@ -88,7 +88,7 @@ atomic_dict_read_4_nodes_at(unsigned long ix, atomic_dict_node *nodes, atomic_di unsigned long region; for (int i = 0; i < 4; ++i) { region = ((ix + i) & ~meta->shift_mask) == little ? node_region_little : node_region_big; - atomic_dict_parse_node_from_region(ix, region, &nodes[i], meta); + atomic_dict_parse_node_from_region(ix + i, region, &nodes[i], meta); } } @@ -126,7 +126,7 @@ atomic_dict_read_16_nodes_at(unsigned long ix, atomic_dict_node *nodes, atomic_d unsigned long region; for (int i = 0; i < 16; ++i) { region = ((ix + i) & ~meta->shift_mask) == little ? node_region_little : node_region_big; - atomic_dict_parse_node_from_region(ix, region, &nodes[i], meta); + atomic_dict_parse_node_from_region(ix + i, region, &nodes[i], meta); } } @@ -175,10 +175,9 @@ atomic_dict_atomic_write_nodes_at(unsigned long ix, int n, atomic_dict_node *exp assert(n <= 16); assert(ix < 1 << meta->log_size); unsigned long shift = ix & meta->shift_mask; - unsigned long little = ix / meta->nodes_in_region; - unsigned long big = (ix + n - 1) / meta->nodes_in_region % (1 << meta->log_size); + unsigned long little = ix & ~meta->shift_mask; + unsigned long big = (ix + n - 1) & ~meta->shift_mask % (1 << meta->log_size); assert(little <= big <= little + 1); // XXX implement index circular behavior - int must_write_nodes = little == big ? meta->nodes_in_region : meta->nodes_in_two_regions; unsigned long long expected_raw = 0, new_raw = 0; int i; for (i = 0; i < n; ++i) { @@ -186,72 +185,10 @@ atomic_dict_atomic_write_nodes_at(unsigned long ix, int n, atomic_dict_node *exp atomic_dict_compute_raw_node(&new[i], meta); } for (i = 0; i < n; ++i) { - expected_raw |= expected[i].node << (meta->node_size * (n - i - 1)); - new_raw |= new[i].node << (meta->node_size * (n - i - 1)); + expected_raw |= expected[i].node << (meta->node_size * i); + new_raw |= new[i].node << (meta->node_size * i); } -// int nodes_missing; -// switch (n_bytes) { -// case 1: -// assert(meta->node_size <= 8); -// nodes_missing = 0; -// break; -// case 2: -// assert(meta->node_size <= 16); -// nodes_missing = 0; -// break; -// case 3: -// assert(meta->node_size <= 8); -// nodes_missing = 1; -// break; -// case 4: -// assert(meta->node_size <= 32); -// nodes_missing = 0; -// break; -// case 5: -// assert(meta->node_size == 8); -// nodes_missing = 1; -// break; -// case 6: -// assert(meta->node_size <= 16); -// nodes_missing = 2 / (meta->node_size / 8); -// break; -// case 7: -// assert(meta->node_size <= 8); -// nodes_missing = 3; -// break; -// case 8: -// assert(meta->node_size <= 64); -// nodes_missing = 0; -// break; -// case 9: -// assert(meta->node_size <= 8); -// nodes_missing = 7; -// break; -// case 10: -// assert(meta->node_size <= 16); -// nodes_missing = 8 / (meta->node_size / 8); -// break; -// case 11: -// assert(meta->node_size <= 8); -// break; -// case 12: -// assert(meta->node_size <= 32); -// break; -// case 13: -// assert(meta->node_size <= 8); -// break; -// case 14: -// assert(meta->node_size <= 16); -// break; -// case 15: -// assert(meta->node_size <= 8); -// break; -// case 16: -// assert(meta->node_size <= 64); -// break; -// default: -// return 0; -// } + int n_bytes = (meta->node_size >> 3) * n; assert(n_bytes <= 16); int must_write; @@ -268,7 +205,7 @@ atomic_dict_atomic_write_nodes_at(unsigned long ix, int n, atomic_dict_node *exp } else { assert(0); } - must_write_nodes = must_write / (meta->node_size / 8); + int must_write_nodes = must_write / (meta->node_size / 8); for (; i < must_write_nodes; ++i) { new_raw |= expected[i].node << (meta->node_size * (meta->node_size - i - 1)); } diff --git a/tests/test_atomic_dict.py b/tests/test_atomic_dict.py index 5e824120..6c2327ae 100644 --- a/tests/test_atomic_dict.py +++ b/tests/test_atomic_dict.py @@ -40,8 +40,7 @@ def test_log_size_bumped(): def test_key_error(): d = AtomicDict() with raises(KeyError): - # noinspection PyStatementEffect - d[0] + d[0] # noqa def test_getitem(): @@ -51,6 +50,15 @@ def test_getitem(): assert c["spam"] == 43 +def test_getitem_confused(): + d = AtomicDict() + d[0] = 1 + d[64] = 2 + d[128] = 3 + with raises(KeyError): + d[256] # noqa + + def test_setitem_updates_a_value_set_at_init(): d = AtomicDict({0: 1}) d[0] = 3 @@ -61,8 +69,10 @@ def test_setitem_inserts_a_value(): d = AtomicDict(initial_size=64 * 4) d[0] = 42 d[2] = 2 + d[128] = 1 assert d[0] == 42 assert d[2] == 2 + assert d[128] == 1 def test_setitem_updates_an_inserted_value(): @@ -73,10 +83,17 @@ def test_setitem_updates_an_inserted_value(): assert d[0] == 2 -# def test_setitem_distance_1_insert(): -# d = AtomicDict({0: 1}) -# d[64] = 42 -# assert d[64] == 42 +def test_setitem_distance_1_insert(): + d = AtomicDict({0: 1}) + d[64] = 42 + assert d[64] == 42 + assert d.debug()['index'][1] == 18 + d = AtomicDict() + d[0] = 1 + d[1] = 2 + d[64] = 3 + assert d[64] == 3 + assert d.debug()['index'][1] == 10 def test_dealloc():