SYS-REQ-115
(_ Config, data []byte, quote byte)
| 299 | |
| 300 | // SYS-REQ-115 |
| 301 | func stringEndConfig(_ Config, data []byte, quote byte) (int, bool) { |
| 302 | // SWAR (SIMD-Within-A-Register) fast path: scan 8 bytes at a time for |
| 303 | // either the closing quote or a backslash, then a per-byte tail that |
| 304 | // counts the run of '\\' before each quote candidate. |
| 305 | // |
| 306 | // Investigation: easyjson's jlexer.findStringLen |
| 307 | // (mailru/easyjson@v0.9.2/jlexer/lexer.go:247) uses a single SIMD |
| 308 | // bytes.IndexByte('"') plus a backward backslash-run count, deferring the |
| 309 | // separate backslash scan to unescapeStringToken. That style was ported |
| 310 | // and benchmarked here as "single IndexByte for the quote + a bounded |
| 311 | // bytes.IndexByte(data[:firstQuote], '\\') for the escape flag". On |
| 312 | // arm64 (Apple M4 Max, NEON) the two IndexByte *function calls* cost more |
| 313 | // than this inline 8-byte SWAR loop, because jsonparser must compute the |
| 314 | // escape flag inline (its callers gate unescapeConfig on it), so the |
| 315 | // second scan cannot be deferred the way easyjson defers it. |
| 316 | // |
| 317 | // Measured on M4 Max (BenchmarkJsonParserLarge, median of 5): |
| 318 | // SWAR (this): ~21000 ns/op |
| 319 | // two-IndexByte port: ~22600 ns/op (-7%) |
| 320 | // For reference easyjson itself is ~33200 ns/op here, so jsonparser |
| 321 | // already leads; the easyjson technique is not beneficial on arm64. |
| 322 | const swarLsb = 0x0101010101010101 |
| 323 | const swarMsb = 0x8080808080808080 |
| 324 | broadcastQuote := uint64(quote) * swarLsb |
| 325 | broadcastBackslash := uint64('\\') * swarLsb |
| 326 | |
| 327 | i := 0 |
| 328 | n := len(data) |
| 329 | for i+8 <= n { |
| 330 | w := binary.LittleEndian.Uint64(data[i:]) |
| 331 | xq := w ^ broadcastQuote |
| 332 | xb := w ^ broadcastBackslash |
| 333 | quoteHit := (xq - swarLsb) & ^xq & swarMsb |
| 334 | bsHit := (xb - swarLsb) & ^xb & swarMsb |
| 335 | if quoteHit|bsHit == 0 { |
| 336 | i += 8 |
| 337 | continue |
| 338 | } |
| 339 | break |
| 340 | } |
| 341 | escaped := false |
| 342 | for ; i < n; i++ { |
| 343 | c := data[i] |
| 344 | if c == quote { |
| 345 | if !escaped { |
| 346 | return i + 1, false |
| 347 | } |
| 348 | j := i - 1 |
| 349 | for { |
| 350 | if j < 0 || data[j] != '\\' { |
| 351 | return i + 1, true // even run of backslashes |
| 352 | } |
| 353 | j-- |
| 354 | if j < 0 || data[j] != '\\' { |
| 355 | break // odd run of backslashes -> quote is escaped |
| 356 | } |
| 357 | j-- |
| 358 | } |
no outgoing calls
no test coverage detected