diff --git a/app/go.mod b/app/go.mod index fe865f1..568962c 100644 --- a/app/go.mod +++ b/app/go.mod @@ -1 +1,3 @@ module app + +go 1.20 \ No newline at end of file diff --git a/app/main.go b/app/main.go index 6cd77c2..068bb39 100644 --- a/app/main.go +++ b/app/main.go @@ -25,6 +25,6 @@ package main func main() { - reflects_main() + packs_main() } diff --git a/app/packs.go b/app/packs.go index 412ebb7..42456e8 100644 --- a/app/packs.go +++ b/app/packs.go @@ -27,6 +27,7 @@ import ( "activething.com/go/gogo/core/errs" "activething.com/go/gogo/packs/pjson" "fmt" + "time" ) func packs_main () { @@ -73,7 +74,7 @@ func packs_main () { */ - +/* var s = struct { F1Int int F2Uint uint @@ -86,11 +87,26 @@ func packs_main () { F4Binary: []byte("dklsdjflsdjflsdkfjlsdkfjlsdkfjlsdfjdlsfjsdlf"), } + + + pk.Pack(s) errs.Test(pk.Error(),true) fmt.Printf("JSON \n%s",pk.Bytes()) +*/ + mp := map[int]any { + 3:"23434434", + 1:time.Now(), + 4:233, + 7:[]uint16{2323,2323,2323}, + } + + pk.Pack(mp) + + errs.Test(pk.Error(),true) + fmt.Printf("JSON \n%s",pk.Bytes()) } \ No newline at end of file diff --git a/app/reflects.go b/app/reflects.go index 03577d7..eecce9c 100644 --- a/app/reflects.go +++ b/app/reflects.go @@ -24,7 +24,7 @@ package main import ( - "activething.com/go/gogo/core/reflects" + "activething.com/go/gogo/packs/reflects" "fmt" "reflect" ) @@ -67,6 +67,11 @@ func reflects_main () { } + + + + + } diff --git a/app/tests/benchmarks_test.go b/app/tests/benchmarks_test.go index bd5399c..db5d3be 100644 --- a/app/tests/benchmarks_test.go +++ b/app/tests/benchmarks_test.go @@ -28,6 +28,7 @@ import ( j "encoding/json" "fmt" "testing" + "time" ) var s = struct { @@ -42,6 +43,15 @@ var s = struct { F4Binary: []byte("dklsdjflsdjflsdkfjlsdkfjlsdkfjlsdfjdlsfjsdlf"), } +var packer = pjson.NewPacker() + +var mp = map[int]any { + 3:"23434434", + 1:time.Now(), + 4:233, + 7:[]uint16{2323,2323,2323}, +} + func BenchmarkHtmlPacker(b *testing.B) { packer := pjson.NewPacker() @@ -74,4 +84,30 @@ func BenchmarkJsonMarshal(b *testing.B) { } } -} \ No newline at end of file +} + + + +func BenchmarkMapPacker(b *testing.B) { + for i := 0; i < b.N; i++ { + packer.Pack(mp) + if packer.Error() != nil { + panic(packer.Error()) + } + packer.Reset() + } +} + +var bf []byte +func BenchmarkMapJSON(b *testing.B) { + var e error + var d []byte + for i := 0; i < b.N; i++ { + d, e = j.Marshal(s) + if e != nil { + panic(e) + } + } + bf = d +} + diff --git a/core/reflects/go.mod b/core/reflects/go.mod deleted file mode 100644 index f373d04..0000000 --- a/core/reflects/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module activething.com/go/gogo/core/reflects - -go 1.20 \ No newline at end of file diff --git a/go.work b/go.work index a070bf8..c72b2b2 100644 --- a/go.work +++ b/go.work @@ -9,7 +9,8 @@ use ( ./core/errs ./core/opts ./core/syncs - ./core/reflects + ./packs + ./packs/reflects ) diff --git a/packs/packabler.go b/packs/packabler.go index cd2ecb2..07615cb 100644 --- a/packs/packabler.go +++ b/packs/packabler.go @@ -31,6 +31,15 @@ type ( Build (packer Packer) ([]byte,error) } + + + PackableFnc func (packer Packer)([]byte,error) + ) + +func (f PackableFnc) Build (packer Packer)([]byte,error) { + return f(packer) +} + diff --git a/packs/packer_state.go b/packs/packer_state.go index 280b74d..4aaacbb 100644 --- a/packs/packer_state.go +++ b/packs/packer_state.go @@ -31,6 +31,8 @@ import ( ) + + type ( @@ -115,6 +117,19 @@ func (s *State) SetKey(key string) bool { } +func (s *State) AddCheck(ptr any) bool { + if _,hs := s.Checks[ptr]; hs { + s.SetError(G.ErrRedundantPack) + return false + } + s.Checks[ptr]=struct{}{} + return true +} + +func (s *State) DelCheck(ptr any) { + delete(s.Checks,ptr) +} + func (s *State) Open(kind NodeKind) { if s.Node.Kind == NodeKindValue { diff --git a/packs/pjson/packer.go b/packs/pjson/packer.go index 910ac10..47c1557 100644 --- a/packs/pjson/packer.go +++ b/packs/pjson/packer.go @@ -28,6 +28,7 @@ import ( "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" "reflect" @@ -526,28 +527,28 @@ func (p *Packer) packListReflected (value reflect.Value) { switch tp.Elem().Kind() { case reflect.String : - p.buildList(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemString(rv.String()) }) + p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemString(rv.String()) }) case reflect.Bool : - p.buildList(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemBool (rv.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.buildList(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemUInt64(rv.Uint()) }) + p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemUInt64(rv.Uint()) }) case reflect.Int8 , reflect.Int16 , reflect.Int32 , reflect.Int64 : - p.buildList(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemInt64(rv.Int()) }) + p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemInt64(rv.Int()) }) case reflect.Float32, reflect.Float64: - p.buildList(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemFloat64(rv.Float()) }) + p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.ItemFloat64(rv.Float()) }) case reflect.Slice : - p.buildList(value, func(bl packs.ListPacker, rv reflect.Value){ + p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ p.packListReflected(rv) }) default: - p.buildList(value, func(bl packs.ListPacker, rv reflect.Value){ + p.buildListReflected(value, func(bl packs.ListPacker, rv reflect.Value){ bl.Item(rv.Interface()) }) @@ -555,7 +556,7 @@ func (p *Packer) packListReflected (value reflect.Value) { } -func (p *Packer) buildList (value reflect.Value, call func(lp packs.ListPacker, rv reflect.Value)) { +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 _,kv := range mp.KeyValues { + pk.addField(kv.Key,func(pk *Packer) { + pk.Pack(kv.RVal.Interface()) + }) + } + }) + + } diff --git a/packs/reflects/go.mod b/packs/reflects/go.mod new file mode 100644 index 0000000..4728818 --- /dev/null +++ b/packs/reflects/go.mod @@ -0,0 +1,3 @@ +module activething.com/go/gogo/packs/reflects + +go 1.20 \ No newline at end of file diff --git a/packs/pjson/struct_field.go b/packs/reflects/gogo.go similarity index 69% rename from packs/pjson/struct_field.go rename to packs/reflects/gogo.go index 666e7cc..0320f02 100644 --- a/packs/pjson/struct_field.go +++ b/packs/reflects/gogo.go @@ -2,9 +2,9 @@ // Project atila // Active Thing (activething.com) git.activething.com/go // -// File name struct_field.go +// File name gogo.go // Created by DEV -// Modified 26/01/2024 +// Modified 28/01/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ @@ -21,7 +21,36 @@ // limitations under the License. // ------------------------------------------------------------------------ -package pjson +package reflects + +import ( + "activething.com/go/gogo/packs" + "reflect" + "sync" +) + +var ( + + G = struct { + + locker sync.Mutex + cache map[reflect.Type]RStruct + + }{ + + } +) + + +func GetRTypePacker (rtype reflect.Type, packer packs.Packer) *RStruct { + G.locker.Lock() + if G.cache == nil { + G.cache = make(map[reflect.Type]RStruct) + } + + G.locker.Unlock() + return nil +} diff --git a/core/reflects/rfield.go b/packs/reflects/rfield.go similarity index 98% rename from core/reflects/rfield.go rename to packs/reflects/rfield.go index 3cd7126..ede3649 100644 --- a/core/reflects/rfield.go +++ b/packs/reflects/rfield.go @@ -45,6 +45,7 @@ type ( Index []int Tags map[string]slices.String + Flags uint16 EqualFoldFn func(s, t []byte) bool } @@ -54,7 +55,7 @@ type ( func NewRField (rfield reflect.StructField, options ...opts.OptionFnc[RField]) *RField { - rf := &RField { + rf := &RField{ RField: rfield, Tags : make(map[string]slices.String), } diff --git a/core/reflects/rfield_tag.go b/packs/reflects/rfield_tag.go similarity index 98% rename from core/reflects/rfield_tag.go rename to packs/reflects/rfield_tag.go index b43348b..da16dad 100644 --- a/core/reflects/rfield_tag.go +++ b/packs/reflects/rfield_tag.go @@ -41,7 +41,7 @@ type ( func NewRFieldTag (rfield reflect.StructField, tag string, onOption func(rf reflect.Type, position int, option string) uint8) *RFieldTag { - f := &RFieldTag { + f := &RFieldTag{ Tag : rfield.Tag.Get(tag), } var ops string diff --git a/core/reflects/rfields.go b/packs/reflects/rfields.go similarity index 100% rename from core/reflects/rfields.go rename to packs/reflects/rfields.go diff --git a/packs/reflects/rmap.go b/packs/reflects/rmap.go new file mode 100644 index 0000000..d863416 --- /dev/null +++ b/packs/reflects/rmap.go @@ -0,0 +1,72 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name rmap.go +// Created by DEV +// Modified 28/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 reflects + +import ( + "reflect" + "sort" +) + + +type ( + + RMap struct { + KeyValues []RKeyValue + } +) + + +func NewRMap (value reflect.Value, stringifyFn func(v reflect.Value) (string,error), scapeFn func(string)string) (*RMap,error) { + ls := make([]RKeyValue,value.Len()) + mi := value.MapRange() + + sf := scapeFn + if sf == nil { + sf = func(v string) string { return v } + } + + for i:=0; mi.Next(); i++ { + k,e := NewRKeyValue(mi.Key(),mi.Value(), stringifyFn,sf) + if e != nil { + return nil,e + } + ls[i]=*k + } + + sort.Slice(ls,func(i,j int) bool { return ls[i].Key < ls[j].Key }) + + return &RMap { + KeyValues: ls, + },nil +} + + + + +func ensureScapeFn (sfn func(string)string) func(string)string { + if sfn == nil { + return func(v string) string { return v } + } + return sfn +} \ No newline at end of file diff --git a/packs/reflects/rmap_key_value.go b/packs/reflects/rmap_key_value.go new file mode 100644 index 0000000..fbe82e2 --- /dev/null +++ b/packs/reflects/rmap_key_value.go @@ -0,0 +1,87 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name rmap.go +// Created by DEV +// Modified 28/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 reflects + +import ( + "activething.com/go/gogo/packs" + "encoding" + "reflect" + "strconv" +) + +type ( + + RKeyValue struct { + RKey reflect.Value + RVal reflect.Value + Key string + } + + +) + + +func NewRKeyValue(key reflect.Value, value reflect.Value, stringifyFn func(v reflect.Value) (string,error), scapeFn func(string) string) (*RKeyValue,error) { + kv := &RKeyValue{ + RKey : key, + RVal : value, + } + if kv.RKey.Kind() == reflect.String { + kv.Key = scapeFn(kv.RKey.String()) + return kv,nil + } + + if tm,hs := kv.RKey.Interface().(encoding.TextMarshaler); hs { + if kv.RKey.Kind() == reflect.Pointer && kv.RKey.IsNil() { + return nil,packs.G.ErrInvalidKey + } + b,e := tm.MarshalText() + if e != nil { + return nil,packs.G.ErrInvalidKey + } + kv.Key = string(b) + return kv,nil + } + + if stringifyFn != nil { + b,e := stringifyFn(kv.RKey) + if e != nil { + return nil,packs.G.ErrInvalidKey.WithCause(e) + } + kv.Key = scapeFn(b) + return kv,nil + } + + switch key.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + kv.Key = strconv.FormatInt(kv.RKey.Int(), 10) + return kv,nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + kv.Key= strconv.FormatUint(kv.RKey.Uint(), 10) + return kv,nil + } + + return nil,packs.G.ErrInvalidKey + +} diff --git a/core/reflects/rstruct.go b/packs/reflects/rstruct.go similarity index 81% rename from core/reflects/rstruct.go rename to packs/reflects/rstruct.go index ffd65ad..f73d9f4 100644 --- a/core/reflects/rstruct.go +++ b/packs/reflects/rstruct.go @@ -23,25 +23,19 @@ package reflects -import ( - "activething.com/go/gogo/core/slices" - "bytes" - "reflect" -) - type ( RStruct struct { - Fields RFields - NameIndex map[string]int + Fields RFields + NameIndex map[string]int } ) - +/* func NewRStruct (rtype reflect.Type) *RStruct { res := &RStruct{ - Fields : make(RFields,0,rtype.NumField()), + Fields : make(RFields,0,rtype.NumField()), NameIndex: make(map[string]int), } @@ -51,7 +45,6 @@ func NewRStruct (rtype reflect.Type) *RStruct { var ( cnt,nxc map[reflect.Type]int vis map[reflect.Type]struct{} - fls RFields nms bytes.Buffer ) @@ -95,17 +88,20 @@ func NewRStruct (rtype reflect.Type) *RStruct { ft=ft.Elem() } - qt := false - if tags.Has("string") { - switch ft.Kind() { - case reflect.Bool, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, - reflect.Float32, reflect.Float64, - reflect.String: - qt = true - } - } + + //qt := false + //if tags.Has("string") { + // switch ft.Kind() { + // case reflect.Bool, + // reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + // reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + // reflect.Float32, reflect.Float64, + // reflect.String: + // qt = true + // } + //} + + index := make([]int, len(f.Index)+1) copy(index, f.Index) @@ -121,15 +117,13 @@ func NewRStruct (rtype reflect.Type) *RStruct { name = sf.Name } - rf := RField{ + res.Fields = append(res.Fields, RField{ RField: sf, RType: ft, Name: name, BName: []byte(name), Tags: map[string]slices.String{"json": append(slices.String(nil), tags...)}, - } - - res.Fields = append(res.Fields, rf) + }) if cnt[f.RType] > 1 { res.Fields = append(res.Fields, res.Fields[len(res.Fields)-1]) } @@ -137,7 +131,7 @@ func NewRStruct (rtype reflect.Type) *RStruct { } nxc[ft]++ if nxc[ft] == 1 { - nxt = append(nxt,RField{ + nxt = append(nxt, RField{ RType: ft, Name: ft.Name(), Index: index, @@ -148,10 +142,12 @@ func NewRStruct (rtype reflect.Type) *RStruct { } } - - + for i,f := range res.Fields { + res.NameIndex[f.Name]=i + } return res } +*/ \ No newline at end of file