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.

663 lines
13 KiB

// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name packer.go
// Created by DEV
// Modified 23/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 _pjson
import (
"activething.com/go/gogo/core/bins"
"activething.com/go/gogo/core/errs"
"activething.com/go/gogo/core/opts"
"activething.com/go/gogo/packs"
"activething.com/go/gogo/packs/reflects"
"encoding/base64"
"encoding/json"
"fmt"
"reflect"
"time"
"unicode/utf8"
)
const notDetectingCycles = 100
type (
Packer struct {
options Options
state *packs.State
}
)
func NewPacker (options ...opts.OptionFnc[Packer]) *Packer {
p := opts.Apply(&Packer{
options: Options{
Flags: JSONFlagQuote | JSONFlagEscapeHTML | JSONByteSliceAsBinary,
dateLayout: time.RFC822Z,
},
state : packs.NewState(),
}, options ...)
if p.options.encoder == nil {
p.options.encoder = JSONEncoderFnc(json.Marshal)
}
return p
}
func (p *Packer) Bytes () []byte {
if p.options.ResetOnRead() {
defer p.Reset()
}
return append([]byte(nil),p.state.Bytes()...)
}
func (p *Packer) Error () error {
return p.state.Error
}
func (p *Packer) Reset () {
p.state.Reset()
p.options.reset()
}
func (p *Packer) Pack (value any) {
if p.state.Error != nil {
panic(p.state.Error)
}
if value == nil {
if p.options.Quote() {
_ = p.state.WriteByte('"')
}
_,_ = p.state.Write(G.nullValue)
if p.options.Quote() {
_ = p.state.WriteByte('"')
}
return
}
switch v := value.(type) {
case bool : p.PackBool(v)
case string : p.PackString(v)
case uint : p.PackUInt64(uint64(v))
case uint8 : p.PackUInt64(uint64(v))
case uint16 : p.PackUInt64(uint64(v))
case uint32 : p.PackUInt64(uint64(v))
case uint64 : p.PackUInt64(v)
case int : p.PackInt64(int64(v))
case int8 : p.PackInt64(int64(v))
case int16 : p.PackInt64(int64(v))
case int32 : p.PackInt64(int64(v))
case int64 : p.PackInt64(v)
case float32: p.packFloat(float64(v),32)
case float64: p.packFloat(v,64)
case time.Time : p.PackDate(v)
case reflect.Value : p.Pack(v.Elem())
default:
if pb,is := value.(packs.Packabler); is {
d, e := pb.Build(p)
if p.state.SetError(e) {
return
}
_, _ = p.state.Write(d)
return
}
if ms,is := value.(json.Marshaler); is {
d,e := ms.MarshalJSON()
if p.state.SetError(e) {
return
}
_,_ = p.state.Write(d)
return
}
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice, reflect.Array:
p.packListReflected(rv)
case reflect.Map:
p.packMapReflected(rv)
case reflect.Struct:
p.packObjectReflected(rv)
default:
d,e := p.options.encoder.Encode(value)
if e != nil {
p.state.SetError(e)
return
}
p.state.Write(d)
}
}
}
func (p *Packer) PackRaw (value []byte) {
if p.state.Error != nil {
panic(p.state.Error)
}
if value == nil {
_,_ = p.state.Write(G.nullValue)
return
}
if p.options.Quote() {
_ = p.state.WriteByte('"')
}
_,_ = p.state.Write(value)
if p.options.Quote() {
_ = p.state.WriteByte('"')
}
}
func (p *Packer) PackBinary (value []byte) {
if p.state.Error != nil {
panic(p.state.Error)
}
if value == nil {
_,_ = p.state.Write(G.nullValue)
return
}
if len(value) == 0 {
p.state.Write2Byte('"','"')
return
}
_ = p.state.WriteByte('"')
ln := base64.StdEncoding.EncodedLen(len(value))
if ln <= len(p.state.Temp) {
dst := p.state.Temp[:ln]
base64.StdEncoding.Encode(dst, value)
_,_= p.state.Write(dst)
} else if ln <= 1024 {
dst := make([]byte, ln)
base64.StdEncoding.Encode(dst, value)
_,_ = p.state.Write(dst)
} else {
enc := base64.NewEncoder(base64.StdEncoding, p.state)
_,_ = enc.Write(value)
p.state.SetError(enc.Close())
return
}
_ = p.state.WriteByte('"')
}
func (p *Packer) PackString(value string) {
if p.state.Error != nil {
panic(p.state.Error)
}
if len(value) == 0 {
p.state.Write2Byte('"','"')
return
}
_ = p.state.WriteByte('"')
start := 0
for i := 0; i < len(value); {
if b := value[i]; b < utf8.RuneSelf {
if !G.jsonChars[b].isHtmlUnsafe() || (!p.options.EscapeHTML() && !G.jsonChars[b].isUnsafe()) {
i++
continue
}
if start < i {
_,_ = p.state.WriteString(value[start:i])
}
_ = p.state.WriteByte('\\')
switch b {
case '\\', '"': _ = p.state.WriteByte(b)
case '\n': _ = p.state.WriteByte('n')
case '\r': _ = p.state.WriteByte('r')
case '\t': _ = p.state.WriteByte('t')
default:
_,_ = p.state.WriteString(`u00`)
p.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 {
_,_ = p.state.WriteString(value[start:i])
}
_,_ = p.state.WriteString(`\ufffd`)
i += size
start = i
continue
}
if c == '\u2028' || c == '\u2029' {
if start < i {
_,_ = p.state.WriteString(value[start:i])
}
_,_ = p.state.WriteString(`\u202`)
_ = p.state.WriteByte(G.hex[c&0xF])
i += size
start = i
continue
}
i += size
}
if start < len(value) {
_,_ = p.state.WriteString(value[start:])
}
_ = p.state.WriteByte('"')
}
func (p *Packer) PackBool (value bool) {
if p.state.Error != nil {
panic(p.state.Error)
}
if p.options.QuoteNumbers() {
_ = p.state.WriteByte('"')
}
if value {
_,_ = p.state.Write(G.trueValue)
} else {
_,_ = p.state.Write(G.falseValue)
}
if p.options.QuoteNumbers() {
_ = p.state.WriteByte('"')
}
}
func (p *Packer) PackInt8 (value int8) { p.PackInt64(int64(value)) }
func (p *Packer) PackInt16 (value int16) { p.PackInt64(int64(value)) }
func (p *Packer) PackInt32 (value int32) { p.PackInt64(int64(value)) }
func (p *Packer) PackInt64 (value int64) {
if p.state.Error != nil {
panic(p.state.Error)
}
if p.options.QuoteNumbers() {
_ = p.state.WriteByte('"')
}
if value == 0 {
_ = p.state.WriteByte('0')
} else {
p.state.WriteIntStr(value, 10)
}
if p.options.QuoteNumbers() {
_ = p.state.WriteByte('"')
}
}
func (p *Packer) PackUInt8 (value uint8 ) { p.PackUInt64(uint64(value)) }
func (p *Packer) PackUInt16 (value uint16) { p.PackUInt64(uint64(value)) }
func (p *Packer) PackUInt32 (value uint32) { p.PackUInt64(uint64(value)) }
func (p *Packer) PackUInt64 (value uint64) {
if p.state.Error != nil {
panic(p.state.Error)
}
if p.options.QuoteNumbers() {
_ = p.state.WriteByte('"')
}
if value == 0 {
_ = p.state.WriteByte('0')
} else {
p.state.WriteUIntStr(value, 10)
}
if p.options.QuoteNumbers() {
_ = p.state.WriteByte('"')
}
}
func (p *Packer) PackFloat32 (value float32) { p.packFloat(float64(value),32) }
func (p *Packer) PackFloat64 (value float64) { p.packFloat(value,64) }
func (p *Packer) packFloat (value float64, bits int) {
if p.state.Error != nil {
panic(p.state.Error)
}
if p.options.QuoteNumbers() {
_ = p.state.WriteByte('"')
}
b := stringifyFloat(p.state.Temp[:0], value,bits)
if b == nil {
p.state.SetError(packs.G.ErrInvalidValue)
return
}
_,_ = p.state.Write(b)
if p.options.QuoteNumbers() {
_ = p.state.WriteByte('"')
}
}
func (p *Packer) PackDate (value time.Time) {
if p.state.Error != nil {
panic(p.state.Error)
}
_ = p.state.WriteByte('"')
p.state.AppendWith(bins.AppendFnc(func(d []byte) []byte {
return value.AppendFormat(d,p.options.dateLayout)
}))
_ = p.state.WriteByte('"')
}
func (p *Packer) PackList (builder func(packer packs.ListPacker)) {
p.packLOM(packs.NodeKindList,'[',']',func(pk *Packer) { builder (pk) })
}
func (p *Packer) PackMap (builder func(packer packs.MapPacker )) {
p.packLOM(packs.NodeKindMap,'{','}',func(pk *Packer) { builder (pk) })
}
func (p *Packer) PackObject(builder func(packer packs.ObjectPacker)) {
p.packLOM(packs.NodeKindObject,'{','}',func(pk *Packer) { builder (pk) })
}
func (p *Packer) packLOM(kind packs.NodeKind, opening, closing byte, buildFnc func(*Packer)) {
if p.state.Error != nil {
panic(p.state.Error)
}
dn := false
defer func(d *bool) {
if !(*d) {
p.state.SetError(errs.ToError(recover()))
}
}(&dn)
p.state.Open(kind)
if p.state.Error == nil {
_ = p.state.WriteByte(opening)
buildFnc(p)
if p.state.Error == nil {
p.state.Close()
if p.state.Error == nil {
_ = p.state.WriteByte(closing)
}
}
}
dn = true
}
func (p *Packer) addKey (key any, call func(*Packer)) {
if p.state.Error != nil {
panic(p.state.Error)
}
k := stringifyKey(p.state.Temp[0:],key)
if k == nil {
p.state.SetError(packs.G.ErrInvalidKey)
return
}
p.addField(string(k),call)
}
func (p *Packer) addField (name string, call func(*Packer)) {
if p.state.Error != nil {
panic(p.state.Error)
}
if p.state.Node.Count > 0 {
_ = p.state.WriteByte(',')
}
if !p.options.IgnoreKeys() && !p.state.SetKey(name) {
return
}
p.PackString(name)
_ = p.state.WriteByte(':')
call(p)
p.state.Node.Count++
}
func (p *Packer) addItem () *Packer {
if p.state.Node.Count > 0 {
_ = p.state.WriteByte(',')
}
p.state.Node.Count++
return p
}
func (p *Packer) packReflected (value reflect.Value) {
defer func() {
r := recover()
if r != nil {
fmt.Printf("Error %s\n",r)
}
}()
switch value.Type().Kind() {
case reflect.String :
p.PackString(value.String())
case reflect.Bool :
p.PackBool(value.Bool())
case reflect.Uint8 ,
reflect.Uint16 ,
reflect.Uint32 ,
reflect.Uint64 :
p.PackUInt64(value.Uint())
case reflect.Int8 ,
reflect.Int16 ,
reflect.Int32 ,
reflect.Int64 :
p.PackInt64(value.Int())
case reflect.Float32,
reflect.Float64:
p.PackFloat64(value.Float())
case reflect.Slice :
p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){
p.packListReflected(rv)
})
default:
p.Pack(value.Interface())
}
}
func (p *Packer) packListReflected (value reflect.Value) {
if value.IsNil() {
_,_ = p.state.Write(G.nullValue)
return
}
if value.Len() == 0 {
p.state.Write2Byte('[',']')
return
}
tp := value.Type()
if tp.Elem().Kind() == reflect.Uint8 && p.options.ByteSliceAsBinary() {
p.PackBinary(value.Bytes())
return
}
if p.state.Level > notDetectingCycles {
pt := struct {
ptr interface{}
len int
} {
ptr : value.UnsafePointer(),
len : value.Len(),
}
if _,has := p.state.Checks[pt]; has {
p.state.SetError(packs.G.ErrRedundantPack)
return
}
p.state.Checks[pt]=struct{}{}
defer delete(p.state.Checks,pt)
}
switch tp.Elem().Kind() {
case reflect.String :
p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemString(rv.String()) })
case reflect.Bool :
p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemBool (rv.Bool()) })
case reflect.Uint8 ,
reflect.Uint16 ,
reflect.Uint32 ,
reflect.Uint64 :
p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemUInt64(rv.Uint()) })
case reflect.Int8 ,
reflect.Int16 ,
reflect.Int32 ,
reflect.Int64 :
p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemInt64(rv.Int()) })
case reflect.Float32,
reflect.Float64:
p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemFloat64(rv.Float()) })
case reflect.Slice :
p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){
p.packListReflected(rv)
})
default:
p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){
bl.Item(rv.Interface())
})
}
}
func (p *Packer) buildListReflected(value reflect.Value, call func(lp packs.ListPacker, rv reflect.Value)) {
p.PackList(func (lp packs.ListPacker){
for i:=0; i<value.Len(); i++ {
call(lp,value.Index(i))
if p.state.Error != nil {
return
}
}
})
}
func (p *Packer) packMapReflected (value reflect.Value) {
if value.IsNil() {
_,_ = p.state.Write(G.nullValue)
return
}
if value.Len() == 0 {
p.state.Write2Byte('{','}')
return
}
if p.state.Level > notDetectingCycles {
pt := value.UnsafePointer()
if !p.state.AddCheck(pt) {
return
}
defer p.state.DelCheck(pt)
}
mp,er := reflects.NewRMap(value,nil,nil)
if er != nil {
p.state.SetError(er)
return
}
p.packLOM(packs.NodeKindMap,'{','}',func(pk *Packer){
for i,kv := range mp.KeyValues {
if i > 0 {
p.state.WriteByte(',')
}
p.state.WriteByte('"')
p.state.WriteString(kv.Key)
p.state.Write2Byte('"',':')
//reflectTypePacker(p,kv.RVal,0)
//pk.packReflected(kv.RVal)
if pk.state.Error != nil {
fmt.Printf("Error %s\n",pk.state.Error)
return
}
}
})
}
func (p *Packer) packObjectReflected (value reflect.Value) {
//todo
}

Powered by TurnKey Linux.