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 | |
| 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.
| -rw-r--r-- | src/lread.c | 114 | ||||
| -rw-r--r-- | test/src/lread-tests.el | 77 |
2 files changed, 126 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; |
diff --git a/test/src/lread-tests.el b/test/src/lread-tests.el index d9b31a6c438..51c93b38e4f 100644 --- a/test/src/lread-tests.el +++ b/test/src/lread-tests.el | |||
| @@ -398,4 +398,81 @@ literals (Bug#20852)." | |||
| 398 | (should (equal val "a\xff")) ; not "aÿ" | 398 | (should (equal val "a\xff")) ; not "aÿ" |
| 399 | (should-not (multibyte-string-p val)))) | 399 | (should-not (multibyte-string-p val)))) |
| 400 | 400 | ||
| 401 | (ert-deftest lread-unintern () | ||
| 402 | (cl-flet ((oa-syms (oa) (let ((syms nil)) | ||
| 403 | (mapatoms (lambda (s) (push s syms)) oa) | ||
| 404 | (sort syms)))) | ||
| 405 | (let* ((oa (obarray-make)) | ||
| 406 | (s1 (intern "abc" oa)) | ||
| 407 | (s2 (intern "def" oa))) | ||
| 408 | (should-not (eq s1 'abc)) | ||
| 409 | (should (eq (unintern "xyz" oa) nil)) | ||
| 410 | (should (eq (unintern 'abc oa) nil)) | ||
| 411 | (should (eq (unintern 'xyz oa) nil)) | ||
| 412 | (should (equal (oa-syms oa) (list s1 s2))) | ||
| 413 | (should (eq (intern-soft "abc" oa) s1)) | ||
| 414 | (should (eq (intern-soft "def" oa) s2)) | ||
| 415 | |||
| 416 | (should (eq (unintern "abc" oa) t)) | ||
| 417 | (should-not (intern-soft "abc" oa)) | ||
| 418 | (should (eq (intern-soft "def" oa) s2)) | ||
| 419 | (should (equal (oa-syms oa) (list s2))) | ||
| 420 | |||
| 421 | (should (eq (unintern s2 oa) t)) | ||
| 422 | (should-not (intern-soft "def" oa)) | ||
| 423 | (should (eq (oa-syms oa) nil))) | ||
| 424 | |||
| 425 | ;; with shorthand | ||
| 426 | (let* ((oa (obarray-make)) | ||
| 427 | (read-symbol-shorthands '(("a·" . "ZZ•"))) | ||
| 428 | (s1 (intern "a·abc" oa)) | ||
| 429 | (s2 (intern "a·def" oa)) | ||
| 430 | (s3 (intern "a·ghi" oa))) | ||
| 431 | (should (equal (oa-syms oa) (list s1 s2 s3))) | ||
| 432 | (should (equal (symbol-name s1) "ZZ•abc")) | ||
| 433 | (should (eq (intern-soft "ZZ•abc" oa) s1)) | ||
| 434 | (should (eq (intern-soft "a·abc" oa) s1)) | ||
| 435 | (should (eq (intern-soft "ZZ•def" oa) s2)) | ||
| 436 | (should (eq (intern-soft "a·def" oa) s2)) | ||
| 437 | (should (eq (intern-soft "ZZ•ghi" oa) s3)) | ||
| 438 | (should (eq (intern-soft "a·ghi" oa) s3)) | ||
| 439 | |||
| 440 | ;; unintern using long name | ||
| 441 | (should (eq (unintern "ZZ•abc" oa) t)) | ||
| 442 | (should-not (intern-soft "ZZ•abc" oa)) | ||
| 443 | (should-not (intern-soft "a·abc" oa)) | ||
| 444 | (should (equal (oa-syms oa) (list s2 s3))) | ||
| 445 | (should (eq (intern-soft "ZZ•def" oa) s2)) | ||
| 446 | (should (eq (intern-soft "a·def" oa) s2)) | ||
| 447 | (should (eq (intern-soft "ZZ•ghi" oa) s3)) | ||
| 448 | (should (eq (intern-soft "a·ghi" oa) s3)) | ||
| 449 | |||
| 450 | ;; unintern using short name | ||
| 451 | (should (eq (unintern "a·def" oa) t)) | ||
| 452 | (should-not (intern-soft "ZZ•def" oa)) | ||
| 453 | (should-not (intern-soft "a·def" oa)) | ||
| 454 | (should (equal (oa-syms oa) (list s3))) | ||
| 455 | (should (eq (intern-soft "ZZ•ghi" oa) s3)) | ||
| 456 | (should (eq (intern-soft "a·ghi" oa) s3)) | ||
| 457 | |||
| 458 | ;; unintern using symbol | ||
| 459 | (should (eq (unintern s3 oa) t)) | ||
| 460 | (should-not (intern-soft "ZZ•ghi" oa)) | ||
| 461 | (should-not (intern-soft "a·ghi" oa)) | ||
| 462 | (should (eq (oa-syms oa) nil))) | ||
| 463 | |||
| 464 | ;; edge case: a symbol whose true name is another's shorthand | ||
| 465 | (let* ((oa (obarray-make)) | ||
| 466 | (s1 (intern "a·abc" oa)) | ||
| 467 | (read-symbol-shorthands '(("a·" . "ZZ•"))) | ||
| 468 | (s2 (intern "a·abc" oa))) | ||
| 469 | (should (equal (oa-syms oa) (list s2 s1))) | ||
| 470 | (should (equal (symbol-name s1) "a·abc")) | ||
| 471 | (should (equal (symbol-name s2) "ZZ•abc")) | ||
| 472 | |||
| 473 | ;; unintern by symbol | ||
| 474 | (should (eq (unintern s1 oa) t)) | ||
| 475 | (should (equal (oa-syms oa) (list s2)))) | ||
| 476 | )) | ||
| 477 | |||
| 401 | ;;; lread-tests.el ends here | 478 | ;;; lread-tests.el ends here |