// Copyright 2023 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.//go:build goexperiment.jsonv2
// Package jsonwire implements stateless functionality for handling JSON text.
package jsonwireimport ()// TrimSuffixWhitespace trims JSON whitespace from the end of b.func ( []byte) []byte {// NOTE: The arguments and logic are kept simple to keep this inlinable. := len() - 1for >= 0 && ([] == ' ' || [] == '\t' || [] == '\r' || [] == '\n') { -- }return [:+1]}// TrimSuffixString trims a valid JSON string at the end of b.// The behavior is undefined if there is not a valid JSON string present.func ( []byte) []byte {// NOTE: The arguments and logic are kept simple to keep this inlinable.iflen() > 0 && [len()-1] == '"' { = [:len()-1] }forlen() >= 2 && !([len()-1] == '"' && [len()-2] != '\\') { = [:len()-1] // trim all characters except an unescaped quote }iflen() > 0 && [len()-1] == '"' { = [:len()-1] }return}// HasSuffixByte reports whether b ends with c.func ( []byte, byte) bool {// NOTE: The arguments and logic are kept simple to keep this inlinable.returnlen() > 0 && [len()-1] == }// TrimSuffixByte removes c from the end of b if it is present.func ( []byte, byte) []byte {// NOTE: The arguments and logic are kept simple to keep this inlinable.iflen() > 0 && [len()-1] == {return [:len()-1] }return}// QuoteRune quotes the first rune in the input.func ( []byte) string { , := utf8.DecodeRune()if == utf8.RuneError && == 1 {return`'\x` + strconv.FormatUint(uint64([0]), 16) + `'` }returnstrconv.QuoteRune()}// CompareUTF16 lexicographically compares x to y according// to the UTF-16 codepoints of the UTF-8 encoded input strings.// This implements the ordering specified in RFC 8785, section 3.2.3.func (, []byte) int {// NOTE: This is an optimized, mostly allocation-free implementation // of CompareUTF16Simple in wire_test.go. FuzzCompareUTF16 verifies that the // two implementations agree on the result of comparing any two strings. := func( rune) bool {return ('\u0000' <= && <= '\uD7FF') || ('\uE000' <= && <= '\uFFFF') }for {iflen() == 0 || len() == 0 {returncmp.Compare(len(), len()) }// ASCII fast-path.if [0] < utf8.RuneSelf || [0] < utf8.RuneSelf {if [0] != [0] {returncmp.Compare([0], [0]) } , = [1:], [1:]continue }// Decode next pair of runes as UTF-8. , := utf8.DecodeRune() , := utf8.DecodeRune() := () := ()switch {// The x rune is a single UTF-16 codepoint, while // the y rune is a surrogate pair of UTF-16 codepoints.case && !: , _ = utf16.EncodeRune()// The y rune is a single UTF-16 codepoint, while // the x rune is a surrogate pair of UTF-16 codepoints.case && !: , _ = utf16.EncodeRune() }if != {returncmp.Compare(, ) }// Check for invalid UTF-8, in which case, // we just perform a byte-for-byte comparison.ifisInvalidUTF8(, ) || isInvalidUTF8(, ) {if [0] != [0] {returncmp.Compare([0], [0]) } } , = [:], [:] }}// TODO(https://go.dev/issue/70547): Use utf8.ErrInvalid instead.varErrInvalidUTF8 = errors.New("invalid UTF-8")func [ ~[]byte | ~string]( , string) error { , := utf8.DecodeRune([]byte())return &InvalidTextError{"character", string([:]), }}func [ ~[]byte | ~string]( ) error {iflen() > 6 {return &InvalidTextError{"surrogate pair", string(), "in string"} }return &InvalidTextError{"escape sequence", string(), "in string"}}typeInvalidTextErrorstruct {Labelstring// e.g., "character" | "escape sequence" | "surrogate pair"Whatstring// raw invalid textWherestring// e.g., "in string" | "at start of value"}func ( *InvalidTextError) () string {varstring := strings.ContainsFunc(.What, func( rune) bool {return == '`' || == utf8.RuneError || unicode.IsSpace() || !unicode.IsPrint() })switch {caseutf8.RuneCount([]byte(.What)) == 1: = QuoteRune([]byte(.What))case : = strconv.Quote(.What)default: = "`" + .What + "`" }returnstrings.TrimSuffix("invalid "+.Label+" "++" "+.Where, " ")}// TruncatePointer optionally truncates the JSON pointer,// enforcing that the length roughly does not exceed n.func ( string, int) string {iflen() <= {return } := / 2 := len() - /2// Avoid truncating a name if there are multiple names present.if := strings.LastIndexByte([:], '/'); > 0 { = }if := strings.IndexByte([:], '/'); >= 0 { += + len("/") }// Avoid truncation in the middle of a UTF-8 rune.for > 0 && isInvalidUTF8(utf8.DecodeLastRuneInString([:])) { -- }for < len() && isInvalidUTF8(utf8.DecodeRuneInString([:])) { ++ }// Determine the right middle fragment to use.varstringswitchstrings.Count([:], "/") {case0: = "…"case1: = "…/…"default: = "…/…/…" }ifstrings.HasPrefix([:], "/") && != "…" { = strings.TrimPrefix(, "…") }ifstrings.HasSuffix([:], "/") && != "…" { = strings.TrimSuffix(, "…") }return [:] + + [:]}func ( rune, int) bool {return == utf8.RuneError && == 1}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)