redefiniendo el builder

packs2
DEV 3 years ago
parent 4ef5088582
commit 8ff022c69a

@ -24,15 +24,30 @@
package main package main
import ( import (
"activething.com/go/gogo/packs" "activething.com/go/gogo/core/errs"
"activething.com/go/gogo/packs/pjson"
"fmt" "fmt"
) )
type A struct {
CampoInt int
Cadena string
Arrays []int64
}
func main() { func main() {
/*
pk := pjson.NewPacker() pk := pjson.NewPacker()
t := A {
CampoInt: 23,
Cadena: "Esto es una cadena",
Arrays: []int64{233,222,221},
}
/*
pk.PackList(func(p packs.PackListBuilder, flags packs.Flags){ pk.PackList(func(p packs.PackListBuilder, flags packs.Flags){
p.ItemUInt64(0,flags) p.ItemUInt64(0,flags)
p.ItemFloat64(7.8,flags) p.ItemFloat64(7.8,flags)
@ -55,13 +70,10 @@ func main() {
*/ */
pk.PackAny(t,0)
errs.Test(pk.Error(),true)
fmt.Printf("Paquete %s\n",pk.Packet())
tag := `json:"first_name,e,-,b32,rfc822"`
nm,fl := packs.TagFlags(tag,"json",packs.FlagByName)
fmt.Printf("%s\nFlags name: %s -- > %s\n",tag,nm,fl.Names())
tag2 := `packs:"first_name,e,-,b32,rfc822z"`
n,f := packs.TagPacksFlags(tag2)
fmt.Printf("%s\nFlags name: %s -- > %s\n",tag2,n,f.Names())
} }

@ -23,12 +23,6 @@
package main package main
import (
"activething.com/go/gogo/packs/reflects"
"fmt"
"reflect"
)
type ( type (
MiTipo struct { MiTipo struct {
@ -46,27 +40,6 @@ func reflects_main () {
t := MiTipo {
FS1 : "dfdfdf",
FU2 : 233,
FB3 : true,
}
rf := reflect.TypeOf(t)
rs := reflects.RStruct{}
for i:=0; i<rf.NumField(); i++ {
rs.Fields = append(rs.Fields,*reflects.NewRField(rf.Field(i)))
}
for i,f := range rs.Fields {
fmt.Printf("%d tags %v\n",i,f.Tags)
}

@ -25,6 +25,7 @@ package bins
import ( import (
"bytes" "bytes"
"chars"
"io" "io"
"unicode/utf8" "unicode/utf8"
) )
@ -232,6 +233,33 @@ func (b *Buffer) WOQ (add bool, quote byte, value []byte) {
} }
func (b *Buffer) WriteHTMLScape (source []byte) {
st := 0
for i,c := range source {
if c == '<' || c == '>' || c == '&' {
if st < i {
b.W(source[st:i])
}
b.WriteString(`\u00`)
b.WriteByte(chars.G.HEX[c>>4])
b.WriteByte(chars.G.HEX[c&0xF])
st = i + 1
}
if c == 0xE2 && i+2 < len(source) && source[i+1] == 0x80 && source[i+2]&^1 == 0xA8 {
if st < i {
b.W(source[st:i])
}
b.WriteString(`\u202`)
b.WriteByte(chars.G.HEX[source[i+2]&0xF])
st = i + 3
}
if st < len(source) {
b.W(source[st:])
}
}
}
func (b *Buffer) WriteByte(value byte) { func (b *Buffer) WriteByte(value byte) {
b.lastOp = opInvalid b.lastOp = opInvalid
b.data[b.tryGrow(1)] = value b.data[b.tryGrow(1)] = value

@ -23,7 +23,7 @@
// //
*/ */
package chrs package chars
const ( const (
ASCIIAttControl CharAtt = 1 << iota ASCIIAttControl CharAtt = 1 << iota
@ -39,6 +39,7 @@ const (
) )
type ( type (
ASCIIAtts struct { ASCIIAtts struct {
Atts CharAtts Atts CharAtts
} }
@ -79,6 +80,21 @@ func (t ASCIIAtts) EqualFold(source, target []byte) bool {
return true return true
} }
func (t ASCIIAtts) SimpleLetterEqualFold(source, target []byte) bool {
if len(source) != len(target) {
return false
}
for i, b := range source {
if IsAlphaUpper(CharAtt(b)) != IsAlphaUpper(CharAtt(target[i])) {
return false
}
}
return true
}
func (t ASCIIAtts) ToUpperCase(chr byte) byte { func (t ASCIIAtts) ToUpperCase(chr byte) byte {
if t.Atts[chr]&ASCIIAttAlphaLower != 0 { if t.Atts[chr]&ASCIIAttAlphaLower != 0 {
return chr + 32 return chr + 32
@ -266,6 +282,8 @@ func (t ASCIIAtts) GetHex2DigitToByte(cha, chb byte) (byte, bool) {
return va | (chb - 'A' + 10), true return va | (chb - 'A' + 10), true
} }
func IsControl(a CharAtt) bool { func IsControl(a CharAtt) bool {
return a%ASCIIAttControl != 0 return a%ASCIIAttControl != 0
} }
@ -302,6 +320,9 @@ func IsHexDigit(a CharAtt) bool {
return a%ASCIIAttDigitHex != 0 return a%ASCIIAttDigitHex != 0
} }
/* /*
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

@ -23,7 +23,7 @@
// //
*/ */
package chrs package chars
const CharAttNone CharAtt = 0 const CharAttNone CharAtt = 0

@ -23,7 +23,7 @@
// //
*/ */
package chrs package chars
const maxTableSize = 1<<8 - 1 const maxTableSize = 1<<8 - 1

@ -23,23 +23,19 @@
// //
*/ */
package chrs package chars
import "sync/atomic"
var ( var (
G = struct { G = struct {
sentinel uint32
ascii ASCIIAtts
}{}
)
func ASCII() ASCIIAtts { ASCII ASCIIAtts
if atomic.CompareAndSwapUint32(&G.sentinel, 0, 1) { HEX [16]byte
G.ascii = *NewASCIIAtts() }{
} ASCII: *NewASCIIAtts(),
return G.ascii HEX : [16]byte{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'},
} }
)
/* /*
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

@ -5,6 +5,7 @@ use (
./app ./app
./core ./core
./core/chars
./core/types ./core/types
./core/bins ./core/bins
./core/errs ./core/errs
@ -12,6 +13,6 @@ use (
./core/syncs ./core/syncs
./packs ./packs
./packs/reflects
) )

@ -0,0 +1,127 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name fold.go
// Created by DEV
// Modified 08/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 (
"bytes"
"chars"
"unicode/utf8"
)
const (
caseMask = ^byte(0x20) // Mask to ignore case in ASCII.
kelvin = '\u212a'
smallLongEss = '\u017f'
)
// GetFoldFunc returns one of four different case folding equivalence
// functions, from most general (and slow) to fastest:
//
// 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8
// 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S')
// 3) asciiEqualFold, no special, but includes non-letters (including _)
// 4) simpleLetterEqualFold, no specials, no non-letters.
//
// The letters S and K are special because they map to 3 runes, not just 2:
// - S maps to s and to U+017F 'ſ' Latin small letter long s
// - k maps to K and to U+212A 'K' Kelvin sign
//
// See https://play.golang.org/p/tTxjOc0OGo
//
// The returned function is specialized for matching against s and
// should only be given s. It's not curried for performance reasons.
type FoldFnc func(s, t []byte) bool
func GetFoldFunc(s []byte) FoldFnc {
nonLetter := false
special := false // special letter
for _, b := range s {
if b >= utf8.RuneSelf {
return bytes.EqualFold
}
upper := b & caseMask
if upper < 'A' || upper > 'Z' {
nonLetter = true
} else if upper == 'K' || upper == 'S' {
// See above for why these letters are special.
special = true
}
}
if special {
return equalFoldRight
}
if nonLetter {
return chars.G.ASCII.EqualFold
}
return chars.G.ASCII.SimpleLetterEqualFold
}
// equalFoldRight is a specialization of bytes.EqualFold when s is
// known to be all ASCII (including punctuation), but contains an 's',
// 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t.
// See comments on GetFoldFunc.
func equalFoldRight(s, t []byte) bool {
for _, sb := range s {
if len(t) == 0 {
return false
}
tb := t[0]
if tb < utf8.RuneSelf {
if sb != tb {
sbUpper := sb & caseMask
if 'A' <= sbUpper && sbUpper <= 'Z' {
if sbUpper != tb&caseMask {
return false
}
} else {
return false
}
}
t = t[1:]
continue
}
// sb is ASCII and t is not. t must be either kelvin
// sign or long s; sb must be s, S, k, or K.
tr, size := utf8.DecodeRune(t)
switch sb {
case 's', 'S':
if tr != smallLongEss {
return false
}
case 'k', 'K':
if tr != kelvin {
return false
}
default:
return false
}
t = t[size:]
}
return len(t) == 0
}

@ -1,40 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name bin_encoder.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 js
type (
binEncoder interface {
EncodedLen (size int) int
Encode(dst, src []byte)
}
binEncoderWriter interface {
Write ([]byte) (int,error)
}
)

@ -1,41 +0,0 @@
// ------------------------------------------------------------------------
// 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 js
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
}

@ -1,188 +0,0 @@
// ------------------------------------------------------------------------
// 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 js
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
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
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
}
}

@ -1,167 +0,0 @@
// ------------------------------------------------------------------------
// 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 js
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
}

@ -1,349 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name value_packer.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 js
import (
"activething.com/go/gogo/packs"
"encoding/base32"
"encoding/base64"
"io"
"math"
"reflect"
"strconv"
"time"
"unicode/utf8"
)
func pack (state *packs.State, value any , flags Flags) {
if state.Error != nil {
panic(state.Error)
}
if value == nil {
if flags.Quote() {
state.WriteByte('"')
}
_,_ = state.Write(G.nullValue)
if flags.Quote() {
state.WriteByte('"')
}
return
}
switch v := value.(type) {
case bool : packBool (state,v,flags)
case string : packString(state,v,flags)
case uint : packUInt(state,uint64(v),flags)
case uint8 : packUInt(state,uint64(v),flags)
case uint16 : packUInt(state,uint64(v),flags)
case uint32 : packUInt(state,uint64(v),flags)
case uint64 : packUInt(state,v,flags)
case int : packInt(state,int64(v),flags)
case int8 : packInt(state,int64(v),flags)
case int16 : packInt(state,int64(v),flags)
case int32 : packInt(state,int64(v),flags)
case int64 : packInt(state,v,flags)
case float32: packFloat(state,float64(v),flags)
case float64: packFloat(state,v,flags)
case time.Time : PackDate(state,v,flags)
case reflect.Value : RPacker(state,v.Elem(),0)
default:
/*
if pb,is := value.(packs.Builder); is {
e := pb.Build(pb)
if e != nil {
state.SetError(e)
return
}
_, _ = state.Write(d)
return
}
if ms,is := value.(json.Marshaler); is {
d,e := ms.MarshalJSON()
if state.SetError(e) {
return
}
_,_ = state.Write(d)
return
}
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice, reflect.Array:
packListReflected(rv)
case reflect.Map:
packMapReflected(rv)
case reflect.Struct:
packObjectReflected(rv)
default:
d,e := options.encoder.Encode(value)
if e != nil {
state.SetError(e)
return
}
state.Write(d)
}
*/
}
}
func packString (state *packs.State, value string , flags Flags) {
if state.Error != nil {
panic(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 , f Flags) {
if state.Error != nil {
panic(state.Error)
}
if value == nil {
_,_ = state.Write(G.nullValue)
return
}
if len(value) == 0 {
state.Write2Byte('"','"')
return
}
state.WriteByte('"')
var ec binEncoder = base64.StdEncoding
if f.Bin32() {
ec = base32.StdEncoding
}
ln := ec.EncodedLen(len(value))
if ln <= len(state.Temp) {
dst := state.Temp[:ln]
ec.Encode(dst, value)
_,_= state.Write(dst)
} else if ln <= 1024 {
dst := make([]byte, ln)
ec.Encode(dst, value)
_,_ = state.Write(dst)
} else {
var wc io.WriteCloser
if f.Bin32() {
wc = base32.NewEncoder(base32.StdEncoding, state)
} else if f.Bin64() {
wc = base64.NewEncoder(base64.StdEncoding, state)
} else {
state.SetError(packs.G.ErrUnsupportedType)
return
}
_,_ = wc.Write(value)
state.SetError(wc.Close())
return
}
state.WriteByte('"')
}
func packBool (state *packs.State, value bool , flags Flags) {
if state.Error != nil {
panic(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 Flags) {
if state.Error != nil {
panic(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 Flags) {
if state.Error != nil {
panic(state.Error)
}
if flags.Quote() {
state.WriteByte('"')
}
if value == 0 {
state.WriteByte('0')
} else {
state.WriteUIntStr(value, 10)
}
if flags.Quote() {
state.WriteByte('"')
}
}
func packFloat (state *packs.State, value float64, flags Flags) {
packFloatString(state, value,64, flags.Quote())
}
func packFloatString (state *packs.State, f float64, bits int, quote bool) {
if state.Error != nil {
panic(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 quote {
state.WriteByte('"')
}
state.W(bf)
if quote {
state.WriteByte('"')
}
}
func PackDate (state *packs.State, value time.Time, flags Flags) {
if state.Error != nil {
panic(state.Error)
}
state.WriteByte('"')
bf := state.Temp[:0]
bf = value.AppendFormat(bf,flags.DateFormat())
state.W(bf)
state.WriteByte('"')
}

@ -1,142 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name rkey.go
// Created by DEV
// Modified 04/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"
"encoding"
"reflect"
"strconv"
)
func stringifyRKey(rt reflect.Type) rMapEntryFnc {
fn,ok := G.keyersCache.Load(rt)
if !ok {
fn, _ = G.keyersCache.LoadOrStore(rt, _stringifyRKey(rt))
}
return fn.(rMapEntryFnc)
}
func _stringifyRKey(rt reflect.Type) rMapEntryFnc {
if rt.Kind() == reflect.String {
return func(e *rMapEntry, k reflect.Value) error {
e.k = k.String()
e.f = FlagScapeHTML
return nil
}
}
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.k = string(bf)
e.f = FlagScapeHTML
}
return er
}
}
switch rt.Kind() {
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64:
return func(e *rMapEntry, 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 *rMapEntry, k reflect.Value) error {
e.k = strconv.FormatUint(k.Uint(), 10)
return nil
}
case reflect.Interface:
return func(e *rMapEntry, k reflect.Value) error {
k = k.Elem()
if k.Type().Kind() == reflect.String {
e.k = k.String()
e.f = 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 = 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
}
}

@ -1,59 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name type_slice_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"
"reflect"
)
type (
rList struct {
valFn packerFn
}
)
func (p rList) pack (s *packs.State, v reflect.Value, f Flags) {
if v.IsNil() {
_,_ = s.Write(G.nullValue)
return
}
if v.Len() == 0 {
s.Write2Byte('[',']')
return
}
s.Open('[')
for i:=0; i<v.Len(); i++ {
if i > 0 {
s.WriteByte(',')
}
p.valFn(s,v.Index(i),f)
if s.Error != nil {
return
}
}
s.Close(']')
}

@ -1,110 +0,0 @@
// ------------------------------------------------------------------------
// 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 : rTypePacker(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; i<len(ls); i++ {
if i>0 {
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('}')
}

@ -1,59 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name key_entry.go
// Created by DEV
// Modified 02/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 "reflect"
type (
rMapEntry struct {
f Flags // flags
v reflect.Value // value
k string // Key
}
rMapEntryFnc func(entry *rMapEntry, rkey reflect.Value) error
rMapEntryList []rMapEntry
)
func (e rMapEntryList) Len() int {
return len(e)
}
func (e rMapEntryList) Less(i, j int) bool {
return e[i].k < e[j].k
}
func (e rMapEntryList) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}

@ -1,110 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name type_packer.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 js
import (
"activething.com/go/gogo/packs"
"reflect"
"sync"
)
type (
packer interface {
}
packerFn func (state *packs.State, value reflect.Value, flags Flags)
)
func RPacker(state *packs.State, value any, flags Flags ) {
v := reflect.ValueOf(value)
t := v.Type()
rTypePacker(t)(state,v,flags)
}
func rTypePacker(rtype reflect.Type) packerFn {
if fn,ok := G.packersCache.Load(rtype); ok {
return fn.(packerFn)
}
var wg sync.WaitGroup
var fn packerFn
wg.Add(1)
fi,ld := G.packersCache.LoadOrStore(rtype, packerFn(func (e *packs.State, v reflect.Value, f Flags) {
wg.Wait()
fn(e,v,f)
}))
if ld {
return fi.(packerFn)
}
fn = _rTypePacker(rtype)
wg.Done()
G.packersCache.Store(rtype,fn)
return fn
}
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 )}
case reflect.String : return func(s *packs.State, v reflect.Value, f 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 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 Flags) { packUInt (s,v.Uint (),f )}
case reflect.Float32: return func(s *packs.State, v reflect.Value, f Flags) { packFloatString(s,v.Float(),32, f.Quote()) }
case reflect.Float64: return func(s *packs.State, v reflect.Value, f Flags) { packFloatString(s,v.Float(),64, f.Quote()) }
case reflect.Slice,
reflect.Array :
return (rList{ valFn: rTypePacker(t.Elem()) }).pack
case reflect.Map :
return newRMap(t).pack
}
return unsupportedTypePacker
}
func unsupportedTypePacker(state *packs.State, _ reflect.Value, _ Flags) {
state.SetError(packs.G.ErrUnsupportedType)
}

@ -53,6 +53,7 @@ var (
packersCache sync.Map packersCache sync.Map
keyersCache sync.Map keyersCache sync.Map
fieldsCache sync.Map
jsonMarshalerType reflect.Type jsonMarshalerType reflect.Type
textMarshalerType reflect.Type textMarshalerType reflect.Type

@ -65,9 +65,9 @@ func rTypePacker (rtype reflect.Type) rPackerFnc {
} }
func _rTypePacker(t reflect.Type) rPackerFnc { func _rTypePacker(rtype reflect.Type) rPackerFnc {
switch t.Kind() { switch rtype.Kind() {
case reflect.Bool : return func(s *packs.State, v reflect.Value, f packs.Flags) { packBool (s,v.Bool (),f )} 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.String : return func(s *packs.State, v reflect.Value, f packs.Flags) { packString(s,v.String(),f )}
case reflect.Int, case reflect.Int,
@ -85,10 +85,17 @@ func _rTypePacker(t reflect.Type) rPackerFnc {
case reflect.Slice, case reflect.Slice,
reflect.Array : reflect.Array :
return (*newRList(t.Elem())).pack return (*newRList(rtype.Elem())).pack
case reflect.Map : case reflect.Map :
return (*newRMap(t)).pack return (*newRMap(rtype)).pack
case reflect.Struct:
if ot,ok := G.fieldsCache.Load(rtype); ok {
return (ot.(rObject)).pack
}
ot,_ := G.fieldsCache.LoadOrStore(rtype,*newRObject(rtype))
return (ot.(rObject)).pack
} }
return unsupportedTypePacker return unsupportedTypePacker
} }

@ -24,9 +24,10 @@
package pjson package pjson
import ( import (
"activething.com/go/gogo/core/bins"
"activething.com/go/gogo/packs" "activething.com/go/gogo/packs"
"bytes"
"reflect" "reflect"
"sort"
) )
type ( type (
@ -39,14 +40,14 @@ type (
func newRObjectFields (rtype reflect.Type) *rObject { func newRObject(rtype reflect.Type) *rObject {
cur := []rObjectField{} cur := rFieldList{}
nxt := []rObjectField{{ rtype: rtype}} nxt := rFieldList{{ rtype: rtype}}
var fls []rObjectField var fls rFieldList
var nms bytes.Buffer var cnt, nxCnt map[reflect.Type]int
var cnt, nxCnt map[reflect.Type]bool
vis := map[reflect.Type]bool{} vis := map[reflect.Type]bool{}
nms := bins.NewBuffer()
for len(nxt) > 0 { for len(nxt) > 0 {
cur,nxt = nxt,cur[:0] cur,nxt = nxt,cur[:0]
@ -110,20 +111,138 @@ func newRObjectFields (rtype reflect.Type) *rObject {
flags : fg, flags : fg,
} }
of.nameB = []byte(of.name) of.nameB = []byte(of.name)
//todo of.equal of.foldFn= packs.GetFoldFunc(of.nameB)
// Build nameEscHTML and nameNonEsc ahead of time. // Build nameEscHTML and nameNonEsc ahead of time.
nms.Reset() nms.Reset()
nms.WriteByte('"')
nms.WriteHTMLScape(of.nameB)
nms.Write2Byte('"',':')
of.nameS = nms.String()
of.nameU = `"` + of.name + `":`
fls = append(fls,of)
if cnt[f.rtype] > 1 {
fls = append(fls,fls[len(fls)-1])
}
continue
} }
nxCnt[ft]++
if nxCnt[ft] == 1 {
nxt = append(nxt,rObjectField{ name: ft.Name(), index: index, rtype: ft})
}
} }
} }
} }
sort.Slice(fls,func(i,j int) bool {
ls := fls
if ls[i].name != ls[j].name {
return ls[i].name < ls[j].name
}
if len(ls[i].index) != len(ls[j].index) {
return len(ls[i].index) < len(ls[j].index)
}
if ls[i].isTagged != ls[j].isTagged {
return ls[i].isTagged
}
return rFieldIndexList(ls).Less(i, j)
})
out := fls[:0]
for ad,i :=0,0; i< len(fls); i += ad {
fi := fls[i]
nm := fi.name
for ad=1; i+ad <len(fls); ad++ {
fj := fls[i+ad]
if fj.name != nm {
break
}
}
if ad == 1 {
out = append(out,fi)
continue
}
do,ok := fls[i:i+ad].dominant()
if ok {
out = append(out,do)
}
}
fls = out
sort.Sort(rFieldIndexList(fls))
return nil for i:= range fls {
f := &fls[i]
f.packerFn = rTypePacker(f.typeByIndex(rtype))
} }
nmIdx := make(map[string]int,len(fls))
for i,fd := range fls {
nmIdx[fd.name]=i
}
return &rObject{
fields: fls,
nameIndex: nmIdx,
}
}
func (o rObject) pack (s *packs.State, v reflect.Value, f packs.Flags) {
nx := byte('{')
FieldLoop:
for i:= range o.fields {
fd := &o.fields[i]
fv := v
for _,i := range fd.index {
if fv.Kind() == reflect.Pointer {
if fv.IsNil() {
continue FieldLoop
}
fv = fv.Elem()
}
fv = fv.Field(i)
}
if fd.flags.OmitEmpty() && isEmptyValue(v) {
continue
}
s.WriteByte(nx)
nx = ','
if fd.flags.EscapeHTML() {
s.WriteString(fd.nameS)
} else {
s.WriteString(fd.nameU)
}
fd.packerFn(s,fv, fd.flags|f)
}
if nx == '{' {
s.Write2Byte('{','}')
} else {
s.WriteByte('}')
}
}
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Pointer:
return v.IsNil()
}
return false
}

@ -40,6 +40,9 @@ type (
index []int index []int
rtype reflect.Type rtype reflect.Type
flags packs.Flags flags packs.Flags
foldFn packs.FoldFnc
packerFn rPackerFnc
} }
rFieldList []rObjectField rFieldList []rObjectField
@ -48,5 +51,41 @@ type (
) )
func (f rObjectField) typeByIndex(rtype reflect.Type) reflect.Type {
for _,i := range f.index {
if rtype.Kind() == reflect.Pointer {
rtype = rtype.Elem()
}
rtype = rtype.Field(i).Type
}
return rtype
}
func (l rFieldList) dominant () (rObjectField,bool) {
if len(l) > 1 && len(l[0].index) == len(l[1].index) && l[0].isTagged == l[1].isTagged {
return rObjectField{},false
}
return l[0],true
}
func (x rFieldIndexList) Len() int { return len(x) }
func (x rFieldIndexList) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x rFieldIndexList) Less(i, j int) bool {
for k, xik := range x[i].index {
if k >= len(x[j].index) {
return false
}
if xik != x[j].index[k] {
return xik < x[j].index[k]
}
}
return len(x[i].index) < len(x[j].index)
}

@ -1,3 +0,0 @@
module activething.com/go/gogo/packs/reflects
go 1.20

@ -1,56 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name gogo.go
// Created by DEV
// Modified 28/01/2024
//
// Copyright 2024 activething.com
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
package reflects
import (
"activething.com/go/gogo/packs"
"reflect"
"sync"
)
var (
G = struct {
locker sync.Mutex
cache map[reflect.Type]RStruct
}{
}
)
func GetRTypePacker (rtype reflect.Type, packer packs.Packer) *RStruct {
G.locker.Lock()
if G.cache == nil {
G.cache = make(map[reflect.Type]RStruct)
}
G.locker.Unlock()
return nil
}

@ -1,112 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name reflect_struct_field.go
// Created by DEV
// Modified 26/01/2024
//
// Copyright 2024 activething.com
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
package reflects
import (
"activething.com/go/gogo/core/opts"
"activething.com/go/gogo/core/slices"
"reflect"
"strings"
"unicode"
)
type (
RField struct {
RType reflect.Type
RField reflect.StructField
Name string
SName string // scaped name
HName string // html name
BName []byte // name bytes
Index []int
Tags map[string]slices.String
Flags uint16
EqualFoldFn func(s, t []byte) bool
}
)
func NewRField (rfield reflect.StructField, options ...opts.OptionFnc[RField]) *RField {
rf := &RField{
RField: rfield,
Tags : make(map[string]slices.String),
}
rf.parseTag("json",nil)
return opts.Apply(rf,options...)
}
func (f *RField) parseTag (tagKey string, onOptionFn func(*RField,int,string)) {
if _,h := f.Tags[tagKey]; !h {
t := f.RField.Tag.Get(tagKey)
if len(t) > 0 {
l := strings.Split(t,",")
f.Tags[tagKey]= l
if onOptionFn != nil {
for i,o := range l {
onOptionFn(f,i,o)
}
}
}
}
}
func parseTag (field reflect.StructField, tagKey string, onOptionFn func(int,string)) (slices.String,bool) {
t := field.Tag.Get(tagKey)
if len(t) > 0 {
l := strings.Split(t,",")
if onOptionFn != nil {
for i,o := range l {
onOptionFn(i,o)
}
}
return l,true
}
return nil,true
}
func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
switch {
case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
case !unicode.IsLetter(c) && !unicode.IsDigit(c):
return false
}
}
return true
}

@ -1,64 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name rfield_tag.go
// Created by DEV
// Modified 27/01/2024
//
// Copyright 2024 activething.com
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
package reflects
import (
"reflect"
"strings"
)
type (
RFieldTag struct {
Tag string
parsed bool
Name string
Flags uint8
}
)
func NewRFieldTag (rfield reflect.StructField, tag string, onOption func(rf reflect.Type, position int, option string) uint8) *RFieldTag {
f := &RFieldTag{
Tag : rfield.Tag.Get(tag),
}
var ops string
f.Name,ops,_ = strings.Cut(f.Tag,",")
if len(ops) == 0 {
f.parsed = true
} else if onOption != nil {
f.parsed = true
for pos:=0; ops != ""; pos++ {
var name string
name, ops, _ = strings.Cut(ops, ",")
f.Flags = onOption(rfield.Type, pos,name)
}
}
return f
}

@ -1,64 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name rfields.go
// Created by DEV
// Modified 27/01/2024
//
// Copyright 2024 activething.com
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
package reflects
type (
RFields []RField
)
/*
func (f RFields) Dominant() (RField, bool) {
// The Fields are sorted in increasing index-length order, then by presence of tag.
// That means that the first RField is the dominant one. We need only check
// for error cases: two Fields at top level, either both tagged or neither tagged.
if len(f) > 1 && len(f[0].Index) == len(f[1].Index) && f[0].Tagged() == f[1].Tagged() {
return RField{}, false
}
return f[0], true
}
*/
func (f RFields) Len() int { return len(f) }
func (f RFields) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
func (f RFields) Less(i, j int) bool {
for k, xik := range f[i].Index {
if k >= len(f[j].Index) {
return false
}
if xik != f[j].Index[k] {
return xik < f[j].Index[k]
}
}
return len(f[i].Index) < len(f[j].Index)
}

@ -1,72 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name rmap.go
// Created by DEV
// Modified 28/01/2024
//
// Copyright 2024 activething.com
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
package reflects
import (
"reflect"
"sort"
)
type (
RMap struct {
KeyValues []RKeyValue
}
)
func NewRMap (value reflect.Value, stringifyFn func(v reflect.Value) (string,error), scapeFn func(string)string) (*RMap,error) {
ls := make([]RKeyValue,value.Len())
mi := value.MapRange()
sf := scapeFn
if sf == nil {
sf = func(v string) string { return v }
}
for i:=0; mi.Next(); i++ {
k,e := NewRKeyValue(mi.Key(),mi.Value(), stringifyFn,sf)
if e != nil {
return nil,e
}
ls[i]=*k
}
sort.Slice(ls,func(i,j int) bool { return ls[i].Key < ls[j].Key })
return &RMap {
KeyValues: ls,
},nil
}
func ensureScapeFn (sfn func(string)string) func(string)string {
if sfn == nil {
return func(v string) string { return v }
}
return sfn
}

@ -1,87 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name rmap.go
// Created by DEV
// Modified 28/01/2024
//
// Copyright 2024 activething.com
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
package reflects
import (
"activething.com/go/gogo/packs"
"encoding"
"reflect"
"strconv"
)
type (
RKeyValue struct {
RKey reflect.Value
RVal reflect.Value
Key string
}
)
func NewRKeyValue(key reflect.Value, value reflect.Value, stringifyFn func(v reflect.Value) (string,error), scapeFn func(string) string) (*RKeyValue,error) {
kv := &RKeyValue{
RKey : key,
RVal : value,
}
if kv.RKey.Kind() == reflect.String {
kv.Key = scapeFn(kv.RKey.String())
return kv,nil
}
if tm,hs := kv.RKey.Interface().(encoding.TextMarshaler); hs {
if kv.RKey.Kind() == reflect.Pointer && kv.RKey.IsNil() {
return nil,packs.G.ErrInvalidKey
}
b,e := tm.MarshalText()
if e != nil {
return nil,packs.G.ErrInvalidKey
}
kv.Key = string(b)
return kv,nil
}
if stringifyFn != nil {
b,e := stringifyFn(kv.RKey)
if e != nil {
return nil,packs.G.ErrInvalidKey.WithCause(e)
}
kv.Key = scapeFn(b)
return kv,nil
}
switch key.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
kv.Key = strconv.FormatInt(kv.RKey.Int(), 10)
return kv,nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
kv.Key= strconv.FormatUint(kv.RKey.Uint(), 10)
return kv,nil
}
return nil,packs.G.ErrInvalidKey
}

@ -1,153 +0,0 @@
// ------------------------------------------------------------------------
// Project atila
// Active Thing (activething.com) git.activething.com/go
//
// File name rRFields_type.go
// Created by DEV
// Modified 26/01/2024
//
// Copyright 2024 activething.com
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
package reflects
type (
RStruct struct {
Fields RFields
NameIndex map[string]int
}
)
/*
func NewRStruct (rtype reflect.Type) *RStruct {
res := &RStruct{
Fields : make(RFields,0,rtype.NumField()),
NameIndex: make(map[string]int),
}
cur := RFields{}
nxt := RFields{{ RType: rtype }}
var (
cnt,nxc map[reflect.Type]int
vis map[reflect.Type]struct{}
nms bytes.Buffer
)
for len(nxt) > 0 {
cur, nxt = nxt, cur[:0]
cnt, nxc = nxc, map[reflect.Type]int{}
for _,f := range cur {
if _,h := vis[f.RType]; h {
continue
}
vis[f.RType]=struct{}{}
for i :=0; i<f.RType.NumField(); i++ {
sf := f.RType.Field(i)
if sf.Anonymous {
st := sf.Type
if st.Kind() == reflect.Pointer {
st = st.Elem()
}
if !sf.IsExported() && st.Kind() != reflect.Struct {
continue
}
} else if !sf.IsExported() {
continue
}
nm := false
tags,ok := parseTag(sf,"json",nil)
if ok {
if tags.Has("-") {
continue
}
if isValidTag(tags[0]) {
nm=true
}
}
ft := sf.Type
if ft.Name() == "" && ft.Kind() == reflect.Pointer {
ft=ft.Elem()
}
//qt := false
//if tags.Has("string") {
// switch ft.Kind() {
// case reflect.Bool,
// reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
// reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
// reflect.Float32, reflect.Float64,
// reflect.String:
// qt = true
// }
//}
index := make([]int, len(f.Index)+1)
copy(index, f.Index)
index[len(f.Index)] = i
var name string
if nm || sf.Anonymous || ft.Kind() != reflect.Struct {
if nm {
name = tags[0]
} else {
name = sf.Name
}
res.Fields = append(res.Fields, RField{
RField: sf,
RType: ft,
Name: name,
BName: []byte(name),
Tags: map[string]slices.String{"json": append(slices.String(nil), tags...)},
})
if cnt[f.RType] > 1 {
res.Fields = append(res.Fields, res.Fields[len(res.Fields)-1])
}
continue
}
nxc[ft]++
if nxc[ft] == 1 {
nxt = append(nxt, RField{
RType: ft,
Name: ft.Name(),
Index: index,
})
}
}
}
}
for i,f := range res.Fields {
res.NameIndex[f.Name]=i
}
return res
}
*/
Loading…
Cancel
Save

Powered by TurnKey Linux.