diff options
| author | Mattias EngdegÄrd | 2025-07-19 16:15:47 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2025-07-19 16:48:11 +0200 |
| commit | f4a9673f615aa8d1fad499784fdcd11ac0ec4042 (patch) | |
| tree | f0009c8133bbf89fc73d6fa10ed8f59f4ed9589d /src | |
| parent | e9deec70dac822f2f312497c6d7a7bd6b4e648bb (diff) | |
| download | emacs-f4a9673f615aa8d1fad499784fdcd11ac0ec4042.tar.gz emacs-f4a9673f615aa8d1fad499784fdcd11ac0ec4042.zip | |
Speed up unintern, and fix symbol shorthand edge case (bug#79035)
Don't do a full lookup if the argument is a symbol, and only compute the
hash index once. Fix a bug that occurred when there is another symbol
whose shorthand is equal to the true name of the symbol being removed.
* src/lread.c (Funintern): Rewrite for speed and correctness.
(oblookup_last_bucket_number, oblookup): Remove now unused variable.
* test/src/lread-tests.el (lread-unintern): New test.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lread.c | 114 |
1 files changed, 49 insertions, 65 deletions
diff --git a/src/lread.c b/src/lread.c index 00b9a33e45a..287528ab32d 100644 --- a/src/lread.c +++ b/src/lread.c | |||
| @@ -4916,10 +4916,6 @@ string_to_number (char const *string, int base, ptrdiff_t *plen) | |||
| 4916 | 4916 | ||
| 4917 | static Lisp_Object initial_obarray; | 4917 | static Lisp_Object initial_obarray; |
| 4918 | 4918 | ||
| 4919 | /* `oblookup' stores the bucket number here, for the sake of Funintern. */ | ||
| 4920 | |||
| 4921 | static size_t oblookup_last_bucket_number; | ||
| 4922 | |||
| 4923 | static Lisp_Object make_obarray (unsigned bits); | 4919 | static Lisp_Object make_obarray (unsigned bits); |
| 4924 | 4920 | ||
| 4925 | /* Slow path obarray check: return the obarray to use or signal an error. */ | 4921 | /* Slow path obarray check: return the obarray to use or signal an error. */ |
| @@ -5130,6 +5126,14 @@ it defaults to the value of `obarray'. */) | |||
| 5130 | } | 5126 | } |
| 5131 | } | 5127 | } |
| 5132 | 5128 | ||
| 5129 | /* Bucket index of the string STR of length SIZE_BYTE bytes in obarray OA. */ | ||
| 5130 | static ptrdiff_t | ||
| 5131 | obarray_index (struct Lisp_Obarray *oa, const char *str, ptrdiff_t size_byte) | ||
| 5132 | { | ||
| 5133 | EMACS_UINT hash = hash_char_array (str, size_byte); | ||
| 5134 | return knuth_hash (reduce_emacs_uint_to_hash_hash (hash), oa->size_bits); | ||
| 5135 | } | ||
| 5136 | |||
| 5133 | DEFUN ("unintern", Funintern, Sunintern, 2, 2, 0, | 5137 | DEFUN ("unintern", Funintern, Sunintern, 2, 2, 0, |
| 5134 | doc: /* Delete the symbol named NAME, if any, from OBARRAY. | 5138 | doc: /* Delete the symbol named NAME, if any, from OBARRAY. |
| 5135 | The value is t if a symbol was found and deleted, nil otherwise. | 5139 | The value is t if a symbol was found and deleted, nil otherwise. |
| @@ -5138,89 +5142,70 @@ is deleted, if it belongs to OBARRAY--no other symbol is deleted. | |||
| 5138 | OBARRAY, if nil, defaults to the value of the variable `obarray'. */) | 5142 | OBARRAY, if nil, defaults to the value of the variable `obarray'. */) |
| 5139 | (Lisp_Object name, Lisp_Object obarray) | 5143 | (Lisp_Object name, Lisp_Object obarray) |
| 5140 | { | 5144 | { |
| 5141 | register Lisp_Object tem; | ||
| 5142 | Lisp_Object string; | ||
| 5143 | |||
| 5144 | if (NILP (obarray)) obarray = Vobarray; | 5145 | if (NILP (obarray)) obarray = Vobarray; |
| 5145 | obarray = check_obarray (obarray); | 5146 | obarray = check_obarray (obarray); |
| 5146 | 5147 | ||
| 5148 | Lisp_Object sym; | ||
| 5147 | if (SYMBOLP (name)) | 5149 | if (SYMBOLP (name)) |
| 5148 | { | 5150 | sym = BARE_SYMBOL_P (name) ? name : XSYMBOL_WITH_POS (name)->sym; |
| 5149 | if (!BARE_SYMBOL_P (name)) | ||
| 5150 | name = XSYMBOL_WITH_POS (name)->sym; | ||
| 5151 | string = SYMBOL_NAME (name); | ||
| 5152 | } | ||
| 5153 | else | 5151 | else |
| 5154 | { | 5152 | { |
| 5155 | CHECK_STRING (name); | 5153 | CHECK_STRING (name); |
| 5156 | string = name; | 5154 | char *longhand = NULL; |
| 5155 | ptrdiff_t longhand_chars = 0; | ||
| 5156 | ptrdiff_t longhand_bytes = 0; | ||
| 5157 | sym = oblookup_considering_shorthand (obarray, SSDATA (name), | ||
| 5158 | SCHARS (name), SBYTES (name), | ||
| 5159 | &longhand, &longhand_chars, | ||
| 5160 | &longhand_bytes); | ||
| 5161 | xfree(longhand); | ||
| 5162 | if (FIXNUMP (sym)) | ||
| 5163 | return Qnil; | ||
| 5157 | } | 5164 | } |
| 5158 | 5165 | ||
| 5159 | char *longhand = NULL; | 5166 | /* There are plenty of symbols which will screw up the Emacs |
| 5160 | ptrdiff_t longhand_chars = 0; | ||
| 5161 | ptrdiff_t longhand_bytes = 0; | ||
| 5162 | tem = oblookup_considering_shorthand (obarray, SSDATA (string), | ||
| 5163 | SCHARS (string), SBYTES (string), | ||
| 5164 | &longhand, &longhand_chars, | ||
| 5165 | &longhand_bytes); | ||
| 5166 | if (longhand) | ||
| 5167 | xfree(longhand); | ||
| 5168 | |||
| 5169 | if (FIXNUMP (tem)) | ||
| 5170 | return Qnil; | ||
| 5171 | /* If arg was a symbol, don't delete anything but that symbol itself. */ | ||
| 5172 | if (BARE_SYMBOL_P (name) && !BASE_EQ (name, tem)) | ||
| 5173 | return Qnil; | ||
| 5174 | |||
| 5175 | /* There are plenty of other symbols which will screw up the Emacs | ||
| 5176 | session if we unintern them, as well as even more ways to use | 5167 | session if we unintern them, as well as even more ways to use |
| 5177 | `setq' or `fset' or whatnot to make the Emacs session | 5168 | `setq' or `fset' or whatnot to make the Emacs session |
| 5178 | unusable. Let's not go down this silly road. --Stef */ | 5169 | unusable. We don't try to prevent such mistakes here. */ |
| 5179 | /* if (NILP (tem) || EQ (tem, Qt)) | ||
| 5180 | error ("Attempt to unintern t or nil"); */ | ||
| 5181 | |||
| 5182 | struct Lisp_Symbol *sym = XBARE_SYMBOL (tem); | ||
| 5183 | sym->u.s.interned = SYMBOL_UNINTERNED; | ||
| 5184 | 5170 | ||
| 5185 | ptrdiff_t idx = oblookup_last_bucket_number; | 5171 | struct Lisp_Obarray *o = XOBARRAY (obarray); |
| 5186 | Lisp_Object *loc = &XOBARRAY (obarray)->buckets[idx]; | 5172 | Lisp_Object symname = SYMBOL_NAME (sym); |
| 5173 | ptrdiff_t idx = obarray_index (o, SSDATA (symname), SBYTES (symname)); | ||
| 5174 | Lisp_Object *loc = &o->buckets[idx]; | ||
| 5175 | if (BASE_EQ (*loc, make_fixnum (0))) | ||
| 5176 | return Qnil; | ||
| 5187 | 5177 | ||
| 5188 | eassert (BARE_SYMBOL_P (*loc)); | 5178 | struct Lisp_Symbol *s = XBARE_SYMBOL (sym); |
| 5189 | struct Lisp_Symbol *prev = XBARE_SYMBOL (*loc); | 5179 | struct Lisp_Symbol *prev = XBARE_SYMBOL (*loc); |
| 5190 | if (sym == prev) | 5180 | if (prev == s) |
| 5191 | *loc = sym->u.s.next ? make_lisp_symbol (sym->u.s.next) : make_fixnum (0); | 5181 | *loc = s->u.s.next ? make_lisp_symbol (s->u.s.next) : make_fixnum (0); |
| 5192 | else | 5182 | else |
| 5193 | while (1) | 5183 | { |
| 5194 | { | 5184 | do |
| 5195 | struct Lisp_Symbol *next = prev->u.s.next; | 5185 | { |
| 5196 | if (next == sym) | 5186 | struct Lisp_Symbol *next = prev->u.s.next; |
| 5197 | { | 5187 | if (next == s) |
| 5198 | prev->u.s.next = next->u.s.next; | 5188 | { |
| 5199 | break; | 5189 | prev->u.s.next = next->u.s.next; |
| 5200 | } | 5190 | goto removed; |
| 5201 | prev = next; | 5191 | } |
| 5202 | } | 5192 | prev = next; |
| 5203 | 5193 | } | |
| 5204 | XOBARRAY (obarray)->count--; | 5194 | while (prev); |
| 5195 | return Qnil; | ||
| 5196 | } | ||
| 5205 | 5197 | ||
| 5198 | removed: | ||
| 5199 | s->u.s.interned = SYMBOL_UNINTERNED; | ||
| 5200 | o->count--; | ||
| 5206 | return Qt; | 5201 | return Qt; |
| 5207 | } | 5202 | } |
| 5208 | 5203 | ||
| 5209 | 5204 | ||
| 5210 | /* Bucket index of the string STR of length SIZE_BYTE bytes in obarray OA. */ | ||
| 5211 | static ptrdiff_t | ||
| 5212 | obarray_index (struct Lisp_Obarray *oa, const char *str, ptrdiff_t size_byte) | ||
| 5213 | { | ||
| 5214 | EMACS_UINT hash = hash_char_array (str, size_byte); | ||
| 5215 | return knuth_hash (reduce_emacs_uint_to_hash_hash (hash), oa->size_bits); | ||
| 5216 | } | ||
| 5217 | |||
| 5218 | /* Return the symbol in OBARRAY whose name matches the string | 5205 | /* Return the symbol in OBARRAY whose name matches the string |
| 5219 | of SIZE characters (SIZE_BYTE bytes) at PTR. | 5206 | of SIZE characters (SIZE_BYTE bytes) at PTR. |
| 5220 | If there is no such symbol, return the integer bucket number of | 5207 | If there is no such symbol, return the integer bucket number of |
| 5221 | where the symbol would be if it were present. | 5208 | where the symbol would be if it were present. */ |
| 5222 | |||
| 5223 | Also store the bucket number in oblookup_last_bucket_number. */ | ||
| 5224 | 5209 | ||
| 5225 | Lisp_Object | 5210 | Lisp_Object |
| 5226 | oblookup (Lisp_Object obarray, register const char *ptr, ptrdiff_t size, ptrdiff_t size_byte) | 5211 | oblookup (Lisp_Object obarray, register const char *ptr, ptrdiff_t size, ptrdiff_t size_byte) |
| @@ -5229,7 +5214,6 @@ oblookup (Lisp_Object obarray, register const char *ptr, ptrdiff_t size, ptrdiff | |||
| 5229 | ptrdiff_t idx = obarray_index (o, ptr, size_byte); | 5214 | ptrdiff_t idx = obarray_index (o, ptr, size_byte); |
| 5230 | Lisp_Object bucket = o->buckets[idx]; | 5215 | Lisp_Object bucket = o->buckets[idx]; |
| 5231 | 5216 | ||
| 5232 | oblookup_last_bucket_number = idx; | ||
| 5233 | if (!BASE_EQ (bucket, make_fixnum (0))) | 5217 | if (!BASE_EQ (bucket, make_fixnum (0))) |
| 5234 | { | 5218 | { |
| 5235 | Lisp_Object sym = bucket; | 5219 | Lisp_Object sym = bucket; |