github.com/segmentio/encoding@v0.4.0/json/string.go (about) 1 package json 2 3 import ( 4 "math/bits" 5 "unsafe" 6 ) 7 8 const ( 9 lsb = 0x0101010101010101 10 msb = 0x8080808080808080 11 ) 12 13 // escapeIndex finds the index of the first char in `s` that requires escaping. 14 // A char requires escaping if it's outside of the range of [0x20, 0x7F] or if 15 // it includes a double quote or backslash. If the escapeHTML mode is enabled, 16 // the chars <, > and & also require escaping. If no chars in `s` require 17 // escaping, the return value is -1. 18 func escapeIndex(s string, escapeHTML bool) int { 19 chunks := stringToUint64(s) 20 for _, n := range chunks { 21 // combine masks before checking for the MSB of each byte. We include 22 // `n` in the mask to check whether any of the *input* byte MSBs were 23 // set (i.e. the byte was outside the ASCII range). 24 mask := n | below(n, 0x20) | contains(n, '"') | contains(n, '\\') 25 if escapeHTML { 26 mask |= contains(n, '<') | contains(n, '>') | contains(n, '&') 27 } 28 if (mask & msb) != 0 { 29 return bits.TrailingZeros64(mask&msb) / 8 30 } 31 } 32 33 for i := len(chunks) * 8; i < len(s); i++ { 34 c := s[i] 35 if c < 0x20 || c > 0x7f || c == '"' || c == '\\' || (escapeHTML && (c == '<' || c == '>' || c == '&')) { 36 return i 37 } 38 } 39 40 return -1 41 } 42 43 // below return a mask that can be used to determine if any of the bytes 44 // in `n` are below `b`. If a byte's MSB is set in the mask then that byte was 45 // below `b`. The result is only valid if `b`, and each byte in `n`, is below 46 // 0x80. 47 func below(n uint64, b byte) uint64 { 48 return n - expand(b) 49 } 50 51 // contains returns a mask that can be used to determine if any of the 52 // bytes in `n` are equal to `b`. If a byte's MSB is set in the mask then 53 // that byte is equal to `b`. The result is only valid if `b`, and each 54 // byte in `n`, is below 0x80. 55 func contains(n uint64, b byte) uint64 { 56 return (n ^ expand(b)) - lsb 57 } 58 59 // expand puts the specified byte into each of the 8 bytes of a uint64. 60 func expand(b byte) uint64 { 61 return lsb * uint64(b) 62 } 63 64 func stringToUint64(s string) []uint64 { 65 return *(*[]uint64)(unsafe.Pointer(&sliceHeader{ 66 Data: *(*unsafe.Pointer)(unsafe.Pointer(&s)), 67 Len: len(s) / 8, 68 Cap: len(s) / 8, 69 })) 70 }