// ------------------------------------------------------------------------ // 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 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 }