// ------------------------------------------------------------------------ // Project atila // Active Thing (activething.com) git.activething.com/go // // File name builder.go // Created by DEV // Modified 21/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 jjson import ( "activething.com/go/gogo/core/opts" "activething.com/go/gogo/core/syncs" "activething.com/go/gogo/packs" "encoding/json" "reflect" "sort" "sync/atomic" "time" ) const( maxDeepUncheck = 100 ) type ( Builder struct { options Options encoder *json.Encoder dateLayout string scratch [64]byte state *packs.State } ) func AcquireBuilder (options ...opts.OptionFnc[Builder]) *Builder { if atomic.CompareAndSwapUint32(&G.sentinel,0,1) { G.builderPool = syncs.NewPool[Builder]( syncs.PoolWithNew[Builder](func() *Builder { return &Builder{ options : jsonOptionsDefaults, state : packs.AcquireState(), dateLayout: time.RFC822Z, } }), ) } return opts.Apply(G.builderPool.Get(),options...) } func ReleaseBuilder (builder *Builder) { if builder != nil && atomic.LoadUint32(&G.sentinel) == 1 { G.builderPool.Put(builder.reset()) } } func (b *Builder) Any (value any) { switch value.(type) { default: rv := reflect.ValueOf(value) switch rv.Kind() { case reflect.Struct : b.objPacker(rv) case reflect.Map : b.mapPacker(rv) case reflect.Slice : b.listPacker(rv) } } } func (b *Builder) Map (builder func(packer packs.BuilderMap)) {} func (b *Builder) reset () *Builder { //todo b.options = jsonOptionsDefaults b.encoder = nil b.dateLayout = time.RFC822Z b.state.Reset() return b } func (b *Builder) mapPacker (rv reflect.Value) { if rv.IsNil() { b.state.Write(G.nullValue) return } if rv.Len() == 0 { b.state.Write2Byte('{','}') return } if b.state.Node.Level > maxDeepUncheck { p := rv.UnsafePointer() if _,ok := b.state.Seen[p]; ok { b.state.SetError(packs.G.ErrRedundantPack) return } b.state.Seen[p] = struct{}{} defer delete(b.state.Seen,p) } mi := rv.MapRange() if b.options.SortMapKeys() { ls := make([]reflectMapElem, rv.Len()) for i := 0; mi.Next(); i++ { ls[i].k = mi.Key() ls[i].v = mi.Value() if !ls[i].stringifyKey(b.options) { b.state.SetError(packs.G.ErrInvalidKey) return } } if b.options.SortMapKeys() { sort.Slice(ls, func(i, j int) bool { return ls[i].s < ls[j].s }) } b.Map(func(bm packs.BuilderMap) { for _, e := range ls { bm.ElemAny(e.s, e.v) if b.state.Error != nil { return } } }) return } b.Map(func(bm packs.BuilderMap) { for i:=0; mi.Next(); i++ { bm.ElemAny(mi.Key(),mi.Value()) if b.state.Error != nil { return } } }) } func (b *Builder) objPacker (rv reflect.Value) { if rv.IsNil() { b.state.Write(G.nullValue) return } if rv.Len() == 0 { b.state.Write2Byte('{','}') return } } func (b *Builder) listPacker(rv reflect.Value) { if rv.IsNil() { b.state.Write(G.nullValue) return } if rv.Len() == 0 { b.state.Write2Byte('[',']') return } if b.state.Node.Level > maxDeepUncheck { p := struct { ptr interface{} // always an unsafe.Pointer, but avoids a dependency on package unsafe len int }{ ptr: rv.UnsafePointer(), len: rv.Len(), } if _,ok := b.state.Seen[p]; ok { b.state.SetError(packs.G.ErrRedundantPack) return } b.state.Seen[p] = struct{}{} defer delete(b.state.Seen,p) } switch rv.Type().Elem().Kind() { case reflect.Int : b._listPacker (rv, func(lb packs.BuilderList, ev reflect.Value) { lb.ItemInt64(int64(ev.Int())) }) } } func (b *Builder) _listPacker (rlist reflect.Value, add func(lb packs.BuilderList, ev reflect.Value)) { for i:=0; i< rlist.Len(); i++ { add(b,rlist.Index(i)) if b.state.Error != nil { return } } } func stringifyKey(key reflect.Value, ops Options) (string,bool) { return "ss",true }