Fix string-replace when the string is mutable
The `from` string argument is converted to a regexp and cached. When `from` is a mutable string this can cause wrong results in the following calls to string-replace. So the string is first converted to an immutable string to be used as the key for the cache.
This commit is contained in:
parent
80aac79507
commit
1753335d34
|
@ -483,4 +483,11 @@
|
|||
(test "foo\\1bar" string-replace "foo===bar" #rx"(=+)" "\\1")
|
||||
(test "foo\\1bar" string-replace "foo===bar" #px"={3}" "\\1"))
|
||||
|
||||
;test that mutable string are not cached incorrectly
|
||||
(let ([str (string-copy "_1_")])
|
||||
(test "!!! _2_" string-replace "_1_ _2_" str "!!!") ;add str to the internal cache
|
||||
(string-set! str 1 #\2)
|
||||
(test "_1_ !!!" string-replace "_1_ _2_" str "!!!") ;verify that the new str is used
|
||||
)
|
||||
|
||||
(report-errs)
|
||||
|
|
|
@ -100,17 +100,36 @@
|
|||
(string-join (internal-split 'string-normalize-spaces str sep trim? +?)
|
||||
space))
|
||||
|
||||
(define replace-cache (make-weak-hasheq))
|
||||
|
||||
;; Caches for string-replace:
|
||||
;; A mutable string weakly holds a immutable copy until it is collected
|
||||
;; or modified (and used as a argument of string-replace).
|
||||
;; The immutable copy weakly holds the regexp that is used in string-replace.
|
||||
;; Using string->immutable-string directly in string-replace is not a useful
|
||||
;; because the immutable copy could be immediately collected.
|
||||
|
||||
(define immutable-cache (make-weak-hasheq))
|
||||
(define (string->immutable-string/cache str)
|
||||
(if (immutable? str)
|
||||
str
|
||||
(let ([old (hash-ref immutable-cache str #f)])
|
||||
(if (and old (string=? str old))
|
||||
old
|
||||
(let ([new (string->immutable-string str)])
|
||||
(hash-set! immutable-cache str new)
|
||||
new)))))
|
||||
|
||||
(define replace-cache (make-weak-hash))
|
||||
(define (string-replace str from to #:all? [all? #t])
|
||||
(unless (string? str) (raise-argument-error 'string-replace "string?" str))
|
||||
(unless (string? to) (raise-argument-error 'string-replace "string?" to))
|
||||
(unless (or (string? from) (regexp? from))
|
||||
(raise-argument-error 'string-replace "(or/c string? regexp?)" from))
|
||||
(define from*
|
||||
(if (regexp? from)
|
||||
from
|
||||
(hash-ref! replace-cache from
|
||||
(λ() (if (string? from)
|
||||
(regexp (regexp-quote from))
|
||||
(raise-argument-error 'string-replace "string?" from))))))
|
||||
(hash-ref! replace-cache (string->immutable-string/cache from)
|
||||
(λ() (regexp (regexp-quote from))))))
|
||||
(define to* (regexp-replace-quote to))
|
||||
(if all?
|
||||
(regexp-replace* from* str to*)
|
||||
|
|
Loading…
Reference in New Issue
Block a user