mirror of
https://github.com/kubernetes/node-problem-detector.git
synced 2026-08-28 01:47:20 +00:00
Fix Grype CVEs: update logrus and prometheus/prometheus
- Update github.com/sirupsen/logrus v1.9.0 -> v1.9.3 in test/go.mod to fix GHSA-4f99-4q7p-p3gh (High) - Update github.com/prometheus/prometheus v0.35.0 -> v0.311.3 to fix GHSA-vffh-x6r8-xx99 (Medium) - Run go mod tidy and go mod vendor to update vendor directory
This commit is contained in:
committed by
Ciprian Hacman
parent
255c6e602c
commit
97bb2fbb44
+8
@@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package adapters exposes a registry of adapters to multiple
|
||||
// JSON serialization libraries.
|
||||
//
|
||||
// All interfaces are defined in package [ifaces.Adapter].
|
||||
package adapters
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package ifaces exposes all interfaces to work with adapters.
|
||||
package ifaces
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ifaces
|
||||
|
||||
import (
|
||||
_ "encoding/json" // for documentation purpose
|
||||
"iter"
|
||||
)
|
||||
|
||||
// Ordered knows how to iterate over the (key,value) pairs of a JSON object.
|
||||
type Ordered interface {
|
||||
OrderedItems() iter.Seq2[string, any]
|
||||
}
|
||||
|
||||
// SetOrdered knows how to append or update the keys of a JSON object,
|
||||
// given an iterator over (key,value) pairs.
|
||||
//
|
||||
// If the provided iterator is nil then the receiver should be set to nil.
|
||||
type SetOrdered interface {
|
||||
SetOrderedItems(iter.Seq2[string, any])
|
||||
}
|
||||
|
||||
// OrderedMap represent a JSON object (i.e. like a map[string,any]),
|
||||
// and knows how to serialize and deserialize JSON with the order of keys maintained.
|
||||
type OrderedMap interface {
|
||||
Ordered
|
||||
SetOrdered
|
||||
|
||||
OrderedMarshalJSON() ([]byte, error)
|
||||
OrderedUnmarshalJSON([]byte) error
|
||||
}
|
||||
|
||||
// MarshalAdapter behaves likes the standard library [json.Marshal].
|
||||
type MarshalAdapter interface {
|
||||
Poolable
|
||||
|
||||
Marshal(any) ([]byte, error)
|
||||
}
|
||||
|
||||
// OrderedMarshalAdapter behaves likes the standard library [json.Marshal], preserving the order of keys in objects.
|
||||
type OrderedMarshalAdapter interface {
|
||||
Poolable
|
||||
|
||||
OrderedMarshal(Ordered) ([]byte, error)
|
||||
}
|
||||
|
||||
// UnmarshalAdapter behaves likes the standard library [json.Unmarshal].
|
||||
type UnmarshalAdapter interface {
|
||||
Poolable
|
||||
|
||||
Unmarshal([]byte, any) error
|
||||
}
|
||||
|
||||
// OrderedUnmarshalAdapter behaves likes the standard library [json.Unmarshal], preserving the order of keys in objects.
|
||||
type OrderedUnmarshalAdapter interface {
|
||||
Poolable
|
||||
|
||||
OrderedUnmarshal([]byte, SetOrdered) error
|
||||
}
|
||||
|
||||
// Adapter exposes an interface like the standard [json] library.
|
||||
type Adapter interface {
|
||||
MarshalAdapter
|
||||
UnmarshalAdapter
|
||||
|
||||
OrderedAdapter
|
||||
}
|
||||
|
||||
// OrderedAdapter exposes interfaces to process JSON and keep the order of object keys.
|
||||
type OrderedAdapter interface {
|
||||
OrderedMarshalAdapter
|
||||
OrderedUnmarshalAdapter
|
||||
NewOrderedMap(capacity int) OrderedMap
|
||||
}
|
||||
|
||||
type Poolable interface {
|
||||
// Self-redeem: for [Adapter] s that are allocated from a pool.
|
||||
// The [Adapter] must not be used after calling [Redeem].
|
||||
Redeem()
|
||||
|
||||
// Reset the state of the [Adapter], if any.
|
||||
Reset()
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ifaces
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Capability indicates what a JSON adapter is capable of.
|
||||
type Capability uint8
|
||||
|
||||
const (
|
||||
CapabilityMarshalJSON Capability = 1 << iota
|
||||
CapabilityUnmarshalJSON
|
||||
CapabilityOrderedMarshalJSON
|
||||
CapabilityOrderedUnmarshalJSON
|
||||
CapabilityOrderedMap
|
||||
)
|
||||
|
||||
func (c Capability) String() string {
|
||||
switch c {
|
||||
case CapabilityMarshalJSON:
|
||||
return "MarshalJSON"
|
||||
case CapabilityUnmarshalJSON:
|
||||
return "UnmarshalJSON"
|
||||
case CapabilityOrderedMarshalJSON:
|
||||
return "OrderedMarshalJSON"
|
||||
case CapabilityOrderedUnmarshalJSON:
|
||||
return "OrderedUnmarshalJSON"
|
||||
case CapabilityOrderedMap:
|
||||
return "OrderedMap"
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
// Capabilities holds several unitary capability flags
|
||||
type Capabilities uint8
|
||||
|
||||
// Has some capability flag enabled.
|
||||
func (c Capabilities) Has(capability Capability) bool {
|
||||
return Capability(c)&capability > 0
|
||||
}
|
||||
|
||||
func (c Capabilities) String() string {
|
||||
var w strings.Builder
|
||||
|
||||
first := true
|
||||
for _, capability := range []Capability{
|
||||
CapabilityMarshalJSON,
|
||||
CapabilityUnmarshalJSON,
|
||||
CapabilityOrderedMarshalJSON,
|
||||
CapabilityOrderedUnmarshalJSON,
|
||||
CapabilityOrderedMap,
|
||||
} {
|
||||
if c.Has(capability) {
|
||||
if !first {
|
||||
w.WriteByte('|')
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
w.WriteString(capability.String())
|
||||
}
|
||||
}
|
||||
|
||||
return w.String()
|
||||
}
|
||||
|
||||
const (
|
||||
AllCapabilities Capabilities = Capabilities(uint8(CapabilityMarshalJSON) |
|
||||
uint8(CapabilityUnmarshalJSON) |
|
||||
uint8(CapabilityOrderedMarshalJSON) |
|
||||
uint8(CapabilityOrderedUnmarshalJSON) |
|
||||
uint8(CapabilityOrderedMap))
|
||||
|
||||
AllUnorderedCapabilities Capabilities = Capabilities(uint8(CapabilityMarshalJSON) | uint8(CapabilityUnmarshalJSON))
|
||||
)
|
||||
|
||||
// RegistryEntry describes how any given adapter registers its capabilities to the [Registrar].
|
||||
type RegistryEntry struct {
|
||||
Who string
|
||||
What Capabilities
|
||||
Constructor func() Adapter
|
||||
Support func(what Capability, value any) bool
|
||||
}
|
||||
|
||||
// Registrar is a type that knows how to keep registration calls from adapters.
|
||||
type Registrar interface {
|
||||
RegisterFor(RegistryEntry)
|
||||
}
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package adapters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
stdlib "github.com/go-openapi/swag/jsonutils/adapters/stdlib/json"
|
||||
)
|
||||
|
||||
// Registry holds the global registry for registered adapters.
|
||||
var Registry = NewRegistrar()
|
||||
|
||||
var (
|
||||
defaultRegistered = stdlib.Register
|
||||
|
||||
_ ifaces.Registrar = &Registrar{}
|
||||
)
|
||||
|
||||
type registryError string
|
||||
|
||||
func (e registryError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// ErrRegistry indicates an error returned by the [Registrar].
|
||||
var ErrRegistry registryError = "JSON adapters registry error"
|
||||
|
||||
type registry []*ifaces.RegistryEntry
|
||||
|
||||
// Registrar holds registered [ifaces.Adapters] for different serialization capabilities.
|
||||
//
|
||||
// Internally, it maintains a cache for data types that favor a given adapter.
|
||||
type Registrar struct {
|
||||
marshalerRegistry registry
|
||||
unmarshalerRegistry registry
|
||||
orderedMarshalerRegistry registry
|
||||
orderedUnmarshalerRegistry registry
|
||||
orderedMapRegistry registry
|
||||
|
||||
gmx sync.RWMutex
|
||||
|
||||
// cache indexed by value type, so we don't have to lookup
|
||||
marshalerCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
unmarshalerCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
orderedMarshalerCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
orderedUnmarshalerCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
orderedMapCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
}
|
||||
|
||||
func NewRegistrar() *Registrar {
|
||||
r := &Registrar{}
|
||||
|
||||
r.marshalerRegistry = make(registry, 0, 1)
|
||||
r.unmarshalerRegistry = make(registry, 0, 1)
|
||||
r.orderedMarshalerRegistry = make(registry, 0, 1)
|
||||
r.orderedUnmarshalerRegistry = make(registry, 0, 1)
|
||||
r.orderedMapRegistry = make(registry, 0, 1)
|
||||
|
||||
r.marshalerCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
r.unmarshalerCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
r.orderedMarshalerCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
r.orderedUnmarshalerCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
r.orderedMapCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
|
||||
defaultRegistered(r)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// ClearCache resets the internal type cache.
|
||||
func (r *Registrar) ClearCache() {
|
||||
r.gmx.Lock()
|
||||
r.clearCache()
|
||||
r.gmx.Unlock()
|
||||
}
|
||||
|
||||
// Reset the [Registrar] to its defaults.
|
||||
func (r *Registrar) Reset() {
|
||||
r.gmx.Lock()
|
||||
r.clearCache()
|
||||
r.marshalerRegistry = r.marshalerRegistry[:0]
|
||||
r.unmarshalerRegistry = r.unmarshalerRegistry[:0]
|
||||
r.orderedMarshalerRegistry = r.orderedMarshalerRegistry[:0]
|
||||
r.orderedUnmarshalerRegistry = r.orderedUnmarshalerRegistry[:0]
|
||||
r.orderedMapRegistry = r.orderedMapRegistry[:0]
|
||||
r.gmx.Unlock()
|
||||
|
||||
defaultRegistered(r)
|
||||
}
|
||||
|
||||
// RegisterFor registers an adapter for some JSON capabilities.
|
||||
func (r *Registrar) RegisterFor(entry ifaces.RegistryEntry) {
|
||||
r.gmx.Lock()
|
||||
if entry.What.Has(ifaces.CapabilityMarshalJSON) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityMarshalJSON)
|
||||
r.marshalerRegistry = slices.Insert(r.marshalerRegistry, 0, &e)
|
||||
}
|
||||
if entry.What.Has(ifaces.CapabilityUnmarshalJSON) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityUnmarshalJSON)
|
||||
r.unmarshalerRegistry = slices.Insert(r.unmarshalerRegistry, 0, &e)
|
||||
}
|
||||
if entry.What.Has(ifaces.CapabilityOrderedMarshalJSON) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityOrderedMarshalJSON)
|
||||
r.orderedMarshalerRegistry = slices.Insert(r.orderedMarshalerRegistry, 0, &e)
|
||||
}
|
||||
if entry.What.Has(ifaces.CapabilityOrderedUnmarshalJSON) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityOrderedUnmarshalJSON)
|
||||
r.orderedUnmarshalerRegistry = slices.Insert(r.orderedUnmarshalerRegistry, 0, &e)
|
||||
}
|
||||
if entry.What.Has(ifaces.CapabilityOrderedMap) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityOrderedMap)
|
||||
r.orderedMapRegistry = slices.Insert(r.orderedMapRegistry, 0, &e)
|
||||
}
|
||||
r.gmx.Unlock()
|
||||
}
|
||||
|
||||
// AdapterFor returns an [ifaces.Adapter] that supports this capability for this type of value.
|
||||
//
|
||||
// The [ifaces.Adapter] may be redeemed to its pool using its Redeem() method, for adapters that support global
|
||||
// pooling. When this is not the case, the redeem function is just a no-operation.
|
||||
func (r *Registrar) AdapterFor(capability ifaces.Capability, value any) ifaces.Adapter {
|
||||
entry := r.findFirstFor(capability, value)
|
||||
if entry == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return entry.Constructor()
|
||||
}
|
||||
|
||||
func (r *Registrar) clearCache() {
|
||||
clear(r.marshalerCache)
|
||||
clear(r.unmarshalerCache)
|
||||
clear(r.orderedMarshalerCache)
|
||||
clear(r.orderedUnmarshalerCache)
|
||||
clear(r.orderedMapCache)
|
||||
}
|
||||
|
||||
func (r *Registrar) findFirstFor(capability ifaces.Capability, value any) *ifaces.RegistryEntry {
|
||||
switch capability {
|
||||
case ifaces.CapabilityMarshalJSON:
|
||||
return r.findFirstInRegistryFor(r.marshalerRegistry, r.marshalerCache, capability, value)
|
||||
case ifaces.CapabilityUnmarshalJSON:
|
||||
return r.findFirstInRegistryFor(r.unmarshalerRegistry, r.unmarshalerCache, capability, value)
|
||||
case ifaces.CapabilityOrderedMarshalJSON:
|
||||
return r.findFirstInRegistryFor(r.orderedMarshalerRegistry, r.orderedMarshalerCache, capability, value)
|
||||
case ifaces.CapabilityOrderedUnmarshalJSON:
|
||||
return r.findFirstInRegistryFor(r.orderedUnmarshalerRegistry, r.orderedUnmarshalerCache, capability, value)
|
||||
case ifaces.CapabilityOrderedMap:
|
||||
return r.findFirstInRegistryFor(r.orderedMapRegistry, r.orderedMapCache, capability, value)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported capability %d: %w", capability, ErrRegistry))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registrar) findFirstInRegistryFor(reg registry, cache map[reflect.Type]*ifaces.RegistryEntry, capability ifaces.Capability, value any) *ifaces.RegistryEntry {
|
||||
r.gmx.RLock()
|
||||
if len(reg) > 1 {
|
||||
if entry, ok := cache[reflect.TypeOf(value)]; ok {
|
||||
// cache hit
|
||||
r.gmx.RUnlock()
|
||||
return entry
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range reg {
|
||||
if !entry.Support(capability, value) {
|
||||
continue
|
||||
}
|
||||
|
||||
r.gmx.RUnlock()
|
||||
|
||||
// update the internal cache
|
||||
r.gmx.Lock()
|
||||
cache[reflect.TypeOf(value)] = entry
|
||||
r.gmx.Unlock()
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// no adapter found
|
||||
r.gmx.RUnlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalAdapterFor returns the first adapter that knows how to Marshal this type of value.
|
||||
func MarshalAdapterFor(value any) ifaces.MarshalAdapter {
|
||||
return Registry.AdapterFor(ifaces.CapabilityMarshalJSON, value)
|
||||
}
|
||||
|
||||
// OrderedMarshalAdapterFor returns the first adapter that knows how to OrderedMarshal this type of value.
|
||||
func OrderedMarshalAdapterFor(value ifaces.Ordered) ifaces.OrderedMarshalAdapter {
|
||||
return Registry.AdapterFor(ifaces.CapabilityOrderedMarshalJSON, value)
|
||||
}
|
||||
|
||||
// UnmarshalAdapterFor returns the first adapter that knows how to Unmarshal this type of value.
|
||||
func UnmarshalAdapterFor(value any) ifaces.UnmarshalAdapter {
|
||||
return Registry.AdapterFor(ifaces.CapabilityUnmarshalJSON, value)
|
||||
}
|
||||
|
||||
// OrderedUnmarshalAdapterFor provides the first adapter that knows how to OrderedUnmarshal this type of value.
|
||||
func OrderedUnmarshalAdapterFor(value ifaces.SetOrdered) ifaces.OrderedUnmarshalAdapter {
|
||||
return Registry.AdapterFor(ifaces.CapabilityOrderedUnmarshalJSON, value)
|
||||
}
|
||||
|
||||
// NewOrderedMap provides the "ordered map" implementation provided by the registry.
|
||||
func NewOrderedMap(capacity int) ifaces.OrderedMap {
|
||||
var v any
|
||||
adapter := Registry.AdapterFor(ifaces.CapabilityOrderedUnmarshalJSON, v)
|
||||
if adapter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer adapter.Redeem()
|
||||
return adapter.NewOrderedMap(capacity)
|
||||
}
|
||||
|
||||
func noopRedeemer() {}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
stdjson "encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
"github.com/go-openapi/swag/typeutils"
|
||||
)
|
||||
|
||||
const sensibleBufferSize = 8192
|
||||
|
||||
type jsonError string
|
||||
|
||||
func (e jsonError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// ErrStdlib indicates that an error comes from the stdlib JSON adapter
|
||||
var ErrStdlib jsonError = "error from the JSON adapter stdlib"
|
||||
|
||||
var _ ifaces.Adapter = &Adapter{}
|
||||
|
||||
type Adapter struct {
|
||||
}
|
||||
|
||||
// NewAdapter yields an [ifaces.Adapter] using the standard library.
|
||||
func NewAdapter() *Adapter {
|
||||
return &Adapter{}
|
||||
}
|
||||
|
||||
func (a *Adapter) Marshal(value any) ([]byte, error) {
|
||||
return stdjson.Marshal(value)
|
||||
}
|
||||
|
||||
func (a *Adapter) Unmarshal(data []byte, value any) error {
|
||||
return stdjson.Unmarshal(data, value)
|
||||
}
|
||||
|
||||
func (a *Adapter) OrderedMarshal(value ifaces.Ordered) ([]byte, error) {
|
||||
w := poolOfWriters.Borrow()
|
||||
defer func() {
|
||||
poolOfWriters.Redeem(w)
|
||||
}()
|
||||
|
||||
if typeutils.IsNil(value) {
|
||||
w.RawString("null")
|
||||
|
||||
return w.BuildBytes()
|
||||
}
|
||||
|
||||
w.RawByte('{')
|
||||
first := true
|
||||
for k, v := range value.OrderedItems() {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
w.RawByte(',')
|
||||
}
|
||||
|
||||
w.String(k)
|
||||
w.RawByte(':')
|
||||
|
||||
switch val := v.(type) {
|
||||
case ifaces.Ordered:
|
||||
w.Raw(a.OrderedMarshal(val))
|
||||
default:
|
||||
w.Raw(stdjson.Marshal(v))
|
||||
}
|
||||
}
|
||||
|
||||
w.RawByte('}')
|
||||
|
||||
return w.BuildBytes()
|
||||
}
|
||||
|
||||
func (a *Adapter) OrderedUnmarshal(data []byte, value ifaces.SetOrdered) error {
|
||||
var m MapSlice
|
||||
if err := m.OrderedUnmarshalJSON(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if typeutils.IsNil(m) {
|
||||
// force input value to nil
|
||||
value.SetOrderedItems(nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
value.SetOrderedItems(m.OrderedItems())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Adapter) NewOrderedMap(capacity int) ifaces.OrderedMap {
|
||||
m := make(MapSlice, 0, capacity)
|
||||
|
||||
return &m
|
||||
}
|
||||
|
||||
// Redeem the [Adapter] when it comes from a pool.
|
||||
//
|
||||
// The adapter becomes immediately unusable once redeemed.
|
||||
func (a *Adapter) Redeem() {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
|
||||
RedeemAdapter(a)
|
||||
}
|
||||
|
||||
func (a *Adapter) Reset() {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package json implements an [ifaces.Adapter] using the standard library.
|
||||
package json
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
stdjson "encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/swag/conv"
|
||||
)
|
||||
|
||||
type token struct {
|
||||
stdjson.Token
|
||||
}
|
||||
|
||||
func (t token) String() string {
|
||||
if t == invalidToken {
|
||||
return "invalid token"
|
||||
}
|
||||
if t == eofToken {
|
||||
return "EOF"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", t.Token)
|
||||
}
|
||||
|
||||
func (t token) Kind() tokenKind {
|
||||
switch t.Token.(type) {
|
||||
case nil:
|
||||
return tokenNull
|
||||
case stdjson.Delim:
|
||||
return tokenDelim
|
||||
case bool:
|
||||
return tokenBool
|
||||
case float64:
|
||||
return tokenFloat
|
||||
case stdjson.Number:
|
||||
return tokenNumber
|
||||
case string:
|
||||
return tokenString
|
||||
default:
|
||||
return tokenUndef
|
||||
}
|
||||
}
|
||||
|
||||
func (t token) Delim() byte {
|
||||
r, ok := t.Token.(stdjson.Delim)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
return byte(r)
|
||||
}
|
||||
|
||||
type tokenKind uint8
|
||||
|
||||
const (
|
||||
tokenUndef tokenKind = iota
|
||||
tokenString
|
||||
tokenNumber
|
||||
tokenFloat
|
||||
tokenBool
|
||||
tokenNull
|
||||
tokenDelim
|
||||
)
|
||||
|
||||
var (
|
||||
invalidToken = token{
|
||||
Token: stdjson.Token(struct{}{}),
|
||||
}
|
||||
|
||||
eofToken = token{
|
||||
Token: stdjson.Token(&struct{}{}),
|
||||
}
|
||||
|
||||
undefToken = token{
|
||||
Token: stdjson.Token(uint8(0)),
|
||||
}
|
||||
)
|
||||
|
||||
// jlexer apes easyjson's jlexer, but uses the standard library decoder under the hood.
|
||||
type jlexer struct {
|
||||
buf *bytesReader
|
||||
dec *stdjson.Decoder
|
||||
err error
|
||||
// current token
|
||||
next token
|
||||
// started bool
|
||||
}
|
||||
|
||||
type bytesReader struct {
|
||||
buf []byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (b *bytesReader) Reset() {
|
||||
b.buf = nil
|
||||
b.offset = 0
|
||||
}
|
||||
|
||||
func (b *bytesReader) Read(p []byte) (int, error) {
|
||||
if b.offset >= len(b.buf) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n := len(p)
|
||||
buf := b.buf[b.offset:]
|
||||
m := len(buf)
|
||||
|
||||
if n >= m {
|
||||
copy(p, buf)
|
||||
b.offset += m
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
copy(p, buf[:n])
|
||||
b.offset += n
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
var _ io.Reader = &bytesReader{}
|
||||
|
||||
func newLexer(data []byte) *jlexer {
|
||||
l := &jlexer{
|
||||
// current: undefToken,
|
||||
next: undefToken,
|
||||
}
|
||||
l.buf = &bytesReader{
|
||||
buf: data,
|
||||
}
|
||||
l.dec = stdjson.NewDecoder(l.buf) // unfortunately, cannot pool this
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *jlexer) Reset() {
|
||||
l.err = nil
|
||||
l.next = undefToken
|
||||
// leave l.dec and l.buf alone, since they are replaced at every Borrow
|
||||
}
|
||||
|
||||
func (l *jlexer) Error() error {
|
||||
return l.err
|
||||
}
|
||||
|
||||
func (l *jlexer) SetErr(err error) {
|
||||
l.err = err
|
||||
}
|
||||
|
||||
func (l *jlexer) Ok() bool {
|
||||
return l.err == nil
|
||||
}
|
||||
|
||||
// NextToken consumes a token
|
||||
func (l *jlexer) NextToken() token {
|
||||
if !l.Ok() {
|
||||
return invalidToken
|
||||
}
|
||||
|
||||
if l.next != undefToken {
|
||||
next := l.next
|
||||
l.next = undefToken
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
return l.fetchToken()
|
||||
}
|
||||
|
||||
// PeekToken returns the next token without consuming it
|
||||
func (l *jlexer) PeekToken() token {
|
||||
if l.next == undefToken {
|
||||
l.next = l.fetchToken()
|
||||
}
|
||||
|
||||
return l.next
|
||||
}
|
||||
|
||||
func (l *jlexer) Skip() {
|
||||
_ = l.NextToken()
|
||||
}
|
||||
|
||||
func (l *jlexer) IsDelim(c byte) bool {
|
||||
if !l.Ok() {
|
||||
return false
|
||||
}
|
||||
|
||||
next := l.PeekToken()
|
||||
if next.Kind() != tokenDelim {
|
||||
return false
|
||||
}
|
||||
|
||||
if next.Delim() != c {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (l *jlexer) IsNull() bool {
|
||||
if !l.Ok() {
|
||||
return false
|
||||
}
|
||||
|
||||
next := l.PeekToken()
|
||||
|
||||
return next.Kind() == tokenNull
|
||||
}
|
||||
|
||||
func (l *jlexer) Delim(c byte) {
|
||||
if !l.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
if tok.Kind() != tokenDelim {
|
||||
l.err = fmt.Errorf("expected a delimiter token but got '%v': %w", tok, ErrStdlib)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if tok.Delim() != c {
|
||||
l.err = fmt.Errorf("expected delimiter '%q' but got '%q': %w", c, tok.Delim(), ErrStdlib)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *jlexer) Null() {
|
||||
if !l.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
if tok.Kind() != tokenNull {
|
||||
l.err = fmt.Errorf("expected a null token but got '%v': %w", tok, ErrStdlib)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *jlexer) Number() any {
|
||||
if !l.Ok() {
|
||||
return 0
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
|
||||
switch tok.Kind() { //nolint:exhaustive
|
||||
case tokenNumber:
|
||||
n := tok.Token.(stdjson.Number).String()
|
||||
f, _ := strconv.ParseFloat(n, 64)
|
||||
if conv.IsFloat64AJSONInteger(f) {
|
||||
return int64(math.Trunc(f))
|
||||
}
|
||||
|
||||
return f
|
||||
|
||||
case tokenFloat:
|
||||
f := tok.Token.(float64)
|
||||
if conv.IsFloat64AJSONInteger(f) {
|
||||
return int64(math.Trunc(f))
|
||||
}
|
||||
|
||||
return f
|
||||
|
||||
default:
|
||||
l.err = fmt.Errorf("expected a number token but got '%v': %w", tok, ErrStdlib)
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (l *jlexer) Bool() bool {
|
||||
if !l.Ok() {
|
||||
return false
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
if tok.Kind() != tokenBool {
|
||||
l.err = fmt.Errorf("expected a bool token but got '%v': %w", tok, ErrStdlib)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return tok.Token.(bool)
|
||||
}
|
||||
|
||||
func (l *jlexer) String() string {
|
||||
if !l.Ok() {
|
||||
return ""
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
if tok.Kind() != tokenString {
|
||||
l.err = fmt.Errorf("expected a string token but got '%v': %w", tok, ErrStdlib)
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
return tok.Token.(string)
|
||||
}
|
||||
|
||||
// Commas and colons are elided.
|
||||
func (l *jlexer) fetchToken() token {
|
||||
jtok, err := l.dec.Token()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return eofToken
|
||||
}
|
||||
|
||||
l.err = errors.Join(err, ErrStdlib)
|
||||
return invalidToken
|
||||
}
|
||||
|
||||
return token{Token: jtok}
|
||||
}
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
stdjson "encoding/json"
|
||||
"fmt"
|
||||
"iter"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
)
|
||||
|
||||
var _ ifaces.OrderedMap = &MapSlice{}
|
||||
|
||||
// MapSlice represents a JSON object, with the order of keys maintained.
|
||||
type MapSlice []MapItem
|
||||
|
||||
func (s MapSlice) OrderedItems() iter.Seq2[string, any] {
|
||||
return func(yield func(string, any) bool) {
|
||||
for _, item := range s {
|
||||
if !yield(item.Key, item.Value) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MapSlice) SetOrderedItems(items iter.Seq2[string, any]) {
|
||||
if items == nil {
|
||||
*s = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
m := *s
|
||||
if len(m) > 0 {
|
||||
// update mode
|
||||
idx := make(map[string]int, len(m))
|
||||
|
||||
for i, item := range m {
|
||||
idx[item.Key] = i
|
||||
}
|
||||
|
||||
for k, v := range items {
|
||||
idx, ok := idx[k]
|
||||
if ok {
|
||||
m[idx].Value = v
|
||||
|
||||
continue
|
||||
}
|
||||
m = append(m, MapItem{Key: k, Value: v})
|
||||
}
|
||||
|
||||
*s = m
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range items {
|
||||
m = append(m, MapItem{Key: k, Value: v})
|
||||
}
|
||||
|
||||
*s = m
|
||||
}
|
||||
|
||||
// MarshalJSON renders a [MapSlice] as JSON bytes, preserving the order of keys.
|
||||
func (s MapSlice) MarshalJSON() ([]byte, error) {
|
||||
return s.OrderedMarshalJSON()
|
||||
}
|
||||
|
||||
func (s MapSlice) OrderedMarshalJSON() ([]byte, error) {
|
||||
w := poolOfWriters.Borrow()
|
||||
defer func() {
|
||||
poolOfWriters.Redeem(w)
|
||||
}()
|
||||
|
||||
s.marshalObject(w)
|
||||
|
||||
return w.BuildBytes() // this clones data, so it's okay to redeem the writer and its buffer
|
||||
}
|
||||
|
||||
// UnmarshalJSON builds a [MapSlice] from JSON bytes, preserving the order of keys.
|
||||
//
|
||||
// Inner objects are unmarshaled as [MapSlice] slices and not map[string]any.
|
||||
func (s *MapSlice) UnmarshalJSON(data []byte) error {
|
||||
return s.OrderedUnmarshalJSON(data)
|
||||
}
|
||||
|
||||
func (s *MapSlice) OrderedUnmarshalJSON(data []byte) error {
|
||||
l := poolOfLexers.Borrow(data)
|
||||
defer func() {
|
||||
poolOfLexers.Redeem(l)
|
||||
}()
|
||||
|
||||
s.unmarshalObject(l)
|
||||
|
||||
return l.Error()
|
||||
}
|
||||
|
||||
func (s MapSlice) marshalObject(w *jwriter) {
|
||||
if s == nil {
|
||||
w.RawString("null")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.RawByte('{')
|
||||
|
||||
if len(s) == 0 {
|
||||
w.RawByte('}')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
s[0].marshalJSON(w)
|
||||
|
||||
for i := 1; i < len(s); i++ {
|
||||
w.RawByte(',')
|
||||
s[i].marshalJSON(w)
|
||||
}
|
||||
|
||||
w.RawByte('}')
|
||||
}
|
||||
|
||||
func (s *MapSlice) unmarshalObject(in *jlexer) {
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
in.Delim('{') // consume token
|
||||
if !in.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
result := make(MapSlice, 0)
|
||||
|
||||
for in.Ok() && !in.IsDelim('}') {
|
||||
var mi MapItem
|
||||
|
||||
mi.unmarshalKeyValue(in)
|
||||
result = append(result, mi)
|
||||
}
|
||||
|
||||
in.Delim('}')
|
||||
|
||||
if !in.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
*s = result
|
||||
}
|
||||
|
||||
// MapItem represents the value of a key in a JSON object held by [MapSlice].
|
||||
//
|
||||
// Notice that [MapItem] should not be marshaled to or unmarshaled from JSON directly,
|
||||
// use this type as part of a [MapSlice] when dealing with JSON bytes.
|
||||
type MapItem struct {
|
||||
Key string
|
||||
Value any
|
||||
}
|
||||
|
||||
func (s MapItem) marshalJSON(w *jwriter) {
|
||||
w.String(s.Key)
|
||||
w.RawByte(':')
|
||||
w.Raw(stdjson.Marshal(s.Value))
|
||||
}
|
||||
|
||||
func (s *MapItem) unmarshalKeyValue(in *jlexer) {
|
||||
key := in.String() // consume string
|
||||
value := s.asInterface(in) // consume any value, including termination tokens '}' or ']'
|
||||
|
||||
if !in.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
s.Key = key
|
||||
s.Value = value
|
||||
}
|
||||
|
||||
func (s *MapItem) unmarshalArray(in *jlexer) []any {
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
in.Delim('[') // consume token
|
||||
if !in.Ok() {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := make([]any, 0)
|
||||
|
||||
for in.Ok() && !in.IsDelim(']') {
|
||||
ret = append(ret, s.asInterface(in))
|
||||
}
|
||||
|
||||
in.Delim(']')
|
||||
if !in.Ok() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// asInterface is very much like [jlexer.Lexer.Interface], but unmarshals an object
|
||||
// into a [MapSlice], not a map[string]any.
|
||||
//
|
||||
// We have to force parsing errors somehow, since [jlexer.Lexer] doesn't let us
|
||||
// set a parsing error directly.
|
||||
func (s *MapItem) asInterface(in *jlexer) any {
|
||||
if !in.Ok() {
|
||||
return nil
|
||||
}
|
||||
|
||||
tok := in.PeekToken() // look-ahead what the next token looks like
|
||||
kind := tok.Kind()
|
||||
|
||||
switch kind {
|
||||
case tokenString:
|
||||
return in.String() // consume string
|
||||
|
||||
case tokenNumber, tokenFloat:
|
||||
return in.Number()
|
||||
|
||||
case tokenBool:
|
||||
return in.Bool()
|
||||
|
||||
case tokenNull:
|
||||
in.Null()
|
||||
|
||||
return nil
|
||||
|
||||
case tokenDelim:
|
||||
switch tok.Delim() {
|
||||
case '{': // not consumed yet
|
||||
ret := make(MapSlice, 0)
|
||||
ret.unmarshalObject(in) // consumes the terminating '}'
|
||||
|
||||
if in.Ok() {
|
||||
return ret
|
||||
}
|
||||
|
||||
// lexer is in an error state: will exhaust
|
||||
return nil
|
||||
|
||||
case '[': // not consumed yet
|
||||
return s.unmarshalArray(in) // consumes the terminating ']'
|
||||
default:
|
||||
in.SetErr(fmt.Errorf("unexpected delimiter: %v: %w", tok, ErrStdlib)) // force error
|
||||
return nil
|
||||
}
|
||||
|
||||
case tokenUndef:
|
||||
fallthrough
|
||||
default:
|
||||
if in.Ok() {
|
||||
in.SetErr(fmt.Errorf("unexpected token: %v: %w", tok, ErrStdlib)) // force error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
)
|
||||
|
||||
type adaptersPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
func (p *adaptersPool) Borrow() *Adapter {
|
||||
return p.Get().(*Adapter)
|
||||
}
|
||||
|
||||
func (p *adaptersPool) BorrowIface() ifaces.Adapter {
|
||||
return p.Get().(*Adapter)
|
||||
}
|
||||
|
||||
func (p *adaptersPool) Redeem(a *Adapter) {
|
||||
p.Put(a)
|
||||
}
|
||||
|
||||
type writersPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
func (p *writersPool) Borrow() *jwriter {
|
||||
ptr := p.Get()
|
||||
|
||||
jw := ptr.(*jwriter)
|
||||
jw.Reset()
|
||||
|
||||
return jw
|
||||
}
|
||||
|
||||
func (p *writersPool) Redeem(w *jwriter) {
|
||||
p.Put(w)
|
||||
}
|
||||
|
||||
type lexersPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
func (p *lexersPool) Borrow(data []byte) *jlexer {
|
||||
ptr := p.Get()
|
||||
|
||||
l := ptr.(*jlexer)
|
||||
l.buf = poolOfReaders.Borrow(data)
|
||||
l.dec = json.NewDecoder(l.buf) // cannot pool, not exposed by the encoding/json API
|
||||
l.Reset()
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (p *lexersPool) Redeem(l *jlexer) {
|
||||
l.dec = nil
|
||||
discard := l.buf
|
||||
l.buf = nil
|
||||
poolOfReaders.Redeem(discard)
|
||||
p.Put(l)
|
||||
}
|
||||
|
||||
type readersPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
func (p *readersPool) Borrow(data []byte) *bytesReader {
|
||||
ptr := p.Get()
|
||||
|
||||
b := ptr.(*bytesReader)
|
||||
b.Reset()
|
||||
b.buf = data
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (p *readersPool) Redeem(b *bytesReader) {
|
||||
p.Put(b)
|
||||
}
|
||||
|
||||
var (
|
||||
poolOfAdapters = &adaptersPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() any {
|
||||
return NewAdapter()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfWriters = &writersPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() any {
|
||||
return newJWriter()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfLexers = &lexersPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() any {
|
||||
return newLexer(nil)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfReaders = &readersPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() any {
|
||||
return &bytesReader{}
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// BorrowAdapter borrows an [Adapter] from the pool, recycling already allocated instances.
|
||||
func BorrowAdapter() *Adapter {
|
||||
return poolOfAdapters.Borrow()
|
||||
}
|
||||
|
||||
// BorrowAdapterIface borrows a stdlib [Adapter] and converts it directly
|
||||
// to [ifaces.Adapter]. This is useful to avoid further allocations when
|
||||
// translating the concrete type into an interface.
|
||||
func BorrowAdapterIface() ifaces.Adapter {
|
||||
return poolOfAdapters.BorrowIface()
|
||||
}
|
||||
|
||||
// RedeemAdapter redeems an [Adapter] to the pool, so it may be recycled.
|
||||
func RedeemAdapter(a *Adapter) {
|
||||
poolOfAdapters.Redeem(a)
|
||||
}
|
||||
|
||||
func RedeemAdapterIface(a ifaces.Adapter) {
|
||||
concrete, ok := a.(*Adapter)
|
||||
if ok {
|
||||
poolOfAdapters.Redeem(concrete)
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
)
|
||||
|
||||
func Register(dispatcher ifaces.Registrar) {
|
||||
t := reflect.TypeOf(Adapter{})
|
||||
dispatcher.RegisterFor(
|
||||
ifaces.RegistryEntry{
|
||||
Who: fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()),
|
||||
What: ifaces.AllCapabilities,
|
||||
Constructor: BorrowAdapterIface,
|
||||
Support: support,
|
||||
})
|
||||
}
|
||||
|
||||
func support(_ ifaces.Capability, _ any) bool {
|
||||
return true
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type jwriter struct {
|
||||
buf *bytes.Buffer
|
||||
err error
|
||||
}
|
||||
|
||||
func newJWriter() *jwriter {
|
||||
buf := make([]byte, 0, sensibleBufferSize)
|
||||
|
||||
return &jwriter{buf: bytes.NewBuffer(buf)}
|
||||
}
|
||||
|
||||
func (w *jwriter) Reset() {
|
||||
w.buf.Reset()
|
||||
w.err = nil
|
||||
}
|
||||
|
||||
func (w *jwriter) RawString(s string) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.buf.WriteString(s)
|
||||
}
|
||||
|
||||
func (w *jwriter) Raw(b []byte, err error) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = w.buf.Write(b)
|
||||
}
|
||||
|
||||
func (w *jwriter) RawByte(c byte) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.buf.WriteByte(c)
|
||||
}
|
||||
|
||||
var quoteReplacer = strings.NewReplacer(`"`, `\"`, `\`, `\\`)
|
||||
|
||||
func (w *jwriter) String(s string) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
// escape quotes and \
|
||||
s = quoteReplacer.Replace(s)
|
||||
|
||||
_ = w.buf.WriteByte('"')
|
||||
json.HTMLEscape(w.buf, []byte(s))
|
||||
_ = w.buf.WriteByte('"')
|
||||
}
|
||||
|
||||
// BuildBytes returns a clone of the internal buffer.
|
||||
func (w *jwriter) BuildBytes() ([]byte, error) {
|
||||
if w.err != nil {
|
||||
return nil, w.err
|
||||
}
|
||||
|
||||
return bytes.Clone(w.buf.Bytes()), nil
|
||||
}
|
||||
Reference in New Issue
Block a user