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" #rx"(=+)" "\\1")
|
||||||
(test "foo\\1bar" string-replace "foo===bar" #px"={3}" "\\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)
|
(report-errs)
|
||||||
|
|
|
@ -100,17 +100,36 @@
|
||||||
(string-join (internal-split 'string-normalize-spaces str sep trim? +?)
|
(string-join (internal-split 'string-normalize-spaces str sep trim? +?)
|
||||||
space))
|
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])
|
(define (string-replace str from to #:all? [all? #t])
|
||||||
(unless (string? str) (raise-argument-error 'string-replace "string?" str))
|
(unless (string? str) (raise-argument-error 'string-replace "string?" str))
|
||||||
(unless (string? to) (raise-argument-error 'string-replace "string?" to))
|
(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*
|
(define from*
|
||||||
(if (regexp? from)
|
(if (regexp? from)
|
||||||
from
|
from
|
||||||
(hash-ref! replace-cache from
|
(hash-ref! replace-cache (string->immutable-string/cache from)
|
||||||
(λ() (if (string? from)
|
(λ() (regexp (regexp-quote from))))))
|
||||||
(regexp (regexp-quote from))
|
|
||||||
(raise-argument-error 'string-replace "string?" from))))))
|
|
||||||
(define to* (regexp-replace-quote to))
|
(define to* (regexp-replace-quote to))
|
||||||
(if all?
|
(if all?
|
||||||
(regexp-replace* from* str to*)
|
(regexp-replace* from* str to*)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user