diff options
| author | Mattias EngdegÄrd | 2023-11-04 16:34:09 +0100 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2024-01-13 20:50:37 +0100 |
| commit | d3cefd3e98354929d96c9396e5920e8a123784dc (patch) | |
| tree | b0fd45c15faa88216653413229e759444810585d /src/pdumper.c | |
| parent | c3d0cc50faf588479db62e20ceabe044dd89e244 (diff) | |
| download | emacs-d3cefd3e98354929d96c9396e5920e8a123784dc.tar.gz emacs-d3cefd3e98354929d96c9396e5920e8a123784dc.zip | |
Leaner hash table dumping and thawing
Only dump the actual data, and the test encoded as an enum. This
simplifies dumping, makes dump files smaller and saves space at run
time.
* src/lisp.h (hash_table_std_test_t): New enum.
(struct Lisp_Hash_Table): Add frozen_test member, consuming no extra space.
* src/fns.c (hashfn_user_defined): Now static.
(hash_table_test_from_std): New.
(hash_table_rehash): Rename to...
(hash_table_thaw): ...this and rewrite.
* src/pdumper.c (hash_table_contents): Only include actual data, not
unused space.
(hash_table_std_test): New.
(hash_table_freeze): Set frozen_test from test.
(dump_hash_table): Dump frozen_test, not the whole test struct.
Don't bother other dumping fields that can be derived.
Diffstat (limited to 'src/pdumper.c')
| -rw-r--r-- | src/pdumper.c | 63 |
1 files changed, 27 insertions, 36 deletions
diff --git a/src/pdumper.c b/src/pdumper.c index 8072148c542..e4349f0cb17 100644 --- a/src/pdumper.c +++ b/src/pdumper.c | |||
| @@ -2646,34 +2646,26 @@ dump_vectorlike_generic (struct dump_context *ctx, | |||
| 2646 | return offset; | 2646 | return offset; |
| 2647 | } | 2647 | } |
| 2648 | 2648 | ||
| 2649 | /* Return a vector of KEY, VALUE pairs in the given hash table H. The | 2649 | /* Return a vector of KEY, VALUE pairs in the given hash table H. |
| 2650 | first H->count pairs are valid, and the rest are unbound. */ | 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 | if (h->test.hashfn == hashfn_user_defined) | 2654 | ptrdiff_t old_size = HASH_TABLE_SIZE (h); |
| 2655 | error ("cannot dump hash tables with user-defined tests"); /* Bug#36769 */ | 2655 | ptrdiff_t size = h->count; |
| 2656 | |||
| 2657 | ptrdiff_t size = HASH_TABLE_SIZE (h); | ||
| 2658 | Lisp_Object key_and_value = make_uninit_vector (2 * size); | 2656 | Lisp_Object key_and_value = make_uninit_vector (2 * size); |
| 2659 | ptrdiff_t n = 0; | 2657 | ptrdiff_t n = 0; |
| 2660 | 2658 | ||
| 2661 | /* Make sure key_and_value ends up in the same order; charset.c | 2659 | /* Make sure key_and_value ends up in the same order; charset.c |
| 2662 | relies on it by expecting hash table indices to stay constant | 2660 | relies on it by expecting hash table indices to stay constant |
| 2663 | across the dump. */ | 2661 | across the dump. */ |
| 2664 | for (ptrdiff_t i = 0; i < size; i++) | 2662 | for (ptrdiff_t i = 0; i < old_size; i++) |
| 2665 | if (!NILP (HASH_HASH (h, i))) | 2663 | if (!NILP (HASH_HASH (h, i))) |
| 2666 | { | 2664 | { |
| 2667 | ASET (key_and_value, n++, HASH_KEY (h, i)); | 2665 | ASET (key_and_value, n++, HASH_KEY (h, i)); |
| 2668 | ASET (key_and_value, n++, HASH_VALUE (h, i)); | 2666 | ASET (key_and_value, n++, HASH_VALUE (h, i)); |
| 2669 | } | 2667 | } |
| 2670 | 2668 | ||
| 2671 | while (n < 2 * size) | ||
| 2672 | { | ||
| 2673 | ASET (key_and_value, n++, Qunbound); | ||
| 2674 | ASET (key_and_value, n++, Qnil); | ||
| 2675 | } | ||
| 2676 | |||
| 2677 | return key_and_value; | 2669 | return key_and_value; |
| 2678 | } | 2670 | } |
| 2679 | 2671 | ||
| @@ -2686,25 +2678,32 @@ dump_hash_table_list (struct dump_context *ctx) | |||
| 2686 | return 0; | 2678 | return 0; |
| 2687 | } | 2679 | } |
| 2688 | 2680 | ||
| 2689 | static void | 2681 | static hash_table_std_test_t |
| 2690 | hash_table_freeze (struct Lisp_Hash_Table *h) | 2682 | hash_table_std_test (const struct hash_table_test *t) |
| 2691 | { | 2683 | { |
| 2692 | ptrdiff_t npairs = ASIZE (h->key_and_value) / 2; | 2684 | if (BASE_EQ (t->name, Qeq)) |
| 2693 | h->key_and_value = hash_table_contents (h); | 2685 | return Test_eq; |
| 2694 | h->next = h->hash = make_fixnum (npairs); | 2686 | if (BASE_EQ (t->name, Qeql)) |
| 2695 | h->index = make_fixnum (ASIZE (h->index)); | 2687 | return Test_eql; |
| 2696 | h->next_free = (npairs == h->count ? -1 : h->count); | 2688 | if (BASE_EQ (t->name, Qequal)) |
| 2689 | return Test_equal; | ||
| 2690 | error ("cannot dump hash tables with user-defined tests"); /* Bug#36769 */ | ||
| 2697 | } | 2691 | } |
| 2698 | 2692 | ||
| 2693 | /* Compact contents and discard inessential information from a hash table, | ||
| 2694 | preparing it for dumping. | ||
| 2695 | See `hash_table_thaw' for the code that restores the object to a usable | ||
| 2696 | state. */ | ||
| 2699 | static void | 2697 | static void |
| 2700 | hash_table_thaw (Lisp_Object hash) | 2698 | hash_table_freeze (struct Lisp_Hash_Table *h) |
| 2701 | { | 2699 | { |
| 2702 | struct Lisp_Hash_Table *h = XHASH_TABLE (hash); | 2700 | h->key_and_value = hash_table_contents (h); |
| 2703 | h->hash = make_nil_vector (XFIXNUM (h->hash)); | 2701 | eassert (ASIZE (h->key_and_value) == h->count * 2); |
| 2704 | h->next = Fmake_vector (h->next, make_fixnum (-1)); | 2702 | h->next = Qnil; |
| 2705 | h->index = Fmake_vector (h->index, make_fixnum (-1)); | 2703 | h->hash = Qnil; |
| 2706 | 2704 | h->index = Qnil; | |
| 2707 | hash_table_rehash (hash); | 2705 | h->count = 0; |
| 2706 | h->frozen_test = hash_table_std_test (&h->test); | ||
| 2708 | } | 2707 | } |
| 2709 | 2708 | ||
| 2710 | static dump_off | 2709 | static dump_off |
| @@ -2724,19 +2723,11 @@ dump_hash_table (struct dump_context *ctx, Lisp_Object object) | |||
| 2724 | dump_pseudovector_lisp_fields (ctx, &out->header, &hash->header); | 2723 | dump_pseudovector_lisp_fields (ctx, &out->header, &hash->header); |
| 2725 | /* TODO: dump the hash bucket vectors synchronously here to keep | 2724 | /* TODO: dump the hash bucket vectors synchronously here to keep |
| 2726 | them as close to the hash table as possible. */ | 2725 | them as close to the hash table as possible. */ |
| 2727 | DUMP_FIELD_COPY (out, hash, count); | ||
| 2728 | DUMP_FIELD_COPY (out, hash, next_free); | ||
| 2729 | DUMP_FIELD_COPY (out, hash, weakness); | 2726 | DUMP_FIELD_COPY (out, hash, weakness); |
| 2730 | DUMP_FIELD_COPY (out, hash, purecopy); | 2727 | DUMP_FIELD_COPY (out, hash, purecopy); |
| 2731 | DUMP_FIELD_COPY (out, hash, mutable); | 2728 | DUMP_FIELD_COPY (out, hash, mutable); |
| 2729 | DUMP_FIELD_COPY (out, hash, frozen_test); | ||
| 2732 | dump_field_lv (ctx, out, hash, &hash->key_and_value, WEIGHT_STRONG); | 2730 | dump_field_lv (ctx, out, hash, &hash->key_and_value, WEIGHT_STRONG); |
| 2733 | dump_field_lv (ctx, out, hash, &hash->test.name, WEIGHT_STRONG); | ||
| 2734 | dump_field_lv (ctx, out, hash, &hash->test.user_hash_function, | ||
| 2735 | WEIGHT_STRONG); | ||
| 2736 | dump_field_lv (ctx, out, hash, &hash->test.user_cmp_function, | ||
| 2737 | WEIGHT_STRONG); | ||
| 2738 | dump_field_emacs_ptr (ctx, out, hash, &hash->test.cmpfn); | ||
| 2739 | dump_field_emacs_ptr (ctx, out, hash, &hash->test.hashfn); | ||
| 2740 | eassert (hash->next_weak == NULL); | 2731 | eassert (hash->next_weak == NULL); |
| 2741 | return finish_dump_pvec (ctx, &out->header); | 2732 | return finish_dump_pvec (ctx, &out->header); |
| 2742 | } | 2733 | } |