mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-28 11:12:25 -05:00
[performance] bump codeberg.org/gruf/go-kv to v2 (#4341)
updates our codeberg.org/gruf/go-kv log key-value formatting library to latest version, which comes with some maaaaaaajor speed boosts in the form of:
- very minimal reflect.Value{} usage
- caching prepared formatting functions per type
~~still a work-in-progress until i make a release tag on the go-kv repository, which itself is waiting on published benchmark results in the README and finishing writing some code comments~~
benchmarks so far show this to be ~3x faster than the "fmt" stdlib package on average, when run across a wide variety (106 different types) of test cases, while still creating more visually friendly log output and actually recursing down nested struct ptrs
Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4341
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
parent
30cd1cd9eb
commit
e3dfd88893
47 changed files with 2819 additions and 61 deletions
3
go.mod
3
go.mod
|
|
@ -24,7 +24,7 @@ require (
|
|||
codeberg.org/gruf/go-fastcopy v1.1.3
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.7
|
||||
codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf
|
||||
codeberg.org/gruf/go-kv v1.6.5
|
||||
codeberg.org/gruf/go-kv/v2 v2.0.3
|
||||
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f
|
||||
codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760
|
||||
codeberg.org/gruf/go-mutexes v1.5.2
|
||||
|
|
@ -99,6 +99,7 @@ require (
|
|||
code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0 // indirect
|
||||
code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 // indirect
|
||||
codeberg.org/gruf/go-fastpath/v2 v2.0.0 // indirect
|
||||
codeberg.org/gruf/go-kv v1.6.5 // indirect
|
||||
codeberg.org/gruf/go-mangler v1.4.4 // indirect
|
||||
codeberg.org/gruf/go-maps v1.0.4 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
|
|
|
|||
2
go.sum
generated
2
go.sum
generated
|
|
@ -32,6 +32,8 @@ codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf h1:84s/ii8N6lYls
|
|||
codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf/go.mod h1:zZAICsp5rY7+hxnws2V0ePrWxE0Z2Z/KXcN3p/RQCfk=
|
||||
codeberg.org/gruf/go-kv v1.6.5 h1:ttPf0NA8F79pDqBttSudPTVCZmGncumeNIxmeM9ztz0=
|
||||
codeberg.org/gruf/go-kv v1.6.5/go.mod h1:c4PsGqw05bDScvISpK+d31SiDEpBorweCL50hsiK3dc=
|
||||
codeberg.org/gruf/go-kv/v2 v2.0.3 h1:Ge4/WFR417EFPwfDdsf8S80XAdKF74RJk5g+VerAg1k=
|
||||
codeberg.org/gruf/go-kv/v2 v2.0.3/go.mod h1:mNL6SrBnYGEyrx6Mh4E1tAdhO0+T9/1iBrPJxIwxY24=
|
||||
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f h1:Ss6Z+vygy+jOGhj96d/GwsYYDd22QmIcH74zM7/nQkw=
|
||||
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f/go.mod h1:F9pl4h34iuVN7kucKam9fLwsItTc+9mmaKt7pNXRd/4=
|
||||
codeberg.org/gruf/go-loosy v0.0.0-20231007123304-bb910d1ab5c4 h1:IXwfoU7f2whT6+JKIKskNl/hBlmWmnF1vZd84Eb3cyA=
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"errors"
|
||||
"time"
|
||||
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/ap"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
|
|
|||
5
internal/cache/timeline/status_test.go
vendored
5
internal/cache/timeline/status_test.go
vendored
|
|
@ -29,8 +29,8 @@ import (
|
|||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/id"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/paging"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
"codeberg.org/gruf/go-structr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
@ -440,7 +440,8 @@ func loadStatusIDsFrom(data []*StatusMeta) func(ids []string) ([]*gtsmodel.Statu
|
|||
return s.ID == id
|
||||
})
|
||||
if i < 0 || i >= len(data) {
|
||||
panic(fmt.Sprintf("could not find %s in %v", id, log.VarDump(data)))
|
||||
kv := kv.Field{K: "data", V: data} // use kv.Field for formatting
|
||||
panic(fmt.Sprintf("could not find %s in %v", id, kv))
|
||||
}
|
||||
statuses = append(statuses, >smodel.Status{
|
||||
ID: data[i].ID,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/httpsig"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
// maxIter defines how many iterations of descendants or
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/uris"
|
||||
errorsv2 "codeberg.org/gruf/go-errors/v2"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
// federatingActor wraps the pub.FederatingActor
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/uris"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util/xslices"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
type errOtherIRIBlocked struct {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
"context"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
"github.com/oklog/ulid"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package log
|
||||
|
||||
import "codeberg.org/gruf/go-kv/format"
|
||||
|
||||
// VarDump returns a serialized, useful log / error output of given variable.
|
||||
func VarDump(a any) string {
|
||||
buf := getBuf()
|
||||
format.Appendf(buf, "{:v}", a)
|
||||
s := string(buf.B)
|
||||
putBuf(buf)
|
||||
return s
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util/xslices"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"os"
|
||||
|
||||
errorsv2 "codeberg.org/gruf/go-errors/v2"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
"codeberg.org/gruf/go-runners"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"codeberg.org/gruf/go-bytesize"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/messages"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/id"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
// Accounts does a partial search for accounts that
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/text"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
// Lookup does a quick, non-resolving search for accounts that
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/stream"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
// Open returns a new Stream for the given account, which will contain a channel for passing messages back to the caller.
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/typeutils"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/uris"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
// clientAPI wraps processing functions
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/id"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/uris"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/admin"
|
||||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
var testModels = []interface{}{
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/messages"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/processing/workers"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
"codeberg.org/gruf/go-byteutil"
|
||||
"codeberg.org/gruf/go-kv/format"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
)
|
||||
|
||||
// Starts workers on the provided state using noop processing functions.
|
||||
|
|
@ -283,7 +282,7 @@ func WaitFor(condition func() bool) bool {
|
|||
|
||||
// dump returns debug output of 'v'.
|
||||
func dump(v any) string {
|
||||
var buf byteutil.Buffer
|
||||
format.Append(&buf, v)
|
||||
return buf.String()
|
||||
var kv kv.Field
|
||||
kv.V = v
|
||||
return kv.Value(false)
|
||||
}
|
||||
|
|
|
|||
9
vendor/codeberg.org/gruf/go-kv/v2/LICENSE
generated
vendored
Normal file
9
vendor/codeberg.org/gruf/go-kv/v2/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 gruf
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
5
vendor/codeberg.org/gruf/go-kv/v2/README.md
generated
vendored
Normal file
5
vendor/codeberg.org/gruf/go-kv/v2/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# go-kv
|
||||
|
||||
This library provides a key-value field structure `kv.Field{}` that plays well with the `"fmt"` package. It gives an easy means of appending key-value fields to log entries, in a more performant manner that also happens to look nice! (it's not far removed from using a `map[string]interface{}`).
|
||||
|
||||
The formatting for these key-value fields is handled by the `"fmt"` package by default. If you set the `kvformat` build tag then it will use a custom formatting library found under `format/`.
|
||||
103
vendor/codeberg.org/gruf/go-kv/v2/field.go
generated
vendored
Normal file
103
vendor/codeberg.org/gruf/go-kv/v2/field.go
generated
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package kv
|
||||
|
||||
import (
|
||||
"codeberg.org/gruf/go-byteutil"
|
||||
)
|
||||
|
||||
// bufsize is the default buffer size per field to alloc
|
||||
// when calling .AppendFormat() from within .String().
|
||||
const bufsize = 64
|
||||
|
||||
// Fields is a typedef for a []Field slice to provide
|
||||
// slightly more performant string formatting for multiples.
|
||||
type Fields []Field
|
||||
|
||||
// Get will return the field with given 'key'.
|
||||
func (f Fields) Get(key string) (*Field, bool) {
|
||||
for i := 0; i < len(f); i++ {
|
||||
if f[i].K == key {
|
||||
return &f[i], true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Set will set an existing field with 'key' to 'value', or append new.
|
||||
func (f *Fields) Set(key string, value interface{}) {
|
||||
for i := 0; i < len(*f); i++ {
|
||||
// Update existing value
|
||||
if (*f)[i].K == key {
|
||||
(*f)[i].V = value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Append new field
|
||||
*f = append(*f, Field{
|
||||
K: key,
|
||||
V: value,
|
||||
})
|
||||
}
|
||||
|
||||
// AppendFormat appends a string representation of receiving Field(s) to 'b'.
|
||||
func (f Fields) AppendFormat(buf *byteutil.Buffer, vbose bool) {
|
||||
for i := 0; i < len(f); i++ {
|
||||
f[i].AppendFormat(buf, vbose)
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
if len(f) > 0 {
|
||||
buf.Truncate(1)
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of receiving Field(s).
|
||||
func (f Fields) String() string {
|
||||
b := make([]byte, 0, bufsize*len(f))
|
||||
buf := byteutil.Buffer{B: b}
|
||||
f.AppendFormat(&buf, false)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// GoString performs .String() but with type prefix.
|
||||
func (f Fields) GoString() string {
|
||||
b := make([]byte, 0, bufsize*len(f))
|
||||
buf := byteutil.Buffer{B: b}
|
||||
f.AppendFormat(&buf, true)
|
||||
return "kv.Fields{" + buf.String() + "}"
|
||||
}
|
||||
|
||||
// Field represents an individual key-value field.
|
||||
type Field struct {
|
||||
K string // Field key
|
||||
V interface{} // Field value
|
||||
}
|
||||
|
||||
// Key returns the formatted key string of this Field.
|
||||
func (f Field) Key() string {
|
||||
buf := byteutil.Buffer{B: make([]byte, 0, bufsize/2)}
|
||||
AppendQuoteString(&buf, f.K)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// String will return a string representation of this Field
|
||||
// of the form `key=value` where `value` is formatted using
|
||||
// fmt package's `%+v` directive. If the .X = true (verbose),
|
||||
// then it uses '%#v'. Both key and value are escaped and
|
||||
// quoted if necessary to fit on single line.
|
||||
//
|
||||
// If the `kvformat` build tag is provided, the formatting
|
||||
// will be performed by the `kv/format` package.
|
||||
func (f Field) String() string {
|
||||
b := make([]byte, 0, bufsize)
|
||||
buf := byteutil.Buffer{B: b}
|
||||
f.AppendFormat(&buf, false)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// GoString performs .String() but with verbose always enabled.
|
||||
func (f Field) GoString() string {
|
||||
b := make([]byte, 0, bufsize)
|
||||
buf := byteutil.Buffer{B: b}
|
||||
f.AppendFormat(&buf, true)
|
||||
return buf.String()
|
||||
}
|
||||
63
vendor/codeberg.org/gruf/go-kv/v2/field_fmt.go
generated
vendored
Normal file
63
vendor/codeberg.org/gruf/go-kv/v2/field_fmt.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//go:build !kvformat
|
||||
// +build !kvformat
|
||||
|
||||
package kv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"codeberg.org/gruf/go-byteutil"
|
||||
)
|
||||
|
||||
// bufPool is a memory pool of byte buffers.
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &byteutil.Buffer{B: make([]byte, 0, 512)}
|
||||
},
|
||||
}
|
||||
|
||||
// AppendFormat will append formatted format of Field to 'buf'. See .String() for details.
|
||||
func (f Field) AppendFormat(buf *byteutil.Buffer, vbose bool) {
|
||||
var fmtstr string
|
||||
if vbose /* verbose */ {
|
||||
fmtstr = `%#v`
|
||||
} else /* regular */ {
|
||||
fmtstr = `%+v`
|
||||
}
|
||||
AppendQuoteString(buf, f.K)
|
||||
buf.WriteByte('=')
|
||||
appendValuef(buf, fmtstr, f.V)
|
||||
}
|
||||
|
||||
// Value returns the formatted value string of this Field.
|
||||
func (f Field) Value(vbose bool) string {
|
||||
var fmtstr string
|
||||
if vbose /* verbose */ {
|
||||
fmtstr = `%#v`
|
||||
} else /* regular */ {
|
||||
fmtstr = `%+v`
|
||||
}
|
||||
buf := byteutil.Buffer{B: make([]byte, 0, bufsize/2)}
|
||||
appendValuef(&buf, fmtstr, f.V)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// appendValuef appends a quoted value string (formatted by fmt.Appendf) to 'buf'.
|
||||
func appendValuef(buf *byteutil.Buffer, format string, args ...interface{}) {
|
||||
// Write format string to a byte buffer
|
||||
fmtbuf := bufPool.Get().(*byteutil.Buffer)
|
||||
fmtbuf.B = fmt.Appendf(fmtbuf.B, format, args...)
|
||||
|
||||
// Append quoted value to dst buffer
|
||||
AppendQuoteValue(buf, fmtbuf.String())
|
||||
|
||||
// Drop overly large capacity buffers
|
||||
if fmtbuf.Cap() > int(^uint16(0)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Replace buffer in pool
|
||||
fmtbuf.Reset()
|
||||
bufPool.Put(fmtbuf)
|
||||
}
|
||||
46
vendor/codeberg.org/gruf/go-kv/v2/field_format.go
generated
vendored
Normal file
46
vendor/codeberg.org/gruf/go-kv/v2/field_format.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//go:build kvformat
|
||||
// +build kvformat
|
||||
|
||||
package kv
|
||||
|
||||
import (
|
||||
"codeberg.org/gruf/go-byteutil"
|
||||
"codeberg.org/gruf/go-kv/v2/format"
|
||||
)
|
||||
|
||||
var formatter format.Formatter
|
||||
|
||||
var argsDefault = format.DefaultArgs()
|
||||
|
||||
var argsVerbose = func() format.Args {
|
||||
args := format.DefaultArgs()
|
||||
args.SetWithType()
|
||||
args.SetNoMethod()
|
||||
return args
|
||||
}()
|
||||
|
||||
// AppendFormat will append formatted format of Field to 'buf'. See .String() for details.
|
||||
func (f Field) AppendFormat(buf *byteutil.Buffer, vbose bool) {
|
||||
var args format.Args
|
||||
if vbose {
|
||||
args = argsVerbose
|
||||
} else {
|
||||
args = argsDefault
|
||||
}
|
||||
AppendQuoteString(buf, f.K)
|
||||
buf.WriteByte('=')
|
||||
buf.B = formatter.Append(buf.B, f.V, args)
|
||||
}
|
||||
|
||||
// Value returns the formatted value string of this Field.
|
||||
func (f Field) Value(vbose bool) string {
|
||||
var args format.Args
|
||||
if vbose {
|
||||
args = argsVerbose
|
||||
} else {
|
||||
args = argsDefault
|
||||
}
|
||||
buf := make([]byte, 0, bufsize/2)
|
||||
buf = formatter.Append(buf, f.V, args)
|
||||
return byteutil.B2S(buf)
|
||||
}
|
||||
262
vendor/codeberg.org/gruf/go-kv/v2/format/abi.go
generated
vendored
Normal file
262
vendor/codeberg.org/gruf/go-kv/v2/format/abi.go
generated
vendored
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
//go:build go1.24 && !go1.25
|
||||
|
||||
package format
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// see: go/src/internal/abi/type.go
|
||||
abi_KindDirectIface uint8 = 1 << 5
|
||||
abi_KindMask uint8 = (1 << 5) - 1
|
||||
)
|
||||
|
||||
// abi_Type is a copy of the memory layout of abi.Type{}.
|
||||
//
|
||||
// see: go/src/internal/abi/type.go
|
||||
type abi_Type struct {
|
||||
_ uintptr
|
||||
PtrBytes uintptr
|
||||
_ uint32
|
||||
_ uint8
|
||||
_ uint8
|
||||
_ uint8
|
||||
Kind_ uint8
|
||||
_ func(unsafe.Pointer, unsafe.Pointer) bool
|
||||
_ *byte
|
||||
_ int32
|
||||
_ int32
|
||||
}
|
||||
|
||||
// abi_EmptyInterface is a copy of the memory layout of abi.EmptyInterface{},
|
||||
// which is to say also the memory layout of any method-less interface.
|
||||
//
|
||||
// see: go/src/internal/abi/iface.go
|
||||
type abi_EmptyInterface struct {
|
||||
Type *abi_Type
|
||||
Data unsafe.Pointer
|
||||
}
|
||||
|
||||
// see: go/src/internal/abi/type.go Type.Kind()
|
||||
func abi_Type_Kind(t reflect.Type) uint8 {
|
||||
iface := (*reflect_nonEmptyInterface)(unsafe.Pointer(&t))
|
||||
atype := (*abi_Type)(unsafe.Pointer(iface.word))
|
||||
return atype.Kind_ & abi_KindMask
|
||||
}
|
||||
|
||||
// see: go/src/internal/abi/type.go Type.IfaceIndir()
|
||||
func abi_Type_IfaceIndir(t reflect.Type) bool {
|
||||
iface := (*reflect_nonEmptyInterface)(unsafe.Pointer(&t))
|
||||
atype := (*abi_Type)(unsafe.Pointer(iface.word))
|
||||
return atype.Kind_&abi_KindDirectIface == 0
|
||||
}
|
||||
|
||||
// pack_iface packs a new reflect.nonEmptyInterface{} using shielded itab
|
||||
// pointer and data (word) pointer, returning a pointer for caller casting.
|
||||
func pack_iface(itab uintptr, word unsafe.Pointer) unsafe.Pointer {
|
||||
return unsafe.Pointer(&reflect_nonEmptyInterface{
|
||||
itab: itab,
|
||||
word: word,
|
||||
})
|
||||
}
|
||||
|
||||
// get_iface_ITab generates a new value of given type,
|
||||
// casts it to the generic param interface type, and
|
||||
// returns the .itab portion of the reflect.nonEmptyInterface{}.
|
||||
// this is useful for later calls to pack_iface for known type.
|
||||
func get_iface_ITab[I any](t reflect.Type) uintptr {
|
||||
s := reflect.New(t).Elem().Interface().(I)
|
||||
i := (*reflect_nonEmptyInterface)(unsafe.Pointer(&s))
|
||||
return i.itab
|
||||
}
|
||||
|
||||
// unpack_eface returns the .Data portion of an abi.EmptyInterface{}.
|
||||
func unpack_eface(a any) unsafe.Pointer {
|
||||
return (*abi_EmptyInterface)(unsafe.Pointer((&a))).Data
|
||||
}
|
||||
|
||||
// add returns the ptr addition of starting ptr and a delta.
|
||||
func add(ptr unsafe.Pointer, delta uintptr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(ptr) + delta)
|
||||
}
|
||||
|
||||
// typeof is short-hand for reflect.TypeFor[T]().
|
||||
func typeof[T any]() reflect.Type {
|
||||
return reflect.TypeFor[T]()
|
||||
}
|
||||
|
||||
// see: go/src/reflect/value.go
|
||||
type reflect_flag uintptr
|
||||
|
||||
const (
|
||||
// see: go/src/reflect/value.go
|
||||
reflect_flagKindWidth = 5 // there are 27 kinds
|
||||
reflect_flagKindMask reflect_flag = 1<<reflect_flagKindWidth - 1
|
||||
reflect_flagStickyRO reflect_flag = 1 << 5
|
||||
reflect_flagEmbedRO reflect_flag = 1 << 6
|
||||
reflect_flagIndir reflect_flag = 1 << 7
|
||||
reflect_flagAddr reflect_flag = 1 << 8
|
||||
reflect_flagMethod reflect_flag = 1 << 9
|
||||
reflect_flagMethodShift = 10
|
||||
reflect_flagRO reflect_flag = reflect_flagStickyRO | reflect_flagEmbedRO
|
||||
|
||||
// custom flag to indicate key types.
|
||||
flagKeyType = 1 << 10
|
||||
)
|
||||
|
||||
// reflect_iface_elem_flags returns the reflect_flag expected of an unboxed interface element of type.
|
||||
//
|
||||
// see: go/src/reflect/value.go unpackElem()
|
||||
func reflect_iface_elem_flags(elemType reflect.Type) reflect_flag {
|
||||
if elemType == nil {
|
||||
return 0
|
||||
}
|
||||
flags := reflect_flag(abi_Type_Kind(elemType))
|
||||
if abi_Type_IfaceIndir(elemType) {
|
||||
flags |= reflect_flagIndir
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
// reflect_pointer_elem_flags returns the reflect_flag expected of a dereferenced pointer element of type.
|
||||
//
|
||||
// see: go/src/reflect/value.go Value.Elem()
|
||||
func reflect_pointer_elem_flags(ptrFlags reflect_flag, elemType reflect.Type) reflect_flag {
|
||||
return ptrFlags | reflect_flagIndir | reflect_flagAddr | reflect_flag(abi_Type_Kind(elemType))
|
||||
}
|
||||
|
||||
// reflect_array_elem_flags returns the reflect_flag expected of an element of type in an array.
|
||||
//
|
||||
// see: go/src/reflect/value.go Value.Index()
|
||||
func reflect_array_elem_flags(arrayFlags reflect_flag, elemType reflect.Type) reflect_flag {
|
||||
return arrayFlags&(reflect_flagIndir|reflect_flagAddr) | reflect_flag(abi_Type_Kind(elemType))
|
||||
}
|
||||
|
||||
// reflect_slice_elem_flags returns the reflect_flag expected of a slice element of type.
|
||||
//
|
||||
// see: go/src/reflect/value.go Value.Index()
|
||||
func reflect_slice_elem_flags(elemType reflect.Type) reflect_flag {
|
||||
return reflect_flagAddr | reflect_flagIndir | reflect_flag(abi_Type_Kind(elemType))
|
||||
}
|
||||
|
||||
// reflect_struct_field_flags returns the reflect_flag expected of a struct field of type.
|
||||
//
|
||||
// see: go/src/reflect/value.go Value.Field()
|
||||
func reflect_struct_field_flags(structFlags reflect_flag, fieldType reflect.Type) reflect_flag {
|
||||
return structFlags&(reflect_flagIndir|reflect_flagAddr) | reflect_flag(abi_Type_Kind(fieldType))
|
||||
}
|
||||
|
||||
// reflect_map_key_flags returns the reflect_flag expected of a map key of type (with our own key type mask set).
|
||||
//
|
||||
// see: go/src/reflect/map_swiss.go MapIter.Key()
|
||||
func reflect_map_key_flags(keyType reflect.Type) reflect_flag {
|
||||
return flagKeyType | reflect_flag(abi_Type_Kind(keyType))
|
||||
}
|
||||
|
||||
// reflect_map_elem_flags returns the reflect_flag expected of a map element of type.
|
||||
//
|
||||
// see: go/src/reflect/map_swiss.go MapIter.Value()
|
||||
func reflect_map_elem_flags(elemType reflect.Type) reflect_flag {
|
||||
return reflect_flag(abi_Type_Kind(elemType))
|
||||
}
|
||||
|
||||
// reflect_nonEmptyInterface is a copy of the memory layout of reflect.nonEmptyInterface,
|
||||
// which is also to say the memory layout of any non-empty (i.e. w/ method) interface.
|
||||
//
|
||||
// see: go/src/reflect/value.go
|
||||
type reflect_nonEmptyInterface struct {
|
||||
itab uintptr
|
||||
word unsafe.Pointer
|
||||
}
|
||||
|
||||
// reflect_Value is a copy of the memory layout of reflect.Value{}.
|
||||
//
|
||||
// see: go/src/reflect/value.go
|
||||
type reflect_Value struct {
|
||||
typ_ unsafe.Pointer
|
||||
ptr unsafe.Pointer
|
||||
reflect_flag
|
||||
}
|
||||
|
||||
func init() {
|
||||
if unsafe.Sizeof(reflect_Value{}) != unsafe.Sizeof(reflect.Value{}) {
|
||||
panic("reflect_Value{} not in sync with reflect.Value{}")
|
||||
}
|
||||
}
|
||||
|
||||
// reflect_type_data returns the .word from the reflect.Type{} cast
|
||||
// as the reflect.nonEmptyInterface{}, which itself will be a pointer
|
||||
// to the actual abi.Type{} that this reflect.Type{} is wrapping.
|
||||
func reflect_type_data(t reflect.Type) unsafe.Pointer {
|
||||
return (*reflect_nonEmptyInterface)(unsafe.Pointer(&t)).word
|
||||
}
|
||||
|
||||
// build_reflect_value manually builds a reflect.Value{} by setting the internal field members.
|
||||
func build_reflect_value(rtype reflect.Type, data unsafe.Pointer, flags reflect_flag) reflect.Value {
|
||||
return *(*reflect.Value)(unsafe.Pointer(&reflect_Value{reflect_type_data(rtype), data, flags}))
|
||||
}
|
||||
|
||||
// maps_Iter is a copy of the memory layout of maps.Iter{}.
|
||||
//
|
||||
// see: go/src/internal/runtime/maps/table.go
|
||||
type maps_Iter struct {
|
||||
key unsafe.Pointer
|
||||
elem unsafe.Pointer
|
||||
_ uintptr
|
||||
_ uintptr
|
||||
_ uint64
|
||||
_ uint64
|
||||
_ uint64
|
||||
_ uint8
|
||||
_ int
|
||||
_ uintptr
|
||||
_ struct{ _ unsafe.Pointer }
|
||||
_ uint64
|
||||
}
|
||||
|
||||
// reflect_MapIter is a copy of the memory layout of reflect.MapIter{}.
|
||||
//
|
||||
// see: go/src/reflect/map_swiss.go
|
||||
type reflect_MapIter struct {
|
||||
m reflect.Value
|
||||
hiter maps_Iter
|
||||
}
|
||||
|
||||
func init() {
|
||||
if unsafe.Sizeof(reflect_MapIter{}) != unsafe.Sizeof(reflect.MapIter{}) {
|
||||
panic("reflect_MapIter{} not in sync with reflect.MapIter{}")
|
||||
}
|
||||
}
|
||||
|
||||
// map_iter creates a new map iterator from value,
|
||||
// skipping the initial v.MapRange() type checking.
|
||||
func map_iter(v reflect.Value) *reflect.MapIter {
|
||||
var i reflect_MapIter
|
||||
i.m = v
|
||||
return (*reflect.MapIter)(unsafe.Pointer(&i))
|
||||
}
|
||||
|
||||
// map_key returns ptr to current map key in iter.
|
||||
func map_key(i *reflect.MapIter) unsafe.Pointer {
|
||||
return (*reflect_MapIter)(unsafe.Pointer(i)).hiter.key
|
||||
}
|
||||
|
||||
// map_elem returns ptr to current map element in iter.
|
||||
func map_elem(i *reflect.MapIter) unsafe.Pointer {
|
||||
return (*reflect_MapIter)(unsafe.Pointer(i)).hiter.elem
|
||||
}
|
||||
|
||||
// see: go/src/internal/unsafeheader/unsafeheader.go
|
||||
type unsafeheader_Slice struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
|
||||
// see: go/src/internal/unsafeheader/unsafeheader.go
|
||||
type unsafeheader_String struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
}
|
||||
247
vendor/codeberg.org/gruf/go-kv/v2/format/args.go
generated
vendored
Normal file
247
vendor/codeberg.org/gruf/go-kv/v2/format/args.go
generated
vendored
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
package format
|
||||
|
||||
const (
|
||||
// TypeMask when set in argument flags
|
||||
// indicates that type information of
|
||||
// the passed value, and all nested types,
|
||||
// should be included in formatted output.
|
||||
TypeMask = uint64(1) << 0
|
||||
|
||||
// LogfmtMask when set in argument flags
|
||||
// indicates that strings should be escaped
|
||||
// and quoted only where necessary. i.e. if
|
||||
// it contains any unsafe ASCII chars or double
|
||||
// quotes it will be quoted and escaped, if it
|
||||
// contains any spaces it will be quoted, and
|
||||
// all else will be printed as-is. This proves
|
||||
// particularly well readable in key-value types.
|
||||
LogfmtMask = uint64(1) << 1
|
||||
|
||||
// NumberMask when set in argument flags
|
||||
// indicates that where possible value
|
||||
// types should be formatted as numbers,
|
||||
// i.e. byte or rune types.
|
||||
NumberMask = uint64(1) << 2
|
||||
|
||||
// TextMask when set in argument flags
|
||||
// indicates that where possible value
|
||||
// types should be formatted as text,
|
||||
// i.e. []byte or []rune types.
|
||||
TextMask = uint64(1) << 3
|
||||
|
||||
// QuotedTextMask when set in argument flags
|
||||
// indicates that text should always be quoted.
|
||||
QuotedTextMask = uint64(1) << 4
|
||||
|
||||
// QuotedAsciiMask when set in argument flags
|
||||
// indicates that text should always be quoted,
|
||||
// and escaped as ASCII characters where needed.
|
||||
QuotedAsciiMask = uint64(1) << 5
|
||||
|
||||
// NoMethodMask when set in argument flags
|
||||
// indicates that where a type supports a
|
||||
// known method, (e.g. Error() or String()),
|
||||
// this should not be used for formatting
|
||||
// instead treating as a method-less type.
|
||||
// e.g. printing the entire struct value of
|
||||
// a &url.URL{} without calling String().
|
||||
NoMethodMask = uint64(1) << 6
|
||||
)
|
||||
|
||||
var (
|
||||
// default set of Args.
|
||||
defaultArgs = Args{
|
||||
Flags: LogfmtMask | TextMask,
|
||||
Int: IntArgs{Base: 10},
|
||||
Uint: IntArgs{Base: 10},
|
||||
Float: FloatArgs{Fmt: 'g', Prec: -1},
|
||||
Complex: ComplexArgs{
|
||||
Real: FloatArgs{Fmt: 'g', Prec: -1},
|
||||
Imag: FloatArgs{Fmt: 'g', Prec: -1},
|
||||
},
|
||||
}
|
||||
|
||||
// zeroArgs used for checking
|
||||
// zero value Arg{} fields.
|
||||
zeroArgs Args
|
||||
)
|
||||
|
||||
// DefaultArgs returns default
|
||||
// set of formatter arguments.
|
||||
func DefaultArgs() Args {
|
||||
return defaultArgs
|
||||
}
|
||||
|
||||
// Args contains arguments
|
||||
// for a call to a FormatFunc.
|
||||
type Args struct {
|
||||
|
||||
// Boolean argument
|
||||
// flags as bit-field.
|
||||
Flags uint64
|
||||
|
||||
// Integer
|
||||
// arguments.
|
||||
// i.e. for:
|
||||
// - int
|
||||
// - int8
|
||||
// - int16
|
||||
// - int32 (treated as rune char, number with NumberMask)
|
||||
// - int64
|
||||
Int IntArgs
|
||||
|
||||
// Unsigned
|
||||
// integer
|
||||
// arguments.
|
||||
// i.e. for:
|
||||
// - uint
|
||||
// - uint8 (treated as byte char, number with NumberMask)
|
||||
// - uint16
|
||||
// - uint32
|
||||
// - uint64
|
||||
Uint IntArgs
|
||||
|
||||
// Float
|
||||
// arguments.
|
||||
// i.e. for:
|
||||
// - float32
|
||||
// - float64
|
||||
Float FloatArgs
|
||||
|
||||
// Complex
|
||||
// arguments.
|
||||
// i.e. for:
|
||||
// - complex64
|
||||
// - complex128
|
||||
Complex ComplexArgs
|
||||
}
|
||||
|
||||
// IntArgs provides a set of
|
||||
// arguments for customizing
|
||||
// integer number serialization.
|
||||
type IntArgs struct {
|
||||
Base int
|
||||
Pad int
|
||||
}
|
||||
|
||||
// FloatArgs provides a set of
|
||||
// arguments for customizing
|
||||
// float number serialization.
|
||||
type FloatArgs struct {
|
||||
Fmt byte
|
||||
Prec int
|
||||
}
|
||||
|
||||
// ComplexArgs provides a set of
|
||||
// arguments for customizing complex
|
||||
// number serialization, as real and
|
||||
// imaginary float number parts.
|
||||
type ComplexArgs struct {
|
||||
Real FloatArgs
|
||||
Imag FloatArgs
|
||||
}
|
||||
|
||||
// WithType returns if TypeMask is set.
|
||||
func (a *Args) WithType() bool {
|
||||
return a.Flags&TypeMask != 0
|
||||
}
|
||||
|
||||
// Logfmt returns if LogfmtMask is set.
|
||||
func (a *Args) Logfmt() bool {
|
||||
return a.Flags&LogfmtMask != 0
|
||||
}
|
||||
|
||||
// AsNumber returns if NumberMask is set.
|
||||
func (a *Args) AsNumber() bool {
|
||||
return a.Flags&NumberMask != 0
|
||||
}
|
||||
|
||||
// AsText returns if TextMask is set.
|
||||
func (a *Args) AsText() bool {
|
||||
return a.Flags&TextMask != 0
|
||||
}
|
||||
|
||||
// AsQuotedText returns if QuotedTextMask is set.
|
||||
func (a *Args) AsQuotedText() bool {
|
||||
return a.Flags&QuotedTextMask != 0
|
||||
}
|
||||
|
||||
// AsQuotedASCII returns if QuotedAsciiMask is set.
|
||||
func (a *Args) AsQuotedASCII() bool {
|
||||
return a.Flags&QuotedAsciiMask != 0
|
||||
}
|
||||
|
||||
// NoMethod returns if NoMethodMask is set.
|
||||
func (a *Args) NoMethod() bool {
|
||||
return a.Flags&NoMethodMask != 0
|
||||
}
|
||||
|
||||
// SetWithType sets the TypeMask bit.
|
||||
func (a *Args) SetWithType() {
|
||||
a.Flags = a.Flags | TypeMask
|
||||
}
|
||||
|
||||
// SetLogfmt sets the LogfmtMask bit.
|
||||
func (a *Args) SetLogfmt() {
|
||||
a.Flags = a.Flags | LogfmtMask
|
||||
}
|
||||
|
||||
// SetAsNumber sets the NumberMask bit.
|
||||
func (a *Args) SetAsNumber() {
|
||||
a.Flags = a.Flags | NumberMask
|
||||
}
|
||||
|
||||
// SetAsText sets the TextMask bit.
|
||||
func (a *Args) SetAsText() {
|
||||
a.Flags = a.Flags | TextMask
|
||||
}
|
||||
|
||||
// SetAsQuotedText sets the QuotedTextMask bit.
|
||||
func (a *Args) SetAsQuotedText() {
|
||||
a.Flags = a.Flags | QuotedTextMask
|
||||
}
|
||||
|
||||
// SetAsQuotedASCII sets the QuotedAsciiMask bit.
|
||||
func (a *Args) SetAsQuotedASCII() {
|
||||
a.Flags = a.Flags | QuotedAsciiMask
|
||||
}
|
||||
|
||||
// SetNoMethod sets the NoMethodMask bit.
|
||||
func (a *Args) SetNoMethod() {
|
||||
a.Flags = a.Flags | NoMethodMask
|
||||
}
|
||||
|
||||
// UnsetWithType unsets the TypeMask bit.
|
||||
func (a *Args) UnsetWithType() {
|
||||
a.Flags = a.Flags & ^TypeMask
|
||||
}
|
||||
|
||||
// UnsetLogfmt unsets the LogfmtMask bit.
|
||||
func (a *Args) UnsetLogfmt() {
|
||||
a.Flags = a.Flags & ^LogfmtMask
|
||||
}
|
||||
|
||||
// UnsetAsNumber unsets the NumberMask bit.
|
||||
func (a *Args) UnsetAsNumber() {
|
||||
a.Flags = a.Flags & ^NumberMask
|
||||
}
|
||||
|
||||
// UnsetAsText unsets the TextMask bit.
|
||||
func (a *Args) UnsetAsText() {
|
||||
a.Flags = a.Flags & ^TextMask
|
||||
}
|
||||
|
||||
// UnsetAsQuotedText unsets the QuotedTextMask bit.
|
||||
func (a *Args) UnsetAsQuotedText() {
|
||||
a.Flags = a.Flags & ^QuotedTextMask
|
||||
}
|
||||
|
||||
// UnsetAsQuotedASCII unsets the QuotedAsciiMask bit.
|
||||
func (a *Args) UnsetAsQuotedASCII() {
|
||||
a.Flags = a.Flags & ^QuotedAsciiMask
|
||||
}
|
||||
|
||||
// UnsetNoMethod unsets the NoMethodMask bit.
|
||||
func (a *Args) UnsetNoMethod() {
|
||||
a.Flags = a.Flags & ^NoMethodMask
|
||||
}
|
||||
231
vendor/codeberg.org/gruf/go-kv/v2/format/array.go
generated
vendored
Normal file
231
vendor/codeberg.org/gruf/go-kv/v2/format/array.go
generated
vendored
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
package format
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// iterArrayType returns a FormatFunc capable of iterating
|
||||
// and formatting the given array type currently in typenode{}.
|
||||
// note this will fetch a sub-FormatFunc for the array element
|
||||
// type, and also handle special cases of [n]byte, [n]rune arrays.
|
||||
func (fmt *Formatter) iterArrayType(t typenode) FormatFunc {
|
||||
|
||||
// Array element type.
|
||||
elem := t.rtype.Elem()
|
||||
|
||||
// Get nested elem typenode with appropriate flags.
|
||||
flags := reflect_array_elem_flags(t.flags, elem)
|
||||
et := t.next(elem, flags)
|
||||
|
||||
// Get elem format func.
|
||||
fn := fmt.loadOrGet(et)
|
||||
if fn == nil {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Handle possible sizes.
|
||||
switch t.rtype.Len() {
|
||||
case 0:
|
||||
return emptyArrayType(t)
|
||||
case 1:
|
||||
return iterSingleArrayType(t, fn)
|
||||
default:
|
||||
return iterMultiArrayType(t, fn)
|
||||
}
|
||||
}
|
||||
|
||||
func emptyArrayType(t typenode) FormatFunc {
|
||||
if !t.needs_typestr() {
|
||||
// Simply append empty.
|
||||
return func(s *State) {
|
||||
s.B = append(s.B, "[]"...)
|
||||
}
|
||||
}
|
||||
|
||||
// Array type string with refs.
|
||||
typestr := t.typestr_with_refs()
|
||||
|
||||
// Append empty with type.
|
||||
return func(s *State) {
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, typestr...)
|
||||
s.B = append(s.B, "{}"...)
|
||||
} else {
|
||||
s.B = append(s.B, "[]"...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func iterSingleArrayType(t typenode, fn FormatFunc) FormatFunc {
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
// Wrap 'fn' in braces.
|
||||
s.B = append(s.B, '[')
|
||||
fn(s)
|
||||
s.B = append(s.B, ']')
|
||||
}
|
||||
}
|
||||
|
||||
// Array type string with refs.
|
||||
typestr := t.typestr_with_refs()
|
||||
|
||||
// Wrap in type+braces.
|
||||
return func(s *State) {
|
||||
|
||||
// Open / close braces.
|
||||
var open, close uint8
|
||||
open, close = '[', ']'
|
||||
|
||||
// Include type info.
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, typestr...)
|
||||
open, close = '{', '}'
|
||||
}
|
||||
|
||||
// Wrap 'fn' in braces.
|
||||
s.B = append(s.B, open)
|
||||
fn(s)
|
||||
s.B = append(s.B, close)
|
||||
}
|
||||
}
|
||||
|
||||
func iterMultiArrayType(t typenode, fn FormatFunc) FormatFunc {
|
||||
// Array element in-memory size.
|
||||
esz := t.rtype.Elem().Size()
|
||||
|
||||
// Number of elements.
|
||||
n := t.rtype.Len()
|
||||
|
||||
if !t.needs_typestr() {
|
||||
// Wrap elems in braces.
|
||||
return func(s *State) {
|
||||
ptr := s.P
|
||||
|
||||
// Prepend array brace.
|
||||
s.B = append(s.B, '[')
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
// Format at array index.
|
||||
offset := esz * uintptr(i)
|
||||
s.P = add(ptr, offset)
|
||||
fn(s)
|
||||
|
||||
// Append separator.
|
||||
s.B = append(s.B, ',')
|
||||
}
|
||||
|
||||
// Drop final space.
|
||||
s.B = s.B[:len(s.B)-1]
|
||||
|
||||
// Prepend array brace.
|
||||
s.B = append(s.B, ']')
|
||||
}
|
||||
}
|
||||
|
||||
// Array type string with refs.
|
||||
typestr := t.typestr_with_refs()
|
||||
|
||||
// Wrap in type+braces.
|
||||
return func(s *State) {
|
||||
ptr := s.P
|
||||
|
||||
// Open / close braces.
|
||||
var open, close uint8
|
||||
open, close = '[', ']'
|
||||
|
||||
// Include type info.
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, typestr...)
|
||||
open, close = '{', '}'
|
||||
}
|
||||
|
||||
// Prepend array brace.
|
||||
s.B = append(s.B, open)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
// Format at array index.
|
||||
offset := esz * uintptr(i)
|
||||
s.P = add(ptr, offset)
|
||||
fn(s)
|
||||
|
||||
// Append separator.
|
||||
s.B = append(s.B, ',')
|
||||
}
|
||||
|
||||
// Drop final comma.
|
||||
s.B = s.B[:len(s.B)-1]
|
||||
|
||||
// Prepend array brace.
|
||||
s.B = append(s.B, close)
|
||||
}
|
||||
}
|
||||
|
||||
func wrapByteArray(t typenode, fn FormatFunc) FormatFunc {
|
||||
n := t.rtype.Len()
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
|
||||
var v string
|
||||
p := (*unsafeheader_String)(unsafe.Pointer(&v))
|
||||
p.Len = n
|
||||
p.Data = s.P
|
||||
appendString(s, v)
|
||||
} else {
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
typestr := t.typestr_with_ptrs()
|
||||
return func(s *State) {
|
||||
if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
|
||||
var v string
|
||||
p := (*unsafeheader_String)(unsafe.Pointer(&v))
|
||||
p.Len = n
|
||||
p.Data = s.P
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, "("+typestr+")("...)
|
||||
appendString(s, v)
|
||||
s.B = append(s.B, ")"...)
|
||||
} else {
|
||||
appendString(s, v)
|
||||
}
|
||||
} else {
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func wrapRuneArray(t typenode, fn FormatFunc) FormatFunc {
|
||||
n := t.rtype.Len()
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
|
||||
var v []rune
|
||||
p := (*unsafeheader_Slice)(unsafe.Pointer(&v))
|
||||
p.Cap = n
|
||||
p.Len = n
|
||||
p.Data = s.P
|
||||
appendString(s, string(v))
|
||||
} else {
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
typestr := t.typestr_with_ptrs()
|
||||
return func(s *State) {
|
||||
if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
|
||||
var v []rune
|
||||
p := (*unsafeheader_Slice)(unsafe.Pointer(&v))
|
||||
p.Cap = n
|
||||
p.Len = n
|
||||
p.Data = s.P
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, "("+typestr+")("...)
|
||||
appendString(s, string(v))
|
||||
s.B = append(s.B, ")"...)
|
||||
} else {
|
||||
appendString(s, string(v))
|
||||
}
|
||||
} else {
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
614
vendor/codeberg.org/gruf/go-kv/v2/format/format.go
generated
vendored
Normal file
614
vendor/codeberg.org/gruf/go-kv/v2/format/format.go
generated
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// FormatFunc defines a function capable of formatting
|
||||
// the value contained in State{}.P, based on args in
|
||||
// State{}.A, storing the result in buffer State{}.B.
|
||||
type FormatFunc func(*State)
|
||||
|
||||
// State contains all necessary
|
||||
// arguments, buffer and value
|
||||
// data pointer required for a
|
||||
// FormatFunc operation, in a
|
||||
// reusable structure if wanted.
|
||||
type State struct {
|
||||
|
||||
// A contains args
|
||||
// passed to this
|
||||
// FormatFunc call.
|
||||
A Args
|
||||
|
||||
// B is the buffer
|
||||
// that values will
|
||||
// be formatted into.
|
||||
B []byte
|
||||
|
||||
// P contains a ptr
|
||||
// to the value type
|
||||
// being formatted.
|
||||
P unsafe.Pointer
|
||||
|
||||
// stores pointers to the
|
||||
// recent interface values
|
||||
// we have visited. to prevent
|
||||
// possible recursion of
|
||||
// runtime defined data.
|
||||
ifaces ptr_ring
|
||||
}
|
||||
|
||||
// ptr_ring size.
|
||||
const ringsz = 16
|
||||
|
||||
// ptr_ring is a ring buffer of pointers,
|
||||
// purposely stored as uintptrs as all we
|
||||
// need them for is value comparisons and
|
||||
// we don't want to hold-up the GC.
|
||||
type ptr_ring struct {
|
||||
p [ringsz]uintptr
|
||||
n uint8
|
||||
}
|
||||
|
||||
func (p *ptr_ring) contains(ptr unsafe.Pointer) bool {
|
||||
for _, eptr := range p.p {
|
||||
if uintptr(ptr) == eptr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *ptr_ring) set(ptr unsafe.Pointer) {
|
||||
p.p[p.n%ringsz] = uintptr(ptr)
|
||||
p.n++
|
||||
}
|
||||
|
||||
func (p *ptr_ring) clear() {
|
||||
p.p = [ringsz]uintptr{}
|
||||
p.n = 0
|
||||
}
|
||||
|
||||
// Formatter provides access to value formatting
|
||||
// provided by this library. It encompasses a set
|
||||
// of configurable default arguments for when none
|
||||
// are set, and an internal concurrency-safe cache
|
||||
// of FormatFuncs to passed value type.
|
||||
type Formatter struct {
|
||||
|
||||
// Defaults defines the default
|
||||
// set of arguments to use when
|
||||
// none are supplied to calls to
|
||||
// Append() and AppendState().
|
||||
Defaults Args
|
||||
|
||||
// internal
|
||||
// format func
|
||||
// cache map.
|
||||
fns sync.Map
|
||||
}
|
||||
|
||||
// LoadFor returns a FormatFunc for the given value type.
|
||||
func (fmt *Formatter) LoadFor(value any) FormatFunc {
|
||||
rtype := reflect.TypeOf(value)
|
||||
flags := reflect_iface_elem_flags(rtype)
|
||||
t := new_typenode(rtype, flags)
|
||||
return fmt.loadOrStore(t)
|
||||
}
|
||||
|
||||
// Append calls AppendState() with a newly allocated State{}, returning byte buffer.
|
||||
func (fmt *Formatter) Append(buf []byte, value any, args Args) []byte {
|
||||
s := new(State)
|
||||
s.A = args
|
||||
s.B = buf
|
||||
fmt.AppendState(s, value)
|
||||
return s.B
|
||||
}
|
||||
|
||||
// AppendState will format the given value into the given
|
||||
// State{}'s byte buffer, using currently-set arguments.
|
||||
func (fmt *Formatter) AppendState(s *State, value any) {
|
||||
switch {
|
||||
case s.A != zeroArgs:
|
||||
break
|
||||
case fmt.Defaults != zeroArgs:
|
||||
// use fmt defaults.
|
||||
s.A = fmt.Defaults
|
||||
default:
|
||||
// global defaults.
|
||||
s.A = defaultArgs
|
||||
}
|
||||
s.P = unpack_eface(value)
|
||||
s.ifaces.clear()
|
||||
s.ifaces.set(s.P)
|
||||
fmt.LoadFor(value)(s)
|
||||
}
|
||||
|
||||
func (fmt *Formatter) loadOrGet(t typenode) FormatFunc {
|
||||
// Look for existing stored
|
||||
// func under this type key.
|
||||
v, _ := fmt.fns.Load(t.key())
|
||||
fn, _ := v.(FormatFunc)
|
||||
|
||||
if fn == nil {
|
||||
// Load format func
|
||||
// for typecontext.
|
||||
fn = fmt.get(t)
|
||||
if fn == nil {
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func (fmt *Formatter) loadOrStore(t typenode) FormatFunc {
|
||||
// Get cache key.
|
||||
key := t.key()
|
||||
|
||||
// Look for existing stored
|
||||
// func under this type key.
|
||||
v, _ := fmt.fns.Load(key)
|
||||
fn, _ := v.(FormatFunc)
|
||||
|
||||
if fn == nil {
|
||||
// Load format func
|
||||
// for typecontext.
|
||||
fn = fmt.get(t)
|
||||
if fn == nil {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Store in map under type.
|
||||
fmt.fns.Store(key, fn)
|
||||
}
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
var (
|
||||
// reflectTypeType is the reflected type of the reflect type,
|
||||
// used in fmt.get() to prevent iter of internal ABI structs.
|
||||
reflectTypeType = reflect.TypeOf(reflect.TypeOf(0))
|
||||
|
||||
// stringable int types.
|
||||
byteType = typeof[byte]()
|
||||
runeType = typeof[rune]()
|
||||
|
||||
// stringable slice types.
|
||||
bytesType = typeof[[]byte]()
|
||||
runesType = typeof[[]rune]()
|
||||
)
|
||||
|
||||
func (fmt *Formatter) get(t typenode) (fn FormatFunc) {
|
||||
if t.rtype == nil {
|
||||
// catch nil type.
|
||||
return appendNil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
debug.PrintStack()
|
||||
panic(r) // keep panicking
|
||||
} else if fn == nil {
|
||||
panic("nil func")
|
||||
}
|
||||
|
||||
// Don't allow method functions for map keys,
|
||||
// to prevent situation of the method receiver
|
||||
// attempting to modify stored map key itself.
|
||||
if t.flags&flagKeyType != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if type supports known method receiver.
|
||||
if methodFn := getMethodType(t); methodFn != nil {
|
||||
|
||||
// Keep ptr to existing
|
||||
// non-method format fn.
|
||||
noMethodFn := fn
|
||||
|
||||
// Wrap 'fn' to switch
|
||||
// between method / none.
|
||||
fn = func(s *State) {
|
||||
if s.A.NoMethod() {
|
||||
noMethodFn(s)
|
||||
} else {
|
||||
methodFn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if t.rtype == reflectTypeType {
|
||||
// DO NOT iterate down internal ABI
|
||||
// types, some are in non-GC memory.
|
||||
return getPointerType(t)
|
||||
}
|
||||
|
||||
if !t.visit() {
|
||||
// On type recursion simply
|
||||
// format as raw pointer.
|
||||
return getPointerType(t)
|
||||
}
|
||||
|
||||
// Get func for type kind.
|
||||
switch t.rtype.Kind() {
|
||||
case reflect.Interface:
|
||||
return fmt.getInterfaceType(t)
|
||||
case reflect.String:
|
||||
return getStringType(t)
|
||||
case reflect.Bool:
|
||||
return getBoolType(t)
|
||||
case reflect.Int,
|
||||
reflect.Int8,
|
||||
reflect.Int16,
|
||||
reflect.Int32,
|
||||
reflect.Int64:
|
||||
return getIntType(t)
|
||||
case reflect.Uint,
|
||||
reflect.Uint8,
|
||||
reflect.Uint16,
|
||||
reflect.Uint32,
|
||||
reflect.Uint64:
|
||||
return getUintType(t)
|
||||
case reflect.Float32,
|
||||
reflect.Float64:
|
||||
return getFloatType(t)
|
||||
case reflect.Complex64,
|
||||
reflect.Complex128:
|
||||
return getComplexType(t)
|
||||
case reflect.Pointer:
|
||||
return fmt.derefPointerType(t)
|
||||
case reflect.Array:
|
||||
elem := t.rtype.Elem()
|
||||
switch fn := fmt.iterArrayType(t); {
|
||||
case elem.AssignableTo(byteType):
|
||||
return wrapByteArray(t, fn)
|
||||
case elem.AssignableTo(runeType):
|
||||
return wrapRuneArray(t, fn)
|
||||
default:
|
||||
return fn
|
||||
}
|
||||
case reflect.Slice:
|
||||
switch fn := fmt.iterSliceType(t); {
|
||||
case t.rtype.AssignableTo(bytesType):
|
||||
return wrapByteSlice(t, fn)
|
||||
case t.rtype.AssignableTo(runesType):
|
||||
return wrapRuneSlice(t, fn)
|
||||
default:
|
||||
return fn
|
||||
}
|
||||
case reflect.Struct:
|
||||
return fmt.iterStructType(t)
|
||||
case reflect.Map:
|
||||
return fmt.iterMapType(t)
|
||||
default:
|
||||
return getPointerType(t)
|
||||
}
|
||||
}
|
||||
|
||||
func (fmt *Formatter) getInterfaceType(t typenode) FormatFunc {
|
||||
if t.rtype.NumMethod() == 0 {
|
||||
return func(s *State) {
|
||||
eface := *(*any)(s.P)
|
||||
s.P = unpack_eface(eface)
|
||||
rtype := reflect.TypeOf(eface)
|
||||
if rtype == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
if s.ifaces.contains(s.P) {
|
||||
getPointerType(t)(s)
|
||||
return
|
||||
}
|
||||
s.ifaces.set(s.P)
|
||||
flags := reflect_iface_elem_flags(rtype)
|
||||
t := new_typenode(rtype, flags)
|
||||
fmt.loadOrStore(t)(s)
|
||||
}
|
||||
} else {
|
||||
return func(s *State) {
|
||||
iface := *(*interface{ M() })(s.P)
|
||||
s.P = unpack_eface(iface)
|
||||
rtype := reflect.TypeOf(iface)
|
||||
if rtype == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
if s.ifaces.contains(s.P) {
|
||||
getPointerType(t)(s)
|
||||
return
|
||||
}
|
||||
s.ifaces.set(s.P)
|
||||
flags := reflect_iface_elem_flags(rtype)
|
||||
t := new_typenode(rtype, flags)
|
||||
fmt.loadOrStore(t)(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getStringType(t typenode) FormatFunc {
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendString(s, *(*string)(s.P))
|
||||
})
|
||||
}
|
||||
|
||||
func getBoolType(t typenode) FormatFunc {
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
s.B = strconv.AppendBool(s.B, *(*bool)(s.P))
|
||||
})
|
||||
}
|
||||
|
||||
func getIntType(t typenode) FormatFunc {
|
||||
switch t.rtype.Bits() {
|
||||
case 8:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendInt(s, int64(*(*int8)(s.P)))
|
||||
})
|
||||
case 16:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendInt(s, int64(*(*int16)(s.P)))
|
||||
})
|
||||
case 32:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
switch {
|
||||
case s.A.AsNumber():
|
||||
// fallthrough
|
||||
case s.A.AsQuotedText():
|
||||
s.B = strconv.AppendQuoteRune(s.B, *(*rune)(s.P))
|
||||
return
|
||||
case s.A.AsQuotedASCII():
|
||||
s.B = strconv.AppendQuoteRuneToASCII(s.B, *(*rune)(s.P))
|
||||
return
|
||||
case s.A.AsText():
|
||||
s.B = AppendEscapeRune(s.B, *(*rune)(s.P))
|
||||
return
|
||||
}
|
||||
appendInt(s, int64(*(*int32)(s.P)))
|
||||
})
|
||||
case 64:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendInt(s, int64(*(*int64)(s.P)))
|
||||
})
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func getUintType(t typenode) FormatFunc {
|
||||
switch t.rtype.Bits() {
|
||||
case 8:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
switch {
|
||||
case s.A.AsNumber():
|
||||
// fallthrough
|
||||
case s.A.AsQuotedText() || s.A.AsQuotedASCII():
|
||||
s.B = AppendQuoteByte(s.B, *(*byte)(s.P))
|
||||
return
|
||||
case s.A.AsText():
|
||||
s.B = AppendEscapeByte(s.B, *(*byte)(s.P))
|
||||
return
|
||||
}
|
||||
appendUint(s, uint64(*(*uint8)(s.P)))
|
||||
})
|
||||
case 16:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendUint(s, uint64(*(*uint16)(s.P)))
|
||||
})
|
||||
case 32:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendUint(s, uint64(*(*uint32)(s.P)))
|
||||
})
|
||||
case 64:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendUint(s, uint64(*(*uint64)(s.P)))
|
||||
})
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func getFloatType(t typenode) FormatFunc {
|
||||
switch t.rtype.Bits() {
|
||||
case 32:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendFloat(s, float64(*(*float32)(s.P)), 32)
|
||||
})
|
||||
case 64:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendFloat(s, float64(*(*float64)(s.P)), 64)
|
||||
})
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func getComplexType(t typenode) FormatFunc {
|
||||
switch t.rtype.Bits() {
|
||||
case 64:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
v := *(*complex64)(s.P)
|
||||
r, i := real(v), imag(v)
|
||||
appendComplex(s, float64(r), float64(i), 32)
|
||||
})
|
||||
case 128:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
v := *(*complex128)(s.P)
|
||||
r, i := real(v), imag(v)
|
||||
appendComplex(s, float64(r), float64(i), 64)
|
||||
})
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func getPointerType(t typenode) FormatFunc {
|
||||
switch t.indirect() {
|
||||
case true:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
s.P = *(*unsafe.Pointer)(s.P)
|
||||
appendPointer(s, s.P)
|
||||
})
|
||||
case false:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
appendPointer(s, s.P)
|
||||
})
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func with_typestr_ptrs(t typenode, fn FormatFunc) FormatFunc {
|
||||
if fn == nil {
|
||||
panic("nil func")
|
||||
}
|
||||
if !t.needs_typestr() {
|
||||
return fn
|
||||
}
|
||||
typestr := t.typestr_with_ptrs()
|
||||
return func(s *State) {
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, "("+typestr+")("...)
|
||||
fn(s)
|
||||
s.B = append(s.B, ")"...)
|
||||
} else {
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func appendString(s *State, v string) {
|
||||
switch {
|
||||
case s.A.Logfmt() || s.A.WithType():
|
||||
if len(v) > SingleTermLine || !IsSafeASCII(v) {
|
||||
// Requires quoting AND escaping
|
||||
s.B = strconv.AppendQuote(s.B, v)
|
||||
} else if ContainsDoubleQuote(v) {
|
||||
// Contains double quotes, needs escaping
|
||||
s.B = append(s.B, '"')
|
||||
s.B = AppendEscape(s.B, v)
|
||||
s.B = append(s.B, '"')
|
||||
} else if s.A.WithType() ||
|
||||
len(v) == 0 || ContainsSpaceOrTab(v) {
|
||||
// Contains space / empty, needs quotes
|
||||
s.B = append(s.B, '"')
|
||||
s.B = append(s.B, v...)
|
||||
s.B = append(s.B, '"')
|
||||
} else {
|
||||
// All else write as-is
|
||||
s.B = append(s.B, v...)
|
||||
}
|
||||
case s.A.AsQuotedText():
|
||||
s.B = strconv.AppendQuote(s.B, v)
|
||||
case s.A.AsQuotedASCII():
|
||||
s.B = strconv.AppendQuoteToASCII(s.B, v)
|
||||
default:
|
||||
s.B = append(s.B, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func appendInt(s *State, v int64) {
|
||||
args := s.A.Int
|
||||
if args == zeroArgs.Int {
|
||||
args = defaultArgs.Int
|
||||
}
|
||||
if args.Pad > 0 {
|
||||
const zeros = `00000000000000000000`
|
||||
if args.Pad > len(zeros) {
|
||||
panic("cannot pad > " + zeros)
|
||||
}
|
||||
if v == 0 {
|
||||
s.B = append(s.B, zeros[:args.Pad]...)
|
||||
return
|
||||
}
|
||||
abs := abs64(v)
|
||||
chars := int(v / int64(args.Base))
|
||||
if v%int64(args.Base) != 0 {
|
||||
chars++
|
||||
}
|
||||
if abs != v {
|
||||
s.B = append(s.B, '-')
|
||||
v = abs
|
||||
}
|
||||
if n := args.Pad - chars; n > 0 {
|
||||
s.B = append(s.B, zeros[:n]...)
|
||||
}
|
||||
}
|
||||
s.B = strconv.AppendInt(s.B, v, args.Base)
|
||||
}
|
||||
|
||||
func appendUint(s *State, v uint64) {
|
||||
args := s.A.Int
|
||||
if args == zeroArgs.Int {
|
||||
args = defaultArgs.Int
|
||||
}
|
||||
if args.Pad > 0 {
|
||||
const zeros = `00000000000000000000`
|
||||
if args.Pad > len(zeros) {
|
||||
panic("cannot pad > " + zeros)
|
||||
}
|
||||
if v == 0 {
|
||||
s.B = append(s.B, zeros[:args.Pad]...)
|
||||
return
|
||||
}
|
||||
chars := int(v / uint64(args.Base))
|
||||
if v%uint64(args.Base) != 0 {
|
||||
chars++
|
||||
}
|
||||
if n := args.Pad - chars; n > 0 {
|
||||
s.B = append(s.B, zeros[:n]...)
|
||||
}
|
||||
}
|
||||
s.B = strconv.AppendUint(s.B, v, args.Base)
|
||||
}
|
||||
|
||||
func appendFloat(s *State, v float64, bits int) {
|
||||
args := s.A.Float
|
||||
if args == zeroArgs.Float {
|
||||
args = defaultArgs.Float
|
||||
}
|
||||
s.B = strconv.AppendFloat(s.B, float64(v), args.Fmt, args.Prec, bits)
|
||||
}
|
||||
|
||||
func appendComplex(s *State, r, i float64, bits int) {
|
||||
args := s.A.Complex
|
||||
if args == zeroArgs.Complex {
|
||||
args = defaultArgs.Complex
|
||||
}
|
||||
s.B = strconv.AppendFloat(s.B, float64(r), args.Real.Fmt, args.Real.Prec, bits)
|
||||
s.B = append(s.B, '+')
|
||||
s.B = strconv.AppendFloat(s.B, float64(i), args.Imag.Fmt, args.Imag.Prec, bits)
|
||||
s.B = append(s.B, 'i')
|
||||
}
|
||||
|
||||
func appendPointer(s *State, v unsafe.Pointer) {
|
||||
if v != nil {
|
||||
s.B = append(s.B, "0x"...)
|
||||
s.B = strconv.AppendUint(s.B, uint64(uintptr(v)), 16)
|
||||
} else {
|
||||
appendNil(s)
|
||||
}
|
||||
}
|
||||
|
||||
func appendNilType(s *State, typestr string) {
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, "("+typestr+")(<nil>)"...)
|
||||
} else {
|
||||
s.B = append(s.B, "<nil>"...)
|
||||
}
|
||||
}
|
||||
|
||||
func appendNil(s *State) {
|
||||
s.B = append(s.B, "<nil>"...)
|
||||
}
|
||||
|
||||
func abs64(i int64) int64 {
|
||||
u := uint64(i >> 63)
|
||||
return (i ^ int64(u)) + int64(u&1)
|
||||
}
|
||||
161
vendor/codeberg.org/gruf/go-kv/v2/format/formatting.go
generated
vendored
Normal file
161
vendor/codeberg.org/gruf/go-kv/v2/format/formatting.go
generated
vendored
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
// SingleTermLine: beyond a certain length of string, all of the
|
||||
// extra checks to handle quoting/not-quoting add a significant
|
||||
// amount of extra processing time. Quoting in this manner only really
|
||||
// effects readability on a single line, so a max string length that
|
||||
// encompasses the maximum number of columns on *most* terminals was
|
||||
// selected. This was chosen using the metric that 1080p is one of the
|
||||
// most common display resolutions, and that a relatively small font size
|
||||
// of 7 requires 223 columns. So 256 should be >= $COLUMNS (fullscreen)
|
||||
// in 99% of usecases (these figures all pulled out of my ass).
|
||||
SingleTermLine = 256
|
||||
)
|
||||
|
||||
// IsSafeASCII checks whether string is printable (i.e. non-control char) ASCII text.
|
||||
func IsSafeASCII(str string) bool {
|
||||
for _, r := range str {
|
||||
if (r < ' ' && r != '\t') ||
|
||||
r >= 0x7f {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ContainsSpaceOrTab checks if "s" contains space or tabs. EXPECTS ASCII.
|
||||
func ContainsSpaceOrTab(s string) bool {
|
||||
if i := strings.IndexByte(s, ' '); i >= 0 {
|
||||
return true // note using indexbyte as it is ASM.
|
||||
} else if i := strings.IndexByte(s, '\t'); i >= 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsDoubleQuote checks if "s" contains a double quote. EXPECTS ASCII.
|
||||
func ContainsDoubleQuote(s string) bool {
|
||||
return (strings.IndexByte(s, '"') >= 0)
|
||||
}
|
||||
|
||||
// AppendEscape will append 's' to 'buf' and escape any double quotes. EXPECTS ASCII.
|
||||
func AppendEscape(buf []byte, str string) []byte {
|
||||
for i := range str {
|
||||
switch str[i] {
|
||||
case '\\':
|
||||
// Append delimited '\'
|
||||
buf = append(buf, '\\', '\\')
|
||||
|
||||
case '"':
|
||||
// Append delimited '"'
|
||||
buf = append(buf, '\\', '"')
|
||||
default:
|
||||
// Append char as-is
|
||||
buf = append(buf, str[i])
|
||||
}
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
const hex = "0123456789abcdef"
|
||||
|
||||
// AppendEscapeByte ...
|
||||
func AppendEscapeByte(buf []byte, c byte) []byte {
|
||||
switch c {
|
||||
case '\a':
|
||||
return append(buf, `\a`...)
|
||||
case '\b':
|
||||
return append(buf, `\b`...)
|
||||
case '\f':
|
||||
return append(buf, `\f`...)
|
||||
case '\n':
|
||||
return append(buf, `\n`...)
|
||||
case '\r':
|
||||
return append(buf, `\r`...)
|
||||
case '\t':
|
||||
return append(buf, `\t`...)
|
||||
case '\v':
|
||||
return append(buf, `\v`...)
|
||||
case '\\':
|
||||
return append(buf, `\\`...)
|
||||
default:
|
||||
if c < ' ' {
|
||||
return append(buf, '\\', 'x', hex[c>>4], hex[c&0xF])
|
||||
}
|
||||
return append(buf, c)
|
||||
}
|
||||
}
|
||||
|
||||
// AppendQuoteByte ...
|
||||
func AppendQuoteByte(buf []byte, c byte) []byte {
|
||||
if c == '\'' {
|
||||
return append(buf, `'\''`...)
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
buf = AppendEscapeByte(buf, c)
|
||||
buf = append(buf, '\'')
|
||||
return buf
|
||||
}
|
||||
|
||||
// AppendEscapeRune ...
|
||||
func AppendEscapeRune(buf []byte, r rune) []byte {
|
||||
if unicode.IsPrint(r) {
|
||||
return utf8.AppendRune(buf, r)
|
||||
}
|
||||
switch r {
|
||||
case '\a':
|
||||
return append(buf, `\a`...)
|
||||
case '\b':
|
||||
return append(buf, `\b`...)
|
||||
case '\f':
|
||||
return append(buf, `\f`...)
|
||||
case '\n':
|
||||
return append(buf, `\n`...)
|
||||
case '\r':
|
||||
return append(buf, `\r`...)
|
||||
case '\t':
|
||||
return append(buf, `\t`...)
|
||||
case '\v':
|
||||
return append(buf, `\v`...)
|
||||
case '\\':
|
||||
return append(buf, `\\`...)
|
||||
default:
|
||||
switch {
|
||||
case r < ' ' || r == 0x7f:
|
||||
buf = append(buf, `\x`...)
|
||||
buf = append(buf, hex[byte(r)>>4])
|
||||
buf = append(buf, hex[byte(r)&0xF])
|
||||
case !utf8.ValidRune(r):
|
||||
r = 0xFFFD
|
||||
fallthrough
|
||||
case r < 0x10000:
|
||||
buf = append(buf, `\u`...)
|
||||
buf = append(buf,
|
||||
hex[r>>uint(12)&0xF],
|
||||
hex[r>>uint(8)&0xF],
|
||||
hex[r>>uint(4)&0xF],
|
||||
hex[r>>uint(0)&0xF],
|
||||
)
|
||||
default:
|
||||
buf = append(buf, `\U`...)
|
||||
buf = append(buf,
|
||||
hex[r>>uint(28)&0xF],
|
||||
hex[r>>uint(24)&0xF],
|
||||
hex[r>>uint(20)&0xF],
|
||||
hex[r>>uint(16)&0xF],
|
||||
hex[r>>uint(12)&0xF],
|
||||
hex[r>>uint(8)&0xF],
|
||||
hex[r>>uint(4)&0xF],
|
||||
hex[r>>uint(0)&0xF],
|
||||
)
|
||||
}
|
||||
}
|
||||
return buf
|
||||
}
|
||||
130
vendor/codeberg.org/gruf/go-kv/v2/format/map.go
generated
vendored
Normal file
130
vendor/codeberg.org/gruf/go-kv/v2/format/map.go
generated
vendored
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
package format
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// iterMapType returns a FormatFunc capable of iterating
|
||||
// and formatting the given map type currently in typenode{}.
|
||||
// note this will fetch sub-FormatFuncs for key / value types.
|
||||
func (fmt *Formatter) iterMapType(t typenode) FormatFunc {
|
||||
|
||||
// Key / value types.
|
||||
key := t.rtype.Key()
|
||||
elem := t.rtype.Elem()
|
||||
|
||||
// Get nested k / v typenodes with appropriate flags.
|
||||
flagsKey := reflect_map_key_flags(key)
|
||||
flagsVal := reflect_map_elem_flags(elem)
|
||||
kt := t.next(t.rtype.Key(), flagsKey)
|
||||
vt := t.next(t.rtype.Elem(), flagsVal)
|
||||
|
||||
// Get key format func.
|
||||
kfn := fmt.loadOrGet(kt)
|
||||
if kfn == nil {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Get value format func.
|
||||
vfn := fmt.loadOrGet(vt)
|
||||
if vfn == nil {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Final map type.
|
||||
rtype := t.rtype
|
||||
flags := t.flags
|
||||
|
||||
// Map type string with ptrs / refs.
|
||||
typestrPtrs := t.typestr_with_ptrs()
|
||||
typestrRefs := t.typestr_with_refs()
|
||||
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
if s.P == nil || *(*unsafe.Pointer)(s.P) == nil {
|
||||
// Append nil.
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
|
||||
// Build reflect value, and then a map iter.
|
||||
v := build_reflect_value(rtype, s.P, flags)
|
||||
i := map_iter(v)
|
||||
|
||||
// Prepend object brace.
|
||||
s.B = append(s.B, '{')
|
||||
|
||||
// Before len.
|
||||
l := len(s.B)
|
||||
|
||||
for i.Next() {
|
||||
// Pass to key fn.
|
||||
s.P = map_key(i)
|
||||
kfn(s)
|
||||
|
||||
// Add key seperator.
|
||||
s.B = append(s.B, '=')
|
||||
|
||||
// Pass to elem fn.
|
||||
s.P = map_elem(i)
|
||||
vfn(s)
|
||||
|
||||
// Add comma pair seperator.
|
||||
s.B = append(s.B, ',', ' ')
|
||||
}
|
||||
|
||||
if len(s.B) != l {
|
||||
// Drop final ", ".
|
||||
s.B = s.B[:len(s.B)-2]
|
||||
}
|
||||
|
||||
// Append object brace.
|
||||
s.B = append(s.B, '}')
|
||||
}
|
||||
}
|
||||
|
||||
return func(s *State) {
|
||||
if s.P == nil || *(*unsafe.Pointer)(s.P) == nil {
|
||||
// Append nil value with type.
|
||||
appendNilType(s, typestrPtrs)
|
||||
return
|
||||
}
|
||||
|
||||
// Build reflect value, and then a map iter.
|
||||
v := build_reflect_value(rtype, s.P, flags)
|
||||
i := map_iter(v)
|
||||
|
||||
// Include type info.
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, typestrRefs...)
|
||||
}
|
||||
|
||||
// Prepend object brace.
|
||||
s.B = append(s.B, '{')
|
||||
|
||||
// Before len.
|
||||
l := len(s.B)
|
||||
|
||||
for i.Next() {
|
||||
// Pass to key fn.
|
||||
s.P = map_key(i)
|
||||
kfn(s)
|
||||
|
||||
// Add key seperator.
|
||||
s.B = append(s.B, '=')
|
||||
|
||||
// Pass to elem fn.
|
||||
s.P = map_elem(i)
|
||||
vfn(s)
|
||||
|
||||
// Add comma pair seperator.
|
||||
s.B = append(s.B, ',', ' ')
|
||||
}
|
||||
|
||||
if len(s.B) != l {
|
||||
// Drop final ", ".
|
||||
s.B = s.B[:len(s.B)-2]
|
||||
}
|
||||
|
||||
// Append object brace.
|
||||
s.B = append(s.B, '}')
|
||||
}
|
||||
}
|
||||
153
vendor/codeberg.org/gruf/go-kv/v2/format/methods.go
generated
vendored
Normal file
153
vendor/codeberg.org/gruf/go-kv/v2/format/methods.go
generated
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Stringer interface{ String() string }
|
||||
|
||||
var (
|
||||
// stringer type for implement checks.
|
||||
stringerType = typeof[Stringer]()
|
||||
|
||||
// error type for implement checks.
|
||||
errorType = typeof[error]()
|
||||
)
|
||||
|
||||
// getMethodType returns a *possible* FormatFunc to handle case
|
||||
// of a type that implements any known interface{} types, else nil.
|
||||
func getMethodType(t typenode) FormatFunc {
|
||||
switch {
|
||||
case t.rtype.Implements(stringerType):
|
||||
switch t.rtype.Kind() {
|
||||
case reflect.Interface:
|
||||
return getInterfaceStringerType(t)
|
||||
default:
|
||||
return getConcreteStringerType(t)
|
||||
}
|
||||
case t.rtype.Implements(errorType):
|
||||
switch t.rtype.Kind() {
|
||||
case reflect.Interface:
|
||||
return getInterfaceErrorType(t)
|
||||
default:
|
||||
return getConcreteErrorType(t)
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getInterfaceStringerType returns a FormatFunc to handle case of an interface{}
|
||||
// type that implements Stringer{}, i.e. Stringer{} itself and any superset of.
|
||||
func getInterfaceStringerType(t typenode) FormatFunc {
|
||||
switch t.indirect() && !t.iface_indir() {
|
||||
case true:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
s.P = *(*unsafe.Pointer)(s.P)
|
||||
if s.P == nil || (*reflect_nonEmptyInterface)(s.P).word == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
v := *(*Stringer)(s.P)
|
||||
appendString(s, v.String())
|
||||
})
|
||||
case false:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
if s.P == nil || (*reflect_nonEmptyInterface)(s.P).word == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
v := *(*Stringer)(s.P)
|
||||
appendString(s, v.String())
|
||||
})
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// getConcreteStringerType returns a FormatFunc to handle case of concrete
|
||||
// (i.e. non-interface{}) type that has a Stringer{} method receiver.
|
||||
func getConcreteStringerType(t typenode) FormatFunc {
|
||||
itab := get_iface_ITab[Stringer](t.rtype)
|
||||
switch t.indirect() && !t.iface_indir() {
|
||||
case true:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
s.P = *(*unsafe.Pointer)(s.P)
|
||||
if s.P == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
v := *(*Stringer)(pack_iface(itab, s.P))
|
||||
appendString(s, v.String())
|
||||
})
|
||||
case false:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
if s.P == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
v := *(*Stringer)(pack_iface(itab, s.P))
|
||||
appendString(s, v.String())
|
||||
})
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// getInterfaceErrorType returns a FormatFunc to handle case of an interface{}
|
||||
// type that implements error{}, i.e. error{} itself and any superset of.
|
||||
func getInterfaceErrorType(t typenode) FormatFunc {
|
||||
switch t.indirect() && !t.iface_indir() {
|
||||
case true:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
s.P = *(*unsafe.Pointer)(s.P)
|
||||
if s.P == nil || (*reflect_nonEmptyInterface)(s.P).word == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
v := *(*error)(s.P)
|
||||
appendString(s, v.Error())
|
||||
})
|
||||
case false:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
if s.P == nil || (*reflect_nonEmptyInterface)(s.P).word == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
v := *(*error)(s.P)
|
||||
appendString(s, v.Error())
|
||||
})
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// getConcreteErrorType returns a FormatFunc to handle case of concrete
|
||||
// (i.e. non-interface{}) type that has an error{} method receiver.
|
||||
func getConcreteErrorType(t typenode) FormatFunc {
|
||||
itab := get_iface_ITab[error](t.rtype)
|
||||
switch t.indirect() && !t.iface_indir() {
|
||||
case true:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
s.P = *(*unsafe.Pointer)(s.P)
|
||||
if s.P == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
v := *(*error)(pack_iface(itab, s.P))
|
||||
appendString(s, v.Error())
|
||||
})
|
||||
case false:
|
||||
return with_typestr_ptrs(t, func(s *State) {
|
||||
if s.P == nil {
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
v := *(*error)(pack_iface(itab, s.P))
|
||||
appendString(s, v.Error())
|
||||
})
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
128
vendor/codeberg.org/gruf/go-kv/v2/format/pointer.go
generated
vendored
Normal file
128
vendor/codeberg.org/gruf/go-kv/v2/format/pointer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// derefPointerType returns a FormatFunc capable of dereferencing
|
||||
// and formatting the given pointer type currently in typenode{}.
|
||||
// note this will fetch a sub-FormatFunc for resulting value type.
|
||||
func (fmt *Formatter) derefPointerType(t typenode) FormatFunc {
|
||||
var n int
|
||||
rtype := t.rtype
|
||||
flags := t.flags
|
||||
|
||||
// Iteratively dereference pointer types.
|
||||
for rtype.Kind() == reflect.Pointer {
|
||||
|
||||
// If this is actual indirect
|
||||
// memory, increase dereferences.
|
||||
if flags&reflect_flagIndir != 0 {
|
||||
n++
|
||||
}
|
||||
|
||||
// Get next elem type.
|
||||
rtype = rtype.Elem()
|
||||
|
||||
// Get next set of dereferenced elem type flags.
|
||||
flags = reflect_pointer_elem_flags(flags, rtype)
|
||||
}
|
||||
|
||||
// Wrap value as typenode.
|
||||
vt := t.next(rtype, flags)
|
||||
|
||||
// Get value format func.
|
||||
fn := fmt.loadOrGet(vt)
|
||||
if fn == nil {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
if !t.needs_typestr() {
|
||||
if n <= 0 {
|
||||
// No derefs are needed.
|
||||
return func(s *State) {
|
||||
if s.P == nil {
|
||||
// Final check.
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
|
||||
// Format
|
||||
// final
|
||||
// value.
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
|
||||
return func(s *State) {
|
||||
// Deref n number times.
|
||||
for i := n; i > 0; i-- {
|
||||
|
||||
if s.P == nil {
|
||||
// Nil check.
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
|
||||
// Further deref pointer value.
|
||||
s.P = *(*unsafe.Pointer)(s.P)
|
||||
}
|
||||
|
||||
if s.P == nil {
|
||||
// Final check.
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
|
||||
// Format
|
||||
// final
|
||||
// value.
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
|
||||
// Final type string with ptrs.
|
||||
typestr := t.typestr_with_ptrs()
|
||||
|
||||
if n <= 0 {
|
||||
// No derefs are needed.
|
||||
return func(s *State) {
|
||||
if s.P == nil {
|
||||
// Final nil value check.
|
||||
appendNilType(s, typestr)
|
||||
return
|
||||
}
|
||||
|
||||
// Format
|
||||
// final
|
||||
// value.
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
|
||||
return func(s *State) {
|
||||
// Deref n number times.
|
||||
for i := n; i > 0; i-- {
|
||||
if s.P == nil {
|
||||
// Check for nil value.
|
||||
appendNilType(s, typestr)
|
||||
return
|
||||
}
|
||||
|
||||
// Further deref pointer value.
|
||||
s.P = *(*unsafe.Pointer)(s.P)
|
||||
}
|
||||
|
||||
if s.P == nil {
|
||||
// Final nil value check.
|
||||
appendNilType(s, typestr)
|
||||
return
|
||||
}
|
||||
|
||||
// Format
|
||||
// final
|
||||
// value.
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
158
vendor/codeberg.org/gruf/go-kv/v2/format/slice.go
generated
vendored
Normal file
158
vendor/codeberg.org/gruf/go-kv/v2/format/slice.go
generated
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
package format
|
||||
|
||||
// iterSliceType returns a FormatFunc capable of iterating
|
||||
// and formatting the given slice type currently in typenode{}.
|
||||
// note this will fetch a sub-FormatFunc for the slice element
|
||||
// type, and also handle special cases of []byte, []rune slices.
|
||||
func (fmt *Formatter) iterSliceType(t typenode) FormatFunc {
|
||||
|
||||
// Get nested element type.
|
||||
elem := t.rtype.Elem()
|
||||
esz := elem.Size()
|
||||
|
||||
// Get nested elem typenode with flags.
|
||||
flags := reflect_slice_elem_flags(elem)
|
||||
et := t.next(elem, flags)
|
||||
|
||||
// Get elem format func.
|
||||
fn := fmt.loadOrGet(et)
|
||||
if fn == nil {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
ptr := s.P
|
||||
|
||||
// Get data as unsafe slice header.
|
||||
hdr := (*unsafeheader_Slice)(ptr)
|
||||
if hdr == nil || hdr.Data == nil {
|
||||
|
||||
// Append nil.
|
||||
appendNil(s)
|
||||
return
|
||||
}
|
||||
|
||||
// Prepend array brace.
|
||||
s.B = append(s.B, '[')
|
||||
|
||||
if hdr.Len > 0 {
|
||||
for i := 0; i < hdr.Len; i++ {
|
||||
// Format at array index.
|
||||
offset := esz * uintptr(i)
|
||||
s.P = add(hdr.Data, offset)
|
||||
fn(s)
|
||||
|
||||
// Append separator.
|
||||
s.B = append(s.B, ',')
|
||||
}
|
||||
|
||||
// Drop final comma.
|
||||
s.B = s.B[:len(s.B)-1]
|
||||
}
|
||||
|
||||
// Append array brace.
|
||||
s.B = append(s.B, ']')
|
||||
}
|
||||
}
|
||||
|
||||
// Slice type string with ptrs / refs.
|
||||
typestrPtrs := t.typestr_with_ptrs()
|
||||
typestrRefs := t.typestr_with_refs()
|
||||
|
||||
return func(s *State) {
|
||||
ptr := s.P
|
||||
|
||||
// Get data as unsafe slice header.
|
||||
hdr := (*unsafeheader_Slice)(ptr)
|
||||
if hdr == nil || hdr.Data == nil {
|
||||
|
||||
// Append nil value with type.
|
||||
appendNilType(s, typestrPtrs)
|
||||
return
|
||||
}
|
||||
|
||||
// Open / close braces.
|
||||
var open, close uint8
|
||||
open, close = '[', ']'
|
||||
|
||||
// Include type info.
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, typestrRefs...)
|
||||
open, close = '{', '}'
|
||||
}
|
||||
|
||||
// Prepend array brace.
|
||||
s.B = append(s.B, open)
|
||||
|
||||
if hdr.Len > 0 {
|
||||
for i := 0; i < hdr.Len; i++ {
|
||||
// Format at array index.
|
||||
offset := esz * uintptr(i)
|
||||
s.P = add(hdr.Data, offset)
|
||||
fn(s)
|
||||
|
||||
// Append separator.
|
||||
s.B = append(s.B, ',')
|
||||
}
|
||||
|
||||
// Drop final comma.
|
||||
s.B = s.B[:len(s.B)-1]
|
||||
}
|
||||
|
||||
// Append array brace.
|
||||
s.B = append(s.B, close)
|
||||
}
|
||||
}
|
||||
|
||||
func wrapByteSlice(t typenode, fn FormatFunc) FormatFunc {
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
|
||||
appendString(s, *(*string)(s.P))
|
||||
} else {
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
typestr := t.typestr_with_ptrs()
|
||||
return func(s *State) {
|
||||
if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, "("+typestr+")("...)
|
||||
appendString(s, *(*string)(s.P))
|
||||
s.B = append(s.B, ")"...)
|
||||
} else {
|
||||
appendString(s, *(*string)(s.P))
|
||||
}
|
||||
} else {
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func wrapRuneSlice(t typenode, fn FormatFunc) FormatFunc {
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
|
||||
appendString(s, string(*(*[]rune)(s.P)))
|
||||
} else {
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
typestr := t.typestr_with_ptrs()
|
||||
return func(s *State) {
|
||||
if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, "("+typestr+")("...)
|
||||
appendString(s, string(*(*[]rune)(s.P)))
|
||||
s.B = append(s.B, ")"...)
|
||||
} else {
|
||||
appendString(s, string(*(*[]rune)(s.P)))
|
||||
}
|
||||
} else {
|
||||
fn(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
173
vendor/codeberg.org/gruf/go-kv/v2/format/struct.go
generated
vendored
Normal file
173
vendor/codeberg.org/gruf/go-kv/v2/format/struct.go
generated
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package format
|
||||
|
||||
// field stores the minimum necessary
|
||||
// data for iterating and formatting
|
||||
// each field in a given struct.
|
||||
type field struct {
|
||||
format FormatFunc
|
||||
name string
|
||||
offset uintptr
|
||||
}
|
||||
|
||||
// iterStructType returns a FormatFunc capable of iterating
|
||||
// and formatting the given struct type currently in typenode{}.
|
||||
// note this will fetch sub-FormatFuncs for each struct field.
|
||||
func (fmt *Formatter) iterStructType(t typenode) FormatFunc {
|
||||
// Number of struct fields.
|
||||
n := t.rtype.NumField()
|
||||
|
||||
// Gather format functions.
|
||||
fields := make([]field, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
// Get struct field at index.
|
||||
sfield := t.rtype.Field(i)
|
||||
rtype := sfield.Type
|
||||
|
||||
// Get nested field typenode with appropriate flags.
|
||||
flags := reflect_struct_field_flags(t.flags, rtype)
|
||||
ft := t.next(sfield.Type, flags)
|
||||
|
||||
// Get field format func.
|
||||
fn := fmt.loadOrGet(ft)
|
||||
if fn == nil {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Set field info.
|
||||
fields[i] = field{
|
||||
format: fn,
|
||||
name: sfield.Name,
|
||||
offset: sfield.Offset,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle no. fields.
|
||||
switch len(fields) {
|
||||
case 0:
|
||||
return emptyStructType(t)
|
||||
case 1:
|
||||
return iterSingleFieldStructType(t, fields[0])
|
||||
default:
|
||||
return iterMultiFieldStructType(t, fields)
|
||||
}
|
||||
}
|
||||
|
||||
func emptyStructType(t typenode) FormatFunc {
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
// Append empty object.
|
||||
s.B = append(s.B, "{}"...)
|
||||
}
|
||||
}
|
||||
|
||||
// Struct type string with refs.
|
||||
typestr := t.typestr_with_refs()
|
||||
|
||||
// Append empty object
|
||||
// with type information.
|
||||
return func(s *State) {
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, typestr...)
|
||||
}
|
||||
s.B = append(s.B, "{}"...)
|
||||
}
|
||||
}
|
||||
|
||||
func iterSingleFieldStructType(t typenode, field field) FormatFunc {
|
||||
if field.format == nil {
|
||||
panic("nil func")
|
||||
}
|
||||
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
// Wrap 'fn' with braces + field name.
|
||||
s.B = append(s.B, "{"+field.name+"="...)
|
||||
field.format(s)
|
||||
s.B = append(s.B, "}"...)
|
||||
}
|
||||
}
|
||||
|
||||
// Struct type string with refs.
|
||||
typestr := t.typestr_with_refs()
|
||||
|
||||
return func(s *State) {
|
||||
// Include type info.
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, typestr...)
|
||||
}
|
||||
|
||||
// Wrap 'fn' with braces + field name.
|
||||
s.B = append(s.B, "{"+field.name+"="...)
|
||||
field.format(s)
|
||||
s.B = append(s.B, "}"...)
|
||||
}
|
||||
}
|
||||
|
||||
func iterMultiFieldStructType(t typenode, fields []field) FormatFunc {
|
||||
for _, field := range fields {
|
||||
if field.format == nil {
|
||||
panic("nil func")
|
||||
}
|
||||
}
|
||||
|
||||
if !t.needs_typestr() {
|
||||
return func(s *State) {
|
||||
ptr := s.P
|
||||
|
||||
// Prepend object brace.
|
||||
s.B = append(s.B, '{')
|
||||
|
||||
for i := 0; i < len(fields); i++ {
|
||||
// Get struct field ptr via offset.
|
||||
s.P = add(ptr, fields[i].offset)
|
||||
|
||||
// Append field name and value separator.
|
||||
s.B = append(s.B, fields[i].name+"="...)
|
||||
|
||||
// Format i'th field.
|
||||
fields[i].format(s)
|
||||
s.B = append(s.B, ',', ' ')
|
||||
}
|
||||
|
||||
// Drop final ", ".
|
||||
s.B = s.B[:len(s.B)-2]
|
||||
|
||||
// Append object brace.
|
||||
s.B = append(s.B, '}')
|
||||
}
|
||||
}
|
||||
|
||||
// Struct type string with refs.
|
||||
typestr := t.typestr_with_refs()
|
||||
|
||||
return func(s *State) {
|
||||
ptr := s.P
|
||||
|
||||
// Include type info.
|
||||
if s.A.WithType() {
|
||||
s.B = append(s.B, typestr...)
|
||||
}
|
||||
|
||||
// Prepend object brace.
|
||||
s.B = append(s.B, '{')
|
||||
|
||||
for i := 0; i < len(fields); i++ {
|
||||
// Get struct field ptr via offset.
|
||||
s.P = add(ptr, fields[i].offset)
|
||||
|
||||
// Append field name and value separator.
|
||||
s.B = append(s.B, fields[i].name+"="...)
|
||||
|
||||
// Format i'th field.
|
||||
fields[i].format(s)
|
||||
s.B = append(s.B, ',', ' ')
|
||||
}
|
||||
|
||||
// Drop final ", ".
|
||||
s.B = s.B[:len(s.B)-2]
|
||||
|
||||
// Append object brace.
|
||||
s.B = append(s.B, '}')
|
||||
}
|
||||
}
|
||||
150
vendor/codeberg.org/gruf/go-kv/v2/format/type.go
generated
vendored
Normal file
150
vendor/codeberg.org/gruf/go-kv/v2/format/type.go
generated
vendored
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// typenode ...
|
||||
type typenode struct {
|
||||
typeinfo
|
||||
parent *typenode
|
||||
}
|
||||
|
||||
// typeinfo ...
|
||||
type typeinfo struct {
|
||||
rtype reflect.Type
|
||||
flags reflect_flag
|
||||
}
|
||||
|
||||
// new_typenode returns a new typenode{} with reflect.Type and flags.
|
||||
func new_typenode(t reflect.Type, flags reflect_flag) typenode {
|
||||
return typenode{typeinfo: typeinfo{
|
||||
rtype: t,
|
||||
flags: flags,
|
||||
}}
|
||||
}
|
||||
|
||||
// key returns data (i.e. type value info)
|
||||
// to store a FormatFunc under in a cache.
|
||||
func (n typenode) key() typeinfo {
|
||||
return n.typeinfo
|
||||
}
|
||||
|
||||
// indirect returns whether reflect_flagIndir is set for given type flags.
|
||||
func (n typenode) indirect() bool {
|
||||
return n.flags&reflect_flagIndir != 0
|
||||
}
|
||||
|
||||
// iface_indir returns the result of abi.Type{}.IfaceIndir() for underlying type.
|
||||
func (n typenode) iface_indir() bool {
|
||||
return abi_Type_IfaceIndir(n.rtype)
|
||||
}
|
||||
|
||||
// next ...
|
||||
func (n typenode) next(t reflect.Type, flags reflect_flag) typenode {
|
||||
child := new_typenode(t, flags)
|
||||
child.parent = &n
|
||||
return child
|
||||
}
|
||||
|
||||
// visit ...
|
||||
func (n typenode) visit() bool {
|
||||
t := n.rtype
|
||||
|
||||
// Check if type is already encountered further up tree.
|
||||
for node := n.parent; node != nil; node = node.parent {
|
||||
if node.rtype == t {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// needs_typestr returns whether the type contained in the
|
||||
// receiving typenode{} needs type string information prefixed
|
||||
// when the TypeMask argument flag bit is set. Certain types
|
||||
// don't need this as the parent type already indicates this.
|
||||
func (n typenode) needs_typestr() bool {
|
||||
if n.parent == nil {
|
||||
return true
|
||||
}
|
||||
switch p := n.parent.rtype; p.Kind() {
|
||||
case reflect.Pointer:
|
||||
return n.parent.needs_typestr()
|
||||
case reflect.Slice,
|
||||
reflect.Array,
|
||||
reflect.Map:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// typestr_with_ptrs returns the type string for
|
||||
// current typenode{} with asterisks for pointers.
|
||||
func (n typenode) typestr_with_ptrs() string {
|
||||
t := n.rtype
|
||||
|
||||
// Check for parent.
|
||||
if n.parent == nil {
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// Get parent type.
|
||||
p := n.parent.rtype
|
||||
|
||||
// If parent is not ptr, then
|
||||
// this was not a deref'd ptr.
|
||||
if p.Kind() != reflect.Pointer {
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// Return un-deref'd
|
||||
// ptr (parent) type.
|
||||
return p.String()
|
||||
}
|
||||
|
||||
// typestr_with_refs returns the type string for
|
||||
// current typenode{} with ampersands for pointers.
|
||||
func (n typenode) typestr_with_refs() string {
|
||||
t := n.rtype
|
||||
|
||||
// Check for parent.
|
||||
if n.parent == nil {
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// Get parent type.
|
||||
p := n.parent.rtype
|
||||
|
||||
var d int
|
||||
|
||||
// Count number of dereferences.
|
||||
for p.Kind() == reflect.Pointer {
|
||||
p = p.Elem()
|
||||
d++
|
||||
}
|
||||
|
||||
if d <= 0 {
|
||||
// Prefer just returning our
|
||||
// own string if possible, to
|
||||
// reduce number of strings
|
||||
// we need to allocate.
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// Value type str.
|
||||
str := t.String()
|
||||
|
||||
// Return with type ptrs
|
||||
// symbolized by 'refs'.
|
||||
var buf strings.Builder
|
||||
buf.Grow(len(str) + d)
|
||||
for i := 0; i < d; i++ {
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
buf.WriteString(str)
|
||||
return buf.String()
|
||||
}
|
||||
147
vendor/codeberg.org/gruf/go-kv/v2/util.go
generated
vendored
Normal file
147
vendor/codeberg.org/gruf/go-kv/v2/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
package kv
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"codeberg.org/gruf/go-byteutil"
|
||||
"codeberg.org/gruf/go-kv/v2/format"
|
||||
)
|
||||
|
||||
// AppendQuoteString will append (and escape/quote where necessary) a field string.
|
||||
func AppendQuoteString(buf *byteutil.Buffer, str string) {
|
||||
switch {
|
||||
case len(str) == 0:
|
||||
// Append empty quotes.
|
||||
buf.B = append(buf.B, `""`...)
|
||||
return
|
||||
|
||||
case len(str) == 1:
|
||||
// Append escaped single byte.
|
||||
buf.B = format.AppendEscapeByte(buf.B, str[0])
|
||||
return
|
||||
|
||||
case len(str) > format.SingleTermLine || !format.IsSafeASCII(str):
|
||||
// Long line or contains non-ascii chars.
|
||||
buf.B = strconv.AppendQuote(buf.B, str)
|
||||
return
|
||||
|
||||
case !isQuoted(str):
|
||||
// Not single/double quoted already.
|
||||
|
||||
if format.ContainsSpaceOrTab(str) {
|
||||
// Quote un-enclosed spaces.
|
||||
buf.B = append(buf.B, '"')
|
||||
buf.B = append(buf.B, str...)
|
||||
buf.B = append(buf.B, '"')
|
||||
return
|
||||
}
|
||||
|
||||
if format.ContainsDoubleQuote(str) {
|
||||
// Contains double quote, double quote
|
||||
// and append escaped existing.
|
||||
buf.B = append(buf.B, '"')
|
||||
buf.B = format.AppendEscape(buf.B, str)
|
||||
buf.B = append(buf.B, '"')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Double quoted, enclosed in braces, or
|
||||
// literally anything else: append as-is.
|
||||
buf.B = append(buf.B, str...)
|
||||
return
|
||||
}
|
||||
|
||||
// AppendQuoteValue will append (and escape/quote where necessary) a formatted value string.
|
||||
func AppendQuoteValue(buf *byteutil.Buffer, str string) {
|
||||
switch {
|
||||
case len(str) == 0:
|
||||
// Append empty quotes.
|
||||
buf.B = append(buf.B, `""`...)
|
||||
return
|
||||
|
||||
case len(str) == 1:
|
||||
// Append quoted single byte.
|
||||
buf.B = format.AppendQuoteByte(buf.B, str[0])
|
||||
return
|
||||
|
||||
case len(str) > format.SingleTermLine || !format.IsSafeASCII(str):
|
||||
// Long line or contains non-ascii chars.
|
||||
buf.B = strconv.AppendQuote(buf.B, str)
|
||||
return
|
||||
|
||||
case !isQuoted(str):
|
||||
// Not single/double quoted already.
|
||||
|
||||
// Get space / tab indices (if any).
|
||||
s := strings.IndexByte(str, ' ')
|
||||
t := strings.IndexByte(str, '\t')
|
||||
|
||||
// Find first whitespace.
|
||||
sp0 := smallest(s, t)
|
||||
if sp0 < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Check if str is enclosed by braces.
|
||||
// (but without any key-value separator).
|
||||
if (enclosedBy(str, sp0, '{', '}') ||
|
||||
enclosedBy(str, sp0, '[', ']') ||
|
||||
enclosedBy(str, sp0, '(', ')')) &&
|
||||
strings.IndexByte(str, '=') < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if format.ContainsDoubleQuote(str) {
|
||||
// Contains double quote, double quote
|
||||
// and append escaped existing.
|
||||
buf.B = append(buf.B, '"')
|
||||
buf.B = format.AppendEscape(buf.B, str)
|
||||
buf.B = append(buf.B, '"')
|
||||
return
|
||||
}
|
||||
|
||||
// Quote un-enclosed spaces.
|
||||
buf.B = append(buf.B, '"')
|
||||
buf.B = append(buf.B, str...)
|
||||
buf.B = append(buf.B, '"')
|
||||
return
|
||||
}
|
||||
|
||||
// Double quoted, enclosed in braces, or
|
||||
// literally anything else: append as-is.
|
||||
buf.B = append(buf.B, str...)
|
||||
return
|
||||
}
|
||||
|
||||
// isQuoted checks if string is single or double quoted.
|
||||
func isQuoted(str string) bool {
|
||||
return (str[0] == '"' && str[len(str)-1] == '"') ||
|
||||
(str[0] == '\'' && str[len(str)-1] == '\'')
|
||||
}
|
||||
|
||||
// smallest attempts to return the smallest positive value of those given.
|
||||
func smallest(i1, i2 int) int {
|
||||
if i1 >= 0 && (i2 < 0 || i1 < i2) {
|
||||
return i1
|
||||
}
|
||||
return i2
|
||||
}
|
||||
|
||||
// enclosedBy will check if given string is enclosed by end, and at least non-whitespace up to start.
|
||||
func enclosedBy(str string, sp int, start, end byte) bool {
|
||||
// Check for ending char in string.
|
||||
if str[len(str)-1] != end {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check for starting char in string.
|
||||
i := strings.IndexByte(str, start)
|
||||
if i < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check before space.
|
||||
return i < sp
|
||||
}
|
||||
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
|
|
@ -255,6 +255,10 @@ codeberg.org/gruf/go-iotools
|
|||
## explicit; go 1.20
|
||||
codeberg.org/gruf/go-kv
|
||||
codeberg.org/gruf/go-kv/format
|
||||
# codeberg.org/gruf/go-kv/v2 v2.0.3
|
||||
## explicit; go 1.24
|
||||
codeberg.org/gruf/go-kv/v2
|
||||
codeberg.org/gruf/go-kv/v2/format
|
||||
# codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f
|
||||
## explicit; go 1.21.3
|
||||
codeberg.org/gruf/go-list
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue