Let's say I find my app spending too much time hashing strings because some rough player passes it ridiculously long strings. To combat it I do a validation: "if string.count < reasonable limit". But that, in turn could take long time, now in the count call itself...
That count has:
is not the whole story, as this simple app shows:
@discardableResult func eqTest(_ a: String, _ b: String, _ length: Int) -> Bool {
let start = Date()
let result = a == b
let elapsed = Date().timeIntervalSince(start)
precondition(b.count == 1)
print("count: \(b.count), len: \(length), utf8 len: \(b.utf8.count), \(elapsed * 1000) msec")
return result
}
func test() {
let a = "some"
var count = 1
var b = "e"
while count < Int.max / 100 {
b += String.init(repeating: "\u{0300}", count: count)
eqTest(a, b, count)
count *= 10
}
}
test()
output:
count: 1, len: 1, utf8 len: 3, 0.12695789337158203 msec
count: 1, len: 10, utf8 len: 23, 0.0019073486328125 msec
count: 1, len: 100, utf8 len: 223, 0.0050067901611328125 msec
count: 1, len: 1000, utf8 len: 2223, 0.027894973754882812 msec
count: 1, len: 10000, utf8 len: 22223, 0.24199485778808594 msec
count: 1, len: 100000, utf8 len: 222223, 2.5069713592529297 msec
count: 1, len: 1000000, utf8 len: 2222223, 25.845050811767578 msec
count: 1, len: 10000000, utf8 len: 22222223, 250.23198127746582 msec
count: 1, len: 100000000, utf8 len: 222222223, 2517.67897605896 msec
Obviously "some" could not be equal to "e" plus any number of "COMBINING GRAVE ACCENT" characters, but current EQ implementation wants to first fully determine that first character (spending an unbound amount of time and space whilst doing so) before it could finally bail out with "not equal".
Sorry for jumping between String.init(rawValue:), String.== and String.count - the first depends on the second and we are trying to use the third to optimise the first two.
Just about any simple parser like in the headline post of this topic.