| 113 | } |
| 114 | |
| 115 | func decodeUnicodeEscape(in []byte) (rune, int) { |
| 116 | if r, ok := decodeSingleUnicodeEscape(in); !ok { |
| 117 | // Invalid Unicode escape |
| 118 | return utf8.RuneError, -1 |
| 119 | } else if !isUTF16EncodedRune(r) { |
| 120 | // Valid Unicode escape in Basic Multilingual Plane. |
| 121 | // Note: a single \uXXXX escape produces r in [0, 0xFFFF], so r is always |
| 122 | // within the BMP. The former r <= basicMultilingualPlaneOffset guard was |
| 123 | // tautological and has been removed — the real discriminator is whether r |
| 124 | // falls in the UTF-16 surrogate range. |
| 125 | return r, 6 |
| 126 | } else if r >= lowSurrogateOffset { |
| 127 | // Lone low surrogate (0xDC00-0xDFFF) with no preceding high surrogate. |
| 128 | // Per RFC 8259/WHATWG a lone surrogate in a JSON string is malformed; |
| 129 | // match encoding/json by substituting U+FFFD and consuming only the 6 |
| 130 | // bytes of this escape. |
| 131 | return utf8.RuneError, 6 |
| 132 | } else if len(in) < 8 || in[6] != '\\' || in[7] != 'u' { |
| 133 | // Lone high surrogate (0xD800-0xDBFF): the high-surrogate escape is not |
| 134 | // followed by a "\u" low-surrogate escape. decodeSingleUnicodeEscape |
| 135 | // assumes the \u prefix and reads hex at fixed offsets, so without this |
| 136 | // guard it would misread whatever bytes follow (e.g. the literal "A7FA" |
| 137 | // after "\uDB29") as a low surrogate and synthesize a bogus code point |
| 138 | // (DEFECT-260727-SNGT). Substitute U+FFFD and consume only the 6 bytes |
| 139 | // of the high surrogate, matching encoding/json. |
| 140 | return utf8.RuneError, 6 |
| 141 | } else if r2, ok := decodeSingleUnicodeEscape(in[6:]); !ok { |
| 142 | // A "\u" follows the high surrogate but the low-surrogate escape is |
| 143 | // itself malformed (truncated / bad hex) — the whole escape is broken. |
| 144 | return utf8.RuneError, -1 |
| 145 | } else if r2 < lowSurrogateOffset || r2 > basicMultilingualPlaneReservedOffset { |
| 146 | // The following "\uXXXX" is not a valid low surrogate (0xDC00-0xDFFF): |
| 147 | // e.g. a BMP codepoint or another high surrogate. Treat the first escape |
| 148 | // as a lone high surrogate → U+FFFD, consuming 6 bytes; the following |
| 149 | // escape is reprocessed by the caller. |
| 150 | return utf8.RuneError, 6 |
| 151 | } else { |
| 152 | // Valid UTF16 surrogate pair |
| 153 | return combineUTF16Surrogates(r, r2), 12 |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | // backslashCharEscapeTable: when '\X' is found for some byte X, it is to be replaced with backslashCharEscapeTable[X] |
| 158 | var backslashCharEscapeTable = [...]byte{ |