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