diff --git a/pkg/vars/README.md b/pkg/vars/README.md index 88728e5..6dd58dc 100644 --- a/pkg/vars/README.md +++ b/pkg/vars/README.md @@ -1,12 +1,12 @@ # VARS ![license](https://img.shields.io/github/license/happy-sdk/vars) -[![PkgGoDev](https://pkg.go.dev/badge/github.com/happy-sdk/happy-go/vars)](https://pkg.go.dev/github.com/happy-sdk/happy-go/vars) -![tests](https://github.com/happy-sdk/happy-go/vars/workflows/tests/badge.svg) -[![Go Report Card](https://goreportcard.com/badge/github.com/happy-sdk/happy-go/vars)](https://goreportcard.com/report/github.com/happy-sdk/happy-go/vars) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/happy-sdk/happy/pkg/vars)](https://pkg.go.dev/github.com/happy-sdk/happy/pkg/vars) +![tests](https://github.com/happy-sdk/happy/pkg/vars/workflows/tests/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/happy-sdk/happy/pkg/vars)](https://goreportcard.com/report/github.com/happy-sdk/happy/pkg/vars) [![Coverage Status](https://coveralls.io/repos/github/happy-sdk/vars/badge.svg?branch=main)](https://coveralls.io/github/happy-sdk/vars?branch=main) -![GitHub last commit](https://img.shields.io/github/last-commit/happy-sdk/vars) +![GitHub last commit](https://img.shields.io/github/last-commit/happy-sdk/happy/vars) ## About Package vars provides the API to parse variables from various input formats/types to common key value pair vars.Value or variable sets to vars.Collection @@ -15,19 +15,19 @@ Package vars provides the API to parse variables from various input formats/type ## Install ``` -go get github.com/happy-sdk/happy-go/vars +go get github.com/happy-sdk/happy/pkg/vars ``` ## Usage -**working with [vars.Value](https://pkg.go.dev/github.com/happy-sdk/happy-go/vars#Value)** +**working with [vars.Value](https://pkg.go.dev/github.com/happy-sdk/happy/pkg/vars#Value)** ```go package main import ( "fmt" - "github.com/happy-sdk/happy-go/vars" + "github.com/happy-sdk/happy/pkg/vars" ) func main() { @@ -89,7 +89,7 @@ func main() { } ``` -**working with [vars.Collection](https://pkg.go.dev/github.com/happy-sdk/happy-go/vars#Collection)** +**working with [vars.Collection](https://pkg.go.dev/github.com/happy-sdk/happy/pkg/vars#Collection)** > Because of underlying `sync.Map` it is meant to be populated once and read many times > read thoroughly sync.Map docs to understand where .Collection may not me right for you! @@ -99,7 +99,7 @@ package main import ( "fmt" - "github.com/happy-sdk/happy-go/vars" + "github.com/happy-sdk/happy/pkg/vars" ) func main() { @@ -149,7 +149,7 @@ package main import ( "fmt" "io/ioutil" - "github.com/happy-sdk/happy-go/vars" + "github.com/happy-sdk/happy/pkg/vars" ) func main() { diff --git a/pkg/vars/key.go b/pkg/vars/key.go index eccb463..0e266a0 100644 --- a/pkg/vars/key.go +++ b/pkg/vars/key.go @@ -4,6 +4,12 @@ package vars +import ( + "strings" + "unicode" + "unicode/utf8" +) + var ( // for faster lookup our custom Unicode Character Table rules // we have following two tables. @@ -23,84 +29,46 @@ var ( // table for Variable key. based on IEEE Std 1003.1-2001. // See The Open Group specification for more details. // https://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html -func parseKey(k string) (key string, err error) { - var ( - ltrimd bool - rtrimd bool - ) - ilen := len(k) - for i := 0; !ltrimd && i < ilen; i++ { - ki := k[0] - if keyAutoTrimableChars[ki] == 1 { - k = k[1:] - continue - } +func parseKey(str string) (key string, err error) { + if len(str) == 0 { + return "", ErrKeyIsEmpty + } - if ki >= 48 && ki <= 57 { - return "", ErrKeyPrefix - } - ltrimd = true + if !utf8.ValidString(str) { + return "", ErrKeyNotValidUTF8 } - for i := len(k) - 1; !rtrimd && i > 0; i-- { - if keyAutoTrimableChars[k[i]] == 1 { - k = k[0:i] - continue + + // remove most outer trimmable characters + key = strings.TrimFunc(str, func(c rune) bool { + if c < 256 { + return keyAutoTrimableChars[c] == 1 + } - rtrimd = true - } + return false + }) - n := len(k) - if n == 0 { - return "", ErrKeyIsEmpty + if len(key) == 0 { + return "", ErrKeyHasIllegalChar } - for i := 0; i < n; { - ki := k[i] + if unicode.IsNumber(rune(key[0])) { + return "", ErrKeyPrefix + } - if ki < utf8RuneSelf { - i++ - if keyIllegalChars[ki] == 1 { - return "", ErrKeyHasIllegalChar - } - if unicodeIsControl(rune(ki)) { - return "", ErrKeyHasControlChar - } - continue - } - x := utf8first[ki] - if x == xx { - return "", ErrKeyHasIllegalStarterByte + ckey := key + for len(ckey) > 0 { + c, size := utf8.DecodeRuneInString(ckey) + ckey = ckey[size:] + if unicode.IsControl(c) { + return "", ErrKeyHasControlChar } - size := int(x & 7) - if i+size > n { - return "", ErrKeyNotValidUTF8 + if !unicode.IsPrint(c) { + return "", ErrKeyHasNonPrintChar } - accept := utf8AcceptRanges[x>>4] - if c := k[i+1]; c < accept.lo || accept.hi < c { - return "", ErrKeyOutOfRange - } else if size == 2 { - r := rune(k[i]&mask2)<<6 | rune(k[i+1]&maskx) - if !unicodeIsPrint(r) { - return "", ErrKeyHasNonPrintChar - } - } else if c := k[i+2]; c < locb || hicb < c { - return "", ErrKeyNotValidUTF8 - } else if size == 3 { - r := rune(k[i]&mask3)<<12 | rune(k[i+1]&maskx)<<6 | rune(k[i+2]&maskx) - if !unicodeIsPrint(r) { - return "", ErrKeyHasNonPrintChar - } - } else if c := k[i+3]; c < locb || hicb < c { - return "", ErrKeyOutOfRange - } else if size == 4 { - r := rune(k[i]&mask4)<<18 | rune(k[i+1]&maskx)<<12 | rune(k[i+2]&maskx)<<6 | rune(k[i+3]&maskx) - if !unicodeIsPrint(r) { - return "", ErrKeyHasNonPrintChar - } + if c < 256 && (keyIllegalChars[c] == 1) { + return "", ErrKeyHasIllegalChar } - i += size } - - return k, nil + return key, nil } diff --git a/pkg/vars/key_test.go b/pkg/vars/key_test.go index d24fc09..2020441 100644 --- a/pkg/vars/key_test.go +++ b/pkg/vars/key_test.go @@ -6,10 +6,7 @@ package vars_test import ( "errors" - "strings" "testing" - "unicode" - "unicode/utf8" "github.com/happy-sdk/happy/pkg/vars" ) @@ -28,54 +25,6 @@ var ( } ) -// parseKeyStd is parseKey equivalent used in tests. -// IMPORTANT! This implementations should reflect optimal key parsing -// done with std libraries so that we can have adequate -// benchmark results for our own implementation. -func parseKeyStd(str string) (key string, err error) { - if len(str) == 0 { - return "", vars.ErrKeyIsEmpty - } - - if !utf8.ValidString(str) { - return "", vars.ErrKeyNotValidUTF8 - } - - // remove most outer trimmable characters - key = strings.TrimFunc(str, func(c rune) bool { - if c < 256 { - return keyAutoTrimableChars[c] == 1 - - } - return false - }) - - if len(key) == 0 { - return "", vars.ErrKeyHasIllegalChar - } - - if unicode.IsNumber(rune(key[0])) { - return "", vars.ErrKeyPrefix - } - - ckey := key - for len(ckey) > 0 { - c, size := utf8.DecodeRuneInString(ckey) - ckey = ckey[size:] - if unicode.IsControl(c) { - return "", vars.ErrKeyHasControlChar - } - - if !unicode.IsPrint(c) { - return "", vars.ErrKeyHasNonPrintChar - } - if c < 256 && (keyIllegalChars[c] == 1) { - return "", vars.ErrKeyHasIllegalChar - } - } - return key, nil -} - type keyTest struct { Key string Want string @@ -169,18 +118,6 @@ func getKeyTests() []keyTest { } } -func TestParseKeyStdTest(t *testing.T) { - for _, test := range getKeyTests() { - key, err := parseKeyStd(test.Key) - if test.Want != key { - t.Errorf("in(%s) want(%s) got(%s) err(%v)", test.Key, test.Want, key, err) - } - if !errors.Is(err, test.Err) { - t.Errorf("in(%s) want err(%s) got err(%v)", test.Key, test.Want, err) - } - } -} - func TestParseKey(t *testing.T) { for _, test := range getKeyTests() { // check that key set is correct @@ -195,21 +132,3 @@ func TestParseKey(t *testing.T) { } } } - -func FuzzVariableKeys(f *testing.F) { - for _, test := range getKeyTests() { - f.Add(test.Key) - } - f.Fuzz(func(t *testing.T, arg string) { - klib, errlib := vars.ParseKey(arg) - kstd, errstd := parseKeyStd(arg) - - if klib != kstd { - t.Errorf("arg(%s) parsed keys do not match std(%s) != lib(%s)", arg, kstd, klib) - } - - if (errlib != nil && errstd == nil) || errlib == nil && errstd != nil { - t.Fatalf("arg(%s) lib error(%v) not like std error(%v)", arg, errlib, errstd) - } - }) -}