1 // +build !amd64,!arm64 go1.25 !go1.16 arm64,!go1.20
2 3 /**
4 * Copyright 2024 ByteDance Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 19 package alg
20 21 import (
22 _ "unsafe"
23 "unicode/utf8"
24 "strconv"
25 "bytes"
26 "encoding/json"
27 28 "github.com/bytedance/sonic/internal/rt"
29 )
30 31 // Valid validates json and returns first non-blank character position,
32 // if it is only one valid json value.
33 // Otherwise returns invalid character position using start.
34 //
35 // Note: it does not check for the invalid UTF-8 characters.
36 func Valid(data []byte) (ok bool, start int) {
37 ok = json.Valid(data)
38 return ok, 0
39 }
40 41 var typeByte = rt.UnpackEface(byte(0)).Type
42 43 func Quote(e []byte, s string, double bool) []byte {
44 if len(s) == 0 {
45 if double {
46 return append(e, `"\"\""`...)
47 }
48 return append(e, `""`...)
49 }
50 51 b := e
52 ss := len(e)
53 e = append(e, '"')
54 start := 0
55 56 for i := 0; i < len(s); {
57 if b := s[i]; b < utf8.RuneSelf {
58 if rt.SafeSet[b] {
59 i++
60 continue
61 }
62 if start < i {
63 e = append(e, s[start:i]...)
64 }
65 e = append(e, '\\')
66 switch b {
67 case '\\', '"':
68 e = append(e, b)
69 case '\n':
70 e = append(e, 'n')
71 case '\r':
72 e = append(e, 'r')
73 case '\t':
74 e = append(e, 't')
75 default:
76 // This encodes bytes < 0x20 except for \t, \n and \r.
77 // If escapeHTML is set, it also escapes <, >, and &
78 // because they can lead to security holes when
79 // user-controlled strings are rendered into JSON
80 // and served to some browsers.
81 e = append(e, `u00`...)
82 e = append(e, rt.Hex[b>>4])
83 e = append(e, rt.Hex[b&0xF])
84 }
85 i++
86 start = i
87 continue
88 }
89 c, size := utf8.DecodeRuneInString(s[i:])
90 // if correct && c == utf8.RuneError && size == 1 {
91 // if start < i {
92 // e = append(e, s[start:i]...)
93 // }
94 // e = append(e, `\ufffd`...)
95 // i += size
96 // start = i
97 // continue
98 // }
99 if c == '\u2028' || c == '\u2029' {
100 if start < i {
101 e = append(e, s[start:i]...)
102 }
103 e = append(e, `\u202`...)
104 e = append(e, rt.Hex[c&0xF])
105 i += size
106 start = i
107 continue
108 }
109 i += size
110 }
111 112 if start < len(s) {
113 e = append(e, s[start:]...)
114 }
115 e = append(e, '"')
116 117 if double {
118 return strconv.AppendQuote(b, string(e[ss:]))
119 } else {
120 return e
121 }
122 }
123 124 func HtmlEscape(dst []byte, src []byte) []byte {
125 buf := bytes.NewBuffer(dst)
126 json.HTMLEscape(buf, src)
127 return buf.Bytes()
128 }
129 130 func F64toa(buf []byte, v float64) ([]byte) {
131 bs := bytes.NewBuffer(buf)
132 _ = json.NewEncoder(bs).Encode(v)
133 return bs.Bytes()
134 }
135 136 func F32toa(buf []byte, v float32) ([]byte) {
137 bs := bytes.NewBuffer(buf)
138 _ = json.NewEncoder(bs).Encode(v)
139 return bs.Bytes()
140 }
141 142 func I64toa(buf []byte, v int64) ([]byte) {
143 return strconv.AppendInt(buf, int64(v), 10)
144 }
145 146 func U64toa(buf []byte, v uint64) ([]byte) {
147 return strconv.AppendUint(buf, v, 10)
148 }
149