diff --git a/app/main.go b/app/main.go index 74bd560..7d45bc7 100644 --- a/app/main.go +++ b/app/main.go @@ -26,55 +26,24 @@ package main import ( "activething.com/go/gogo/core/errs" "activething.com/go/gogo/packs" - "activething.com/go/gogo/packs/js" - "encoding/json" + "activething.com/go/gogo/packs/pjson" "fmt" ) -func createDefer () func() { - return func() { - fmt.Printf("Saliend\n") - } -} - -func execute () { - defer createDefer()() - fmt.Printf("Execute\n") -} - -type miTextor struct{} - - -func (_ miTextor) MarshalText ()([]byte,error) { - return []byte("ClaveDeTexto"),nil -} - func main() { - execute() - - st := packs.NewState() - - - cas := []string {"a1111","b2222","c3333"} - - js.RPacker(st,cas,0) - fmt.Printf("JSON \n%s\n",st.Bytes()) - st.Reset() - - - - - //a := map[int]string {1:"344343",3:"4343434",5:"frtrtrtr"} - a := map[string]int {"{basd1/>":344343,"{casd2/>":4343434,"{asd41/>":67} - js.RPacker(st,a,0) - errs.Test(st.Error,true) - fmt.Printf("Pack \n%s\n",st.Bytes()) - st.Reset() + pk := pjson.NewPacker() - d,e:=json.Marshal(a) - errs.Test(e,true) - fmt.Printf("JSON \n%s\n",d) + pk.PackList(func(p packs.ListPacker, flags packs.Flags){ + p.ItemUInt64(0,flags) + p.ItemFloat64(7.8,flags) + p.ItemList(func (p1 packs.ListPacker, f packs.Flags){ + p1.ItemString("MICasa",flags) + p1.ItemString("Coche",flags) + },flags) + },0) + errs.Test(pk.Error(),true) + fmt.Printf("Paquete %s\n",pk.Packet()) } diff --git a/app/tests/benchmarks_test.go b/app/tests/benchmarks_test.go index 3927165..a59fb83 100644 --- a/app/tests/benchmarks_test.go +++ b/app/tests/benchmarks_test.go @@ -26,6 +26,7 @@ package main import ( "activething.com/go/gogo/packs" "activething.com/go/gogo/packs/js" + "activething.com/go/gogo/packs/pjson" j "encoding/json" "testing" ) @@ -154,10 +155,20 @@ func BenchmarkListJSON (b *testing.B) { //var ma = map[string]string {"{asd1/>":"344343","{asd2/>":"4343434","{asd41/>":"frtrtrtr"} -var mas = map[string]int {"{basd1/>":344343,"{casd2/>":4343434,"{asd41/>":67,"{ba2sd1/>":344343,"{11casd2/>":4343434,"{33asd41/>":67} +var mas = map[string]any {"{basd1/>":344343,"{casd2/>":4343434,"{asd41/>":67,"{ba2sd1/>":344343,"{11casd2/>":4343434,"{33asd41/>":67,"slice":[]int{34,34,234,4}} func BenchmarkMapPack (b *testing.B) { + s := pjson.NewPacker() + for i:=0; i< b.N; i++ { + s.Pack(ma,0) + s.Reset() + } +} + + + +func BenchmarkPacker (b *testing.B) { s := packs.NewState() for i:=0; i< b.N; i++ { js.RPacker(s,ma,0) @@ -177,7 +188,6 @@ func BenchmarkMapJSON (b *testing.B) { - var ma = map[string]string { "clave0": "valor123", "clave1": "rojo", diff --git a/core/bins/buffer.go b/core/bins/buffer.go index 2fdd459..d6125a2 100644 --- a/core/bins/buffer.go +++ b/core/bins/buffer.go @@ -218,6 +218,18 @@ func (b *Buffer) W (value []byte) { copy(b.data[b.tryGrow(len(value)):], value) } +// WOQ With Optional Quoted +func (b *Buffer) WOQ (add bool, quote byte, value []byte) { + b.lastOp = opInvalid + if !add { + copy(b.data[b.tryGrow(len(value)):], value) + return + } + sz := b.tryGrow(len(value)+2) + copy(b.data[sz:],value) +} + + func (b *Buffer) WriteByte(value byte) { b.lastOp = opInvalid b.data[b.tryGrow(1)] = value diff --git a/core/bins/buffer_pool.go b/core/bins/buffer_pool.go index 88426e6..b788c70 100644 --- a/core/bins/buffer_pool.go +++ b/core/bins/buffer_pool.go @@ -43,6 +43,7 @@ const ( ) type ( + BufferPool struct { syncs.PoolStats @@ -57,6 +58,7 @@ type ( threshold uint64 percentile float32 } + ) func NewBufferPool(options ...opts.OptionFnc[BufferPool]) *BufferPool { diff --git a/core/bins/gogo.go b/core/bins/gogo.go index 56f2eeb..a25eaf4 100644 --- a/core/bins/gogo.go +++ b/core/bins/gogo.go @@ -33,6 +33,8 @@ var ( ErrTooLarge errs.ErrCode ErrNegativeRead errs.ErrCode ErrUnreadInvalid errs.ErrCode + + }{ ErrOutOfRange: errs.ErrCode("core:bins.out-of-range"), ErrWriteCount: errs.ErrCode("core:bins.write-count"), diff --git a/packs/gogo.go b/packs/gogo.go index 6c25c24..66c76ef 100644 --- a/packs/gogo.go +++ b/packs/gogo.go @@ -36,6 +36,7 @@ var ( statePool *syncs.Pool[State] ErrInvalidState errs.ErrCode + ErrInvalidNode errs.ErrCode ErrInvalidKey errs.ErrCode ErrDuplicatedKey errs.ErrCode ErrKeyRequired errs.ErrCode @@ -45,7 +46,8 @@ var ( ErrInvalidValue errs.ErrCode ErrUnsupportedType errs.ErrCode }{ - ErrInvalidState : errs.ErrCode("packs.invalid-stae"), + ErrInvalidState : errs.ErrCode("packs.invalid-state"), + ErrInvalidNode : errs.ErrCode("packs.invalid-node"), ErrInvalidKey : errs.ErrCode("packs.invalid-key"), ErrDuplicatedKey : errs.ErrCode("packs:duplicated-key"), ErrKeyRequired : errs.ErrCode("packs:key-required"), diff --git a/packs/js/packer.go b/packs/js/packer.go index 87fb0b3..8c491e9 100644 --- a/packs/js/packer.go +++ b/packs/js/packer.go @@ -27,7 +27,6 @@ import ( "activething.com/go/gogo/packs" "encoding/base32" "encoding/base64" - "encoding/json" "io" "math" "reflect" @@ -74,11 +73,13 @@ func pack (state *packs.State, value any , flags Flags) { case float32: packFloat(state,float64(v),flags) case float64: packFloat(state,v,flags) - case time.Time : PackDate(v) + case time.Time : PackDate(state,v,flags) - case reflect.Value : Pack(v.Elem()) + case reflect.Value : RPacker(state,v.Elem(),0) default: + + /* if pb,is := value.(packs.Builder); is { e := pb.Build(pb) if e != nil { @@ -113,6 +114,8 @@ func pack (state *packs.State, value any , flags Flags) { } state.Write(d) } + + */ } } diff --git a/packs/js/rkey.go b/packs/js/rkey.go index 8069fa3..a3fa5b9 100644 --- a/packs/js/rkey.go +++ b/packs/js/rkey.go @@ -42,7 +42,6 @@ func stringifyRKey(rt reflect.Type) rMapEntryFnc { - func _stringifyRKey(rt reflect.Type) rMapEntryFnc { if rt.Kind() == reflect.String { diff --git a/packs/packer.go b/packs/packer.go index 30f2c57..b5a8f9e 100644 --- a/packs/packer.go +++ b/packs/packer.go @@ -23,42 +23,16 @@ package packs -import "time" - type ( Packer interface { - Pack (value any, flags Flags) ([]byte,error) - - - PackAny (value any, flags Flags) - - PackRaw (value []byte, flags Flags) - PackBinary (value []byte, flags Flags) - PackString (value string, flags Flags) - - PackBool (value bool, flags Flags) - - PackInt8 (value int8, flags Flags) - PackInt16 (value int16, flags Flags) - PackInt32 (value int32, flags Flags) - PackInt64 (value int64, flags Flags) - - PackUInt8 (value uint8 , flags Flags) - PackUInt16 (value uint16, flags Flags) - PackUInt32 (value uint32, flags Flags) - PackUInt64 (value uint64, flags Flags) - - PackFloat32 (value float32, flags Flags) - PackFloat64 (value float64, flags Flags) - - PackDate (value time.Time, flags Flags) + Pack (value any, flags Flags) - PackList (builder func(packer ListPacker , flags Flags)) - PackMap (builder func(packer MapPacker , flags Flags)) - PackObject(builder func(packer ObjectPacker, flags Flags)) + Packet () []byte + Error () error + Reset () } ) diff --git a/packs/packer_list.go b/packs/packer_list.go new file mode 100644 index 0000000..9ada953 --- /dev/null +++ b/packs/packer_list.go @@ -0,0 +1,62 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com, flags Flags) git.activething.com/go +// +// File name list_Itemer.go +// Created by DEV +// Modified 23/01/2024 +// +// Copyright 2024 activething.com +// ------------------------------------------------------------------------ +// Licensed under the Apache License, Version 2.0 (the "License", flags Flags); +// 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 packs + +import "time" + +type ( + + ListPacker interface { + + ItemAny (value any, flags Flags) + + ItemRaw (value []byte, flags Flags) + + ItemBinary (value []byte, flags Flags) + ItemString (value string, flags Flags) + + ItemBool (value bool, flags Flags) + + ItemInt8 (value int8, flags Flags) + ItemInt16 (value int16, flags Flags) + ItemInt32 (value int32, flags Flags) + ItemInt64 (value int64, flags Flags) + + ItemUInt8 (value uint8, flags Flags) + ItemUInt16 (value uint16, flags Flags) + ItemUInt32 (value uint32, flags Flags) + ItemUInt64 (value uint64, flags Flags) + + ItemFloat32 (value float32, flags Flags) + ItemFloat64 (value float64, flags Flags) + + ItemDate (value time.Time, flags Flags) + + ItemList (builder func(packer ListPacker, flags Flags), flags Flags) + ItemMap (builder func(packer MapPacker , flags Flags), flags Flags) + ItemObject(builder func(packer ObjectPacker, flags Flags), flags Flags) + + } +) + diff --git a/packs/map_packer.go b/packs/packer_map.go similarity index 96% rename from packs/map_packer.go rename to packs/packer_map.go index 8afebe1..bd90a49 100644 --- a/packs/map_packer.go +++ b/packs/packer_map.go @@ -29,9 +29,9 @@ type ( MapPacker interface { - KeyValue (key any, value any) + KeyValueAny (key any, value any) - KeyValueRaw (key any,value []byte) + KeyValueRaw (key any,value []byte) KeyValueBinary(key any, value []byte) KeyValueString(key any, value string) diff --git a/packs/packer_state.go b/packs/packer_state.go index 86f029b..dde81e7 100644 --- a/packs/packer_state.go +++ b/packs/packer_state.go @@ -41,6 +41,7 @@ type ( Level int Error error checks map[any]struct{} + counts []int } ) @@ -53,6 +54,7 @@ func NewState() *State { return &State{ Buffer: bins.NewBuffer(), checks: make(map[any]struct{}), + counts: []int{0}, } }), ) @@ -65,6 +67,7 @@ func ReleaseState (state *State) { if state == nil { state.Buffer.Reset() state.Error = nil + state.counts= append(state.counts[:0],0) if len(state.checks) > 0 { for k,_ := range state.checks { delete(state.checks,k) @@ -107,6 +110,7 @@ func (s *State) CheckCycle (ptr any) (func(),bool) { func (s *State) Open(opener byte) { s.WriteByte(opener) s.Level++ + s.counts = append(s.counts,0) } @@ -116,6 +120,18 @@ func (s *State) Close(closer byte) { if s.Level < 0 { s.SetError(G.ErrInvalidNode) } + s.counts = s.counts[:len(s.counts)-1] } +func (s *State) Count() int { + if s.Error != nil { + return 0 + } + return s.counts[s.Level] +} +func (s *State) Inc () { + if s.Error == nil { + s.counts[s.Level] += 1 + } +} \ No newline at end of file diff --git a/packs/packer_value.go b/packs/packer_value.go new file mode 100644 index 0000000..f4129f1 --- /dev/null +++ b/packs/packer_value.go @@ -0,0 +1,65 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name value_packer.go +// Created by DEV +// Modified 05/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 packs + +import "time" + +type ( + + ValuePacker interface { + + PackAny (value any, flags Flags) + + PackRaw (value []byte, flags Flags) + PackBinary (value []byte, flags Flags) + PackString (value string, flags Flags) + + PackBool (value bool, flags Flags) + + PackInt8 (value int8, flags Flags) + PackInt16 (value int16, flags Flags) + PackInt32 (value int32, flags Flags) + PackInt64 (value int64, flags Flags) + + PackUInt8 (value uint8 , flags Flags) + PackUInt16 (value uint16, flags Flags) + PackUInt32 (value uint32, flags Flags) + PackUInt64 (value uint64, flags Flags) + + PackFloat32 (value float32, flags Flags) + PackFloat64 (value float64, flags Flags) + + PackDate (value time.Time, flags Flags) + + PackList (builder func(packer ListPacker , flags Flags), flags Flags) + PackMap (builder func(packer MapPacker , flags Flags), flags Flags) + PackObject(builder func(packer ObjectPacker, flags Flags), flags Flags) + + } +) + + + + + diff --git a/packs/pjson/gogo.go b/packs/pjson/gogo.go index 31273e5..d66cf66 100644 --- a/packs/pjson/gogo.go +++ b/packs/pjson/gogo.go @@ -27,9 +27,7 @@ import ( "activething.com/go/gogo/packs" "encoding" "encoding/json" - "math" "reflect" - "strconv" "sync" "unicode/utf8" ) @@ -79,10 +77,10 @@ var ( textMarshalerType: reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem(), packMarshalerType: reflect.TypeOf((*packs.Packer)(nil)).Elem(), - } ) + func initJSONChars() [utf8.RuneSelf]jsonCharAtt { var tb = [utf8.RuneSelf]jsonCharAtt{ '"': jsonCharAttAllUnsafe, @@ -99,70 +97,7 @@ func initJSONChars() [utf8.RuneSelf]jsonCharAtt { -func stringifyKey(buffer []byte, value any) []byte { - if value == nil { - return G.nullValue - } - - if st,is := value.(string); is { - return append(buffer[:0],st...) - } - if tm,is := value.(encoding.TextMarshaler); is { - if bf,er := tm.MarshalText(); er == nil { - return append(buffer[:0],bf...) - } - return nil - } - switch tp := value.(type) { - case bool: - if tp { - return G.trueValue - } - return G.falseValue - - case int : return strconv.AppendInt (buffer[:0],int64(tp), 10) - case int8 : return strconv.AppendInt (buffer[:0],int64(tp), 10) - case int16 : return strconv.AppendInt (buffer[:0],int64(tp), 10) - case int32 : return strconv.AppendInt (buffer[:0],int64(tp), 10) - case int64 : return strconv.AppendInt (buffer[:0],tp, 10) - - case uint : return strconv.AppendUint(buffer[:0],uint64(tp), 10) - case uint8 : return strconv.AppendUint(buffer[:0],uint64(tp), 10) - case uint16 : return strconv.AppendUint(buffer[:0],uint64(tp), 10) - case uint32 : return strconv.AppendUint(buffer[:0],uint64(tp), 10) - case uint64 : return strconv.AppendUint(buffer[:0],tp, 10) - - case float32: return stringifyFloat(buffer[:0],float64(tp),32) - case float64: return stringifyFloat(buffer[:0],tp,64) - } - return nil -} -func stringifyFloat (buffer []byte, f float64, bits int) []byte { - if math.IsInf(f, 0) || math.IsNaN(f) { - return nil - } - - 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' - } - } - buffer = strconv.AppendFloat(buffer[:0], f, fmt, -1, int(bits)) - if fmt == 'e' { - // clean up e-09 to e-9 - n := len(buffer) - if n >= 4 && buffer[n-4] == 'e' && buffer[n-3] == '-' && buffer[n-2] == '0' { - buffer[n-2] = buffer[n-1] - buffer = buffer[:n-1] - } - } - return buffer -} - diff --git a/packs/pjson/options.go b/packs/pjson/options.go index c974073..3409b21 100644 --- a/packs/pjson/options.go +++ b/packs/pjson/options.go @@ -31,3 +31,4 @@ type ( ) +func (o *Options) reset () {} \ No newline at end of file diff --git a/packs/pjson/packer.go b/packs/pjson/packer.go index d421e1c..89c05f8 100644 --- a/packs/pjson/packer.go +++ b/packs/pjson/packer.go @@ -26,14 +26,6 @@ package pjson import ( "activething.com/go/gogo/core/opts" "activething.com/go/gogo/packs" - "encoding/base32" - "encoding/base64" - "encoding/json" - "math" - "reflect" - "strconv" - "time" - "unicode/utf8" ) type ( @@ -41,6 +33,7 @@ type ( Packer struct { state *packs.State + dirty bool Options } @@ -49,353 +42,39 @@ type ( ) -func NewPacker (options opts.OptionFnc[Packer]) *Packer { +func NewPacker (options ... opts.OptionFnc[Packer]) *Packer { return opts.Apply[Packer](&Packer { state : packs.NewState(), - }, options) + }, options ...) } -func (p *Packer) Pack (value any, flags packs.Flags) ([]byte,error) { - if p.state.Error != nil { - return nil,packs.G.ErrInvalidState.WithCause(p.state.Error) - } - - p.PackAny(value,flags) - if p.state.Error != nil { - return nil,p.state.Error - } - return p.state.Bytes(),nil -} - - - - - - -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 reflect.Value: - rPackAny(p.state,v,flags) - - default: - rPackAny(p.state,reflect.ValueOf(value), flags) - } -} - - - - -func (p *Packer) PackRaw (value []byte, flags packs.Flags) { +func (p *Packer) Pack (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 - } - if len(value) == 0 { - p.state.Write2Byte('"','"') - return - } - p.state.W(value) -} - - -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.ListPacker, packs.Flags)) {} -func (p *Packer) PackMap (builder func(packs.MapPacker, packs.Flags)) {} -func (p *Packer) PackObject(builder func(packs.ObjectPacker, packs.Flags)) {} - - - - - -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 flags.Quote() { - state.WriteByte('"') - } - if value { - state.W(G.trueValue) - } else { - state.W(G.falseValue) - } - if flags.Quote() { - state.WriteByte('"') - } -} - -func packInt (state *packs.State, value int64 , flags packs.Flags) { - if state.Error != nil { - panic(packs.G.ErrInvalidState.WithCause(state.Error)) - } - - if flags.Quote() { - state.WriteByte('"') - } - if value == 0 { - state.WriteByte('0') - } else { - state.WriteIntStr(value, 10) - } - if flags.Quote() { - state.WriteByte('"') + if p.dirty { + panic(packs.G.ErrInvalidState) } + p.dirty = true + p.PackAny(value,flags) } -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 (p *Packer) Packet () []byte { + return p.state.Bytes() } -func packFloat (state *packs.State, value float64, flags packs.Flags) { - packFloatString(state, value, flags, 64) +func (p *Packer) Error () error { + return p.state.Error } -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] - } - } - - if flags.Quote() { - state.WriteByte('"') - state.W(bf) - state.WriteByte('"') - return - } - state.W(bf) +func (p *Packer) Reset () { + p.state.Reset() + p.Options.reset() } -func PackDate (state *packs.State, value time.Time, flags packs.Flags) { - if state.Error != nil { - panic(packs.G.ErrInvalidState.WithCause(state.Error)) - } - state.WriteByte('"') - bf := state.Temp[:0] - bf = value.AppendFormat(bf,flags.DateFormat()) - state.W(bf) - state.WriteByte('"') - -} diff --git a/packs/pjson/packer_list.go b/packs/pjson/packer_list.go new file mode 100644 index 0000000..90e0b9c --- /dev/null +++ b/packs/pjson/packer_list.go @@ -0,0 +1,187 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name packer_list.go +// Created by DEV +// Modified 05/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/packs" + "time" +) + + +func (p *Packer) ItemAny (value any, flags packs.Flags) { p.addSep(); p.PackAny(value,flags) } + +func (p *Packer) ItemRaw (value []byte, flags packs.Flags) { p.addSep(); packRaw(p.state,value,flags); p.state.Inc() } + +func (p *Packer) ItemBinary (value []byte, flags packs.Flags) { p.addSep(); packBinary(p.state,value,flags); p.state.Inc() } +func (p *Packer) ItemString (value string, flags packs.Flags) { p.addSep(); packString(p.state,value,flags); p.state.Inc() } + +func (p *Packer) ItemBool (value bool, flags packs.Flags) { p.addSep(); packBool(p.state,value,flags); p.state.Inc() } + +func (p *Packer) ItemInt8 (value int8 , flags packs.Flags) { p.addSep(); packInt(p.state,int64(value),flags); p.state.Inc() } +func (p *Packer) ItemInt16 (value int16, flags packs.Flags) { p.addSep(); packInt(p.state,int64(value),flags); p.state.Inc() } +func (p *Packer) ItemInt32 (value int32, flags packs.Flags) { p.addSep(); packInt(p.state,int64(value),flags); p.state.Inc() } +func (p *Packer) ItemInt64 (value int64, flags packs.Flags) { p.addSep(); packInt(p.state, value,flags); p.state.Inc() } + +func (p *Packer) ItemUInt8 (value uint8 , flags packs.Flags){ p.addSep(); packUInt(p.state,uint64(value),flags); p.state.Inc() } +func (p *Packer) ItemUInt16 (value uint16, flags packs.Flags){ p.addSep(); packUInt(p.state,uint64(value),flags); p.state.Inc() } +func (p *Packer) ItemUInt32 (value uint32, flags packs.Flags){ p.addSep(); packUInt(p.state,uint64(value),flags); p.state.Inc() } +func (p *Packer) ItemUInt64 (value uint64, flags packs.Flags){ p.addSep(); packUInt(p.state,value,flags); p.state.Inc() } + +func (p *Packer) ItemFloat32 (value float32, flags packs.Flags) { p.addSep(); packFloatString(p.state,float64(value),flags,32); p.state.Inc() } +func (p *Packer) ItemFloat64 (value float64, flags packs.Flags) { p.addSep(); packFloatString(p.state,value,flags,64); p.state.Inc() } + +func (p *Packer) ItemDate (value time.Time, flags packs.Flags) { p.addSep(); packDate(p.state,value,flags); p.state.Inc() } + +func (p *Packer) ItemList (builder func(packer packs.ListPacker, flags packs.Flags), flags packs.Flags) { + p.addSep() + p.PackList(builder,flags) + p.state.Inc() +} + +func (p *Packer) ItemMap (builder func(packer packs.MapPacker , flags packs.Flags), flags packs.Flags) {} + +func (p *Packer) ItemObject(builder func(packer packs.ObjectPacker, flags packs.Flags), flags packs.Flags) {} + + +func (p *Packer) addSep () { + if p.state.Count() > 0 { + p.state.WriteByte(',') + } +} + + +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 +} diff --git a/packs/pjson/packer_value.go b/packs/pjson/packer_value.go index f8ba0b4..220da2e 100644 --- a/packs/pjson/packer_value.go +++ b/packs/pjson/packer_value.go @@ -2,9 +2,9 @@ // Project atila // Active Thing (activething.com) git.activething.com/go // -// File name packer_value.go +// File name value_packer.go // Created by DEV -// Modified 05/02/2024 +// Modified 06/02/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ @@ -24,6 +24,7 @@ package pjson import ( + "activething.com/go/gogo/core/errs" "activething.com/go/gogo/packs" "encoding/base32" "encoding/base64" @@ -56,42 +57,67 @@ func (p *Packer) PackAny (value any, flags packs.Flags) { p.state.W(d) } - case reflect.Value: - rPackAny(p.state,v,flags) + case bool : packBool (p.state,v,flags ) - default: - rPackAny(p.state,reflect.ValueOf(value), 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) -func (p *Packer) PackRaw (value []byte, 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 - } - if len(value) == 0 { - p.state.Write2Byte('"','"') - return + 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) } - p.state.W(value) } -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) 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) } @@ -105,17 +131,67 @@ func (p *Packer) PackUInt64 (value uint64, flags packs.Flags) { packUInt(p.stat 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) PackDate (value time.Time, flags packs.Flags) { packDate(p.state,value,flags) } + + +func (p *Packer) PackList (builder func(packs.ListPacker, 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.MapPacker, 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.ObjectPacker, 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 (p *Packer) PackList (builder func(packs.ListPacker, packs.Flags)) {} -func (p *Packer) PackMap (builder func(packs.MapPacker, packs.Flags)) {} -func (p *Packer) PackObject(builder func(packs.ObjectPacker, packs.Flags)) {} +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)) @@ -198,7 +274,7 @@ func packBinary (state *packs.State, value []byte , flags packs.Flags) { return } state.WriteByte('"') - + if flags.Bin64() { packBinary64(state, value, 0) } else if flags.Bin32() { @@ -255,16 +331,10 @@ func packBool (state *packs.State, value bool , flags packs.Flags) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(state.Error)) } - if flags.Quote() { - state.WriteByte('"') - } if value { - state.W(G.trueValue) - } else { - state.W(G.falseValue) - } - if flags.Quote() { - state.WriteByte('"') + state.WOQ(flags.Quote(),'"',G.trueValue) + } else { + state.WOQ(flags.Quote(),'"',G.falseValue) } } @@ -273,7 +343,8 @@ func packInt (state *packs.State, value int64 , flags packs.Flags) { panic(packs.G.ErrInvalidState.WithCause(state.Error)) } - if flags.Quote() { + q :=flags.Quote() + if q { state.WriteByte('"') } if value == 0 { @@ -281,11 +352,12 @@ func packInt (state *packs.State, value int64 , flags packs.Flags) { } else { state.WriteIntStr(value, 10) } - if flags.Quote() { + 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)) @@ -337,26 +409,16 @@ func packFloatString (state *packs.State, f float64, flags packs.Flags, bits int } } - if flags.Quote() { - state.WriteByte('"') - state.W(bf) - state.WriteByte('"') - return - } - state.W(bf) + state.WOQ(flags.Quote(),'"',bf) } -func PackDate (state *packs.State, value time.Time, flags packs.Flags) { +func packDate(state *packs.State, value time.Time, flags packs.Flags) { if state.Error != nil { panic(packs.G.ErrInvalidState.WithCause(state.Error)) } - state.WriteByte('"') bf := state.Temp[:0] - bf = value.AppendFormat(bf,flags.DateFormat()) - state.W(bf) - state.WriteByte('"') - + state.WOQ(true,'"',bf) } diff --git a/packs/pjson/rpacker.go b/packs/pjson/rpacker.go index 284ea0f..b3b511e 100644 --- a/packs/pjson/rpacker.go +++ b/packs/pjson/rpacker.go @@ -26,6 +26,7 @@ package pjson import ( "activething.com/go/gogo/packs" "reflect" + "sync" ) type ( @@ -38,5 +39,64 @@ type ( +func rTypePacker (rtype reflect.Type) rPackerFnc { + if fn,ok := G.packersCache.Load(rtype); ok { + return fn.(rPackerFnc) + } + + var wg sync.WaitGroup + var fn rPackerFnc + + wg.Add(1) + + fi,ld := G.packersCache.LoadOrStore(rtype, rPackerFnc(func (e *packs.State, v reflect.Value, f packs.Flags) { + wg.Wait() + fn(e,v,f) + })) + if ld { + return fi.(rPackerFnc) + } + + fn = _rTypePacker(rtype) + wg.Done() + + G.packersCache.Store(rtype,fn) + return fn +} + + +func _rTypePacker(t reflect.Type) rPackerFnc { + + switch t.Kind() { + case reflect.Bool : return func(s *packs.State, v reflect.Value, f packs.Flags) { packBool (s,v.Bool (),f )} + case reflect.String : return func(s *packs.State, v reflect.Value, f packs.Flags) { packString(s,v.String(),f )} + case reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64 : return func(s *packs.State, v reflect.Value, f packs.Flags) { packInt (s,v.Int (),f )} + case reflect.Uint, + reflect.Uint8, + reflect.Uint16, + reflect.Uint32, + reflect.Uint64 : return func(s *packs.State, v reflect.Value, f packs.Flags) { packUInt (s,v.Uint (),f )} + case reflect.Float32: return func(s *packs.State, v reflect.Value, f packs.Flags) { packFloatString(s,v.Float(),f,32) } + case reflect.Float64: return func(s *packs.State, v reflect.Value, f packs.Flags) { packFloatString(s,v.Float(),f,64) } + + case reflect.Slice, + reflect.Array : + return (*newRPackerList(t.Elem())).pack + + case reflect.Map : + return (*newRPackerMap(t)).pack + } + return unsupportedTypePacker +} + + +func unsupportedTypePacker(state *packs.State, _ reflect.Value, _ packs.Flags) { + state.SetError(packs.G.ErrUnsupportedType) +} + + -func rPackAny (state *packs.State, rvalue reflect.Value, flags packs.Flags) {} \ No newline at end of file diff --git a/packs/pjson/rpacker_key.go b/packs/pjson/rpacker_key.go new file mode 100644 index 0000000..821a374 --- /dev/null +++ b/packs/pjson/rpacker_key.go @@ -0,0 +1,166 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name rkey.go +// Created by DEV +// Modified 05/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/packs" + "encoding" + "math" + "reflect" + "strconv" +) + +func stringifyRKey(rt reflect.Type) rPackerMapEntryFnc { + fn,ok := G.keyersCache.Load(rt) + if !ok { + fn, _ = G.keyersCache.LoadOrStore(rt, _stringifyRKey(rt)) + } + return fn.(rPackerMapEntryFnc) +} + + +func _stringifyRKey(rt reflect.Type) rPackerMapEntryFnc { + + if rt.Kind() == reflect.String { + return func(e *rPackerMapEntry, k reflect.Value) error { + e.k = k.String() + e.f = packs.FlagScapeHTML + return nil + } + } + + if rt.Implements(G.textMarshalerType) { + return func(e *rPackerMapEntry, k reflect.Value) error { + if k.Kind() == reflect.Pointer && k.IsNil() { + return nil + } + bf, er := (k.Interface().(encoding.TextMarshaler)).MarshalText() + if er == nil { + e.k = string(bf) + e.f = packs.FlagScapeHTML + } + return er + } + } + + switch rt.Kind() { + + case reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64: + return func(e *rPackerMapEntry, k reflect.Value) error { + e.k = strconv.FormatInt(k.Int(), 10) + return nil + } + case reflect.Uint, + reflect.Uint8, + reflect.Uint16, + reflect.Uint32, + reflect.Uint64, + reflect.Uintptr: + return func(e *rPackerMapEntry, k reflect.Value) error { + e.k = strconv.FormatUint(k.Uint(), 10) + return nil + } + + case reflect.Interface: + return func(e *rPackerMapEntry, k reflect.Value) error { + k = k.Elem() + + if k.Type().Kind() == reflect.String { + e.k = k.String() + e.f = packs.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.k = string(bf) + e.f = packs.FlagScapeHTML + } + return er + } + + switch rt.Kind() { + + case reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64: + e.k = strconv.FormatInt(k.Int(), 10) + return nil + + case reflect.Uint, + reflect.Uint8, + reflect.Uint16, + reflect.Uint32, + reflect.Uint64, + reflect.Uintptr: + e.k = strconv.FormatUint(k.Uint(), 10) + return nil + + default: + return packs.G.ErrInvalidKey + } + } + + default: + return nil + } +} + + + +func stringifyFloat (buffer []byte, f float64, bits int) []byte { + if math.IsInf(f, 0) || math.IsNaN(f) { + return nil + } + + 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' + } + } + buffer = strconv.AppendFloat(buffer[:0], f, fmt, -1, int(bits)) + if fmt == 'e' { + // clean up e-09 to e-9 + n := len(buffer) + if n >= 4 && buffer[n-4] == 'e' && buffer[n-3] == '-' && buffer[n-2] == '0' { + buffer[n-2] = buffer[n-1] + buffer = buffer[:n-1] + } + } + return buffer +} + diff --git a/packs/pjson/rpacker_list.go b/packs/pjson/rpacker_list.go new file mode 100644 index 0000000..ec1fc1d --- /dev/null +++ b/packs/pjson/rpacker_list.go @@ -0,0 +1,69 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name packer_list.go +// Created by DEV +// Modified 05/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/packs" + "reflect" +) + +type ( + + rPackerList struct { + packFn rPackerFnc + } +) + +func newRPackerList (rtype reflect.Type) *rPackerList { + return &rPackerList{ + packFn: rTypePacker(rtype), + } +} + + + +func (p rPackerList) pack (s *packs.State, v reflect.Value, f packs.Flags) { + if v.IsNil() { + _,_ = s.Write(G.nullValue) + return + } + if v.Len() == 0 { + s.Write2Byte('[',']') + return + } + s.Open('[') + for i:=0; i 0 { + s.WriteByte(',') + } + p.packFn(s,v.Index(i),f) + if s.Error != nil { + return + } + } + s.Close(']') +} + + + diff --git a/packs/pjson/rpacker_map.go b/packs/pjson/rpacker_map.go new file mode 100644 index 0000000..ad22cfd --- /dev/null +++ b/packs/pjson/rpacker_map.go @@ -0,0 +1,107 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name rpacker_map.go +// Created by DEV +// Modified 05/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/packs" + "reflect" + "sort" +) + +type ( + + rPackerMap struct { + keyFn rPackerMapEntryFnc + packFn rPackerFnc + } +) + + + +func newRPackerMap (rtype reflect.Type) *rPackerMap { + return &rPackerMap{ + packFn: rTypePacker (rtype.Elem()), + keyFn : stringifyRKey(rtype.Key()), + } +} + + + +func (p *rPackerMap) pack (s *packs.State, v reflect.Value, f packs.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(rPackerMapEntryList,v.Len()) + + for i:=0; mi.Next(); i++ { + ls[i] = rPackerMapEntry{ + 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.packFn(s,ls[i].v,0) + if s.Error != nil { + return + } + } + s.Close('}') +} + diff --git a/packs/list_packer.go b/packs/pjson/rpacker_map_entry.go similarity index 57% rename from packs/list_packer.go rename to packs/pjson/rpacker_map_entry.go index c437bbc..6aedbff 100644 --- a/packs/list_packer.go +++ b/packs/pjson/rpacker_map_entry.go @@ -2,9 +2,9 @@ // Project atila // Active Thing (activething.com) git.activething.com/go // -// File name list_Itemer.go +// File name rpacker_map_entry.go // Created by DEV -// Modified 23/01/2024 +// Modified 05/02/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ @@ -21,44 +21,45 @@ // limitations under the License. // ------------------------------------------------------------------------ -package packs +package pjson -import "time" +import ( + "activething.com/go/gogo/packs" + "reflect" +) type ( - - ListPacker interface { - Item (value any) - ItemRaw (value []byte) - - ItemBinary (value []byte) - ItemString (value string) + rPackerMapEntry struct { + f packs.Flags // flags + v reflect.Value // value + k string // Key + } + + rPackerMapEntryFnc func(entry *rPackerMapEntry, rkey reflect.Value) error - ItemBool (value bool) + rPackerMapEntryList []rPackerMapEntry - ItemInt8 (value int8) - ItemInt16 (value int16) - ItemInt32 (value int32) - ItemInt64 (value int64) +) - ItemUInt8 (value uint8) - ItemUInt16 (value uint16) - ItemUInt32 (value uint32) - ItemUInt64 (value uint64) - ItemFloat32 (value float32) - ItemFloat64 (value float64) - ItemDate (value time.Time) - ItemList (builder func(packer ListPacker)) - ItemMap (builder func(packer MapPacker )) - ItemObject(builder func(packer ObjectPacker)) - - } - -) + +func (e rPackerMapEntryList) Len() int { + return len(e) +} + +func (e rPackerMapEntryList) Less(i, j int) bool { + return e[i].k < e[j].k +} + +func (e rPackerMapEntryList) Swap(i, j int) { + e[i], e[j] = e[j], e[i] +} + + +