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.
650 lines
15 KiB
650 lines
15 KiB
// ------------------------------------------------------------------------
|
|
// Project atila
|
|
// Active Thing (activething.com) git.activething.com/go
|
|
//
|
|
// File name value_packer.go
|
|
// Created by DEV
|
|
// Modified 06/02/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 pjson
|
|
|
|
import (
|
|
"crypts"
|
|
"encoding/base32"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errs"
|
|
"math"
|
|
"packs"
|
|
"reflect"
|
|
"strconv"
|
|
"time"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
|
|
type (
|
|
|
|
|
|
JSONPacker struct {
|
|
state *pkrs.State
|
|
dirty bool
|
|
}
|
|
|
|
|
|
JSONPackerFnc func (packer *JSONPacker)
|
|
)
|
|
|
|
|
|
func NewJSONPacker(options ...JSONPackerFnc) *JSONPacker {
|
|
pk := &JSONPacker{
|
|
state : pkrs.NewState(),
|
|
}
|
|
for _,o := range options {
|
|
o(pk)
|
|
}
|
|
return pk
|
|
}
|
|
|
|
|
|
|
|
func (p *JSONPacker) Pack (value any, flags pkrs.Flags) {
|
|
if p.state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(p.state.Error))
|
|
}
|
|
if p.dirty {
|
|
panic(pkrs.G.ErrInvalidState)
|
|
}
|
|
p.dirty = true
|
|
p.PackAny(value,flags)
|
|
}
|
|
|
|
|
|
func (p *JSONPacker) Packet () []byte {
|
|
|
|
|
|
|
|
return p.state.Bytes() }
|
|
|
|
|
|
func (p *JSONPacker) Error () error {
|
|
return p.state.Error }
|
|
|
|
|
|
func (p *JSONPacker) Reset () {
|
|
p.state.Reset()
|
|
p.dirty = false
|
|
}
|
|
|
|
func (p *JSONPacker) Release () {
|
|
p.dirty = false
|
|
pkrs.ReleaseState(p.state)
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *JSONPacker) PackAny (value any, flags pkrs.Flags) {
|
|
if p.state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(p.state.Error))
|
|
}
|
|
if value == nil {
|
|
p.state.W(g.nullValue)
|
|
return
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case pkrs.Builder:
|
|
p.state.SetError(v.Build(p))
|
|
|
|
case json.Marshaler:
|
|
d,e := v.MarshalJSON()
|
|
if e != nil {
|
|
p.state.SetError(e)
|
|
} else {
|
|
p.state.W(d)
|
|
}
|
|
|
|
case bool : packBool (p.state,v,flags )
|
|
|
|
case string : packString(p.state,v,flags )
|
|
|
|
case int : packInt(p.state,int64(v),flags)
|
|
case int8 : packInt(p.state,int64(v),flags)
|
|
case int16 : packInt(p.state,int64(v),flags)
|
|
case int32 : packInt(p.state,int64(v),flags)
|
|
case int64 : packInt(p.state,v,flags )
|
|
|
|
case uint : packUInt(p.state,uint64(v),flags)
|
|
case uint8 : packUInt(p.state,uint64(v),flags)
|
|
case uint16 : packUInt(p.state,uint64(v),flags)
|
|
case uint32 : packUInt(p.state,uint64(v),flags)
|
|
case uint64 : packUInt(p.state,v,flags )
|
|
|
|
case float32: packFloatString(p.state,float64(v),flags,32)
|
|
case float64: packFloatString(p.state,v,flags,64)
|
|
|
|
case time.Time: packDate (p.state,v,flags)
|
|
|
|
case reflect.Value: rTypePacker(v.Type())(p.state,v,flags)
|
|
|
|
case []uint8 :
|
|
if flags.Binary() {
|
|
packBinary(p.state,v,flags)
|
|
return
|
|
}
|
|
packListUInt[uint8 ](p.state,v,flags)
|
|
case []uint : packListUInt [uint ] (p.state,v,flags)
|
|
case []uint16 : packListUInt [uint16] (p.state,v,flags)
|
|
case []uint32 : packListUInt [uint32] (p.state,v,flags)
|
|
case []uint64 : packListUInt [uint64] (p.state,v,flags)
|
|
case []int : packListInt [int ] (p.state,v,flags)
|
|
case []int8 : packListInt [int8 ] (p.state,v,flags)
|
|
case []int16 : packListInt [int16] (p.state,v,flags)
|
|
case []int32 : packListInt [int32] (p.state,v,flags)
|
|
case []int64 : packListInt [int64] (p.state,v,flags)
|
|
case []float32: packListFloat[float32](p.state,v,flags,32)
|
|
case []float64: packListFloat[float64](p.state,v,flags,64)
|
|
|
|
case []bool : packListBool (p.state,v,flags)
|
|
case []string : packListString(p.state,v,flags)
|
|
case []time.Time: packListDate(p.state,v,flags)
|
|
|
|
default:
|
|
rv := reflect.ValueOf(value)
|
|
rTypePacker(rv.Type())(p.state,rv,flags)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *JSONPacker) PackRaw (value []byte, flags pkrs.Flags) { packRaw(p.state,value,flags) }
|
|
|
|
func (p *JSONPacker) PackBinary (value []byte, flags pkrs.Flags) { packBinary(p.state,value,flags) }
|
|
func (p *JSONPacker) PackString (value string, flags pkrs.Flags) { packString (p.state,value,flags) }
|
|
|
|
func (p *JSONPacker) PackBool (value bool, flags pkrs.Flags) { packBool(p.state,value,flags) }
|
|
|
|
func (p *JSONPacker) PackInt8 (value int8 , flags pkrs.Flags) { packInt(p.state,int64(value),flags) }
|
|
func (p *JSONPacker) PackInt16 (value int16, flags pkrs.Flags) { packInt(p.state,int64(value),flags) }
|
|
func (p *JSONPacker) PackInt32 (value int32, flags pkrs.Flags) { packInt(p.state,int64(value),flags) }
|
|
func (p *JSONPacker) PackInt64 (value int64, flags pkrs.Flags) { packInt(p.state,value,flags)}
|
|
|
|
func (p *JSONPacker) PackUInt8 (value uint8 , flags pkrs.Flags) { packUInt(p.state,uint64(value),flags) }
|
|
func (p *JSONPacker) PackUInt16 (value uint16, flags pkrs.Flags) { packUInt(p.state,uint64(value),flags) }
|
|
func (p *JSONPacker) PackUInt32 (value uint32, flags pkrs.Flags) { packUInt(p.state,uint64(value),flags) }
|
|
func (p *JSONPacker) PackUInt64 (value uint64, flags pkrs.Flags) { packUInt(p.state, value,flags) }
|
|
|
|
func (p *JSONPacker) PackFloat32 (value float32, flags pkrs.Flags) { packFloatString(p.state, float64(value), flags, 32) }
|
|
func (p *JSONPacker) PackFloat64 (value float64, flags pkrs.Flags) { packFloatString(p.state, value, flags, 64) }
|
|
|
|
func (p *JSONPacker) PackDate (value time.Time, flags pkrs.Flags) { packDate(p.state,value,flags) }
|
|
|
|
|
|
func (p *JSONPacker) PackList (builder func(pkrs.ListPackBuilder, pkrs.Flags), flags pkrs.Flags) {
|
|
p.packCompound('[',']',builder==nil,func(pk *JSONPacker, fl pkrs.Flags){ builder(pk,fl) },flags)
|
|
}
|
|
|
|
func (p *JSONPacker) PackMap (builder func(pkrs.MapPackBuilder, pkrs.Flags), flags pkrs.Flags) {
|
|
p.packCompound('{','}',builder==nil,func(pk *JSONPacker, fl pkrs.Flags){ builder(pk,fl) },flags)
|
|
}
|
|
|
|
func (p *JSONPacker) PackObject(builder func(pkrs.ObjectPackBuilder, pkrs.Flags), flags pkrs.Flags) {
|
|
p.packCompound('{','}',builder==nil,func(pk *JSONPacker, fl pkrs.Flags){ builder(pk,fl) },flags)
|
|
}
|
|
|
|
|
|
func (p *JSONPacker) packCompound (opener, closer byte, isnil bool, buildFn func(*JSONPacker, pkrs.Flags), flags pkrs.Flags) {
|
|
if p.state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(p.state.Error))
|
|
}
|
|
if isnil {
|
|
p.state.W(g.nullValue)
|
|
return
|
|
}
|
|
|
|
dn := false
|
|
defer func(d *bool) {
|
|
if !(*d) {
|
|
p.state.SetError(errs.ToError(recover()))
|
|
}
|
|
}(&dn)
|
|
|
|
p.state.Open(opener)
|
|
buildFn(p,flags)
|
|
if p.state.Error != nil {
|
|
return
|
|
}
|
|
p.state.Close(closer)
|
|
dn = true
|
|
}
|
|
|
|
|
|
|
|
func packRaw (state *pkrs.State, value []byte, flags pkrs.Flags) {
|
|
if state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(state.Error))
|
|
}
|
|
if value == nil {
|
|
state.W(g.nullValue)
|
|
return
|
|
}
|
|
if len(value) == 0 {
|
|
state.Write2Byte('"','"')
|
|
return
|
|
}
|
|
state.WOQ(flags.Quote(),'"',value)
|
|
}
|
|
|
|
func packString (state *pkrs.State, value string , flags pkrs.Flags) {
|
|
if state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(state.Error))
|
|
}
|
|
|
|
if len(value) == 0 {
|
|
state.Write2Byte('"','"')
|
|
return
|
|
}
|
|
|
|
if flags.Encrypted() {
|
|
packEncrypted(state, []byte(value), flags|pkrs.FlagBinary64)
|
|
return
|
|
}
|
|
|
|
if flags.Binary() {
|
|
packBinary(state,[]byte(value),flags)
|
|
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 packEncrypted (state *pkrs.State, value []byte, flags pkrs.Flags) {
|
|
r := state.Encrypter.Encrypt(value)
|
|
if !r.Ok() {
|
|
state.SetError(r.Fail())
|
|
return
|
|
}
|
|
|
|
state.WriteByte('"')
|
|
if flags.Bin64() || !flags.Binary() {
|
|
packBinary64(state, r.Value(), flags)
|
|
} else {
|
|
packBinary32(state, r.Value(), flags)
|
|
}
|
|
if state.Error == nil {
|
|
state.WriteByte('"')
|
|
}
|
|
return
|
|
}
|
|
|
|
func packBinary (state *pkrs.State, value []byte , flags pkrs.Flags) {
|
|
if state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(state.Error))
|
|
}
|
|
|
|
if value == nil {
|
|
_,_ = state.Write(g.nullValue)
|
|
return
|
|
}
|
|
if len(value) == 0 {
|
|
state.Write2Byte('"','"')
|
|
return
|
|
}
|
|
|
|
if flags.Encrypted() {
|
|
packEncrypted(state,value,flags)
|
|
return
|
|
}
|
|
|
|
state.WriteByte('"')
|
|
if flags.Bin64() {
|
|
packBinary64(state, value, 0)
|
|
} else if flags.Bin32() {
|
|
packBinary32(state, value, 0)
|
|
} else {
|
|
state.SetError(pkrs.G.ErrUnsupportedType)
|
|
return
|
|
}
|
|
if state.Error == nil {
|
|
state.WriteByte('"')
|
|
}
|
|
}
|
|
|
|
|
|
func packBinary64 (state *pkrs.State, value []byte , _ pkrs.Flags) {
|
|
ln := base64.StdEncoding.EncodedLen(len(value))
|
|
if ln <= len(state.Temp) {
|
|
dst := state.Temp[:ln]
|
|
base64.StdEncoding.Encode(dst, value)
|
|
_,_= state.Write(dst)
|
|
} else if ln <= 1024 {
|
|
dst := make([]byte, ln)
|
|
base64.StdEncoding.Encode(dst, value)
|
|
_,_ = state.Write(dst)
|
|
} else {
|
|
ec := base64.NewEncoder(base64.StdEncoding, state)
|
|
_,_ = ec.Write(value)
|
|
state.SetError(ec.Close())
|
|
}
|
|
}
|
|
|
|
func packBinary32 (state *pkrs.State, value []byte , _ pkrs.Flags) {
|
|
ln := base32.StdEncoding.EncodedLen(len(value))
|
|
if ln <= len(state.Temp) {
|
|
dst := state.Temp[:ln]
|
|
base32.StdEncoding.Encode(dst, value)
|
|
_,_= state.Write(dst)
|
|
} else if ln <= 1024 {
|
|
dst := make([]byte, ln)
|
|
base32.StdEncoding.Encode(dst, value)
|
|
_,_ = state.Write(dst)
|
|
} else {
|
|
ec := base32.NewEncoder(base32.StdEncoding, state)
|
|
_,_ = ec.Write(value)
|
|
state.SetError(ec.Close())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
func packBool (state *pkrs.State, value bool , flags pkrs.Flags) {
|
|
if state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(state.Error))
|
|
}
|
|
if value {
|
|
state.WOQ(flags.Quote(),'"', g.trueValue)
|
|
} else {
|
|
state.WOQ(flags.Quote(),'"', g.falseValue)
|
|
}
|
|
}
|
|
|
|
func packInt (state *pkrs.State, value int64 , flags pkrs.Flags) {
|
|
if state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(state.Error))
|
|
}
|
|
|
|
q :=flags.Quote()
|
|
if q {
|
|
state.WriteByte('"')
|
|
}
|
|
if value == 0 {
|
|
state.WriteByte('0')
|
|
} else {
|
|
state.WriteIntStr(value, 10)
|
|
}
|
|
if q {
|
|
state.WriteByte('"')
|
|
}
|
|
}
|
|
|
|
|
|
func packUInt (state *pkrs.State, value uint64 , flags pkrs.Flags) {
|
|
if state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(state.Error))
|
|
}
|
|
q :=flags.Quote()
|
|
if q {
|
|
state.WriteByte('"')
|
|
}
|
|
if value == 0 {
|
|
state.WriteByte('0')
|
|
} else {
|
|
state.WriteUIntStr(value, 10)
|
|
}
|
|
if q {
|
|
state.WriteByte('"')
|
|
}
|
|
}
|
|
|
|
func packFloat (state *pkrs.State, value float64, flags pkrs.Flags) {
|
|
packFloatString(state, value, flags, 64)
|
|
}
|
|
|
|
func packFloatString (state *pkrs.State, f float64, flags pkrs.Flags, bits int) {
|
|
if state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(state.Error))
|
|
}
|
|
|
|
if math.IsInf(f, 0) || math.IsNaN(f) {
|
|
state.SetError(pkrs.G.ErrInvalidValue)
|
|
return
|
|
}
|
|
|
|
abs := math.Abs(f)
|
|
fm := byte('f')
|
|
|
|
if abs != 0 {
|
|
if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
|
|
fm = 'e'
|
|
}
|
|
}
|
|
bf := state.Temp[:0]
|
|
bf = strconv.AppendFloat(bf, f, fm, -1, int(bits))
|
|
if fm == '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]
|
|
}
|
|
}
|
|
|
|
state.WOQ(flags.Quote(),'"',bf)
|
|
}
|
|
|
|
|
|
func packDate(state *pkrs.State, value time.Time, flags pkrs.Flags) {
|
|
if state.Error != nil {
|
|
panic(pkrs.G.ErrInvalidState.WrapWithCause(state.Error))
|
|
}
|
|
state.WOQ(true,'"',value.AppendFormat(state.Temp[:0],flags.DateFormat()))
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func packListString (state *pkrs.State, list []string, flags pkrs.Flags) {
|
|
if !checkList(state,list == nil, len(list) ==0) {
|
|
return
|
|
}
|
|
state.WriteByte('[')
|
|
for i,v := range list {
|
|
if i > 0 {
|
|
state.WriteByte(',')
|
|
}
|
|
packString(state,v,flags)
|
|
if state.Error != nil {
|
|
return
|
|
}
|
|
}
|
|
state.WriteByte(']')
|
|
}
|
|
|
|
|
|
func packListBool (state *pkrs.State, list []bool, flags pkrs.Flags) {
|
|
if !checkList(state,list == nil, len(list) ==0) {
|
|
return
|
|
}
|
|
state.WriteByte('[')
|
|
for i,v := range list {
|
|
if i > 0 {
|
|
state.WriteByte(',')
|
|
}
|
|
packBool(state,v,flags)
|
|
if state.Error != nil {
|
|
return
|
|
}
|
|
}
|
|
state.WriteByte(']')
|
|
}
|
|
|
|
|
|
func packListInt [T int | int8 | int16 | int32 | int64](state *pkrs.State, list []T, flags pkrs.Flags) {
|
|
if !checkList(state,list == nil, len(list) ==0) {
|
|
return
|
|
}
|
|
state.WriteByte('[')
|
|
for i,v := range list {
|
|
if i > 0 {
|
|
state.WriteByte(',')
|
|
}
|
|
packInt(state,int64(v),flags)
|
|
if state.Error != nil {
|
|
return
|
|
}
|
|
}
|
|
state.WriteByte(']')
|
|
}
|
|
|
|
|
|
func packListUInt [T uint | uint8 | uint16 | uint32 | uint64](state *pkrs.State, list []T, flags pkrs.Flags) {
|
|
if !checkList(state,list == nil, len(list) ==0) {
|
|
return
|
|
}
|
|
state.WriteByte('[')
|
|
for i,v := range list {
|
|
if i > 0 {
|
|
state.WriteByte(',')
|
|
}
|
|
packUInt(state,uint64(v),flags)
|
|
if state.Error != nil {
|
|
return
|
|
}
|
|
}
|
|
state.WriteByte(']')
|
|
}
|
|
|
|
|
|
func packListFloat[T float32 | float64](state *pkrs.State, list []T, flags pkrs.Flags, bits int) {
|
|
if !checkList(state,list == nil, len(list) ==0) {
|
|
return
|
|
}
|
|
state.WriteByte('[')
|
|
for i,v := range list {
|
|
if i > 0 {
|
|
state.WriteByte(',')
|
|
}
|
|
packFloatString(state,float64(v),flags,bits)
|
|
if state.Error != nil {
|
|
return
|
|
}
|
|
}
|
|
state.WriteByte(']')
|
|
}
|
|
|
|
func packListDate (state *pkrs.State, list []time.Time, flags pkrs.Flags) {
|
|
if !checkList(state,list == nil, len(list) ==0) {
|
|
return
|
|
}
|
|
state.WriteByte('[')
|
|
for i,v := range list {
|
|
if i > 0 {
|
|
state.WriteByte(',')
|
|
}
|
|
packDate(state,v,flags)
|
|
if state.Error != nil {
|
|
return
|
|
}
|
|
}
|
|
state.WriteByte(']')
|
|
}
|
|
|
|
|
|
func checkList (state *pkrs.State, isNull, isEmpty bool) bool {
|
|
if isNull {
|
|
state.Write2Byte('"','"')
|
|
return false
|
|
}
|
|
if isEmpty {
|
|
state.Write2Byte('[',']')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
|
|
|
|
func WithEncrypter(encrypter crypts.Encrypter) JSONPackerFnc {
|
|
return func(p *JSONPacker) {
|
|
if encrypter == nil {
|
|
encrypter = crypts.EncrypterNop{}
|
|
}
|
|
p.state.Encrypter = encrypter
|
|
}
|
|
}
|