spec.go raw
1 //go:build (amd64 && go1.16 && !go1.25) || (arm64 && go1.20 && !go1.25)
2 // +build amd64,go1.16,!go1.25 arm64,go1.20,!go1.25
3
4 /**
5 * Copyright 2024 ByteDance Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 package alg
21
22 import (
23 "runtime"
24 "unsafe"
25
26 "github.com/bytedance/sonic/internal/native"
27 "github.com/bytedance/sonic/internal/native/types"
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 n := len(data)
38 if n == 0 {
39 return false, -1
40 }
41 s := rt.Mem2Str(data)
42 p := 0
43 m := types.NewStateMachine()
44 ret := native.ValidateOne(&s, &p, m, 0)
45 types.FreeStateMachine(m)
46
47 if ret < 0 {
48 return false, p-1
49 }
50
51 /* check for trailing spaces */
52 for ;p < n; p++ {
53 if (types.SPACE_MASK & (1 << data[p])) == 0 {
54 return false, p
55 }
56 }
57
58 return true, ret
59 }
60
61 var typeByte = rt.UnpackEface(byte(0)).Type
62
63 //go:nocheckptr
64 func Quote(buf []byte, val string, double bool) []byte {
65 if len(val) == 0 {
66 if double {
67 return append(buf, `"\"\""`...)
68 }
69 return append(buf, `""`...)
70 }
71
72 if double {
73 buf = append(buf, `"\"`...)
74 } else {
75 buf = append(buf, `"`...)
76 }
77 sp := rt.IndexChar(val, 0)
78 nb := len(val)
79 b := (*rt.GoSlice)(unsafe.Pointer(&buf))
80
81 // input buffer
82 for nb > 0 {
83 // output buffer
84 dp := unsafe.Pointer(uintptr(b.Ptr) + uintptr(b.Len))
85 dn := b.Cap - b.Len
86 // call native.Quote, dn is byte count it outputs
87 opts := uint64(0)
88 if double {
89 opts = types.F_DOUBLE_UNQUOTE
90 }
91 ret := native.Quote(sp, nb, dp, &dn, opts)
92 // update *buf length
93 b.Len += dn
94
95 // no need more output
96 if ret >= 0 {
97 break
98 }
99
100 // double buf size
101 *b = rt.GrowSlice(typeByte, *b, b.Cap*2)
102 // ret is the complement of consumed input
103 ret = ^ret
104 // update input buffer
105 nb -= ret
106 sp = unsafe.Pointer(uintptr(sp) + uintptr(ret))
107 }
108
109 runtime.KeepAlive(buf)
110 runtime.KeepAlive(sp)
111 if double {
112 buf = append(buf, `\""`...)
113 } else {
114 buf = append(buf, `"`...)
115 }
116
117 return buf
118 }
119
120 func HtmlEscape(dst []byte, src []byte) []byte {
121 var sidx int
122
123 dst = append(dst, src[:0]...) // avoid check nil dst
124 sbuf := (*rt.GoSlice)(unsafe.Pointer(&src))
125 dbuf := (*rt.GoSlice)(unsafe.Pointer(&dst))
126
127 /* grow dst if it is shorter */
128 if cap(dst)-len(dst) < len(src)+types.BufPaddingSize {
129 cap := len(src)*3/2 + types.BufPaddingSize
130 *dbuf = rt.GrowSlice(typeByte, *dbuf, cap)
131 }
132
133 for sidx < sbuf.Len {
134 sp := rt.Add(sbuf.Ptr, uintptr(sidx))
135 dp := rt.Add(dbuf.Ptr, uintptr(dbuf.Len))
136
137 sn := sbuf.Len - sidx
138 dn := dbuf.Cap - dbuf.Len
139 nb := native.HTMLEscape(sp, sn, dp, &dn)
140
141 /* check for errors */
142 if dbuf.Len += dn; nb >= 0 {
143 break
144 }
145
146 /* not enough space, grow the slice and try again */
147 sidx += ^nb
148 *dbuf = rt.GrowSlice(typeByte, *dbuf, dbuf.Cap*2)
149 }
150 return dst
151 }
152
153 func F64toa(buf []byte, v float64) ([]byte) {
154 if v == 0 {
155 return append(buf, '0')
156 }
157 buf = rt.GuardSlice2(buf, 64)
158 ret := native.F64toa((*byte)(rt.IndexByte(buf, len(buf))), v)
159 if ret > 0 {
160 return buf[:len(buf)+ret]
161 } else {
162 return buf
163 }
164 }
165
166 func F32toa(buf []byte, v float32) ([]byte) {
167 if v == 0 {
168 return append(buf, '0')
169 }
170 buf = rt.GuardSlice2(buf, 64)
171 ret := native.F32toa((*byte)(rt.IndexByte(buf, len(buf))), v)
172 if ret > 0 {
173 return buf[:len(buf)+ret]
174 } else {
175 return buf
176 }
177 }
178
179 func I64toa(buf []byte, v int64) ([]byte) {
180 buf = rt.GuardSlice2(buf, 32)
181 ret := native.I64toa((*byte)(rt.IndexByte(buf, len(buf))), v)
182 if ret > 0 {
183 return buf[:len(buf)+ret]
184 } else {
185 return buf
186 }
187 }
188
189 func U64toa(buf []byte, v uint64) ([]byte) {
190 buf = rt.GuardSlice2(buf, 32)
191 ret := native.U64toa((*byte)(rt.IndexByte(buf, len(buf))), v)
192 if ret > 0 {
193 return buf[:len(buf)+ret]
194 } else {
195 return buf
196 }
197 }
198
199