diff --git a/packs/js/cache.go b/packs/js/cache.go deleted file mode 100644 index 369493f..0000000 --- a/packs/js/cache.go +++ /dev/null @@ -1,49 +0,0 @@ -// ------------------------------------------------------------------------ -// Project atila -// Active Thing (activething.com) git.activething.com/go -// -// File name cache.go -// Created by DEV -// Modified 30/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 js - -import ( - "reflect" - "sync" -) - -type ( - - cachePackers struct { - sync.RWMutex - packers map[reflect.Type]packerFn - - keyers map[reflect.Type]StringerFn - - } -) - -func NewCachePackers () *cachePackers { - return &cachePackers{ - keyers : make(map[reflect.Type]StringerFn), - packers: make(map[reflect.Type]packerFn), - } -} - - diff --git a/packs/js/flags.go b/packs/js/flags.go index 09ffe47..4e36f62 100644 --- a/packs/js/flags.go +++ b/packs/js/flags.go @@ -2,7 +2,7 @@ // Project atila // Active Thing (activething.com) git.activething.com/go // -// File name flags.go +// File name f.go // Created by DEV // Modified 29/01/2024 // diff --git a/packs/js/gogo.go b/packs/js/gogo.go index dc06892..6db8710 100644 --- a/packs/js/gogo.go +++ b/packs/js/gogo.go @@ -25,8 +25,11 @@ package js import ( "encoding" + "encoding/json" "math" + "reflect" "strconv" + "sync" "unicode/utf8" ) @@ -46,14 +49,17 @@ var ( mapEmpty []byte listEmpty []byte objectEmpty []byte - /* + floatNaN []byte floatInfPos []byte floatInfNeg []byte - */ - packers *cachePackers + packers sync.Map + keyers sync.Map + + jsonMarshalerType reflect.Type + textMarshalerType reflect.Type }{ @@ -68,14 +74,14 @@ var ( mapEmpty: []byte{'{', '}'}, objectEmpty: []byte{'{', '}'}, listEmpty: []byte{'[', ']'}, - /* + floatNaN: []byte{'"', 'N', 'a', 'N', '"'}, floatInfPos: []byte{'"', 'I', 'n', 'f', '"'}, floatInfNeg: []byte{'"', '-', 'I', 'n', 'f', '"'}, - */ - packers: NewCachePackers(), + jsonMarshalerType: reflect.TypeOf((*json.Marshaler)(nil)).Elem(), + textMarshalerType: reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem(), } diff --git a/packs/js/kv_packer.go b/packs/js/rkey.go similarity index 51% rename from packs/js/kv_packer.go rename to packs/js/rkey.go index e7d401c..52974a5 100644 --- a/packs/js/kv_packer.go +++ b/packs/js/rkey.go @@ -2,9 +2,9 @@ // Project atila // Active Thing (activething.com) git.activething.com/go // -// File name key_val_packer.go +// File name rkey.go // Created by DEV -// Modified 03/02/2024 +// Modified 04/02/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ @@ -27,110 +27,41 @@ import ( "activething.com/go/gogo/packs" "encoding" "reflect" - "sort" "strconv" ) -type ( - kvPacker struct { - keyFn keyStringerFnc - valFn packerFn - } - - keyStringerFnc func(entry *kvEntry, rkey reflect.Value) error - -) - - - -func newKVPacker (rtype reflect.Type) *kvPacker { - return &kvPacker{ - valFn : typePacker(rtype.Elem()), - keyFn : _keyStringer(rtype.Key()), +func stringifyRKey(rt reflect.Type) rMapEntryFnc { + fn,ok := G.keyers.Load(rt) + if !ok { + fn, _ = G.keyers.LoadOrStore(rt, _stringifyRKey(rt)) } + return fn.(rMapEntryFnc) } -func (p *kvPacker) pack (s *packs.State, v reflect.Value, f Flags) { - if p.keyFn == nil { - s.SetError(packs.G.ErrInvalidKey) - return - } - - if v.IsNil() { - s.W(G.nullValue) - return - } - if v.Len() == 0{ - s.Write2Byte('{','}') - return - } - - if s.Level > startDetectingCyclesAfter { - if fn,ok := s.CheckCycle(v.UnsafePointer()); ok { - defer fn() - } else { - s.SetError(packs.G.ErrRedundantPack) - return - } - } - - mi := v.MapRange() - ls := make(kvEntryList,v.Len()) - - for i:=0; mi.Next(); i++ { - ls[i] = kvEntry { - rval: mi.Value(), - } - if er := p.keyFn(&ls[i], mi.Key()); er != nil { - s.SetError(er) - return - } - } - - sort.Sort(ls) - - s.Open ('{') - for i:=0; i0 { - s.WriteByte(',') - } - packString(s,ls[i].key,ls[i].flags) - if s.Error != nil { - return - } - s.WriteByte(':') - p.valFn(s,ls[i].rval,0) - if s.Error != nil { - return - } - } - s.Close('}') -} - +func _stringifyRKey(rt reflect.Type) rMapEntryFnc { -func _keyStringer(rt reflect.Type) func(me *kvEntry, key reflect.Value) error { if rt.Kind() == reflect.String { - return func(e *kvEntry, k reflect.Value) error { - e.key = k.String() - e.flags = FlagScapeHTML + return func(e *rMapEntry, k reflect.Value) error { + e.k = k.String() + e.f = FlagScapeHTML return nil } } - if rt.Implements(reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()) { - return func(e *kvEntry, k reflect.Value) error { + if rt.Implements(G.textMarshalerType) { + return func(e *rMapEntry, k reflect.Value) error { if k.Kind() == reflect.Pointer && k.IsNil() { return nil } bf, er := (k.Interface().(encoding.TextMarshaler)).MarshalText() if er == nil { - e.key = string(bf) - e.flags = FlagScapeHTML + e.k = string(bf) + e.f = FlagScapeHTML } return er } @@ -143,8 +74,8 @@ func _keyStringer(rt reflect.Type) func(me *kvEntry, key reflect.Value) error { reflect.Int16, reflect.Int32, reflect.Int64: - return func(e *kvEntry, k reflect.Value) error { - e.key = strconv.FormatInt(k.Int(), 10) + return func(e *rMapEntry, k reflect.Value) error { + e.k = strconv.FormatInt(k.Int(), 10) return nil } case reflect.Uint, @@ -153,18 +84,18 @@ func _keyStringer(rt reflect.Type) func(me *kvEntry, key reflect.Value) error { reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return func(e *kvEntry, k reflect.Value) error { - e.key = strconv.FormatUint(k.Uint(), 10) + return func(e *rMapEntry, k reflect.Value) error { + e.k = strconv.FormatUint(k.Uint(), 10) return nil } case reflect.Interface: - return func(e *kvEntry, k reflect.Value) error { + return func(e *rMapEntry, k reflect.Value) error { k = k.Elem() if k.Type().Kind() == reflect.String { - e.key = k.String() - e.flags = FlagScapeHTML + e.k = k.String() + e.f = FlagScapeHTML return nil } @@ -174,8 +105,8 @@ func _keyStringer(rt reflect.Type) func(me *kvEntry, key reflect.Value) error { } bf, er := t.MarshalText() if er == nil { - e.key = string(bf) - e.flags = FlagScapeHTML + e.k = string(bf) + e.f = FlagScapeHTML } return er } @@ -187,7 +118,7 @@ func _keyStringer(rt reflect.Type) func(me *kvEntry, key reflect.Value) error { reflect.Int16, reflect.Int32, reflect.Int64: - e.key = strconv.FormatInt(k.Int(), 10) + e.k = strconv.FormatInt(k.Int(), 10) return nil case reflect.Uint, @@ -196,7 +127,7 @@ func _keyStringer(rt reflect.Type) func(me *kvEntry, key reflect.Value) error { reflect.Uint32, reflect.Uint64, reflect.Uintptr: - e.key = strconv.FormatUint(k.Uint(), 10) + e.k = strconv.FormatUint(k.Uint(), 10) return nil default: diff --git a/packs/js/type_slice_packer.go b/packs/js/rlist.go similarity index 89% rename from packs/js/type_slice_packer.go rename to packs/js/rlist.go index 105a968..6391d76 100644 --- a/packs/js/type_slice_packer.go +++ b/packs/js/rlist.go @@ -30,13 +30,13 @@ import ( type ( - typeSlicePacker struct { - packer packerFn + rList struct { + valFn packerFn } ) -func (p typeSlicePacker) pack (s *packs.State, v reflect.Value, f Flags) { +func (p rList) pack (s *packs.State, v reflect.Value, f Flags) { if v.IsNil() { _,_ = s.Write(G.nullValue) return @@ -50,7 +50,7 @@ func (p typeSlicePacker) pack (s *packs.State, v reflect.Value, f Flags) { if i > 0 { s.WriteByte(',') } - p.packer(s,v.Index(i),f) + p.valFn(s,v.Index(i),f) if s.Error != nil { return } diff --git a/packs/js/rmap.go b/packs/js/rmap.go new file mode 100644 index 0000000..e07cbad --- /dev/null +++ b/packs/js/rmap.go @@ -0,0 +1,110 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name key_val_packer.go +// Created by DEV +// Modified 03/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 js + +import ( + "activething.com/go/gogo/packs" + "reflect" + "sort" +) + +type ( + + rMap struct { + keyFn rMapEntryFnc + valFn packerFn + } + + +) + + + +func newRMap (rtype reflect.Type) *rMap { + return &rMap{ + valFn : typePacker(rtype.Elem()), + keyFn : stringifyRKey(rtype.Key()), + } +} + + + +func (p *rMap) pack (s *packs.State, v reflect.Value, f Flags) { + if p.keyFn == nil { + s.SetError(packs.G.ErrInvalidKey) + return + } + + if v.IsNil() { + s.W(G.nullValue) + return + } + if v.Len() == 0{ + s.Write2Byte('{','}') + return + } + + if s.Level > startDetectingCyclesAfter { + if fn,ok := s.CheckCycle(v.UnsafePointer()); ok { + defer fn() + } else { + s.SetError(packs.G.ErrRedundantPack) + return + } + } + + mi := v.MapRange() + ls := make(rMapEntryList,v.Len()) + + for i:=0; mi.Next(); i++ { + ls[i] = rMapEntry{ + v: mi.Value(), + } + if er := p.keyFn(&ls[i], mi.Key()); er != nil { + s.SetError(er) + return + } + } + + sort.Sort(ls) + + s.Open ('{') + for i:=0; i0 { + s.WriteByte(',') + } + packString(s,ls[i].k,ls[i].f) + if s.Error != nil { + return + } + s.WriteByte(':') + p.valFn(s,ls[i].v,0) + if s.Error != nil { + return + } + } + s.Close('}') +} + + diff --git a/packs/js/kv_entry.go b/packs/js/rmap_entry.go similarity index 76% rename from packs/js/kv_entry.go rename to packs/js/rmap_entry.go index 7caf11f..0f2ea0e 100644 --- a/packs/js/kv_entry.go +++ b/packs/js/rmap_entry.go @@ -28,13 +28,15 @@ import "reflect" type ( - kvEntry struct { - flags Flags - rval reflect.Value - key string + rMapEntry struct { + f Flags // flags + v reflect.Value // value + k string // Key } - kvEntryList []kvEntry + rMapEntryFnc func(entry *rMapEntry, rkey reflect.Value) error + + rMapEntryList []rMapEntry ) @@ -42,15 +44,15 @@ type ( -func (e kvEntryList) Len() int { +func (e rMapEntryList) Len() int { return len(e) } -func (e kvEntryList) Less(i, j int) bool { - return e[i].key < e[j].key +func (e rMapEntryList) Less(i, j int) bool { + return e[i].k < e[j].k } -func (e kvEntryList) Swap(i, j int) { +func (e rMapEntryList) Swap(i, j int) { e[i], e[j] = e[j], e[i] } diff --git a/packs/js/type_packer.go b/packs/js/rtype.go similarity index 86% rename from packs/js/type_packer.go rename to packs/js/rtype.go index 3f1b57d..c886f3f 100644 --- a/packs/js/type_packer.go +++ b/packs/js/rtype.go @@ -50,24 +50,15 @@ func RPacker(state *packs.State, value any, flags Flags ) { func typePacker (rtype reflect.Type) packerFn { - G.packers.RLock() - if f,h := G.packers.packers[rtype]; h { - G.packers.RUnlock() - return f + fn,ok := G.packers.Load(rtype) + if !ok { + fn,_ = G.packers.LoadOrStore(rtype, _rTypePacker(rtype)) } - G.packers.RUnlock() - - f := buildTypePacker(rtype) - - G.packers.Lock() - G.packers.packers[rtype]=f - G.packers.Unlock() - - return f + return fn.(packerFn) } -func buildTypePacker(t reflect.Type) packerFn { +func _rTypePacker(t reflect.Type) packerFn { switch t.Kind() { case reflect.Bool : return func(s *packs.State, v reflect.Value, f Flags) { packBool (s,v.Bool (),f )} @@ -87,11 +78,10 @@ func buildTypePacker(t reflect.Type) packerFn { case reflect.Slice, reflect.Array : - return (typeSlicePacker{ packer: typePacker(t.Elem()) }).pack + return (rList{ valFn: typePacker(t.Elem()) }).pack case reflect.Map : - return newKVPacker(t).pack - // return newTypeMapPacker(t).pack + return newRMap(t).pack } diff --git a/packs/js/type_map_packer.go b/packs/js/type_map_packer.go deleted file mode 100644 index e1ee16c..0000000 --- a/packs/js/type_map_packer.go +++ /dev/null @@ -1,289 +0,0 @@ -// ------------------------------------------------------------------------ -// Project atila -// Active Thing (activething.com) git.activething.com/go -// -// File name type_map_packer.go -// Created by DEV -// Modified 30/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 js - -import ( - "activething.com/go/gogo/packs" - "encoding" - "reflect" - "sort" - "strconv" -) - -type ( - - typeMapPacker struct { - keyStringer StringerFn - valPacker packerFn - } - - - StringerFn func(me *TypeMapEntry, key reflect.Value) error - - - - TypeMapEntry struct { - Val reflect.Value - Key string - flags Flags - } - - Entries []TypeMapEntry -) - -func (e Entries) Len() int { - return len(e) -} - -// Less compares two entries based on their keys -func (e Entries) Less(i, j int) bool { - return e[i].Key < e[j].Key -} - -// Swap swaps two entries in the slice -func (e Entries) Swap(i, j int) { - e[i], e[j] = e[j], e[i] -} - - - - -func newTypeMapPacker (rtype reflect.Type) *typeMapPacker { - return &typeMapPacker{ - valPacker : typePacker (rtype.Elem()), - keyStringer: keyStringer(rtype.Key ()), - } -} - - - - -func (m typeMapPacker) pack (s *packs.State, v reflect.Value, _ Flags) { - if v.IsNil() { - _,_ = s.Write(G.nullValue) - return - } - - mi := v.MapRange() - ls := make(Entries,v.Len()) - - if s.Level > startDetectingCyclesAfter { - if fn,ok := s.CheckCycle(v.UnsafePointer()); ok { - defer fn() - } else { - s.SetError(packs.G.ErrRedundantPack) - return - } - } - - for i:=0; mi.Next(); i++ { - ls[i] = TypeMapEntry{ - Val: mi.Value(), - } - if er := m.keyStringer(&ls[i], mi.Key()); er != nil { - s.SetError(er) - return - } - } - - sort.Sort(ls) - //sort.Slice(ls, func(i, j int) bool { return ls[i].Key < ls[j].Key }) - - s.Open ('{') - for i:=0; i0 { - s.WriteByte(',') - } - packString(s,ls[i].Key,ls[i].flags) - if s.Error != nil { - return - } - s.WriteByte(':') - m.valPacker(s,ls[i].Val,0) - if s.Error != nil { - return - } - } - s.Close('}') -} - - - - -/* -func (m typeMapPacker) pack (s *packs.State, v reflect.Value, _ Flags) { - if v.IsNil() { - _,_ = s.Write(G.nullValue) - return - } - - - - mi := v.MapRange() - ls := make(Entries,v.Len()) - - if s.Level > startDetectingCyclesAfter { - if fn,ok := s.CheckCycle(v.UnsafePointer()); ok { - defer fn() - } else { - s.SetError(packs.G.ErrRedundantPack) - return - } - } - - for i:=0; mi.Next(); i++ { - ls[i] = TypeMapEntry{ - Val: mi.Value(), - } - if er := m.keyStringer(&ls[i], mi.Key()); er != nil { - s.SetError(er) - return - } - } - - sort.Sort(ls) - //sort.Slice(ls, func(i, j int) bool { return ls[i].Key < ls[j].Key }) - - s.Open ('{') - for i:=0; i0 { - s.WriteByte(',') - } - packString(s,ls[i].Key,ls[i].flags) - if s.Error != nil { - return - } - s.WriteByte(':') - m.valPacker(s,ls[i].Val,0) - if s.Error != nil { - return - } - } - s.Close('}') -} - - - - - */ - -func keyStringer(rt reflect.Type) func(me *TypeMapEntry, key reflect.Value) error { - if rt.Kind() == reflect.String { - return func(e *TypeMapEntry, k reflect.Value) error { - e.Key = k.String() - e.flags = FlagScapeHTML - return nil - } - } - - if rt.Implements(reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()) { - return func(e *TypeMapEntry, k reflect.Value) error { - if k.Kind() == reflect.Pointer && k.IsNil() { - return nil - } - bf, er := (k.Interface().(encoding.TextMarshaler)).MarshalText() - if er == nil { - e.Key = string(bf) - e.flags = FlagScapeHTML - } - return er - } - } - - switch rt.Kind() { - - case reflect.Int, - reflect.Int8, - reflect.Int16, - reflect.Int32, - reflect.Int64: - return func(e *TypeMapEntry, k reflect.Value) error { - e.Key = strconv.FormatInt(k.Int(), 10) - return nil - } - case reflect.Uint, - reflect.Uint8, - reflect.Uint16, - reflect.Uint32, - reflect.Uint64, - reflect.Uintptr: - return func(e *TypeMapEntry, k reflect.Value) error { - e.Key = strconv.FormatUint(k.Uint(), 10) - return nil - } - - case reflect.Interface: - return func(e *TypeMapEntry, k reflect.Value) error { - k = k.Elem() - - if k.Type().Kind() == reflect.String { - e.Key = k.String() - e.flags = FlagScapeHTML - return nil - } - - if t,h := k.Interface().(encoding.TextMarshaler); h { - if k.Type().Kind() == reflect.Pointer && k.IsNil() { - return nil - } - bf, er := t.MarshalText() - if er == nil { - e.Key = string(bf) - e.flags = FlagScapeHTML - } - return er - } - - switch rt.Kind() { - - case reflect.Int, - reflect.Int8, - reflect.Int16, - reflect.Int32, - reflect.Int64: - e.Key = strconv.FormatInt(k.Int(), 10) - return nil - - case reflect.Uint, - reflect.Uint8, - reflect.Uint16, - reflect.Uint32, - reflect.Uint64, - reflect.Uintptr: - e.Key = strconv.FormatUint(k.Uint(), 10) - return nil - - default: - return packs.G.ErrInvalidKey - } - } - - default: - return func(e *TypeMapEntry, k reflect.Value) error { - return packs.G.ErrInvalidKey - } - } - - -}