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:
Bruno Chauvet
2026-05-04 10:45:24 +03:00
committed by Ciprian Hacman
parent 255c6e602c
commit 97bb2fbb44
279 changed files with 17945 additions and 29165 deletions
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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()
}
@@ -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
View File
@@ -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() {}
@@ -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() {
}
@@ -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
@@ -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}
}
@@ -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
}
}
@@ -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)
}
}
@@ -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
}
@@ -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
}