// ------------------------------------------------------------------------ // 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 ( "activething.com/go/gogo/core/errs" "activething.com/go/gogo/core/opts" "activething.com/go/gogo/packs" "encoding/base32" "encoding/base64" "encoding/json" "math" "reflect" "strconv" "time" "unicode/utf8" ) type ( Packer struct { state *packs.State dirty bool Options } ) func NewPacker (options ... opts.OptionFnc[Packer]) *Packer { return opts.Apply[Packer](&Packer { state : packs.NewState(), }, options ...) } func (p *Packer) Pack (value any, flags packs.Flags) { if p.state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(p.state.Error)) } if p.dirty { panic(packs.G.ErrInvalidState) } p.dirty = true p.PackAny(value,flags) } func (p *Packer) Packet () []byte { return p.state.Bytes() } func (p *Packer) Error () error { return p.state.Error } func (p *Packer) Reset () { p.state.Reset() p.Options.reset() p.dirty = false } func (p *Packer) PackAny (value any, flags packs.Flags) { if p.state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(p.state.Error)) } if value == nil { p.state.W(G.nullValue) return } switch v := value.(type) { case packs.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 *Packer) PackRaw (value []byte, flags packs.Flags) { packRaw(p.state,value,flags) } func (p *Packer) PackBinary (value []byte, flags packs.Flags) { packBinary(p.state,value,flags) } func (p *Packer) PackString (value string, flags packs.Flags) { packString (p.state,value,flags) } func (p *Packer) PackBool (value bool, flags packs.Flags) { packBool(p.state,value,flags) } func (p *Packer) PackInt8 (value int8 , flags packs.Flags) { packInt(p.state,int64(value),flags) } func (p *Packer) PackInt16 (value int16, flags packs.Flags) { packInt(p.state,int64(value),flags) } func (p *Packer) PackInt32 (value int32, flags packs.Flags) { packInt(p.state,int64(value),flags) } func (p *Packer) PackInt64 (value int64, flags packs.Flags) { packInt(p.state,value,flags)} func (p *Packer) PackUInt8 (value uint8 , flags packs.Flags) { packUInt(p.state,uint64(value),flags) } func (p *Packer) PackUInt16 (value uint16, flags packs.Flags) { packUInt(p.state,uint64(value),flags) } func (p *Packer) PackUInt32 (value uint32, flags packs.Flags) { packUInt(p.state,uint64(value),flags) } func (p *Packer) PackUInt64 (value uint64, flags packs.Flags) { packUInt(p.state, value,flags) } func (p *Packer) PackFloat32 (value float32, flags packs.Flags) { packFloatString(p.state, float64(value), flags, 32) } func (p *Packer) PackFloat64 (value float64, flags packs.Flags) { packFloatString(p.state, value, flags, 64) } func (p *Packer) PackDate (value time.Time, flags packs.Flags) { packDate(p.state,value,flags) } func (p *Packer) PackList (builder func(packs.PackListBuilder, packs.Flags), flags packs.Flags) { p.packCompound('[',']',builder==nil,func(pk *Packer, fl packs.Flags){ builder(pk,fl) },flags) } func (p *Packer) PackMap (builder func(packs.PackMapBuilder, packs.Flags), flags packs.Flags) { p.packCompound('{','}',builder==nil,func(pk *Packer, fl packs.Flags){ builder(pk,fl) },flags) } func (p *Packer) PackObject(builder func(packs.PackObjectBuilder, packs.Flags), flags packs.Flags) { p.packCompound('{','}',builder==nil,func(pk *Packer, fl packs.Flags){ builder(pk,fl) },flags) } func (p *Packer) packCompound (opener, closer byte, isnil bool, buildFn func(*Packer,packs.Flags), flags packs.Flags) { if p.state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(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 *packs.State, value []byte, flags packs.Flags) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(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 *packs.State, value string , flags packs.Flags) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(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 , flags packs.Flags) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(state.Error)) } if value == nil { _,_ = state.Write(G.nullValue) return } if len(value) == 0 { state.Write2Byte('"','"') return } state.WriteByte('"') if flags.Bin64() { packBinary64(state, value, 0) } else if flags.Bin32() { packBinary32(state, value, 0) } else { state.SetError(packs.G.ErrUnsupportedType) return } if state.Error == nil { state.WriteByte('"') } } func packBinary64 (state *packs.State, value []byte , _ packs.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 *packs.State, value []byte , _ packs.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 *packs.State, value bool , flags packs.Flags) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(state.Error)) } if value { state.WOQ(flags.Quote(),'"',G.trueValue) } else { state.WOQ(flags.Quote(),'"',G.falseValue) } } func packInt (state *packs.State, value int64 , flags packs.Flags) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(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 *packs.State, value uint64 , flags packs.Flags) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(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 *packs.State, value float64, flags packs.Flags) { packFloatString(state, value, flags, 64) } func packFloatString (state *packs.State, f float64, flags packs.Flags, bits int) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(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] } } state.WOQ(flags.Quote(),'"',bf) } func packDate(state *packs.State, value time.Time, flags packs.Flags) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(state.Error)) } state.WOQ(true,'"',value.AppendFormat(state.Temp[:0],flags.DateFormat())) } func packListString (state *packs.State, list []string, flags packs.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 *packs.State, list []bool, flags packs.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 *packs.State, list []T, flags packs.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 *packs.State, list []T, flags packs.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 *packs.State, list []T, flags packs.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 *packs.State, list []time.Time, flags packs.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 *packs.State, isNull, isEmpty bool) bool { if isNull { state.Write2Byte('"','"') return false } if isEmpty { state.Write2Byte('[',']') return false } return true }