// ------------------------------------------------------------------------ // 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 pkjson import ( "apacks" "codecs/cjson" "crypts" "encoding/json" "errs" "reflect" "time" ) type ( JSONPacker struct { state *apacks.State dirty bool } JSONPackerFnc func (packer *JSONPacker) ) func NewJSONPacker(options ...JSONPackerFnc) *JSONPacker { pk := &JSONPacker{ state : apacks.NewState(), } for _,o := range options { o(pk) } return pk } func (p *JSONPacker) Pack (value any, flags apacks.Flags) { if p.state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(p.state.Error)) } if p.dirty { panic(apacks.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 apacks.ReleaseState(p.state) } func (p *JSONPacker) PackAny (value any, flags apacks.Flags) { if p.state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(p.state.Error)) } if value == nil { p.state.W(cjson.G.NullValue) return } switch v := value.(type) { case apacks.PackBuilder: p.state.SetError(v.BuildPack(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 apacks.Flags) { packRaw(p.state,value,flags) } func (p *JSONPacker) PackBinary (value []byte, flags apacks.Flags) { packBinary(p.state,value,flags) } func (p *JSONPacker) PackString (value string, flags apacks.Flags) { packString (p.state,value,flags) } func (p *JSONPacker) PackBool (value bool, flags apacks.Flags) { packBool(p.state,value,flags) } func (p *JSONPacker) PackInt8 (value int8 , flags apacks.Flags) { packInt(p.state,int64(value),flags) } func (p *JSONPacker) PackInt16 (value int16, flags apacks.Flags) { packInt(p.state,int64(value),flags) } func (p *JSONPacker) PackInt32 (value int32, flags apacks.Flags) { packInt(p.state,int64(value),flags) } func (p *JSONPacker) PackInt64 (value int64, flags apacks.Flags) { packInt(p.state,value,flags)} func (p *JSONPacker) PackUInt8 (value uint8 , flags apacks.Flags) { packUInt(p.state,uint64(value),flags) } func (p *JSONPacker) PackUInt16 (value uint16, flags apacks.Flags) { packUInt(p.state,uint64(value),flags) } func (p *JSONPacker) PackUInt32 (value uint32, flags apacks.Flags) { packUInt(p.state,uint64(value),flags) } func (p *JSONPacker) PackUInt64 (value uint64, flags apacks.Flags) { packUInt(p.state, value,flags) } func (p *JSONPacker) PackFloat32 (value float32, flags apacks.Flags) { packFloatString(p.state, float64(value), flags, 32) } func (p *JSONPacker) PackFloat64 (value float64, flags apacks.Flags) { packFloatString(p.state, value, flags, 64) } func (p *JSONPacker) PackDate (value time.Time, flags apacks.Flags) { packDate(p.state,value,flags) } func (p *JSONPacker) PackList (builder func(apacks.ListPackBuilder, apacks.Flags), flags apacks.Flags) { p.packCompound('[',']',builder==nil,func(pk *JSONPacker, fl apacks.Flags){ builder(pk,fl) },flags) } func (p *JSONPacker) PackMap (builder func(apacks.MapPackBuilder, apacks.Flags), flags apacks.Flags) { p.packCompound('{','}',builder==nil,func(pk *JSONPacker, fl apacks.Flags){ builder(pk,fl) },flags) } func (p *JSONPacker) PackObject(builder func(apacks.ObjectPackBuilder, apacks.Flags), flags apacks.Flags) { p.packCompound('{','}',builder==nil,func(pk *JSONPacker, fl apacks.Flags){ builder(pk,fl) },flags) } func (p *JSONPacker) packCompound (opener, closer byte, isnil bool, buildFn func(*JSONPacker, apacks.Flags), flags apacks.Flags) { if p.state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(p.state.Error)) } if isnil { p.state.W(cjson.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 *apacks.State, value []byte, flags apacks.Flags) { if state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(state.Error)) } if value == nil { state.W(cjson.G.NullValue) return } if len(value) == 0 { state.Write2Byte('"','"') return } state.WOQ(flags.Quote(),'"',value) } func packString (state *apacks.State, value string , flags apacks.Flags) { if state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(state.Error)) } if len(value) == 0 { state.Write2Byte('"','"') return } if flags.Encrypted() { packEncrypted(state, []byte(value), flags|apacks.FlagBinary64) return } if flags.Binary() { packBinary(state,[]byte(value),flags) return } cjson.WriteString(state.ABuffer,value,flags.EscapeHTML()) } func packEncrypted (state *apacks.State, value []byte, flags apacks.Flags) { r := state.Encrypter.Encrypt(value) if !r.Ok() { state.SetError(r.Fail()) return } if !flags.Binary() { flags|= apacks.FlagBinary64 } packBinary(state,value,flags) } func packBinary (state *apacks.State, value []byte , flags apacks.Flags) { if state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(state.Error)) } if flags.Bin64() { state.SetError(cjson.WriteBase64(state.ABuffer,value)) return } if flags.Bin32() { state.SetError(cjson.WriteBase32(state.ABuffer,value)) return } state.SetError(apacks.G.ErrUnsupportedType) } func packBool (state *apacks.State, value bool , flags apacks.Flags) { if state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(state.Error)) } cjson.WriteBool(state.ABuffer,value,flags.Quote()) } func packInt (state *apacks.State, value int64 , flags apacks.Flags) { if state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(state.Error)) } cjson.WriteInt(state.ABuffer,value,flags.Quote()) } func packUInt (state *apacks.State, value uint64 , flags apacks.Flags) { if state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(state.Error)) } cjson.WriteUint(state.ABuffer,value,flags.Quote()) } func packFloat (state *apacks.State, value float64, flags apacks.Flags) { packFloatString(state, value, flags, 64) } func packFloatString (state *apacks.State, f float64, flags apacks.Flags, bits int) { if state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(state.Error)) } cjson.WriteFloat(state.ABuffer,f,bits,flags.Quote()) } func packDate(state *apacks.State, value time.Time, flags apacks.Flags) { if state.Error != nil { panic(apacks.G.ErrInvalidState.WrapWithCause(state.Error)) } cjson.WriteDate(state.ABuffer,value,flags.DateFormat(),true) } func packListString (state *apacks.State, list []string, flags apacks.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 *apacks.State, list []bool, flags apacks.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 *apacks.State, list []T, flags apacks.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 *apacks.State, list []T, flags apacks.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 *apacks.State, list []T, flags apacks.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 *apacks.State, list []time.Time, flags apacks.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 *apacks.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 } }