You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
348 lines
6.9 KiB
348 lines
6.9 KiB
// ------------------------------------------------------------------------
|
|
// Project atila
|
|
// Active Thing (activething.com) git.activething.com/go
|
|
//
|
|
// File name value_packer.go
|
|
// Created by DEV
|
|
// Modified 29/01/2024
|
|
//
|
|
// Copyright 2024 activething.com
|
|
// ------------------------------------------------------------------------
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// ------------------------------------------------------------------------
|
|
|
|
package js
|
|
|
|
import (
|
|
"activething.com/go/gogo/packs"
|
|
"encoding/base32"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"io"
|
|
"math"
|
|
"reflect"
|
|
"strconv"
|
|
"time"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
|
|
|
|
func pack (state *packs.State, value any , flags Flags) {
|
|
if state.Error != nil {
|
|
panic(state.Error)
|
|
}
|
|
|
|
if value == nil {
|
|
if flags.Quote() {
|
|
state.WriteByte('"')
|
|
}
|
|
_,_ = state.Write(G.nullValue)
|
|
if flags.Quote() {
|
|
state.WriteByte('"')
|
|
}
|
|
return
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
|
|
case bool : packBool (state,v,flags)
|
|
case string : packString(state,v,flags)
|
|
|
|
case uint : packUInt(state,uint64(v),flags)
|
|
case uint8 : packUInt(state,uint64(v),flags)
|
|
case uint16 : packUInt(state,uint64(v),flags)
|
|
case uint32 : packUInt(state,uint64(v),flags)
|
|
case uint64 : packUInt(state,v,flags)
|
|
|
|
case int : packInt(state,int64(v),flags)
|
|
case int8 : packInt(state,int64(v),flags)
|
|
case int16 : packInt(state,int64(v),flags)
|
|
case int32 : packInt(state,int64(v),flags)
|
|
case int64 : packInt(state,v,flags)
|
|
|
|
case float32: packFloat(state,float64(v),flags)
|
|
case float64: packFloat(state,v,flags)
|
|
|
|
case time.Time : PackDate(v)
|
|
|
|
case reflect.Value : Pack(v.Elem())
|
|
|
|
default:
|
|
if pb,is := value.(packs.Packabler); is {
|
|
d, e := pb.Build(p)
|
|
if state.SetError(e) {
|
|
return
|
|
}
|
|
_, _ = state.Write(d)
|
|
return
|
|
}
|
|
if ms,is := value.(json.Marshaler); is {
|
|
d,e := ms.MarshalJSON()
|
|
if state.SetError(e) {
|
|
return
|
|
}
|
|
_,_ = state.Write(d)
|
|
return
|
|
}
|
|
|
|
rv := reflect.ValueOf(value)
|
|
switch rv.Kind() {
|
|
case reflect.Slice, reflect.Array:
|
|
packListReflected(rv)
|
|
case reflect.Map:
|
|
packMapReflected(rv)
|
|
case reflect.Struct:
|
|
packObjectReflected(rv)
|
|
default:
|
|
d,e := options.encoder.Encode(value)
|
|
if e != nil {
|
|
state.SetError(e)
|
|
return
|
|
}
|
|
state.Write(d)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
func packString (state *packs.State, value string , flags Flags) {
|
|
if state.Error != nil {
|
|
panic(state.Error)
|
|
}
|
|
|
|
if len(value) == 0 {
|
|
state.Write2Byte('"','"')
|
|
return
|
|
}
|
|
|
|
state.WriteByte('"')
|
|
start := 0
|
|
for i := 0; i < len(value); {
|
|
if b := value[i]; b < utf8.RuneSelf {
|
|
if !G.jsonChars[b].isHtmlUnsafe() || (!flags.EscapeHTML() && !G.jsonChars[b].isUnsafe()) {
|
|
i++
|
|
continue
|
|
}
|
|
|
|
if start < i {
|
|
state.WriteString(value[start:i])
|
|
}
|
|
state.WriteByte('\\')
|
|
switch b {
|
|
case '\\', '"': state.WriteByte(b)
|
|
case '\n': state.WriteByte('n')
|
|
case '\r': state.WriteByte('r')
|
|
case '\t': state.WriteByte('t')
|
|
default:
|
|
state.WriteString(`u00`)
|
|
state.Write2Byte(G.hex[b>>4], G.hex[b&0xF])
|
|
}
|
|
i++
|
|
start = i
|
|
continue
|
|
}
|
|
c, size := utf8.DecodeRuneInString(value[i:])
|
|
if c == utf8.RuneError && size == 1 {
|
|
if start < i {
|
|
state.WriteString(value[start:i])
|
|
}
|
|
state.WriteString(`\ufffd`)
|
|
i += size
|
|
start = i
|
|
continue
|
|
}
|
|
|
|
if c == '\u2028' || c == '\u2029' {
|
|
if start < i {
|
|
state.WriteString(value[start:i])
|
|
}
|
|
state.WriteString(`\u202`)
|
|
state.WriteByte(G.hex[c&0xF])
|
|
i += size
|
|
start = i
|
|
continue
|
|
}
|
|
i += size
|
|
}
|
|
if start < len(value) {
|
|
state.WriteString(value[start:])
|
|
}
|
|
state.WriteByte('"')
|
|
}
|
|
|
|
|
|
|
|
func packBinary (state *packs.State, value []byte , f Flags) {
|
|
if state.Error != nil {
|
|
panic(state.Error)
|
|
}
|
|
|
|
if value == nil {
|
|
_,_ = state.Write(G.nullValue)
|
|
return
|
|
}
|
|
if len(value) == 0 {
|
|
state.Write2Byte('"','"')
|
|
return
|
|
}
|
|
state.WriteByte('"')
|
|
|
|
var ec binEncoder = base64.StdEncoding
|
|
if f.Bin32() {
|
|
ec = base32.StdEncoding
|
|
}
|
|
|
|
ln := ec.EncodedLen(len(value))
|
|
if ln <= len(state.Temp) {
|
|
dst := state.Temp[:ln]
|
|
ec.Encode(dst, value)
|
|
_,_= state.Write(dst)
|
|
} else if ln <= 1024 {
|
|
dst := make([]byte, ln)
|
|
ec.Encode(dst, value)
|
|
_,_ = state.Write(dst)
|
|
} else {
|
|
var wc io.WriteCloser
|
|
|
|
if f.Bin32() {
|
|
wc = base32.NewEncoder(base32.StdEncoding, state)
|
|
} else if f.Bin64() {
|
|
wc = base64.NewEncoder(base64.StdEncoding, state)
|
|
} else {
|
|
state.SetError(packs.G.ErrUnsupportedType)
|
|
return
|
|
}
|
|
_,_ = wc.Write(value)
|
|
state.SetError(wc.Close())
|
|
return
|
|
}
|
|
state.WriteByte('"')
|
|
}
|
|
|
|
|
|
|
|
func packBool (state *packs.State, value bool , flags Flags) {
|
|
if state.Error != nil {
|
|
panic(state.Error)
|
|
}
|
|
if flags.Quote() {
|
|
state.WriteByte('"')
|
|
}
|
|
if value {
|
|
state.W(G.trueValue)
|
|
} else {
|
|
state.W(G.falseValue)
|
|
}
|
|
if flags.Quote() {
|
|
state.WriteByte('"')
|
|
}
|
|
}
|
|
|
|
func packInt (state *packs.State, value int64 , flags Flags) {
|
|
if state.Error != nil {
|
|
panic(state.Error)
|
|
}
|
|
|
|
if flags.Quote() {
|
|
state.WriteByte('"')
|
|
}
|
|
if value == 0 {
|
|
state.WriteByte('0')
|
|
} else {
|
|
state.WriteIntStr(value, 10)
|
|
}
|
|
if flags.Quote() {
|
|
state.WriteByte('"')
|
|
}
|
|
}
|
|
|
|
func packUInt (state *packs.State, value uint64 , flags Flags) {
|
|
if state.Error != nil {
|
|
panic(state.Error)
|
|
}
|
|
if flags.Quote() {
|
|
state.WriteByte('"')
|
|
}
|
|
if value == 0 {
|
|
state.WriteByte('0')
|
|
} else {
|
|
state.WriteUIntStr(value, 10)
|
|
}
|
|
if flags.Quote() {
|
|
state.WriteByte('"')
|
|
}
|
|
}
|
|
|
|
func packFloat (state *packs.State, value float64, flags Flags) {
|
|
packFloatString(state, value,64, flags.Quote())
|
|
}
|
|
|
|
func packFloatString (state *packs.State, f float64, bits int, quote bool) {
|
|
if state.Error != nil {
|
|
panic(state.Error)
|
|
}
|
|
|
|
if math.IsInf(f, 0) || math.IsNaN(f) {
|
|
state.SetError(packs.G.ErrInvalidValue)
|
|
return
|
|
}
|
|
|
|
abs := math.Abs(f)
|
|
fmt := byte('f')
|
|
|
|
if abs != 0 {
|
|
if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
|
|
fmt = 'e'
|
|
}
|
|
}
|
|
bf := state.Temp[:0]
|
|
bf = strconv.AppendFloat(bf, f, fmt, -1, int(bits))
|
|
if fmt == 'e' {
|
|
// clean up e-09 to e-9
|
|
n := len(bf)
|
|
if n >= 4 && bf[n-4] == 'e' && bf[n-3] == '-' && bf[n-2] == '0' {
|
|
bf[n-2] = bf[n-1]
|
|
bf = bf[:n-1]
|
|
}
|
|
}
|
|
|
|
if quote {
|
|
state.WriteByte('"')
|
|
}
|
|
state.W(bf)
|
|
if quote {
|
|
state.WriteByte('"')
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
func PackDate (state *packs.State, value time.Time, flags Flags) {
|
|
if state.Error != nil {
|
|
panic(state.Error)
|
|
}
|
|
state.WriteByte('"')
|
|
bf := state.Temp[:0]
|
|
bf = value.AppendFormat(bf,flags.DateFormat())
|
|
state.W(bf)
|
|
state.WriteByte('"')
|
|
|
|
}
|
|
|
|
|
|
|