diff options
| author | Mattias EngdegÄrd | 2023-10-27 22:15:09 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2024-01-13 20:50:38 +0100 |
| commit | fa5c07fc87d557e642fc325852e8d0c87a9c176e (patch) | |
| tree | c83fc3b64e9a642d9b303a19852bc484a8b46de2 /src | |
| parent | 49fd4d120deb0b878ad262aea7d849c7275bc12c (diff) | |
| download | emacs-fa5c07fc87d557e642fc325852e8d0c87a9c176e.tar.gz emacs-fa5c07fc87d557e642fc325852e8d0c87a9c176e.zip | |
Use non-Lisp allocation for internal hash-table vectors
Using xmalloc for allocating these arrays is much cheaper than using
Lisp vectors since they are no longer marked or swept by the GC, and
deallocated much sooner. This makes GC faster and less frequent, and
improves temporal locality.
Zero-sized tables use NULL for their (0-length) vectors except the
index vector which has size 1 and uses a shared constant static vector
since it cannot be modified anyway. This makes creation and
destruction of zero-sized hash tables very fast; they consume no
memory outside the base object.
* src/lisp.h (struct Lisp_Hash_Table): Retype the index, next, hash
and key_and_value vectors from Lisp_Object to appropriately typed
arrays (although hash values are still stored as Lisp fixnums). Add
explicit table_size and index_size members. All users updated.
* src/alloc.c (gcstat): Add total_hash_table_bytes.
(hash_table_allocated_bytes): New.
(cleanup_vector): Free hash table vectors when sweeping
the object.
(hash_table_alloc_bytes, hash_table_free_bytes): New.
(sweep_vectors): Update gcstat.total_hash_table_bytes.
(total_bytes_of_live_objects): Use it.
(purecopy_hash_table): Adapt allocation of hash table vectors.
(process_mark_stack): No more Lisp slots in the struct to trace.
* src/fns.c (empty_hash_index_vector): New.
(allocate_hash_table): Allocate without automatically GCed slots.
(alloc_larger_vector): Remove.
(make_hash_table, copy_hash_table, maybe_resize_hash_table):
Adapt vector allocation and initialisation.
* src/pdumper.c (hash_table_freeze, hash_table_thaw, dump_hash_table)
(dump_hash_table_contents):
Adapt dumping and loading to field changes.
Diffstat (limited to 'src')
| -rw-r--r-- | src/alloc.c | 86 | ||||
| -rw-r--r-- | src/fns.c | 217 | ||||
| -rw-r--r-- | src/lisp.h | 61 | ||||
| -rw-r--r-- | src/pdumper.c | 56 | ||||
| -rw-r--r-- | src/print.c | 4 |
5 files changed, 290 insertions, 134 deletions
diff --git a/src/alloc.c b/src/alloc.c index 636b4972c84..7432163db25 100644 --- a/src/alloc.c +++ b/src/alloc.c | |||
| @@ -359,8 +359,16 @@ static struct gcstat | |||
| 359 | object_ct total_floats, total_free_floats; | 359 | object_ct total_floats, total_free_floats; |
| 360 | object_ct total_intervals, total_free_intervals; | 360 | object_ct total_intervals, total_free_intervals; |
| 361 | object_ct total_buffers; | 361 | object_ct total_buffers; |
| 362 | |||
| 363 | /* Size of the ancillary arrays of live hash-table objects. | ||
| 364 | The objects themselves are not included (counted as vectors above). */ | ||
| 365 | byte_ct total_hash_table_bytes; | ||
| 362 | } gcstat; | 366 | } gcstat; |
| 363 | 367 | ||
| 368 | /* Total size of ancillary arrays of all allocated hash-table objects, | ||
| 369 | both dead and alive. This number is always kept up-to-date. */ | ||
| 370 | static ptrdiff_t hash_table_allocated_bytes = 0; | ||
| 371 | |||
| 364 | /* Points to memory space allocated as "spare", to be freed if we run | 372 | /* Points to memory space allocated as "spare", to be freed if we run |
| 365 | out of memory. We keep one large block, four cons-blocks, and | 373 | out of memory. We keep one large block, four cons-blocks, and |
| 366 | two string blocks. */ | 374 | two string blocks. */ |
| @@ -3430,6 +3438,23 @@ cleanup_vector (struct Lisp_Vector *vector) | |||
| 3430 | } | 3438 | } |
| 3431 | #endif | 3439 | #endif |
| 3432 | break; | 3440 | break; |
| 3441 | case PVEC_HASH_TABLE: | ||
| 3442 | { | ||
| 3443 | struct Lisp_Hash_Table *h = PSEUDOVEC_STRUCT (vector, Lisp_Hash_Table); | ||
| 3444 | if (h->table_size > 0) | ||
| 3445 | { | ||
| 3446 | eassert (h->index_size > 1); | ||
| 3447 | xfree (h->index); | ||
| 3448 | xfree (h->key_and_value); | ||
| 3449 | xfree (h->next); | ||
| 3450 | xfree (h->hash); | ||
| 3451 | ptrdiff_t bytes = (h->table_size * (2 * sizeof *h->key_and_value | ||
| 3452 | + sizeof *h->hash | ||
| 3453 | + sizeof *h->next) | ||
| 3454 | + h->index_size * sizeof *h->index); | ||
| 3455 | hash_table_allocated_bytes -= bytes; | ||
| 3456 | } | ||
| 3457 | } | ||
| 3433 | /* Keep the switch exhaustive. */ | 3458 | /* Keep the switch exhaustive. */ |
| 3434 | case PVEC_NORMAL_VECTOR: | 3459 | case PVEC_NORMAL_VECTOR: |
| 3435 | case PVEC_FREE: | 3460 | case PVEC_FREE: |
| @@ -3440,7 +3465,6 @@ cleanup_vector (struct Lisp_Vector *vector) | |||
| 3440 | case PVEC_WINDOW: | 3465 | case PVEC_WINDOW: |
| 3441 | case PVEC_BOOL_VECTOR: | 3466 | case PVEC_BOOL_VECTOR: |
| 3442 | case PVEC_BUFFER: | 3467 | case PVEC_BUFFER: |
| 3443 | case PVEC_HASH_TABLE: | ||
| 3444 | case PVEC_TERMINAL: | 3468 | case PVEC_TERMINAL: |
| 3445 | case PVEC_WINDOW_CONFIGURATION: | 3469 | case PVEC_WINDOW_CONFIGURATION: |
| 3446 | case PVEC_OTHER: | 3470 | case PVEC_OTHER: |
| @@ -3554,6 +3578,8 @@ sweep_vectors (void) | |||
| 3554 | lisp_free (lv); | 3578 | lisp_free (lv); |
| 3555 | } | 3579 | } |
| 3556 | } | 3580 | } |
| 3581 | |||
| 3582 | gcstat.total_hash_table_bytes = hash_table_allocated_bytes; | ||
| 3557 | } | 3583 | } |
| 3558 | 3584 | ||
| 3559 | /* Maximum number of elements in a vector. This is a macro so that it | 3585 | /* Maximum number of elements in a vector. This is a macro so that it |
| @@ -5606,6 +5632,28 @@ valid_lisp_object_p (Lisp_Object obj) | |||
| 5606 | return 0; | 5632 | return 0; |
| 5607 | } | 5633 | } |
| 5608 | 5634 | ||
| 5635 | /* Like xmalloc, but makes allocation count toward the total consing. | ||
| 5636 | Return NULL for a zero-sized allocation. */ | ||
| 5637 | void * | ||
| 5638 | hash_table_alloc_bytes (ptrdiff_t nbytes) | ||
| 5639 | { | ||
| 5640 | if (nbytes == 0) | ||
| 5641 | return NULL; | ||
| 5642 | tally_consing (nbytes); | ||
| 5643 | hash_table_allocated_bytes += nbytes; | ||
| 5644 | return xmalloc (nbytes); | ||
| 5645 | } | ||
| 5646 | |||
| 5647 | /* Like xfree, but makes allocation count toward the total consing. */ | ||
| 5648 | void | ||
| 5649 | hash_table_free_bytes (void *p, ptrdiff_t nbytes) | ||
| 5650 | { | ||
| 5651 | tally_consing (-nbytes); | ||
| 5652 | hash_table_allocated_bytes -= nbytes; | ||
| 5653 | xfree (p); | ||
| 5654 | } | ||
| 5655 | |||
| 5656 | |||
| 5609 | /*********************************************************************** | 5657 | /*********************************************************************** |
| 5610 | Pure Storage Management | 5658 | Pure Storage Management |
| 5611 | ***********************************************************************/ | 5659 | ***********************************************************************/ |
| @@ -5897,10 +5945,28 @@ purecopy_hash_table (struct Lisp_Hash_Table *table) | |||
| 5897 | pure->test.name = purecopy (table->test.name); | 5945 | pure->test.name = purecopy (table->test.name); |
| 5898 | pure->test.user_hash_function = purecopy (table->test.user_hash_function); | 5946 | pure->test.user_hash_function = purecopy (table->test.user_hash_function); |
| 5899 | pure->test.user_cmp_function = purecopy (table->test.user_cmp_function); | 5947 | pure->test.user_cmp_function = purecopy (table->test.user_cmp_function); |
| 5900 | pure->hash = purecopy (table->hash); | 5948 | |
| 5901 | pure->next = purecopy (table->next); | 5949 | if (table->table_size > 0) |
| 5902 | pure->index = purecopy (table->index); | 5950 | { |
| 5903 | pure->key_and_value = purecopy (table->key_and_value); | 5951 | ptrdiff_t hash_bytes = table->table_size * sizeof *table->hash; |
| 5952 | pure->hash = pure_alloc (hash_bytes, -(int)sizeof *table->hash); | ||
| 5953 | memcpy (pure->hash, table->hash, hash_bytes); | ||
| 5954 | |||
| 5955 | ptrdiff_t next_bytes = table->table_size * sizeof *table->next; | ||
| 5956 | pure->next = pure_alloc (next_bytes, -(int)sizeof *table->next); | ||
| 5957 | memcpy (pure->next, table->next, next_bytes); | ||
| 5958 | |||
| 5959 | ptrdiff_t nvalues = table->table_size * 2; | ||
| 5960 | ptrdiff_t kv_bytes = nvalues * sizeof *table->key_and_value; | ||
| 5961 | pure->key_and_value = pure_alloc (kv_bytes, | ||
| 5962 | -(int)sizeof *table->key_and_value); | ||
| 5963 | for (ptrdiff_t i = 0; i < nvalues; i++) | ||
| 5964 | pure->key_and_value[i] = purecopy (table->key_and_value[i]); | ||
| 5965 | |||
| 5966 | ptrdiff_t index_bytes = table->index_size * sizeof *table->index; | ||
| 5967 | pure->index = pure_alloc (index_bytes, -(int)sizeof *table->index); | ||
| 5968 | memcpy (pure->index, table->index, index_bytes); | ||
| 5969 | } | ||
| 5904 | 5970 | ||
| 5905 | return pure; | 5971 | return pure; |
| 5906 | } | 5972 | } |
| @@ -6084,6 +6150,7 @@ total_bytes_of_live_objects (void) | |||
| 6084 | tot += object_bytes (gcstat.total_floats, sizeof (struct Lisp_Float)); | 6150 | tot += object_bytes (gcstat.total_floats, sizeof (struct Lisp_Float)); |
| 6085 | tot += object_bytes (gcstat.total_intervals, sizeof (struct interval)); | 6151 | tot += object_bytes (gcstat.total_intervals, sizeof (struct interval)); |
| 6086 | tot += object_bytes (gcstat.total_strings, sizeof (struct Lisp_String)); | 6152 | tot += object_bytes (gcstat.total_strings, sizeof (struct Lisp_String)); |
| 6153 | tot += gcstat.total_hash_table_bytes; | ||
| 6087 | return tot; | 6154 | return tot; |
| 6088 | } | 6155 | } |
| 6089 | 6156 | ||
| @@ -7227,23 +7294,20 @@ process_mark_stack (ptrdiff_t base_sp) | |||
| 7227 | case PVEC_HASH_TABLE: | 7294 | case PVEC_HASH_TABLE: |
| 7228 | { | 7295 | { |
| 7229 | struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *)ptr; | 7296 | struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *)ptr; |
| 7230 | ptrdiff_t size = ptr->header.size & PSEUDOVECTOR_SIZE_MASK; | ||
| 7231 | set_vector_marked (ptr); | 7297 | set_vector_marked (ptr); |
| 7232 | mark_stack_push_values (ptr->contents, size); | ||
| 7233 | mark_stack_push_value (h->test.name); | 7298 | mark_stack_push_value (h->test.name); |
| 7234 | mark_stack_push_value (h->test.user_hash_function); | 7299 | mark_stack_push_value (h->test.user_hash_function); |
| 7235 | mark_stack_push_value (h->test.user_cmp_function); | 7300 | mark_stack_push_value (h->test.user_cmp_function); |
| 7236 | if (h->weakness == Weak_None) | 7301 | if (h->weakness == Weak_None) |
| 7237 | mark_stack_push_value (h->key_and_value); | 7302 | mark_stack_push_values (h->key_and_value, |
| 7303 | 2 * h->table_size); | ||
| 7238 | else | 7304 | else |
| 7239 | { | 7305 | { |
| 7240 | /* For weak tables, mark only the vector and not its | 7306 | /* For weak tables, don't mark the |
| 7241 | contents --- that's what makes it weak. */ | 7307 | contents --- that's what makes it weak. */ |
| 7242 | eassert (h->next_weak == NULL); | 7308 | eassert (h->next_weak == NULL); |
| 7243 | h->next_weak = weak_hash_tables; | 7309 | h->next_weak = weak_hash_tables; |
| 7244 | weak_hash_tables = h; | 7310 | weak_hash_tables = h; |
| 7245 | if (!PURE_P (h->key_and_value)) | ||
| 7246 | set_vector_marked (XVECTOR (h->key_and_value)); | ||
| 7247 | } | 7311 | } |
| 7248 | break; | 7312 | break; |
| 7249 | } | 7313 | } |
| @@ -4275,17 +4275,20 @@ CHECK_HASH_TABLE (Lisp_Object x) | |||
| 4275 | static void | 4275 | static void |
| 4276 | set_hash_next_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val) | 4276 | set_hash_next_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val) |
| 4277 | { | 4277 | { |
| 4278 | gc_aset (h->next, idx, make_fixnum (val)); | 4278 | eassert (idx >= 0 && idx < h->table_size); |
| 4279 | h->next[idx] = val; | ||
| 4279 | } | 4280 | } |
| 4280 | static void | 4281 | static void |
| 4281 | set_hash_hash_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) | 4282 | set_hash_hash_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) |
| 4282 | { | 4283 | { |
| 4283 | gc_aset (h->hash, idx, val); | 4284 | eassert (idx >= 0 && idx < h->table_size); |
| 4285 | h->hash[idx] = val; | ||
| 4284 | } | 4286 | } |
| 4285 | static void | 4287 | static void |
| 4286 | set_hash_index_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val) | 4288 | set_hash_index_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val) |
| 4287 | { | 4289 | { |
| 4288 | gc_aset (h->index, idx, make_fixnum (val)); | 4290 | eassert (idx >= 0 && idx < h->index_size); |
| 4291 | h->index[idx] = val; | ||
| 4289 | } | 4292 | } |
| 4290 | 4293 | ||
| 4291 | /* If OBJ is a Lisp hash table, return a pointer to its struct | 4294 | /* If OBJ is a Lisp hash table, return a pointer to its struct |
| @@ -4375,7 +4378,8 @@ larger_vector (Lisp_Object vec, ptrdiff_t incr_min, ptrdiff_t nitems_max) | |||
| 4375 | static ptrdiff_t | 4378 | static ptrdiff_t |
| 4376 | HASH_NEXT (struct Lisp_Hash_Table *h, ptrdiff_t idx) | 4379 | HASH_NEXT (struct Lisp_Hash_Table *h, ptrdiff_t idx) |
| 4377 | { | 4380 | { |
| 4378 | return XFIXNUM (AREF (h->next, idx)); | 4381 | eassert (idx >= 0 && idx < h->table_size); |
| 4382 | return h->next[idx]; | ||
| 4379 | } | 4383 | } |
| 4380 | 4384 | ||
| 4381 | /* Return the index of the element in hash table H that is the start | 4385 | /* Return the index of the element in hash table H that is the start |
| @@ -4384,7 +4388,8 @@ HASH_NEXT (struct Lisp_Hash_Table *h, ptrdiff_t idx) | |||
| 4384 | static ptrdiff_t | 4388 | static ptrdiff_t |
| 4385 | HASH_INDEX (struct Lisp_Hash_Table *h, ptrdiff_t idx) | 4389 | HASH_INDEX (struct Lisp_Hash_Table *h, ptrdiff_t idx) |
| 4386 | { | 4390 | { |
| 4387 | return XFIXNUM (AREF (h->index, idx)); | 4391 | eassert (idx >= 0 && idx < h->index_size); |
| 4392 | return h->index[idx]; | ||
| 4388 | } | 4393 | } |
| 4389 | 4394 | ||
| 4390 | /* Restore a hash table's mutability after the critical section exits. */ | 4395 | /* Restore a hash table's mutability after the critical section exits. */ |
| @@ -4495,8 +4500,7 @@ struct hash_table_test const | |||
| 4495 | static struct Lisp_Hash_Table * | 4500 | static struct Lisp_Hash_Table * |
| 4496 | allocate_hash_table (void) | 4501 | allocate_hash_table (void) |
| 4497 | { | 4502 | { |
| 4498 | return ALLOCATE_PSEUDOVECTOR (struct Lisp_Hash_Table, | 4503 | return ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Hash_Table, PVEC_HASH_TABLE); |
| 4499 | index, PVEC_HASH_TABLE); | ||
| 4500 | } | 4504 | } |
| 4501 | 4505 | ||
| 4502 | /* An upper bound on the size of a hash table index. It must fit in | 4506 | /* An upper bound on the size of a hash table index. It must fit in |
| @@ -4528,6 +4532,10 @@ hash_index_size (ptrdiff_t size) | |||
| 4528 | return index_size; | 4532 | return index_size; |
| 4529 | } | 4533 | } |
| 4530 | 4534 | ||
| 4535 | /* Constant hash index vector used when the table size is zero. | ||
| 4536 | This avoids allocating it from the heap. */ | ||
| 4537 | static const ptrdiff_t empty_hash_index_vector[] = {-1}; | ||
| 4538 | |||
| 4531 | /* Create and initialize a new hash table. | 4539 | /* Create and initialize a new hash table. |
| 4532 | 4540 | ||
| 4533 | TEST specifies the test the hash table will use to compare keys. | 4541 | TEST specifies the test the hash table will use to compare keys. |
| @@ -4547,36 +4555,54 @@ Lisp_Object | |||
| 4547 | make_hash_table (struct hash_table_test test, EMACS_INT size, | 4555 | make_hash_table (struct hash_table_test test, EMACS_INT size, |
| 4548 | hash_table_weakness_t weak, bool purecopy) | 4556 | hash_table_weakness_t weak, bool purecopy) |
| 4549 | { | 4557 | { |
| 4550 | struct Lisp_Hash_Table *h; | ||
| 4551 | Lisp_Object table; | ||
| 4552 | ptrdiff_t i; | ||
| 4553 | |||
| 4554 | /* Preconditions. */ | ||
| 4555 | eassert (SYMBOLP (test.name)); | 4558 | eassert (SYMBOLP (test.name)); |
| 4556 | eassert (0 <= size && size <= MOST_POSITIVE_FIXNUM); | 4559 | eassert (0 <= size && size <= min (MOST_POSITIVE_FIXNUM, PTRDIFF_MAX)); |
| 4557 | 4560 | ||
| 4558 | /* Allocate a table and initialize it. */ | 4561 | struct Lisp_Hash_Table *h = allocate_hash_table (); |
| 4559 | h = allocate_hash_table (); | ||
| 4560 | 4562 | ||
| 4561 | /* Initialize hash table slots. */ | ||
| 4562 | h->test = test; | 4563 | h->test = test; |
| 4563 | h->weakness = weak; | 4564 | h->weakness = weak; |
| 4564 | h->count = 0; | 4565 | h->count = 0; |
| 4565 | h->key_and_value = make_vector (2 * size, HASH_UNUSED_ENTRY_KEY); | 4566 | h->table_size = size; |
| 4566 | h->hash = make_nil_vector (size); | 4567 | int index_size = hash_index_size (size); |
| 4567 | h->next = make_vector (size, make_fixnum (-1)); | 4568 | h->index_size = index_size; |
| 4568 | h->index = make_vector (hash_index_size (size), make_fixnum (-1)); | 4569 | |
| 4570 | if (size == 0) | ||
| 4571 | { | ||
| 4572 | h->key_and_value = NULL; | ||
| 4573 | h->hash = NULL; | ||
| 4574 | h->next = NULL; | ||
| 4575 | eassert (index_size == 1); | ||
| 4576 | h->index = (ptrdiff_t *)empty_hash_index_vector; | ||
| 4577 | h->next_free = -1; | ||
| 4578 | } | ||
| 4579 | else | ||
| 4580 | { | ||
| 4581 | h->key_and_value = hash_table_alloc_bytes (2 * size | ||
| 4582 | * sizeof *h->key_and_value); | ||
| 4583 | for (ptrdiff_t i = 0; i < 2 * size; i++) | ||
| 4584 | h->key_and_value[i] = HASH_UNUSED_ENTRY_KEY; | ||
| 4585 | |||
| 4586 | h->hash = hash_table_alloc_bytes (size * sizeof *h->hash); | ||
| 4587 | memclear (h->hash, size * sizeof *h->hash); | ||
| 4588 | |||
| 4589 | h->next = hash_table_alloc_bytes (size * sizeof *h->next); | ||
| 4590 | for (ptrdiff_t i = 0; i < size - 1; i++) | ||
| 4591 | h->next[i] = i + 1; | ||
| 4592 | h->next[size - 1] = -1; | ||
| 4593 | |||
| 4594 | h->index = hash_table_alloc_bytes (index_size * sizeof *h->index); | ||
| 4595 | for (ptrdiff_t i = 0; i < index_size; i++) | ||
| 4596 | h->index[i] = -1; | ||
| 4597 | |||
| 4598 | h->next_free = 0; | ||
| 4599 | } | ||
| 4600 | |||
| 4569 | h->next_weak = NULL; | 4601 | h->next_weak = NULL; |
| 4570 | h->purecopy = purecopy; | 4602 | h->purecopy = purecopy; |
| 4571 | h->mutable = true; | 4603 | h->mutable = true; |
| 4572 | 4604 | ||
| 4573 | /* Set up the free list. */ | 4605 | Lisp_Object table; |
| 4574 | for (i = 0; i < size - 1; ++i) | ||
| 4575 | set_hash_next_slot (h, i, i + 1); | ||
| 4576 | if (size > 0) | ||
| 4577 | set_hash_next_slot (h, size - 1, -1); | ||
| 4578 | h->next_free = size > 0 ? 0 : -1; | ||
| 4579 | |||
| 4580 | XSET_HASH_TABLE (table, h); | 4606 | XSET_HASH_TABLE (table, h); |
| 4581 | eassert (HASH_TABLE_P (table)); | 4607 | eassert (HASH_TABLE_P (table)); |
| 4582 | eassert (XHASH_TABLE (table) == h); | 4608 | eassert (XHASH_TABLE (table) == h); |
| @@ -4597,35 +4623,37 @@ copy_hash_table (struct Lisp_Hash_Table *h1) | |||
| 4597 | h2 = allocate_hash_table (); | 4623 | h2 = allocate_hash_table (); |
| 4598 | *h2 = *h1; | 4624 | *h2 = *h1; |
| 4599 | h2->mutable = true; | 4625 | h2->mutable = true; |
| 4600 | h2->key_and_value = Fcopy_sequence (h1->key_and_value); | 4626 | |
| 4601 | h2->hash = Fcopy_sequence (h1->hash); | 4627 | if (h1->table_size > 0) |
| 4602 | h2->next = Fcopy_sequence (h1->next); | 4628 | { |
| 4603 | h2->index = Fcopy_sequence (h1->index); | 4629 | ptrdiff_t kv_bytes = 2 * h1->table_size * sizeof *h1->key_and_value; |
| 4630 | h2->key_and_value = hash_table_alloc_bytes (kv_bytes); | ||
| 4631 | memcpy (h2->key_and_value, h1->key_and_value, kv_bytes); | ||
| 4632 | |||
| 4633 | ptrdiff_t hash_bytes = h1->table_size * sizeof *h1->hash; | ||
| 4634 | h2->hash = hash_table_alloc_bytes (hash_bytes); | ||
| 4635 | memcpy (h2->hash, h1->hash, hash_bytes); | ||
| 4636 | |||
| 4637 | ptrdiff_t next_bytes = h1->table_size * sizeof *h1->next; | ||
| 4638 | h2->next = hash_table_alloc_bytes (next_bytes); | ||
| 4639 | memcpy (h2->next, h1->next, next_bytes); | ||
| 4640 | |||
| 4641 | ptrdiff_t index_bytes = h1->index_size * sizeof *h1->index; | ||
| 4642 | h2->index = hash_table_alloc_bytes (index_bytes); | ||
| 4643 | memcpy (h2->index, h1->index, index_bytes); | ||
| 4644 | } | ||
| 4604 | XSET_HASH_TABLE (table, h2); | 4645 | XSET_HASH_TABLE (table, h2); |
| 4605 | 4646 | ||
| 4606 | return table; | 4647 | return table; |
| 4607 | } | 4648 | } |
| 4608 | 4649 | ||
| 4609 | 4650 | ||
| 4610 | /* Allocate a Lisp vector of NEW_SIZE elements. | ||
| 4611 | Copy elements from VEC and leave the rest undefined. */ | ||
| 4612 | static Lisp_Object | ||
| 4613 | alloc_larger_vector (Lisp_Object vec, ptrdiff_t new_size) | ||
| 4614 | { | ||
| 4615 | eassert (VECTORP (vec)); | ||
| 4616 | ptrdiff_t old_size = ASIZE (vec); | ||
| 4617 | eassert (new_size >= old_size); | ||
| 4618 | struct Lisp_Vector *v = allocate_vector (new_size); | ||
| 4619 | memcpy (v->contents, XVECTOR (vec)->contents, old_size * sizeof *v->contents); | ||
| 4620 | XSETVECTOR (vec, v); | ||
| 4621 | return vec; | ||
| 4622 | } | ||
| 4623 | |||
| 4624 | /* Compute index into the index vector from a hash value. */ | 4651 | /* Compute index into the index vector from a hash value. */ |
| 4625 | static inline ptrdiff_t | 4652 | static inline ptrdiff_t |
| 4626 | hash_index_index (struct Lisp_Hash_Table *h, Lisp_Object hash_code) | 4653 | hash_index_index (struct Lisp_Hash_Table *h, Lisp_Object hash_code) |
| 4627 | { | 4654 | { |
| 4628 | return XUFIXNUM (hash_code) % ASIZE (h->index); | 4655 | eassert (h->index_size > 0); |
| 4656 | return XUFIXNUM (hash_code) % h->index_size; | ||
| 4629 | } | 4657 | } |
| 4630 | 4658 | ||
| 4631 | /* Resize hash table H if it's too full. If H cannot be resized | 4659 | /* Resize hash table H if it's too full. If H cannot be resized |
| @@ -4650,37 +4678,56 @@ maybe_resize_hash_table (struct Lisp_Hash_Table *h) | |||
| 4650 | 4678 | ||
| 4651 | /* Allocate all the new vectors before updating *H, to | 4679 | /* Allocate all the new vectors before updating *H, to |
| 4652 | avoid problems if memory is exhausted. */ | 4680 | avoid problems if memory is exhausted. */ |
| 4653 | Lisp_Object next = alloc_larger_vector (h->next, new_size); | 4681 | ptrdiff_t *next = hash_table_alloc_bytes (new_size * sizeof *next); |
| 4654 | for (ptrdiff_t i = old_size; i < new_size - 1; i++) | 4682 | for (ptrdiff_t i = old_size; i < new_size - 1; i++) |
| 4655 | ASET (next, i, make_fixnum (i + 1)); | 4683 | next[i] = i + 1; |
| 4656 | ASET (next, new_size - 1, make_fixnum (-1)); | 4684 | next[new_size - 1] = -1; |
| 4657 | 4685 | ||
| 4658 | /* Build the new&larger key_and_value vector, making sure the new | 4686 | Lisp_Object *key_and_value |
| 4659 | fields are initialized to `unbound`. */ | 4687 | = hash_table_alloc_bytes (2 * new_size * sizeof *key_and_value); |
| 4660 | Lisp_Object key_and_value | 4688 | memcpy (key_and_value, h->key_and_value, |
| 4661 | = alloc_larger_vector (h->key_and_value, 2 * new_size); | 4689 | 2 * old_size * sizeof *key_and_value); |
| 4662 | for (ptrdiff_t i = 2 * old_size; i < 2 * new_size; i++) | 4690 | for (ptrdiff_t i = 2 * old_size; i < 2 * new_size; i++) |
| 4663 | ASET (key_and_value, i, HASH_UNUSED_ENTRY_KEY); | 4691 | key_and_value[i] = HASH_UNUSED_ENTRY_KEY; |
| 4664 | 4692 | ||
| 4665 | Lisp_Object hash = alloc_larger_vector (h->hash, new_size); | 4693 | Lisp_Object *hash = hash_table_alloc_bytes (new_size * sizeof *hash); |
| 4666 | memclear (XVECTOR (hash)->contents + old_size, | 4694 | memcpy (hash, h->hash, old_size * sizeof *hash); |
| 4667 | (new_size - old_size) * word_size); | 4695 | memclear (hash + old_size, (new_size - old_size) * word_size); |
| 4696 | |||
| 4697 | ptrdiff_t old_index_size = h->index_size; | ||
| 4668 | ptrdiff_t index_size = hash_index_size (new_size); | 4698 | ptrdiff_t index_size = hash_index_size (new_size); |
| 4669 | h->index = make_vector (index_size, make_fixnum (-1)); | 4699 | ptrdiff_t *index = hash_table_alloc_bytes (index_size * sizeof *index); |
| 4700 | for (ptrdiff_t i = 0; i < index_size; i++) | ||
| 4701 | index[i] = -1; | ||
| 4702 | |||
| 4703 | h->index_size = index_size; | ||
| 4704 | h->table_size = new_size; | ||
| 4705 | h->next_free = old_size; | ||
| 4706 | |||
| 4707 | if (old_index_size > 1) | ||
| 4708 | hash_table_free_bytes (h->index, old_index_size * sizeof *h->index); | ||
| 4709 | h->index = index; | ||
| 4710 | |||
| 4711 | hash_table_free_bytes (h->key_and_value, | ||
| 4712 | 2 * old_size * sizeof *h->key_and_value); | ||
| 4670 | h->key_and_value = key_and_value; | 4713 | h->key_and_value = key_and_value; |
| 4714 | |||
| 4715 | hash_table_free_bytes (h->hash, old_size * sizeof *h->hash); | ||
| 4671 | h->hash = hash; | 4716 | h->hash = hash; |
| 4717 | |||
| 4718 | hash_table_free_bytes (h->next, old_size * sizeof *h->next); | ||
| 4672 | h->next = next; | 4719 | h->next = next; |
| 4673 | h->next_free = old_size; | ||
| 4674 | 4720 | ||
| 4675 | /* Rehash. */ | 4721 | h->key_and_value = key_and_value; |
| 4722 | |||
| 4723 | /* Rehash: all data occupy entries 0..old_size-1. */ | ||
| 4676 | for (ptrdiff_t i = 0; i < old_size; i++) | 4724 | for (ptrdiff_t i = 0; i < old_size; i++) |
| 4677 | if (!NILP (HASH_HASH (h, i))) | 4725 | { |
| 4678 | { | 4726 | Lisp_Object hash_code = HASH_HASH (h, i); |
| 4679 | Lisp_Object hash_code = HASH_HASH (h, i); | 4727 | ptrdiff_t start_of_bucket = hash_index_index (h, hash_code); |
| 4680 | ptrdiff_t start_of_bucket = hash_index_index (h, hash_code); | 4728 | set_hash_next_slot (h, i, HASH_INDEX (h, start_of_bucket)); |
| 4681 | set_hash_next_slot (h, i, HASH_INDEX (h, start_of_bucket)); | 4729 | set_hash_index_slot (h, start_of_bucket, i); |
| 4682 | set_hash_index_slot (h, start_of_bucket, i); | 4730 | } |
| 4683 | } | ||
| 4684 | 4731 | ||
| 4685 | #ifdef ENABLE_CHECKING | 4732 | #ifdef ENABLE_CHECKING |
| 4686 | if (HASH_TABLE_P (Vpurify_flag) && XHASH_TABLE (Vpurify_flag) == h) | 4733 | if (HASH_TABLE_P (Vpurify_flag) && XHASH_TABLE (Vpurify_flag) == h) |
| @@ -4710,14 +4757,22 @@ hash_table_thaw (Lisp_Object hash_table) | |||
| 4710 | /* Freezing discarded most non-essential information; recompute it. | 4757 | /* Freezing discarded most non-essential information; recompute it. |
| 4711 | The allocation is minimal with no room for growth. */ | 4758 | The allocation is minimal with no room for growth. */ |
| 4712 | h->test = *hash_table_test_from_std (h->frozen_test); | 4759 | h->test = *hash_table_test_from_std (h->frozen_test); |
| 4713 | ptrdiff_t size = ASIZE (h->key_and_value) / 2; | 4760 | ptrdiff_t size = h->count; |
| 4714 | h->count = size; | 4761 | h->table_size = size; |
| 4715 | ptrdiff_t index_size = hash_index_size (size); | 4762 | ptrdiff_t index_size = hash_index_size (size); |
| 4763 | h->index_size = index_size; | ||
| 4716 | h->next_free = -1; | 4764 | h->next_free = -1; |
| 4717 | 4765 | ||
| 4718 | h->hash = make_nil_vector (size); | 4766 | h->hash = hash_table_alloc_bytes (size * sizeof *h->hash); |
| 4719 | h->next = make_vector (size, make_fixnum (-1)); | 4767 | memclear (h->hash, size * sizeof *h->hash); |
| 4720 | h->index = make_vector (index_size, make_fixnum (-1)); | 4768 | |
| 4769 | h->next = hash_table_alloc_bytes (size * sizeof *h->next); | ||
| 4770 | for (ptrdiff_t i = 0; i < size; i++) | ||
| 4771 | h->next[i] = -1; | ||
| 4772 | |||
| 4773 | h->index = hash_table_alloc_bytes (index_size * sizeof *h->index); | ||
| 4774 | for (ptrdiff_t i = 0; i < index_size; i++) | ||
| 4775 | h->index[i] = -1; | ||
| 4721 | 4776 | ||
| 4722 | /* Recompute the actual hash codes for each entry in the table. | 4777 | /* Recompute the actual hash codes for each entry in the table. |
| 4723 | Order is still invalid. */ | 4778 | Order is still invalid. */ |
| @@ -4843,7 +4898,7 @@ hash_clear (struct Lisp_Hash_Table *h) | |||
| 4843 | if (h->count > 0) | 4898 | if (h->count > 0) |
| 4844 | { | 4899 | { |
| 4845 | ptrdiff_t size = HASH_TABLE_SIZE (h); | 4900 | ptrdiff_t size = HASH_TABLE_SIZE (h); |
| 4846 | memclear (xvector_contents (h->hash), size * word_size); | 4901 | memclear (h->hash, size * word_size); |
| 4847 | for (ptrdiff_t i = 0; i < size; i++) | 4902 | for (ptrdiff_t i = 0; i < size; i++) |
| 4848 | { | 4903 | { |
| 4849 | set_hash_next_slot (h, i, i < size - 1 ? i + 1 : -1); | 4904 | set_hash_next_slot (h, i, i < size - 1 ? i + 1 : -1); |
| @@ -4851,8 +4906,8 @@ hash_clear (struct Lisp_Hash_Table *h) | |||
| 4851 | set_hash_value_slot (h, i, Qnil); | 4906 | set_hash_value_slot (h, i, Qnil); |
| 4852 | } | 4907 | } |
| 4853 | 4908 | ||
| 4854 | for (ptrdiff_t i = 0; i < ASIZE (h->index); i++) | 4909 | for (ptrdiff_t i = 0; i < h->index_size; i++) |
| 4855 | ASET (h->index, i, make_fixnum (-1)); | 4910 | h->index[i] = -1; |
| 4856 | 4911 | ||
| 4857 | h->next_free = 0; | 4912 | h->next_free = 0; |
| 4858 | h->count = 0; | 4913 | h->count = 0; |
| @@ -4890,7 +4945,7 @@ keep_entry_p (hash_table_weakness_t weakness, | |||
| 4890 | bool | 4945 | bool |
| 4891 | sweep_weak_table (struct Lisp_Hash_Table *h, bool remove_entries_p) | 4946 | sweep_weak_table (struct Lisp_Hash_Table *h, bool remove_entries_p) |
| 4892 | { | 4947 | { |
| 4893 | ptrdiff_t n = gc_asize (h->index); | 4948 | ptrdiff_t n = h->index_size; |
| 4894 | bool marked = false; | 4949 | bool marked = false; |
| 4895 | 4950 | ||
| 4896 | for (ptrdiff_t bucket = 0; bucket < n; ++bucket) | 4951 | for (ptrdiff_t bucket = 0; bucket < n; ++bucket) |
| @@ -4928,8 +4983,7 @@ sweep_weak_table (struct Lisp_Hash_Table *h, bool remove_entries_p) | |||
| 4928 | /* Clear key, value, and hash. */ | 4983 | /* Clear key, value, and hash. */ |
| 4929 | set_hash_key_slot (h, i, HASH_UNUSED_ENTRY_KEY); | 4984 | set_hash_key_slot (h, i, HASH_UNUSED_ENTRY_KEY); |
| 4930 | set_hash_value_slot (h, i, Qnil); | 4985 | set_hash_value_slot (h, i, Qnil); |
| 4931 | if (!NILP (h->hash)) | 4986 | set_hash_hash_slot (h, i, Qnil); |
| 4932 | set_hash_hash_slot (h, i, Qnil); | ||
| 4933 | 4987 | ||
| 4934 | eassert (h->count != 0); | 4988 | eassert (h->count != 0); |
| 4935 | h->count--; | 4989 | h->count--; |
| @@ -5563,7 +5617,7 @@ DEFUN ("internal--hash-table-histogram", | |||
| 5563 | struct Lisp_Hash_Table *h = check_hash_table (hash_table); | 5617 | struct Lisp_Hash_Table *h = check_hash_table (hash_table); |
| 5564 | ptrdiff_t size = HASH_TABLE_SIZE (h); | 5618 | ptrdiff_t size = HASH_TABLE_SIZE (h); |
| 5565 | ptrdiff_t *freq = xzalloc (size * sizeof *freq); | 5619 | ptrdiff_t *freq = xzalloc (size * sizeof *freq); |
| 5566 | ptrdiff_t index_size = ASIZE (h->index); | 5620 | ptrdiff_t index_size = h->index_size; |
| 5567 | for (ptrdiff_t i = 0; i < index_size; i++) | 5621 | for (ptrdiff_t i = 0; i < index_size; i++) |
| 5568 | { | 5622 | { |
| 5569 | ptrdiff_t n = 0; | 5623 | ptrdiff_t n = 0; |
| @@ -5591,7 +5645,7 @@ Internal use only. */) | |||
| 5591 | { | 5645 | { |
| 5592 | struct Lisp_Hash_Table *h = check_hash_table (hash_table); | 5646 | struct Lisp_Hash_Table *h = check_hash_table (hash_table); |
| 5593 | Lisp_Object ret = Qnil; | 5647 | Lisp_Object ret = Qnil; |
| 5594 | ptrdiff_t index_size = ASIZE (h->index); | 5648 | ptrdiff_t index_size = h->index_size; |
| 5595 | for (ptrdiff_t i = 0; i < index_size; i++) | 5649 | for (ptrdiff_t i = 0; i < index_size; i++) |
| 5596 | { | 5650 | { |
| 5597 | Lisp_Object bucket = Qnil; | 5651 | Lisp_Object bucket = Qnil; |
| @@ -5612,8 +5666,7 @@ DEFUN ("internal--hash-table-index-size", | |||
| 5612 | (Lisp_Object hash_table) | 5666 | (Lisp_Object hash_table) |
| 5613 | { | 5667 | { |
| 5614 | struct Lisp_Hash_Table *h = check_hash_table (hash_table); | 5668 | struct Lisp_Hash_Table *h = check_hash_table (hash_table); |
| 5615 | ptrdiff_t index_size = ASIZE (h->index); | 5669 | return make_int (h->index_size); |
| 5616 | return make_int (index_size); | ||
| 5617 | } | 5670 | } |
| 5618 | 5671 | ||
| 5619 | 5672 | ||
diff --git a/src/lisp.h b/src/lisp.h index f863df6bca0..dd457392cca 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -2450,25 +2450,28 @@ struct Lisp_Hash_Table | |||
| 2450 | The table is physically split into three vectors (hash, next, | 2450 | The table is physically split into three vectors (hash, next, |
| 2451 | key_and_value) which may or may not be beneficial. */ | 2451 | key_and_value) which may or may not be beneficial. */ |
| 2452 | 2452 | ||
| 2453 | /* Vector of hash codes, or nil if the table needs rehashing. | 2453 | /* Bucket vector. An entry of -1 indicates no item is present, |
| 2454 | If the I-th entry is unused, then hash[I] should be nil. */ | 2454 | and a nonnegative entry is the index of the first item in |
| 2455 | Lisp_Object hash; | 2455 | a collision chain. |
| 2456 | This vector is index_size entries long. | ||
| 2457 | If index_size is 1 (and table_size is 0), then this is the | ||
| 2458 | constant read-only vector {-1}, shared between all instances. | ||
| 2459 | Otherwise it is heap-allocated. */ | ||
| 2460 | ptrdiff_t *index; | ||
| 2461 | ptrdiff_t index_size; /* Size of the index vector. */ | ||
| 2462 | |||
| 2463 | ptrdiff_t table_size; /* Size of the next and hash vectors. */ | ||
| 2464 | |||
| 2465 | /* Vector of hash codes. Each entry is either a fixnum, or nil if unused. | ||
| 2466 | This vector is table_size entries long. */ | ||
| 2467 | Lisp_Object *hash; | ||
| 2456 | 2468 | ||
| 2457 | /* Vector used to chain entries. If entry I is free, next[I] is the | 2469 | /* Vector used to chain entries. If entry I is free, next[I] is the |
| 2458 | entry number of the next free item. If entry I is non-free, | 2470 | entry number of the next free item. If entry I is non-free, |
| 2459 | next[I] is the index of the next entry in the collision chain, | 2471 | next[I] is the index of the next entry in the collision chain, |
| 2460 | or -1 if there is such entry. */ | 2472 | or -1 if there is no such entry. |
| 2461 | Lisp_Object next; | 2473 | This vector is table_size entries long. */ |
| 2462 | 2474 | ptrdiff_t *next; | |
| 2463 | /* Bucket vector. An entry of -1 indicates no item is present, | ||
| 2464 | and a nonnegative entry is the index of the first item in | ||
| 2465 | a collision chain. This vector's size can be larger than the | ||
| 2466 | hash table size to reduce collisions. */ | ||
| 2467 | Lisp_Object index; | ||
| 2468 | |||
| 2469 | /* Only the fields above are traced normally by the GC. The ones after | ||
| 2470 | 'index' are special and are either ignored by the GC or traced in | ||
| 2471 | a special way (e.g. because of weakness). */ | ||
| 2472 | 2475 | ||
| 2473 | /* Number of key/value entries in the table. */ | 2476 | /* Number of key/value entries in the table. */ |
| 2474 | ptrdiff_t count; | 2477 | ptrdiff_t count; |
| @@ -2494,8 +2497,9 @@ struct Lisp_Hash_Table | |||
| 2494 | /* Vector of keys and values. The key of item I is found at index | 2497 | /* Vector of keys and values. The key of item I is found at index |
| 2495 | 2 * I, the value is found at index 2 * I + 1. | 2498 | 2 * I, the value is found at index 2 * I + 1. |
| 2496 | If the key is HASH_UNUSED_ENTRY_KEY, then this slot is unused. | 2499 | If the key is HASH_UNUSED_ENTRY_KEY, then this slot is unused. |
| 2497 | This is gc_marked specially if the table is weak. */ | 2500 | This is gc_marked specially if the table is weak. |
| 2498 | Lisp_Object key_and_value; | 2501 | This vector is 2 * table_size entries long. */ |
| 2502 | Lisp_Object *key_and_value; | ||
| 2499 | 2503 | ||
| 2500 | /* The comparison and hash functions. */ | 2504 | /* The comparison and hash functions. */ |
| 2501 | struct hash_table_test test; | 2505 | struct hash_table_test test; |
| @@ -2506,9 +2510,6 @@ struct Lisp_Hash_Table | |||
| 2506 | struct Lisp_Hash_Table *next_weak; | 2510 | struct Lisp_Hash_Table *next_weak; |
| 2507 | } GCALIGNED_STRUCT; | 2511 | } GCALIGNED_STRUCT; |
| 2508 | 2512 | ||
| 2509 | /* Sanity-check pseudovector layout. */ | ||
| 2510 | verify (offsetof (struct Lisp_Hash_Table, hash) == header_size); | ||
| 2511 | |||
| 2512 | /* Key value that marks an unused hash table entry. */ | 2513 | /* Key value that marks an unused hash table entry. */ |
| 2513 | #define HASH_UNUSED_ENTRY_KEY Qunbound | 2514 | #define HASH_UNUSED_ENTRY_KEY Qunbound |
| 2514 | 2515 | ||
| @@ -2539,28 +2540,31 @@ XHASH_TABLE (Lisp_Object a) | |||
| 2539 | INLINE Lisp_Object | 2540 | INLINE Lisp_Object |
| 2540 | HASH_KEY (const struct Lisp_Hash_Table *h, ptrdiff_t idx) | 2541 | HASH_KEY (const struct Lisp_Hash_Table *h, ptrdiff_t idx) |
| 2541 | { | 2542 | { |
| 2542 | return AREF (h->key_and_value, 2 * idx); | 2543 | eassert (idx >= 0 && idx < h->table_size); |
| 2544 | return h->key_and_value[2 * idx]; | ||
| 2543 | } | 2545 | } |
| 2544 | 2546 | ||
| 2545 | /* Value is the value part of entry IDX in hash table H. */ | 2547 | /* Value is the value part of entry IDX in hash table H. */ |
| 2546 | INLINE Lisp_Object | 2548 | INLINE Lisp_Object |
| 2547 | HASH_VALUE (const struct Lisp_Hash_Table *h, ptrdiff_t idx) | 2549 | HASH_VALUE (const struct Lisp_Hash_Table *h, ptrdiff_t idx) |
| 2548 | { | 2550 | { |
| 2549 | return AREF (h->key_and_value, 2 * idx + 1); | 2551 | eassert (idx >= 0 && idx < h->table_size); |
| 2552 | return h->key_and_value[2 * idx + 1]; | ||
| 2550 | } | 2553 | } |
| 2551 | 2554 | ||
| 2552 | /* Value is the hash code computed for entry IDX in hash table H. */ | 2555 | /* Value is the hash code computed for entry IDX in hash table H. */ |
| 2553 | INLINE Lisp_Object | 2556 | INLINE Lisp_Object |
| 2554 | HASH_HASH (const struct Lisp_Hash_Table *h, ptrdiff_t idx) | 2557 | HASH_HASH (const struct Lisp_Hash_Table *h, ptrdiff_t idx) |
| 2555 | { | 2558 | { |
| 2556 | return AREF (h->hash, idx); | 2559 | eassert (idx >= 0 && idx < h->table_size); |
| 2560 | return h->hash[idx]; | ||
| 2557 | } | 2561 | } |
| 2558 | 2562 | ||
| 2559 | /* Value is the size of hash table H. */ | 2563 | /* Value is the size of hash table H. */ |
| 2560 | INLINE ptrdiff_t | 2564 | INLINE ptrdiff_t |
| 2561 | HASH_TABLE_SIZE (const struct Lisp_Hash_Table *h) | 2565 | HASH_TABLE_SIZE (const struct Lisp_Hash_Table *h) |
| 2562 | { | 2566 | { |
| 2563 | return ASIZE (h->next); | 2567 | return h->table_size; |
| 2564 | } | 2568 | } |
| 2565 | 2569 | ||
| 2566 | /* Compute hash value for KEY in hash table H. */ | 2570 | /* Compute hash value for KEY in hash table H. */ |
| @@ -3781,13 +3785,15 @@ vcopy (Lisp_Object v, ptrdiff_t offset, Lisp_Object const *args, | |||
| 3781 | INLINE void | 3785 | INLINE void |
| 3782 | set_hash_key_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) | 3786 | set_hash_key_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) |
| 3783 | { | 3787 | { |
| 3784 | gc_aset (h->key_and_value, 2 * idx, val); | 3788 | eassert (idx >= 0 && idx < h->table_size); |
| 3789 | h->key_and_value[2 * idx] = val; | ||
| 3785 | } | 3790 | } |
| 3786 | 3791 | ||
| 3787 | INLINE void | 3792 | INLINE void |
| 3788 | set_hash_value_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) | 3793 | set_hash_value_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) |
| 3789 | { | 3794 | { |
| 3790 | gc_aset (h->key_and_value, 2 * idx + 1, val); | 3795 | eassert (idx >= 0 && idx < h->table_size); |
| 3796 | h->key_and_value[2 * idx + 1] = val;; | ||
| 3791 | } | 3797 | } |
| 3792 | 3798 | ||
| 3793 | /* Use these functions to set Lisp_Object | 3799 | /* Use these functions to set Lisp_Object |
| @@ -4458,6 +4464,9 @@ extern void syms_of_alloc (void); | |||
| 4458 | extern struct buffer *allocate_buffer (void) ATTRIBUTE_RETURNS_NONNULL; | 4464 | extern struct buffer *allocate_buffer (void) ATTRIBUTE_RETURNS_NONNULL; |
| 4459 | extern int valid_lisp_object_p (Lisp_Object); | 4465 | extern int valid_lisp_object_p (Lisp_Object); |
| 4460 | 4466 | ||
| 4467 | void *hash_table_alloc_bytes (ptrdiff_t nbytes); | ||
| 4468 | void hash_table_free_bytes (void *p, ptrdiff_t nbytes); | ||
| 4469 | |||
| 4461 | /* Defined in gmalloc.c. */ | 4470 | /* Defined in gmalloc.c. */ |
| 4462 | #if !defined DOUG_LEA_MALLOC && !defined HYBRID_MALLOC && !defined SYSTEM_MALLOC | 4471 | #if !defined DOUG_LEA_MALLOC && !defined HYBRID_MALLOC && !defined SYSTEM_MALLOC |
| 4463 | extern size_t __malloc_extra_blocks; | 4472 | extern size_t __malloc_extra_blocks; |
diff --git a/src/pdumper.c b/src/pdumper.c index e4349f0cb17..8a93c45e07b 100644 --- a/src/pdumper.c +++ b/src/pdumper.c | |||
| @@ -2648,12 +2648,13 @@ dump_vectorlike_generic (struct dump_context *ctx, | |||
| 2648 | 2648 | ||
| 2649 | /* Return a vector of KEY, VALUE pairs in the given hash table H. | 2649 | /* Return a vector of KEY, VALUE pairs in the given hash table H. |
| 2650 | No room for growth is included. */ | 2650 | No room for growth is included. */ |
| 2651 | static Lisp_Object | 2651 | static Lisp_Object * |
| 2652 | hash_table_contents (struct Lisp_Hash_Table *h) | 2652 | hash_table_contents (struct Lisp_Hash_Table *h) |
| 2653 | { | 2653 | { |
| 2654 | ptrdiff_t old_size = HASH_TABLE_SIZE (h); | 2654 | ptrdiff_t old_size = HASH_TABLE_SIZE (h); |
| 2655 | ptrdiff_t size = h->count; | 2655 | ptrdiff_t size = h->count; |
| 2656 | Lisp_Object key_and_value = make_uninit_vector (2 * size); | 2656 | Lisp_Object *key_and_value = hash_table_alloc_bytes (2 * size |
| 2657 | * sizeof *key_and_value); | ||
| 2657 | ptrdiff_t n = 0; | 2658 | ptrdiff_t n = 0; |
| 2658 | 2659 | ||
| 2659 | /* Make sure key_and_value ends up in the same order; charset.c | 2660 | /* Make sure key_and_value ends up in the same order; charset.c |
| @@ -2662,8 +2663,8 @@ hash_table_contents (struct Lisp_Hash_Table *h) | |||
| 2662 | for (ptrdiff_t i = 0; i < old_size; i++) | 2663 | for (ptrdiff_t i = 0; i < old_size; i++) |
| 2663 | if (!NILP (HASH_HASH (h, i))) | 2664 | if (!NILP (HASH_HASH (h, i))) |
| 2664 | { | 2665 | { |
| 2665 | ASET (key_and_value, n++, HASH_KEY (h, i)); | 2666 | key_and_value[n++] = HASH_KEY (h, i); |
| 2666 | ASET (key_and_value, n++, HASH_VALUE (h, i)); | 2667 | key_and_value[n++] = HASH_VALUE (h, i); |
| 2667 | } | 2668 | } |
| 2668 | 2669 | ||
| 2669 | return key_and_value; | 2670 | return key_and_value; |
| @@ -2698,15 +2699,38 @@ static void | |||
| 2698 | hash_table_freeze (struct Lisp_Hash_Table *h) | 2699 | hash_table_freeze (struct Lisp_Hash_Table *h) |
| 2699 | { | 2700 | { |
| 2700 | h->key_and_value = hash_table_contents (h); | 2701 | h->key_and_value = hash_table_contents (h); |
| 2701 | eassert (ASIZE (h->key_and_value) == h->count * 2); | 2702 | h->next = NULL; |
| 2702 | h->next = Qnil; | 2703 | h->hash = NULL; |
| 2703 | h->hash = Qnil; | 2704 | h->index = NULL; |
| 2704 | h->index = Qnil; | 2705 | h->table_size = 0; |
| 2705 | h->count = 0; | 2706 | h->index_size = 0; |
| 2706 | h->frozen_test = hash_table_std_test (&h->test); | 2707 | h->frozen_test = hash_table_std_test (&h->test); |
| 2707 | } | 2708 | } |
| 2708 | 2709 | ||
| 2709 | static dump_off | 2710 | static dump_off |
| 2711 | dump_hash_table_contents (struct dump_context *ctx, struct Lisp_Hash_Table *h) | ||
| 2712 | { | ||
| 2713 | dump_align_output (ctx, DUMP_ALIGNMENT); | ||
| 2714 | dump_off start_offset = ctx->offset; | ||
| 2715 | ptrdiff_t n = 2 * h->count; | ||
| 2716 | |||
| 2717 | struct dump_flags old_flags = ctx->flags; | ||
| 2718 | ctx->flags.pack_objects = true; | ||
| 2719 | |||
| 2720 | for (ptrdiff_t i = 0; i < n; i++) | ||
| 2721 | { | ||
| 2722 | Lisp_Object out; | ||
| 2723 | const Lisp_Object *slot = &h->key_and_value[i]; | ||
| 2724 | dump_object_start (ctx, &out, sizeof out); | ||
| 2725 | dump_field_lv (ctx, &out, slot, slot, WEIGHT_STRONG); | ||
| 2726 | dump_object_finish (ctx, &out, sizeof out); | ||
| 2727 | } | ||
| 2728 | |||
| 2729 | ctx->flags = old_flags; | ||
| 2730 | return start_offset; | ||
| 2731 | } | ||
| 2732 | |||
| 2733 | static dump_off | ||
| 2710 | dump_hash_table (struct dump_context *ctx, Lisp_Object object) | 2734 | dump_hash_table (struct dump_context *ctx, Lisp_Object object) |
| 2711 | { | 2735 | { |
| 2712 | #if CHECK_STRUCTS && !defined HASH_Lisp_Hash_Table_6D63EDB618 | 2736 | #if CHECK_STRUCTS && !defined HASH_Lisp_Hash_Table_6D63EDB618 |
| @@ -2721,15 +2745,21 @@ dump_hash_table (struct dump_context *ctx, Lisp_Object object) | |||
| 2721 | 2745 | ||
| 2722 | START_DUMP_PVEC (ctx, &hash->header, struct Lisp_Hash_Table, out); | 2746 | START_DUMP_PVEC (ctx, &hash->header, struct Lisp_Hash_Table, out); |
| 2723 | dump_pseudovector_lisp_fields (ctx, &out->header, &hash->header); | 2747 | dump_pseudovector_lisp_fields (ctx, &out->header, &hash->header); |
| 2724 | /* TODO: dump the hash bucket vectors synchronously here to keep | 2748 | DUMP_FIELD_COPY (out, hash, count); |
| 2725 | them as close to the hash table as possible. */ | ||
| 2726 | DUMP_FIELD_COPY (out, hash, weakness); | 2749 | DUMP_FIELD_COPY (out, hash, weakness); |
| 2727 | DUMP_FIELD_COPY (out, hash, purecopy); | 2750 | DUMP_FIELD_COPY (out, hash, purecopy); |
| 2728 | DUMP_FIELD_COPY (out, hash, mutable); | 2751 | DUMP_FIELD_COPY (out, hash, mutable); |
| 2729 | DUMP_FIELD_COPY (out, hash, frozen_test); | 2752 | DUMP_FIELD_COPY (out, hash, frozen_test); |
| 2730 | dump_field_lv (ctx, out, hash, &hash->key_and_value, WEIGHT_STRONG); | 2753 | if (hash->key_and_value) |
| 2754 | dump_field_fixup_later (ctx, out, hash, &hash->key_and_value); | ||
| 2731 | eassert (hash->next_weak == NULL); | 2755 | eassert (hash->next_weak == NULL); |
| 2732 | return finish_dump_pvec (ctx, &out->header); | 2756 | dump_off offset = finish_dump_pvec (ctx, &out->header); |
| 2757 | if (hash->key_and_value) | ||
| 2758 | dump_remember_fixup_ptr_raw | ||
| 2759 | (ctx, | ||
| 2760 | offset + dump_offsetof (struct Lisp_Hash_Table, key_and_value), | ||
| 2761 | dump_hash_table_contents (ctx, hash)); | ||
| 2762 | return offset; | ||
| 2733 | } | 2763 | } |
| 2734 | 2764 | ||
| 2735 | static dump_off | 2765 | static dump_off |
diff --git a/src/print.c b/src/print.c index cc8df639f4f..c27c66ae40a 100644 --- a/src/print.c +++ b/src/print.c | |||
| @@ -1455,8 +1455,8 @@ print_preprocess (Lisp_Object obj) | |||
| 1455 | if (HASH_TABLE_P (obj)) | 1455 | if (HASH_TABLE_P (obj)) |
| 1456 | { | 1456 | { |
| 1457 | struct Lisp_Hash_Table *h = XHASH_TABLE (obj); | 1457 | struct Lisp_Hash_Table *h = XHASH_TABLE (obj); |
| 1458 | obj = h->key_and_value; | 1458 | pp_stack_push_values (h->key_and_value, |
| 1459 | continue; | 1459 | 2 * h->table_size); |
| 1460 | } | 1460 | } |
| 1461 | break; | 1461 | break; |
| 1462 | } | 1462 | } |