// Package jsonvalue 比较 JSON 的实际内容,不将 JSON 数值转换为浮点数。 package jsonvalue import ( "bytes" "encoding/json" "errors" "io" "sort" "strconv" "strings" "unicode/utf8" ) // Canonical 生成内部 orbit-json-v1 摘要输入:对象键排序、重复键拒绝、 // 精确十进制数规范化。该格式不是对外 JSON 编码承诺,也不宣称实现 RFC 8785。 func Canonical(raw []byte) ([]byte, error) { if !utf8.Valid(raw) { return nil, errors.New("JSON must be valid UTF-8") } if err := validEscapes(raw); err != nil { return nil, err } d := json.NewDecoder(bytes.NewReader(raw)) d.UseNumber() b, err := value(d, 0) if err != nil { return nil, err } if _, err := d.Token(); err != io.EOF { return nil, errors.New("exactly one JSON value required") } return b, nil } func value(d *json.Decoder, depth int) ([]byte, error) { if depth > 32 { return nil, errors.New("JSON nesting exceeds 32 levels") } t, err := d.Token() if err != nil { return nil, err } switch v := t.(type) { case json.Delim: switch v { case '{': fields := map[string][]byte{} keys := []string{} for d.More() { token, err := d.Token() if err != nil { return nil, err } key, ok := token.(string) if !ok { return nil, errors.New("invalid object key") } if _, ok := fields[key]; ok { return nil, errors.New("duplicate JSON object key") } b, err := value(d, depth+1) if err != nil { return nil, err } fields[key] = b keys = append(keys, key) } if close, err := d.Token(); err != nil || close != json.Delim('}') { return nil, errors.New("unterminated JSON object") } sort.Strings(keys) var b bytes.Buffer b.WriteByte('{') for i, k := range keys { if i > 0 { b.WriteByte(',') } key, _ := json.Marshal(k) b.Write(key) b.WriteByte(':') b.Write(fields[k]) } b.WriteByte('}') return b.Bytes(), nil case '[': var b bytes.Buffer b.WriteByte('[') n := 0 for d.More() { v, err := value(d, depth+1) if err != nil { return nil, err } if n > 0 { b.WriteByte(',') } b.Write(v) n++ } if close, err := d.Token(); err != nil || close != json.Delim(']') { return nil, errors.New("unterminated JSON array") } b.WriteByte(']') return b.Bytes(), nil default: return nil, errors.New("unexpected JSON delimiter") } case json.Number: return number(string(v)) default: return json.Marshal(v) } } func number(s string) ([]byte, error) { if len(s) > 256 { return nil, errors.New("JSON number exceeds 256 characters") } // 仅用作有限范围检查;摘要始终由下面的十进制文本运算生成。 if _, err := strconv.ParseFloat(s, 64); err != nil { return nil, errors.New("JSON number outside supported range") } negative := strings.HasPrefix(s, "-") s = strings.TrimPrefix(s, "-") exponent := 0 if i := strings.IndexAny(s, "eE"); i >= 0 { var err error exponent, err = strconv.Atoi(s[i+1:]) if err != nil || exponent < -308 || exponent > 308 { return nil, errors.New("JSON exponent outside supported range") } s = s[:i] } if i := strings.IndexByte(s, '.'); i >= 0 { exponent -= len(s) - i - 1 s = s[:i] + s[i+1:] } s = strings.TrimLeft(s, "0") if s == "" { return []byte("0"), nil } for strings.HasSuffix(s, "0") { s = s[:len(s)-1] exponent++ } if exponent < -308 || exponent > 308 { return nil, errors.New("normalized JSON exponent outside supported range") } if exponent+len(s)-1 > 308 { return nil, errors.New("JSON number outside supported range") } if negative { s = "-" + s } if exponent != 0 { s += "e" + strconv.Itoa(exponent) } return []byte(s), nil } // encoding/json 会替换孤立 UTF-16 代理项;拒绝它们,避免两个不同输入被悄悄合并。 func validEscapes(raw []byte) error { for i := 0; i < len(raw); i++ { if raw[i] != '\\' { continue } i++ if i >= len(raw) || raw[i] != 'u' { continue } if i+4 >= len(raw) { return errors.New("invalid unicode escape") } v, err := strconv.ParseUint(string(raw[i+1:i+5]), 16, 16) if err != nil { return err } i += 4 if v >= 0xdc00 && v <= 0xdfff { return errors.New("unpaired unicode surrogate") } if v < 0xd800 || v > 0xdbff { continue } if i+6 >= len(raw) || raw[i+1] != '\\' || raw[i+2] != 'u' { return errors.New("unpaired unicode surrogate") } v, err = strconv.ParseUint(string(raw[i+3:i+7]), 16, 16) if err != nil || v < 0xdc00 || v > 0xdfff { return errors.New("unpaired unicode surrogate") } i += 6 } return nil }