diff --git a/packs/packabler.go b/packs/builder.go similarity index 85% rename from packs/packabler.go rename to packs/builder.go index 07615cb..008d966 100644 --- a/packs/packabler.go +++ b/packs/builder.go @@ -27,19 +27,19 @@ package packs type ( - Packabler interface { + Builder interface { - Build (packer Packer) ([]byte,error) + Build (packer Packer) error } - PackableFnc func (packer Packer)([]byte,error) + BuildFnc func (packer Packer) error ) -func (f PackableFnc) Build (packer Packer)([]byte,error) { +func (f BuildFnc) Build (packer Packer) error { return f(packer) } diff --git a/packs/flags.go b/packs/flags.go new file mode 100644 index 0000000..513fe67 --- /dev/null +++ b/packs/flags.go @@ -0,0 +1,192 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name f.go +// Created by DEV +// Modified 29/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 packs + +import ( + "strings" + "time" +) + +const ( + + FlagOmitEmpty Flags = 1 << iota + FlagQuote + FlagScapeHTML + + FlagBinary64 Flags = 0b0000100000000000 + FlagBinary32 Flags = 0b0000010000000000 + + FlagDateRFC822 Flags = 0b0001000000000000 + FlagDateUnix Flags = 0b0010000000000000 + FlagDateANSIC Flags = 0b0100000000000000 + FlagDateRFC850 Flags = 0b1000000000000000 + + binMask = 0b0000110000000000 + dateMask = 0b1111000000000000 + flagMaskBin Flags = 0b1111001111111111 + flagMaskDate Flags = 0b0000111111111111 + +) + + +type Flags uint16 + + +func (f Flags) OmitEmpty () bool { + return f&FlagOmitEmpty != 0 +} + +func (f Flags) Quote () bool { + return f&FlagQuote != 0 +} + +func (f Flags) EscapeHTML () bool { + return f&FlagScapeHTML != 0 +} + +func (f Flags) Binary () bool { + return f&(FlagBinary64|FlagBinary32) != 0 +} + +func (f Flags) Bin64 () bool { + return f&FlagBinary64 != 0 +} + + +func (f Flags) Bin32 () bool { + return f&FlagBinary32 != 0 +} + + +func (f Flags) SetWithName(flag string) Flags { + switch strings.ToLower(flag) { + case "o","omitempty": return f | FlagOmitEmpty + case "q","quote" : return f | FlagQuote + case "h","html" : return f | FlagScapeHTML + + // formato binario + case "b64","bin64" : return (f & flagMaskBin) | FlagBinary64 + case "b32","bin32" : return (f & flagMaskBin) | FlagBinary32 + + // formatos de fechas + case "unix" : return (f & flagMaskDate) | FlagDateUnix + case "rfc822" : return (f & flagMaskDate) | FlagDateRFC822 + case "ansic" : return (f & flagMaskDate) | FlagDateANSIC + case "rfc850" : return (f & flagMaskDate) | FlagDateRFC850 + case "rfc822z": return f & flagMaskDate + + default: return f + } +} + + + +func (f Flags) SetWithNames(flags ... string) Flags { + nf := f + for _,n := range flags { + switch strings.ToLower(n) { + case "o","omitempty": nf |= FlagOmitEmpty + case "q","quote" : nf |= FlagQuote + case "h","html" : nf |= FlagScapeHTML + + // formatos binarios + case "b32","bin32" : nf = (nf & flagMaskBin) | FlagBinary32 + case "b64","bin64" : nf = (nf & flagMaskBin) | FlagBinary64 + + // formatos de fechas + case "unix" : nf = (nf & flagMaskDate) | FlagDateUnix + case "rfc822" : nf = (nf & flagMaskDate) | FlagDateRFC822 + case "ansic" : nf = (nf & flagMaskDate) | FlagDateANSIC + case "rfc850" : nf = (nf & flagMaskDate) | FlagDateRFC850 + case "rfc822z": nf &= flagMaskDate + } + } + return nf +} + +func (f Flags) SetWithString (flags string) Flags { + if len(flags) == 0 { + return f + } + nf := f + for flags != "" { + var op string + op,flags,_ = strings.Cut(flags,",") + nf |= f.SetWithName(op) + } + return nf +} + + + +func (f Flags) Names () string { + var ls []string + if f&FlagOmitEmpty != 0 { ls = append(ls,"omitempty")} + if f&FlagQuote != 0 { ls = append(ls,"quote")} + if f&FlagScapeHTML != 0 { ls = append(ls,"html")} + + if f&flagMaskBin != 0 { + ls = append(ls,f.binName()) + } + if f&dateMask != 0 { + ls = append(ls,f.dateName()) + } + + if len(ls) > 0 { + return strings.Join(ls,",") + } + return "" +} + +func (f Flags) binName () string { + switch f & binMask { + case FlagBinary64 : return "b64" + case FlagBinary32 : return "b32" + } + return "" +} + + +func (f Flags) dateName() string { + switch f & dateMask { + case FlagDateUnix : return "unix" + case FlagDateRFC822: return "rfc822" + case FlagDateANSIC : return "ansic" + case FlagDateRFC850: return "rfc8550" + default: return time.RFC822Z + } +} + + +func (f Flags) DateFormat () string { + switch f & dateMask { + case FlagDateUnix : return time.UnixDate + case FlagDateRFC822: return time.RFC822 + case FlagDateANSIC : return time.ANSIC + case FlagDateRFC850: return time.RFC850 + default: return time.RFC822Z + } +} + + diff --git a/packs/gogo.go b/packs/gogo.go index f172c3b..6c25c24 100644 --- a/packs/gogo.go +++ b/packs/gogo.go @@ -34,10 +34,8 @@ var ( poolsSem uint32 statePool *syncs.Pool[State] - nodePool *syncs.Pool[Node] - ErrInvalidNode errs.ErrCode - ErrInvalidElement errs.ErrCode + ErrInvalidState errs.ErrCode ErrInvalidKey errs.ErrCode ErrDuplicatedKey errs.ErrCode ErrKeyRequired errs.ErrCode @@ -47,8 +45,7 @@ var ( ErrInvalidValue errs.ErrCode ErrUnsupportedType errs.ErrCode }{ - ErrInvalidNode : errs.ErrCode("packs.invalid-node"), - ErrInvalidElement : errs.ErrCode("packs.invalid-element"), + ErrInvalidState : errs.ErrCode("packs.invalid-stae"), ErrInvalidKey : errs.ErrCode("packs.invalid-key"), ErrDuplicatedKey : errs.ErrCode("packs:duplicated-key"), ErrKeyRequired : errs.ErrCode("packs:key-required"), diff --git a/packs/js/gogo.go b/packs/js/gogo.go index 6db8710..248803d 100644 --- a/packs/js/gogo.go +++ b/packs/js/gogo.go @@ -24,6 +24,7 @@ package js import ( + "activething.com/go/gogo/packs" "encoding" "encoding/json" "math" @@ -41,39 +42,32 @@ var ( hex string jsonChars [utf8.RuneSelf]jsonCharAtt - sep byte nullValue []byte zeroValue []byte trueValue []byte falseValue []byte - mapEmpty []byte - listEmpty []byte - objectEmpty []byte floatNaN []byte floatInfPos []byte floatInfNeg []byte - packers sync.Map - keyers sync.Map + packersCache sync.Map + keyersCache sync.Map jsonMarshalerType reflect.Type textMarshalerType reflect.Type + packMarshalerType reflect.Type }{ hex: "0123456789abcdef", jsonChars: initJSONChars(), - sep: ',', nullValue: []byte{'n', 'u', 'l', 'l'}, zeroValue: []byte{'0'}, trueValue: []byte{'"','t', 'r', 'u', 'e','"'}, falseValue: []byte{'"','f', 'a', 'l', 's', 'e','"'}, - mapEmpty: []byte{'{', '}'}, - objectEmpty: []byte{'{', '}'}, - listEmpty: []byte{'[', ']'}, floatNaN: []byte{'"', 'N', 'a', 'N', '"'}, floatInfPos: []byte{'"', 'I', 'n', 'f', '"'}, @@ -82,6 +76,7 @@ var ( jsonMarshalerType: reflect.TypeOf((*json.Marshaler)(nil)).Elem(), textMarshalerType: reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem(), + packMarshalerType: reflect.TypeOf((*packs.Packer)(nil)).Elem(), } diff --git a/packs/js/packer.go b/packs/js/packer.go index d23a7a1..87fb0b3 100644 --- a/packs/js/packer.go +++ b/packs/js/packer.go @@ -79,9 +79,10 @@ func pack (state *packs.State, value any , flags Flags) { case reflect.Value : Pack(v.Elem()) default: - if pb,is := value.(packs.Packabler); is { - d, e := pb.Build(p) - if state.SetError(e) { + if pb,is := value.(packs.Builder); is { + e := pb.Build(pb) + if e != nil { + state.SetError(e) return } _, _ = state.Write(d) @@ -329,8 +330,6 @@ func packFloatString (state *packs.State, f float64, bits int, quote bool) { } - - func PackDate (state *packs.State, value time.Time, flags Flags) { if state.Error != nil { panic(state.Error) diff --git a/packs/js/rkey.go b/packs/js/rkey.go index 52974a5..8069fa3 100644 --- a/packs/js/rkey.go +++ b/packs/js/rkey.go @@ -32,9 +32,9 @@ import ( func stringifyRKey(rt reflect.Type) rMapEntryFnc { - fn,ok := G.keyers.Load(rt) + fn,ok := G.keyersCache.Load(rt) if !ok { - fn, _ = G.keyers.LoadOrStore(rt, _stringifyRKey(rt)) + fn, _ = G.keyersCache.LoadOrStore(rt, _stringifyRKey(rt)) } return fn.(rMapEntryFnc) } diff --git a/packs/js/rtype.go b/packs/js/rtype.go index c288e3c..8df4a50 100644 --- a/packs/js/rtype.go +++ b/packs/js/rtype.go @@ -50,7 +50,7 @@ func RPacker(state *packs.State, value any, flags Flags ) { func rTypePacker(rtype reflect.Type) packerFn { - if fn,ok := G.packers.Load(rtype); ok { + if fn,ok := G.packersCache.Load(rtype); ok { return fn.(packerFn) } @@ -59,7 +59,7 @@ func rTypePacker(rtype reflect.Type) packerFn { wg.Add(1) - fi,ld := G.packers.LoadOrStore(rtype, packerFn(func (e *packs.State, v reflect.Value, f Flags) { + fi,ld := G.packersCache.LoadOrStore(rtype, packerFn(func (e *packs.State, v reflect.Value, f Flags) { wg.Wait() fn(e,v,f) })) @@ -70,7 +70,7 @@ func rTypePacker(rtype reflect.Type) packerFn { fn = _rTypePacker(rtype) wg.Done() - G.packers.Store(rtype,fn) + G.packersCache.Store(rtype,fn) return fn } diff --git a/packs/packer.go b/packs/packer.go index ff4891a..30f2c57 100644 --- a/packs/packer.go +++ b/packs/packer.go @@ -29,33 +29,35 @@ type ( Packer interface { - Pack (value any) + Pack (value any, flags Flags) ([]byte,error) - PackRaw (value []byte) - PackBinary (value []byte) - PackString (value string) - PackBool (value bool) + PackAny (value any, flags Flags) - PackInt8 (value int8) - PackInt16 (value int16) - PackInt32 (value int32) - PackInt64 (value int64) + PackRaw (value []byte, flags Flags) + PackBinary (value []byte, flags Flags) + PackString (value string, flags Flags) - PackUInt8 (value uint8) - PackUInt16 (value uint16) - PackUInt32 (value uint32) - PackUInt64 (value uint64) + PackBool (value bool, flags Flags) - PackFloat32 (value float32) - PackFloat64 (value float64) + PackInt8 (value int8, flags Flags) + PackInt16 (value int16, flags Flags) + PackInt32 (value int32, flags Flags) + PackInt64 (value int64, flags Flags) - PackDate (value time.Time) + 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) - PackList (builder func(packer ListPacker)) - PackMap (builder func(packer MapPacker )) - PackObject(builder func(packer ObjectPacker)) + PackDate (value time.Time, flags Flags) + + PackList (builder func(packer ListPacker , flags Flags)) + PackMap (builder func(packer MapPacker , flags Flags)) + PackObject(builder func(packer ObjectPacker, flags Flags)) } ) diff --git a/packs/packer_state.go b/packs/packer_state.go index 5d34e4e..86f029b 100644 --- a/packs/packer_state.go +++ b/packs/packer_state.go @@ -39,8 +39,8 @@ type ( /*Embedded*/*bins.Buffer Temp [64]byte Level int - checks map[any]struct{} Error error + checks map[any]struct{} } ) diff --git a/packs/pjson/char_atts.go b/packs/pjson/char_atts.go new file mode 100644 index 0000000..b034c41 --- /dev/null +++ b/packs/pjson/char_atts.go @@ -0,0 +1,41 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name json_char_atts.go +// Created by DEV +// Modified 18/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 + +const ( + jsonCharAttUnsafe jsonCharAtt = 1 << iota + jsonCharAttHtmlUnsafe + + jsonCharAttAllUnsafe = jsonCharAttUnsafe | jsonCharAttHtmlUnsafe +) + +type jsonCharAtt uint8 + +func (a jsonCharAtt) isUnsafe() bool { + return a&jsonCharAttUnsafe != 0 +} + +func (a jsonCharAtt) isHtmlUnsafe() bool { + return a&jsonCharAttHtmlUnsafe != 0 +} diff --git a/packs/pjson/gogo.go b/packs/pjson/gogo.go new file mode 100644 index 0000000..31273e5 --- /dev/null +++ b/packs/pjson/gogo.go @@ -0,0 +1,168 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name gogo.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/packs" + "encoding" + "encoding/json" + "math" + "reflect" + "strconv" + "sync" + "unicode/utf8" +) + + +const startDetectingCyclesAfter = 100 + + +var ( + G = struct { + hex string + jsonChars [utf8.RuneSelf]jsonCharAtt + + nullValue []byte + zeroValue []byte + trueValue []byte + falseValue []byte + + floatNaN []byte + floatInfPos []byte + floatInfNeg []byte + + + packersCache sync.Map + keyersCache sync.Map + + jsonMarshalerType reflect.Type + textMarshalerType reflect.Type + packMarshalerType reflect.Type + + }{ + + hex: "0123456789abcdef", + jsonChars: initJSONChars(), + + nullValue: []byte{'n', 'u', 'l', 'l'}, + zeroValue: []byte{'0'}, + trueValue: []byte{'"','t', 'r', 'u', 'e','"'}, + falseValue: []byte{'"','f', 'a', 'l', 's', 'e','"'}, + + floatNaN: []byte{'"', 'N', 'a', 'N', '"'}, + floatInfPos: []byte{'"', 'I', 'n', 'f', '"'}, + floatInfNeg: []byte{'"', '-', 'I', 'n', 'f', '"'}, + + + jsonMarshalerType: reflect.TypeOf((*json.Marshaler)(nil)).Elem(), + 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, + '&': jsonCharAttHtmlUnsafe, + '<': jsonCharAttHtmlUnsafe, + '>': jsonCharAttHtmlUnsafe, + '\\': jsonCharAttAllUnsafe, + } + for i := 0; i <= 31; i++ { + tb[i] = jsonCharAttAllUnsafe + } + return tb +} + + + +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/node_kind.go b/packs/pjson/options.go similarity index 84% rename from packs/node_kind.go rename to packs/pjson/options.go index ac9531b..c974073 100644 --- a/packs/node_kind.go +++ b/packs/pjson/options.go @@ -2,9 +2,9 @@ // Project atila // Active Thing (activething.com) git.activething.com/go // -// File name node_kind.go +// File name options.go // Created by DEV -// Modified 23/01/2024 +// Modified 05/02/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ @@ -21,20 +21,13 @@ // limitations under the License. // ------------------------------------------------------------------------ -package packs +package pjson -const ( +type ( - NodeKindValue NodeKind = iota - NodeKindList - NodeKindMap - NodeKindObject + Options struct {} ) -type NodeKind uint8 - - - diff --git a/packs/pjson/packer.go b/packs/pjson/packer.go new file mode 100644 index 0000000..d421e1c --- /dev/null +++ b/packs/pjson/packer.go @@ -0,0 +1,401 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name 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 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 ( + + + Packer struct { + state *packs.State + Options + } + + + +) + + +func NewPacker (options opts.OptionFnc[Packer]) *Packer { + return opts.Apply[Packer](&Packer { + state : packs.NewState(), + }, 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) { + 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('"') + } +} + +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 packFloat (state *packs.State, value float64, flags packs.Flags) { + packFloatString(state, value, flags, 64) +} + +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 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_value.go b/packs/pjson/packer_value.go new file mode 100644 index 0000000..f8ba0b4 --- /dev/null +++ b/packs/pjson/packer_value.go @@ -0,0 +1,363 @@ +// ------------------------------------------------------------------------ +// Project atila +// Active Thing (activething.com) git.activething.com/go +// +// File name packer_value.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/base32" + "encoding/base64" + "encoding/json" + "math" + "reflect" + "strconv" + "time" + "unicode/utf8" +) + +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) { + 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('"') + } +} + +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 packFloat (state *packs.State, value float64, flags packs.Flags) { + packFloatString(state, value, flags, 64) +} + +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 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/node.go b/packs/pjson/rpacker.go similarity index 67% rename from packs/node.go rename to packs/pjson/rpacker.go index 2b97c5c..284ea0f 100644 --- a/packs/node.go +++ b/packs/pjson/rpacker.go @@ -2,9 +2,9 @@ // Project atila // Active Thing (activething.com) git.activething.com/go // -// File name packer_node.go +// File name rpacker.go // Created by DEV -// Modified 23/01/2024 +// Modified 05/02/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ @@ -21,34 +21,22 @@ // limitations under the License. // ------------------------------------------------------------------------ -package packs +package pjson -import "activething.com/go/gogo/core/slices" +import ( + "activething.com/go/gogo/packs" + "reflect" +) type ( - Node struct { - Parent *Node - Start int - Count int - Kind NodeKind - keys slices.String - } -) -func (n *Node) Init(parent *Node, kind NodeKind, start int) *Node { - n.Parent = parent - n.Count = 0 - n.Start = start - n.Kind = kind - return n -} -func (n *Node) Reset() *Node { - n.Parent = nil - n.keys = n.keys[:0] - return n -} + rPackerFnc func (state *packs.State, rvalue reflect.Value, flags packs.Flags) + +) + +func rPackAny (state *packs.State, rvalue reflect.Value, flags packs.Flags) {} \ No newline at end of file