Bump reva to pull in the latest fixes and improvements

This commit is contained in:
André Duffeck
2026-01-21 08:43:08 +01:00
parent ef3c0da0cb
commit a93769ae9b
88 changed files with 7473 additions and 577 deletions
-1
View File
@@ -1,5 +1,4 @@
//go:build linux && !appengine && !tinygo
// +build linux,!appengine,!tinygo
package msgp
-1
View File
@@ -1,5 +1,4 @@
//go:build (!linux && !tinygo && !windows) || appengine
// +build !linux,!tinygo,!windows appengine
package msgp
+159
View File
@@ -0,0 +1,159 @@
package msgp
import "strconv"
// AutoShim provides helper functions for converting between string and
// numeric types.
type AutoShim struct{}
// ParseUint converts a string to a uint.
func (a AutoShim) ParseUint(s string) (uint, error) {
v, err := strconv.ParseUint(s, 10, strconv.IntSize)
return uint(v), err
}
// ParseUint8 converts a string to a uint8.
func (a AutoShim) ParseUint8(s string) (uint8, error) {
v, err := strconv.ParseUint(s, 10, 8)
return uint8(v), err
}
// ParseUint16 converts a string to a uint16.
func (a AutoShim) ParseUint16(s string) (uint16, error) {
v, err := strconv.ParseUint(s, 10, 16)
return uint16(v), err
}
// ParseUint32 converts a string to a uint32.
func (a AutoShim) ParseUint32(s string) (uint32, error) {
v, err := strconv.ParseUint(s, 10, 32)
return uint32(v), err
}
// ParseUint64 converts a string to a uint64.
func (a AutoShim) ParseUint64(s string) (uint64, error) {
v, err := strconv.ParseUint(s, 10, 64)
return v, err
}
// ParseInt converts a string to an int.
func (a AutoShim) ParseInt(s string) (int, error) {
v, err := strconv.ParseInt(s, 10, strconv.IntSize)
return int(v), err
}
// ParseInt8 converts a string to an int8.
func (a AutoShim) ParseInt8(s string) (int8, error) {
v, err := strconv.ParseInt(s, 10, 8)
return int8(v), err
}
// ParseInt16 converts a string to an int16.
func (a AutoShim) ParseInt16(s string) (int16, error) {
v, err := strconv.ParseInt(s, 10, 16)
return int16(v), err
}
// ParseInt32 converts a string to an int32.
func (a AutoShim) ParseInt32(s string) (int32, error) {
v, err := strconv.ParseInt(s, 10, 32)
return int32(v), err
}
// ParseInt64 converts a string to an int64.
func (a AutoShim) ParseInt64(s string) (int64, error) {
v, err := strconv.ParseInt(s, 10, 64)
return v, err
}
// ParseBool converts a string to a bool.
func (a AutoShim) ParseBool(s string) (bool, error) {
return strconv.ParseBool(s)
}
// ParseFloat64 converts a string to a float64.
func (a AutoShim) ParseFloat64(s string) (float64, error) {
return strconv.ParseFloat(s, 64)
}
// ParseFloat32 converts a string to a float32.
func (a AutoShim) ParseFloat32(s string) (float32, error) {
v, err := strconv.ParseFloat(s, 32)
return float32(v), err
}
// ParseByte converts a string to a byte.
func (a AutoShim) ParseByte(s string) (byte, error) {
v, err := strconv.ParseUint(s, 10, 8)
return byte(v), err
}
// Uint8String returns the string representation of a uint8.
func (a AutoShim) Uint8String(v uint8) string {
return strconv.FormatUint(uint64(v), 10)
}
// UintString returns the string representation of a uint.
func (a AutoShim) UintString(v uint) string {
return strconv.FormatUint(uint64(v), 10)
}
// Uint16String returns the string representation of a uint16.
func (a AutoShim) Uint16String(v uint16) string {
return strconv.FormatUint(uint64(v), 10)
}
// Uint32String returns the string representation of a uint32.
func (a AutoShim) Uint32String(v uint32) string {
return strconv.FormatUint(uint64(v), 10)
}
// Uint64String returns the string representation of a uint64.
func (a AutoShim) Uint64String(v uint64) string {
return strconv.FormatUint(v, 10)
}
// IntString returns the string representation of an int.
func (a AutoShim) IntString(v int) string {
return strconv.FormatInt(int64(v), 10)
}
// Int8String returns the string representation of an int8.
func (a AutoShim) Int8String(v int8) string {
return strconv.FormatInt(int64(v), 10)
}
// Int16String returns the string representation of an int16.
func (a AutoShim) Int16String(v int16) string {
return strconv.FormatInt(int64(v), 10)
}
// Int32String returns the string representation of an int32.
func (a AutoShim) Int32String(v int32) string {
return strconv.FormatInt(int64(v), 10)
}
// Int64String returns the string representation of an int64.
func (a AutoShim) Int64String(v int64) string {
return strconv.FormatInt(v, 10)
}
// BoolString returns the string representation of a bool.
func (a AutoShim) BoolString(v bool) string {
return strconv.FormatBool(v)
}
// Float64String returns the string representation of a float64.
func (a AutoShim) Float64String(v float64) string {
return strconv.FormatFloat(v, 'g', -1, 64)
}
// Float32String returns the string representation of a float32.
func (a AutoShim) Float32String(v float32) string {
return strconv.FormatFloat(float64(v), 'g', -1, 32)
}
// ByteString returns the string representation of a byte.
func (a AutoShim) ByteString(v byte) string {
return strconv.FormatUint(uint64(v), 10)
}
+21
View File
@@ -26,6 +26,27 @@
// the wiki at http://github.com/tinylib/msgp
package msgp
// RT is the runtime interface for all types that can be encoded and decoded.
type RT interface {
Decodable
Encodable
Sizer
Unmarshaler
Marshaler
}
// PtrTo is the runtime interface for all types that can be encoded and decoded.
type PtrTo[T any] interface {
~*T
}
// RTFor is the runtime interface for all types that can be encoded and decoded.
// Use for generic types.
type RTFor[T any] interface {
PtrTo[T]
RT
}
const (
last4 = 0x0f
first4 = 0xf0
+1 -1
View File
@@ -58,7 +58,7 @@ func HasKey(key string, raw []byte) bool {
return false
}
var field []byte
for i := uint32(0); i < sz; i++ {
for range sz {
field, bts, err = ReadStringZC(bts)
if err != nil {
return false
+1 -2
View File
@@ -1,5 +1,4 @@
//go:build !tinygo
// +build !tinygo
package msgp
@@ -10,7 +9,7 @@ package msgp
var sizes [256]bytespec
func init() {
for i := 0; i < 256; i++ {
for i := range 256 {
sizes[i] = calcBytespec(byte(i))
}
}
-1
View File
@@ -1,5 +1,4 @@
//go:build tinygo
// +build tinygo
package msgp
+12 -3
View File
@@ -17,6 +17,10 @@ var (
// This should only realistically be seen on adversarial data trying to exhaust the stack.
ErrRecursion error = errRecursion{}
// ErrLimitExceeded is returned when a set limit is exceeded.
// Limits can be set on the Reader to prevent excessive memory usage by adversarial data.
ErrLimitExceeded error = errLimitExceeded{}
// this error is only returned
// if we reach code that should
// be unreachable
@@ -73,7 +77,7 @@ func Resumable(e error) bool {
//
// ErrShortBytes is not wrapped with any context due to backward compatibility
// issues with the public API.
func WrapError(err error, ctx ...interface{}) error {
func WrapError(err error, ctx ...any) error {
switch e := err.(type) {
case errShort:
return e
@@ -143,6 +147,11 @@ type errRecursion struct{}
func (e errRecursion) Error() string { return "msgp: recursion limit reached" }
func (e errRecursion) Resumable() bool { return false }
type errLimitExceeded struct{}
func (e errLimitExceeded) Error() string { return "msgp: configured reader limit exceeded" }
func (e errLimitExceeded) Resumable() bool { return false }
// ArrayError is an error returned
// when decoding a fix-sized array
// of the wrong size
@@ -382,8 +391,8 @@ l: // loop through string bytes (not UTF-8 characters)
}
// anything else is \x
sb = append(sb, `\x`...)
sb = append(sb, lowerhex[byte(b)>>4])
sb = append(sb, lowerhex[byte(b)&0xF])
sb = append(sb, lowerhex[b>>4])
sb = append(sb, lowerhex[b&0xF])
continue l
}
}
+1 -2
View File
@@ -1,5 +1,4 @@
//go:build !tinygo
// +build !tinygo
package msgp
@@ -9,7 +8,7 @@ import (
)
// ctxString converts the incoming interface{} slice into a single string.
func ctxString(ctx []interface{}) string {
func ctxString(ctx []any) string {
out := ""
for idx, cv := range ctx {
if idx > 0 {
-1
View File
@@ -1,5 +1,4 @@
//go:build tinygo
// +build tinygo
package msgp
+10 -4
View File
@@ -181,7 +181,7 @@ func (mw *Writer) writeExtensionHeader(length int, extType int8) error {
return err
}
mw.buf[o] = mext8
mw.buf[o+1] = byte(uint8(length))
mw.buf[o+1] = byte(length)
mw.buf[o+2] = byte(extType)
case length < math.MaxUint16:
o, err := mw.require(4)
@@ -342,7 +342,7 @@ func (m *Reader) peekExtensionHeader() (offset int, length int, extType int8, er
}
offset = 3
extType = int8(p[2])
length = int(uint8(p[1]))
length = int(p[1])
case mext16:
p, err = m.R.Peek(4)
@@ -383,6 +383,9 @@ func (m *Reader) ReadExtension(e Extension) error {
if expectedType := e.ExtensionType(); extType != expectedType {
return errExt(extType, expectedType)
}
if uint32(length) > m.GetMaxElements() {
return ErrLimitExceeded
}
p, err := m.R.Peek(offset + length)
if err != nil {
@@ -404,6 +407,9 @@ func (m *Reader) ReadExtensionRaw() (int8, []byte, error) {
if err != nil {
return 0, nil, err
}
if uint32(length) > m.GetMaxElements() {
return 0, nil, ErrLimitExceeded
}
payload, err := m.R.Next(offset + length)
if err != nil {
@@ -455,7 +461,7 @@ func AppendExtension(b []byte, e Extension) ([]byte, error) {
case l < math.MaxUint8:
o, n = ensure(b, l+3)
o[n] = mext8
o[n+1] = byte(uint8(l))
o[n+1] = byte(l)
o[n+2] = byte(e.ExtensionType())
n += 3
case l < math.MaxUint16:
@@ -528,7 +534,7 @@ func readExt(b []byte) (typ int8, remain []byte, data []byte, err error) {
sz = 16
off = 2
case mext8:
sz = int(uint8(b[1]))
sz = int(b[1])
typ = int8(b[2])
off = 3
if sz == 0 {
-3
View File
@@ -1,7 +1,4 @@
//go:build (linux || darwin || dragonfly || freebsd || illumos || netbsd || openbsd) && !appengine && !tinygo
// +build linux darwin dragonfly freebsd illumos netbsd openbsd
// +build !appengine
// +build !tinygo
package msgp
-1
View File
@@ -1,5 +1,4 @@
//go:build windows || appengine || tinygo
// +build windows appengine tinygo
package msgp
+3 -3
View File
@@ -133,11 +133,11 @@ func putMuint8(b []byte, u uint8) {
_ = b[1] // bounds check elimination
b[0] = muint8
b[1] = byte(u)
b[1] = u
}
func getMuint8(b []byte) uint8 {
return uint8(b[1])
return b[1]
}
func getUnix(b []byte) (sec int64, nsec int32) {
@@ -161,7 +161,7 @@ func prefixu8(b []byte, pre byte, sz uint8) {
_ = b[1] // bounds check elimination
b[0] = pre
b[1] = byte(sz)
b[1] = sz
}
// write prefix and big-endian uint16
+386
View File
@@ -0,0 +1,386 @@
package msgp
import (
"cmp"
"fmt"
"iter"
"maps"
"math"
"slices"
)
// ReadArray returns an iterator that can be used to iterate over the elements
// of an array in the MessagePack data while being read by the provided Reader.
// The type parameter V specifies the type of the elements in the array.
// The returned iterator implements the iter.Seq[V] interface,
// allowing for sequential access to the array elements.
// The iterator will always stop after one error has been encountered.
func ReadArray[T any](m *Reader, readFn func() (T, error)) iter.Seq2[T, error] {
return func(yield func(T, error) bool) {
// Check if nil
if m.IsNil() {
m.ReadNil()
return
}
// Regular array.
var empty T
length, err := m.ReadArrayHeader()
if err != nil {
yield(empty, fmt.Errorf("cannot read array header: %w", err))
return
}
for range length {
var v T
v, err = readFn()
if !yield(v, err) || err != nil {
return
}
}
}
}
// WriteArray writes an array to the provided Writer.
// The writeFn parameter specifies the function to use to write each element of the array.
func WriteArray[T any](w *Writer, a []T, writeFn func(T) error) error {
// Check if nil
if a == nil {
return w.WriteNil()
}
if uint64(len(a)) > math.MaxUint32 {
return fmt.Errorf("array too large to encode: %d elements", len(a))
}
// Write array header
err := w.WriteArrayHeader(uint32(len(a)))
if err != nil {
return err
}
// Write elements
for _, v := range a {
err = writeFn(v)
if err != nil {
return err
}
}
return nil
}
// ReadMap returns an iterator that can be used to iterate over the elements
// of a map in the MessagePack data while being read by the provided Reader.
// The type parameters K and V specify the types of the keys and values in the map.
// The returned iterator implements the iter.Seq2[K, V] interface,
// allowing for sequential access to the map elements.
// The returned function can be used to read any error that
// occurred during iteration when iteration is done.
func ReadMap[K, V any](m *Reader, readKey func() (K, error), readVal func() (V, error)) (iter.Seq2[K, V], func() error) {
var err error
return func(yield func(K, V) bool) {
var sz uint32
if m.IsNil() {
err = m.ReadNil()
return
}
sz, err = m.ReadMapHeader()
if err != nil {
err = fmt.Errorf("cannot read map header: %w", err)
return
}
for range sz {
var k K
k, err = readKey()
if err != nil {
err = fmt.Errorf("cannot read key: %w", err)
return
}
var v V
v, err = readVal()
if err != nil {
err = fmt.Errorf("cannot read value: %w", err)
return
}
if !yield(k, v) {
return
}
}
}, func() error { return err }
}
// WriteMap writes a map to the provided Writer.
// The writeKey and writeVal parameters specify the functions
// to use to write each key and value of the map.
func WriteMap[K comparable, V any](w *Writer, m map[K]V, writeKey func(K) error, writeVal func(V) error) error {
if m == nil {
return w.WriteNil()
}
if uint64(len(m)) > math.MaxUint32 {
return fmt.Errorf("map too large to encode: %d elements", len(m))
}
// Write map header
err := w.WriteMapHeader(uint32(len(m)))
if err != nil {
return err
}
// Write elements
for k, v := range m {
err = writeKey(k)
if err != nil {
return err
}
err = writeVal(v)
if err != nil {
return err
}
}
return nil
}
// WriteMapSorted writes a map to the provided Writer.
// The keys of the map are sorted before writing.
// This provides deterministic output, but will allocate to sort the keys.
// The writeKey and writeVal parameters specify the functions
// to use to write each key and value of the map.
func WriteMapSorted[K cmp.Ordered, V any](w *Writer, m map[K]V, writeKey func(K) error, writeVal func(V) error) error {
if m == nil {
return w.WriteNil()
}
if uint64(len(m)) > math.MaxUint32 {
return fmt.Errorf("map too large to encode: %d elements", len(m))
}
// Write map header
err := w.WriteMapHeader(uint32(len(m)))
if err != nil {
return err
}
// Write elements
for _, k := range slices.Sorted(maps.Keys(m)) {
err = writeKey(k)
if err != nil {
return err
}
err = writeVal(m[k])
if err != nil {
return err
}
}
return nil
}
// ReadArrayBytes returns an iterator that can be used to iterate over the elements
// of an array in the MessagePack data while being read by the provided Reader.
// The type parameter V specifies the type of the elements in the array.
// After the iterator is exhausted, the remaining bytes in the buffer
// and any error can be read by calling the returned function.
func ReadArrayBytes[T any](b []byte, readFn func([]byte) (T, []byte, error)) (iter.Seq[T], func() (remain []byte, err error)) {
if IsNil(b) {
b, err := ReadNilBytes(b)
return func(yield func(T) bool) {}, func() ([]byte, error) { return b, err }
}
sz, b, err := ReadArrayHeaderBytes(b)
if err != nil || sz == 0 {
return func(yield func(T) bool) {}, func() ([]byte, error) { return b, err }
}
return func(yield func(T) bool) {
for range sz {
var v T
v, b, err = readFn(b)
if err != nil || !yield(v) {
return
}
}
}, func() ([]byte, error) {
return b, err
}
}
// AppendArray writes an array to the provided buffer.
// The writeFn parameter specifies the function to use to write each element of the array.
// The returned buffer contains the encoded array.
// The function panics if the array is larger than math.MaxUint32 elements.
func AppendArray[T any](b []byte, a []T, writeFn func(b []byte, v T) []byte) []byte {
if a == nil {
return AppendNil(b)
}
if uint64(len(a)) > math.MaxUint32 {
panic(fmt.Sprintf("array too large to encode: %d elements", len(a)))
}
b = AppendArrayHeader(b, uint32(len(a)))
for _, v := range a {
b = writeFn(b, v)
}
return b
}
// ReadMapBytes returns an iterator over key/value
// pairs from a MessagePack map encoded in b.
// The iterator yields K,V pairs, and this function also returns
// a closure to get the remaining bytes and any error.
func ReadMapBytes[K any, V any](b []byte,
readK func([]byte) (K, []byte, error),
readV func([]byte) (V, []byte, error)) (iter.Seq2[K, V], func() (remain []byte, err error)) {
var err error
var sz uint32
if IsNil(b) {
b, err = ReadNilBytes(b)
return func(yield func(K, V) bool) {}, func() ([]byte, error) { return b, err }
}
sz, b, err = ReadMapHeaderBytes(b)
if err != nil || sz == 0 {
return func(yield func(K, V) bool) {}, func() ([]byte, error) { return b, err }
}
return func(yield func(K, V) bool) {
for range sz {
var k K
k, b, err = readK(b)
if err != nil {
err = fmt.Errorf("cannot read map key: %w", err)
return
}
var v V
v, b, err = readV(b)
if err != nil {
err = fmt.Errorf("cannot read map value: %w", err)
return
}
if !yield(k, v) {
return
}
}
}, func() ([]byte, error) { return b, err }
}
// AppendMap writes a map to the provided buffer.
// The writeK and writeV parameters specify the functions to use to write each key and value of the map.
// The returned buffer contains the encoded map.
// The function panics if the map is larger than math.MaxUint32 elements.
func AppendMap[K comparable, V any](b []byte, m map[K]V,
writeK func(b []byte, k K) []byte,
writeV func(b []byte, v V) []byte) []byte {
if m == nil {
return AppendNil(b)
}
if uint64(len(m)) > math.MaxUint32 {
panic(fmt.Sprintf("map too large to encode: %d elements", len(m)))
}
b = AppendMapHeader(b, uint32(len(m)))
for k, v := range m {
b = writeK(b, k)
b = writeV(b, v)
}
return b
}
// AppendMapSorted writes a map to the provided buffer.
// Keys are sorted before writing.
// This provides deterministic output, but will allocate to sort the keys.
// The writeK and writeV parameters specify the functions to use to write each key and value of the map.
// The returned buffer contains the encoded map.
// The function panics if the map is larger than math.MaxUint32 elements.
func AppendMapSorted[K cmp.Ordered, V any](b []byte, m map[K]V,
writeK func(b []byte, k K) []byte,
writeV func(b []byte, v V) []byte) []byte {
if m == nil {
return AppendNil(b)
}
if uint64(len(m)) > math.MaxUint32 {
panic(fmt.Sprintf("map too large to encode: %d elements", len(m)))
}
b = AppendMapHeader(b, uint32(len(m)))
for _, k := range slices.Sorted(maps.Keys(m)) {
b = writeK(b, k)
b = writeV(b, m[k])
}
return b
}
// DecodePtr is a convenience type for decoding into a pointer.
type DecodePtr[T any] interface {
*T
Decodable
}
// DecoderFrom allows augmenting any type with a DecodeMsg method into a method
// that reads from Reader and returns a T.
// Provide an instance of T. This value isn't used.
// See ReadArray/ReadMap "struct" examples for usage.
func DecoderFrom[T any, PT DecodePtr[T]](r *Reader, _ T) func() (T, error) {
return func() (T, error) {
var t T
tPtr := PT(&t)
err := tPtr.DecodeMsg(r)
return t, err
}
}
// FlexibleEncoder is a constraint for types where either T or *T implements Encodable
type FlexibleEncoder[T any] interface {
Encodable
*T
}
// EncoderTo allows augmenting any type with an EncodeMsg
// method into a method that writes to Writer on each call.
// Provide an instance of T. This value isn't used.
// See ReadArray or ReadMap "struct" examples for usage.
func EncoderTo[T any, _ FlexibleEncoder[T]](w *Writer, _ T) func(T) error {
return func(t T) error {
// Check if T implements Marshaler
if marshaler, ok := any(t).(Encodable); ok {
return marshaler.EncodeMsg(w)
}
// Check if *T implements Marshaler
if ptrMarshaler, ok := any(&t).(Encodable); ok {
return ptrMarshaler.EncodeMsg(w)
}
// The compiler should have asserted this.
panic("type does not implement Marshaler")
}
}
// UnmarshalPtr is a convenience type for unmarshaling into a pointer.
type UnmarshalPtr[T any] interface {
*T
Unmarshaler
}
// DecoderFromBytes allows augmenting any type with an UnmarshalMsg
// method into a method that reads from []byte and returns a T.
// Provide an instance of T. This value isn't used.
// See ReadArrayBytes or ReadMapBytes "struct" examples for usage.
func DecoderFromBytes[T any, PT UnmarshalPtr[T]](_ T) func([]byte) (T, []byte, error) {
return func(b []byte) (T, []byte, error) {
var t T
tPtr := PT(&t)
b, err := tPtr.UnmarshalMsg(b)
return t, b, err
}
}
// FlexibleMarshaler is a constraint for types where either T or *T implements Marshaler
type FlexibleMarshaler[T any] interface {
Marshaler
*T // Include *T in the interface
}
// EncoderToBytes allows augmenting any type with a MarshalMsg method into a method
// that reads from T and returns a []byte.
// Provide an instance of T. This value isn't used.
// See ReadArrayBytes or ReadMapBytes "struct" examples for usage.
func EncoderToBytes[T any, _ FlexibleMarshaler[T]](_ T) func([]byte, T) []byte {
return func(b []byte, t T) []byte {
// Check if T implements Marshaler
if marshaler, ok := any(t).(Marshaler); ok {
b, _ = marshaler.MarshalMsg(b)
return b
}
// Check if *T implements Marshaler
if ptrMarshaler, ok := any(&t).(Marshaler); ok {
b, _ = ptrMarshaler.MarshalMsg(b)
return b
}
// The compiler should have asserted this.
panic("type does not implement Marshaler")
}
}
+7 -3
View File
@@ -60,7 +60,7 @@ func CopyToJSON(dst io.Writer, src io.Reader) (n int64, err error) {
// WriteToJSON translates MessagePack from 'r' and writes it as
// JSON to 'w' until the underlying reader returns io.EOF. It returns
// the number of bytes written, and an error if it stopped before EOF.
func (r *Reader) WriteToJSON(w io.Writer) (n int64, err error) {
func (m *Reader) WriteToJSON(w io.Writer) (n int64, err error) {
var j jsWriter
var bf *bufio.Writer
if jsw, ok := w.(jsWriter); ok {
@@ -71,7 +71,7 @@ func (r *Reader) WriteToJSON(w io.Writer) (n int64, err error) {
}
var nn int
for err == nil {
nn, err = rwNext(j, r)
nn, err = rwNext(j, m)
n += int64(nn)
}
if err != io.EOF {
@@ -364,7 +364,7 @@ func rwString(dst jsWriter, src *Reader) (n int, err error) {
if err != nil {
return
}
read = int(uint8(p[1]))
read = int(p[1])
case mstr16:
p, err = src.R.Next(3)
if err != nil {
@@ -382,6 +382,10 @@ func rwString(dst jsWriter, src *Reader) (n int, err error) {
return
}
write:
if uint64(read) > src.GetMaxStringLength() {
err = ErrLimitExceeded
return
}
p, err = src.R.Next(read)
if err != nil {
return
+2 -2
View File
@@ -91,7 +91,7 @@ func rwArrayBytes(w jsWriter, msg []byte, scratch []byte, depth int) ([]byte, []
if err != nil {
return msg, scratch, err
}
for i := uint32(0); i < sz; i++ {
for i := range sz {
if i != 0 {
err = w.WriteByte(',')
if err != nil {
@@ -119,7 +119,7 @@ func rwMapBytes(w jsWriter, msg []byte, scratch []byte, depth int) ([]byte, []by
if err != nil {
return msg, scratch, err
}
for i := uint32(0); i < sz; i++ {
for i := range sz {
if i != 0 {
err = w.WriteByte(',')
if err != nil {
+126 -2
View File
@@ -2,6 +2,7 @@ package msgp
import (
"math"
"math/bits"
"strconv"
)
@@ -77,7 +78,7 @@ func (n *Number) Uint() (uint64, bool) {
}
// Float casts the number to a float64, and
// returns whether or not that was the underlying
// returns whether that was the underlying
// type (either a float64 or a float32).
func (n *Number) Float() (float64, bool) {
switch n.typ {
@@ -182,7 +183,7 @@ func (n *Number) MarshalMsg(b []byte) ([]byte, error) {
case IntType:
return AppendInt64(b, int64(n.bits)), nil
case UintType:
return AppendUint64(b, uint64(n.bits)), nil
return AppendUint64(b, n.bits), nil
case Float64Type:
return AppendFloat64(b, math.Float64frombits(n.bits)), nil
case Float32Type:
@@ -208,6 +209,129 @@ func (n *Number) EncodeMsg(w *Writer) error {
}
}
// CoerceInt attempts to coerce the value of
// the number into a signed integer and returns
// whether it was successful.
// "Success" implies that no precision in the value of
// the number was lost, which means that the number was an integer or
// a floating point that mapped exactly to an integer without rounding.
func (n *Number) CoerceInt() (int64, bool) {
switch n.typ {
case InvalidType, IntType:
// InvalidType just means un-initialized.
return int64(n.bits), true
case UintType:
return int64(n.bits), n.bits <= math.MaxInt64
case Float32Type:
f := math.Float32frombits(uint32(n.bits))
if n.isExactInt() && f <= math.MaxInt64 && f >= math.MinInt64 {
return int64(f), true
}
if n.bits == 0 || n.bits == 1<<31 {
return 0, true
}
case Float64Type:
f := math.Float64frombits(n.bits)
if n.isExactInt() && f <= math.MaxInt64 && f >= math.MinInt64 {
return int64(f), true
}
return 0, n.bits == 0 || n.bits == 1<<63
}
return 0, false
}
// CoerceUInt attempts to coerce the value of
// the number into an unsigned integer and returns
// whether it was successful.
// "Success" implies that no precision in the value of
// the number was lost, which means that the number was an integer or
// a floating point that mapped exactly to an integer without rounding.
func (n *Number) CoerceUInt() (uint64, bool) {
switch n.typ {
case InvalidType, IntType:
// InvalidType just means un-initialized.
if int64(n.bits) >= 0 {
return n.bits, true
}
case UintType:
return n.bits, true
case Float32Type:
f := math.Float32frombits(uint32(n.bits))
if f >= 0 && f <= math.MaxUint64 && n.isExactInt() {
return uint64(f), true
}
if n.bits == 0 || n.bits == 1<<31 {
return 0, true
}
case Float64Type:
f := math.Float64frombits(n.bits)
if f >= 0 && f <= math.MaxUint64 && n.isExactInt() {
return uint64(f), true
}
return 0, n.bits == 0 || n.bits == 1<<63
}
return 0, false
}
// isExactInt will return true if the number represents an integer value.
// NaN, Inf returns false.
func (n *Number) isExactInt() bool {
var eBits int // Exponent bits
var mBits int // Mantissa bits
switch n.typ {
case InvalidType, IntType, UintType:
return true
case Float32Type:
eBits = 8
mBits = 23
case Float64Type:
eBits = 11
mBits = 52
default:
return false
}
// Calculate float parts
exp := int(n.bits>>mBits) & ((1 << eBits) - 1)
mant := n.bits & ((1 << mBits) - 1)
if exp == 0 && mant == 0 {
// Handle zero value.
return true
}
exp -= (1 << (eBits - 1)) - 1
if exp < 0 || exp == 1<<(eBits-1) {
// Negative exponent is never integer (except zero handled above)
// Handles NaN (exp all 1s)
return false
}
if exp >= mBits {
// If we have more exponent than mantissa bits it is always an integer.
return true
}
// Check if all bits below the exponent are zero.
return bits.TrailingZeros64(mant) >= mBits-exp
}
// CoerceFloat returns the number as a float64.
// If the number is an integer, it will be
// converted to a float64 with the closest representation.
func (n *Number) CoerceFloat() float64 {
switch n.typ {
case IntType:
return float64(int64(n.bits))
case UintType:
return float64(n.bits)
case Float32Type:
return float64(math.Float32frombits(uint32(n.bits)))
case Float64Type:
return math.Float64frombits(n.bits)
default:
return 0.0
}
}
// Msgsize implements msgp.Sizer
func (n *Number) Msgsize() int {
switch n.typ {
-1
View File
@@ -1,5 +1,4 @@
//go:build (purego && !unsafe) || appengine
// +build purego,!unsafe appengine
package msgp
+141 -16
View File
@@ -1,8 +1,10 @@
package msgp
import (
"encoding"
"encoding/binary"
"encoding/json"
"fmt"
"io"
"math"
"strconv"
@@ -13,7 +15,7 @@ import (
)
// where we keep old *Readers
var readerPool = sync.Pool{New: func() interface{} { return &Reader{} }}
var readerPool = sync.Pool{New: func() any { return &Reader{} }}
// Type is a MessagePack wire type,
// including this package's built-in
@@ -152,6 +154,10 @@ type Reader struct {
R *fwd.Reader
scratch []byte
recursionDepth int
maxRecursionDepth int // maximum recursion depth
maxElements uint32 // maximum number of elements in arrays and maps
maxStrLen uint64 // maximum number of bytes in any string
}
// Read implements `io.Reader`
@@ -171,7 +177,7 @@ func (m *Reader) CopyNext(w io.Writer) (int64, error) {
// Opportunistic optimization: if we can fit the whole thing in the m.R
// buffer, then just get a pointer to that, and pass it to w.Write,
// avoiding an allocation.
if int(sz) <= m.R.BufferSize() {
if int(sz) >= 0 && int(sz) <= m.R.BufferSize() {
var nn int
var buf []byte
buf, err = m.R.Next(int(sz))
@@ -203,7 +209,7 @@ func (m *Reader) CopyNext(w io.Writer) (int64, error) {
defer done()
}
// for maps and slices, read elements
for x := uintptr(0); x < o; x++ {
for range o {
var n2 int64
n2, err = m.CopyNext(w)
if err != nil {
@@ -214,10 +220,53 @@ func (m *Reader) CopyNext(w io.Writer) (int64, error) {
return n, nil
}
// SetMaxRecursionDepth sets the maximum recursion depth.
func (m *Reader) SetMaxRecursionDepth(d int) {
m.maxRecursionDepth = d
}
// GetMaxRecursionDepth returns the maximum recursion depth.
// Set to 0 to use the default value of 100000.
func (m *Reader) GetMaxRecursionDepth() int {
if m.maxRecursionDepth <= 0 {
return recursionLimit
}
return m.maxRecursionDepth
}
// SetMaxElements sets the maximum number of elements to allow in map, bin, array or extension payload.
// Setting this to 0 will allow any number of elements - math.MaxUint32.
// This does currently apply to generated code.
func (m *Reader) SetMaxElements(d uint32) {
m.maxElements = d
}
// GetMaxElements will return the maximum number of elements in a map, bin, array or extension payload.
func (m *Reader) GetMaxElements() uint32 {
if m.maxElements <= 0 {
return math.MaxUint32
}
return m.maxElements
}
// SetMaxStringLength sets the maximum number of bytes to allow in strings.
// Setting this == 0 will allow any number of elements - math.MaxUint64.
func (m *Reader) SetMaxStringLength(d uint64) {
m.maxStrLen = d
}
// GetMaxStringLength will return the current string length limit.
func (m *Reader) GetMaxStringLength() uint64 {
if m.maxStrLen <= 0 {
return math.MaxUint64
}
return min(m.maxStrLen, math.MaxUint64)
}
// recursiveCall will increment the recursion depth and return an error if it is exceeded.
// If a nil error is returned, done must be called to decrement the counter.
func (m *Reader) recursiveCall() (done func(), err error) {
if m.recursionDepth >= recursionLimit {
if m.recursionDepth >= m.GetMaxRecursionDepth() {
return func() {}, ErrRecursion
}
m.recursionDepth++
@@ -415,7 +464,11 @@ func (m *Reader) ReadMapKey(scratch []byte) ([]byte, error) {
out, err := m.ReadStringAsBytes(scratch)
if err != nil {
if tperr, ok := err.(TypeError); ok && tperr.Encoded == BinType {
return m.ReadBytes(scratch)
key, err := m.ReadBytes(scratch)
if uint64(len(key)) > m.GetMaxStringLength() {
return nil, ErrLimitExceeded
}
return key, err
}
return nil, err
}
@@ -468,6 +521,9 @@ fill:
if read == 0 {
return nil, ErrShortBytes
}
if uint64(read) > m.GetMaxStringLength() {
return nil, ErrLimitExceeded
}
return m.R.Next(read)
}
@@ -528,7 +584,7 @@ func (m *Reader) ReadFloat64() (f float64, err error) {
var p []byte
p, err = m.R.Peek(9)
if err != nil {
// we'll allow a coversion from float32 to float64,
// we'll allow a conversion from float32 to float64,
// since we don't lose any precision
if err == io.EOF && len(p) > 0 && p[0] == mfloat32 {
ef, err := m.ReadFloat32()
@@ -816,7 +872,7 @@ func (m *Reader) ReadUint64() (u uint64, err error) {
if err != nil {
return
}
v := int64(getMint64(p))
v := getMint64(p)
if v < 0 {
err = UintBelowZero{Value: v}
return
@@ -941,6 +997,10 @@ func (m *Reader) ReadBytes(scratch []byte) (b []byte, err error) {
return
}
if int64(cap(scratch)) < read {
if read > int64(m.GetMaxElements()) {
err = ErrLimitExceeded
return
}
b = make([]byte, read)
} else {
b = scratch[0:read]
@@ -980,10 +1040,10 @@ func (m *Reader) ReadBytesHeader() (sz uint32, err error) {
if err != nil {
return
}
sz = uint32(big.Uint32(p[1:]))
sz = big.Uint32(p[1:])
return
default:
err = badPrefix(BinType, p[0])
err = badPrefix(BinType, lead)
return
}
}
@@ -1052,7 +1112,7 @@ func (m *Reader) ReadStringAsBytes(scratch []byte) (b []byte, err error) {
if err != nil {
return
}
read = int64(uint8(p[1]))
read = int64(p[1])
case mstr16:
p, err = m.R.Next(3)
if err != nil {
@@ -1070,6 +1130,10 @@ func (m *Reader) ReadStringAsBytes(scratch []byte) (b []byte, err error) {
return
}
fill:
if uint64(read) > m.GetMaxStringLength() {
err = ErrLimitExceeded
return
}
if int64(cap(scratch)) < read {
b = make([]byte, read)
} else {
@@ -1143,7 +1207,7 @@ func (m *Reader) ReadString() (s string, err error) {
if err != nil {
return
}
read = int64(uint8(p[1]))
read = int64(p[1])
case mstr16:
p, err = m.R.Next(3)
if err != nil {
@@ -1165,6 +1229,11 @@ fill:
s, err = "", nil
return
}
if uint64(read) > m.GetMaxStringLength() {
err = ErrLimitExceeded
return
}
// reading into the memory
// that will become the string
// itself has vastly superior
@@ -1235,7 +1304,7 @@ func (m *Reader) ReadComplex128() (f complex128, err error) {
// ReadMapStrIntf reads a MessagePack map into a map[string]interface{}.
// (You must pass a non-nil map into the function.)
func (m *Reader) ReadMapStrIntf(mp map[string]interface{}) (err error) {
func (m *Reader) ReadMapStrIntf(mp map[string]any) (err error) {
var sz uint32
sz, err = m.ReadMapHeader()
if err != nil {
@@ -1244,9 +1313,13 @@ func (m *Reader) ReadMapStrIntf(mp map[string]interface{}) (err error) {
for key := range mp {
delete(mp, key)
}
if sz > m.GetMaxElements() {
err = ErrLimitExceeded
return
}
for i := uint32(0); i < sz; i++ {
var key string
var val interface{}
var val any
key, err = m.ReadString()
if err != nil {
return
@@ -1376,7 +1449,7 @@ func (m *Reader) ReadJSONNumber() (n json.Number, err error) {
// Arrays are decoded as []interface{}, and maps are decoded
// as map[string]interface{}. Integers are decoded as int64
// and unsigned integers are decoded as uint64.
func (m *Reader) ReadIntf() (i interface{}, err error) {
func (m *Reader) ReadIntf() (i any, err error) {
var t Type
t, err = m.NextType()
if err != nil {
@@ -1446,7 +1519,7 @@ func (m *Reader) ReadIntf() (i interface{}, err error) {
defer done()
}
mp := make(map[string]interface{})
mp := make(map[string]any)
err = m.ReadMapStrIntf(mp)
i = mp
return
@@ -1477,8 +1550,12 @@ func (m *Reader) ReadIntf() (i interface{}, err error) {
} else {
defer done()
}
if sz > m.GetMaxElements() {
err = ErrLimitExceeded
return
}
out := make([]interface{}, int(sz))
out := make([]any, int(sz))
for j := range out {
out[j], err = m.ReadIntf()
if err != nil {
@@ -1492,3 +1569,51 @@ func (m *Reader) ReadIntf() (i interface{}, err error) {
return nil, fatal // unreachable
}
}
// ReadBinaryUnmarshal reads a binary-encoded object from the reader and unmarshals it into dst.
func (m *Reader) ReadBinaryUnmarshal(dst encoding.BinaryUnmarshaler) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("msgp: panic during UnmarshalBinary: %v", r)
}
}()
tmp := bytesPool.Get().([]byte)
defer bytesPool.Put(tmp) //nolint:staticcheck
tmp, err = m.ReadBytes(tmp[:0])
if err != nil {
return
}
return dst.UnmarshalBinary(tmp)
}
// ReadTextUnmarshal reads a text-encoded bin array from the reader and unmarshals it into dst.
func (m *Reader) ReadTextUnmarshal(dst encoding.TextUnmarshaler) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("msgp: panic during UnmarshalText: %v", r)
}
}()
tmp := bytesPool.Get().([]byte)
defer bytesPool.Put(tmp) //nolint:staticcheck
tmp, err = m.ReadBytes(tmp[:0])
if err != nil {
return
}
return dst.UnmarshalText(tmp)
}
// ReadTextUnmarshalString reads a text-encoded string from the reader and unmarshals it into dst.
func (m *Reader) ReadTextUnmarshalString(dst encoding.TextUnmarshaler) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("msgp: panic during UnmarshalText: %v", r)
}
}()
tmp := bytesPool.Get().([]byte)
defer bytesPool.Put(tmp) //nolint:staticcheck
tmp, err = m.ReadStringAsBytes(tmp[:0])
if err != nil {
return
}
return dst.UnmarshalText(tmp)
}
+19 -10
View File
@@ -1130,7 +1130,7 @@ func ReadTimeBytes(b []byte) (t time.Time, o []byte, err error) {
return
}
default:
err = errExt(int8(b[2]), TimeExtension)
err = errExt(typ, TimeExtension)
return
}
}
@@ -1138,11 +1138,11 @@ func ReadTimeBytes(b []byte) (t time.Time, o []byte, err error) {
// ReadMapStrIntfBytes reads a map[string]interface{}
// out of 'b' and returns the map and remaining bytes.
// If 'old' is non-nil, the values will be read into that map.
func ReadMapStrIntfBytes(b []byte, old map[string]interface{}) (v map[string]interface{}, o []byte, err error) {
func ReadMapStrIntfBytes(b []byte, old map[string]any) (v map[string]any, o []byte, err error) {
return readMapStrIntfBytesDepth(b, old, 0)
}
func readMapStrIntfBytesDepth(b []byte, old map[string]interface{}, depth int) (v map[string]interface{}, o []byte, err error) {
func readMapStrIntfBytesDepth(b []byte, old map[string]any, depth int) (v map[string]any, o []byte, err error) {
if depth >= recursionLimit {
err = ErrRecursion
return
@@ -1155,14 +1155,18 @@ func readMapStrIntfBytesDepth(b []byte, old map[string]interface{}, depth int) (
if err != nil {
return
}
// Map key, min size is 2 bytes. Value min 1 byte.
if int64(len(b)) < int64(sz)*3 {
err = ErrShortBytes
return
}
if old != nil {
for key := range old {
delete(old, key)
}
v = old
} else {
v = make(map[string]interface{}, int(sz))
v = make(map[string]any, int(sz))
}
for z := uint32(0); z < sz; z++ {
@@ -1175,7 +1179,7 @@ func readMapStrIntfBytesDepth(b []byte, old map[string]interface{}, depth int) (
if err != nil {
return
}
var val interface{}
var val any
val, o, err = readIntfBytesDepth(o, depth)
if err != nil {
return
@@ -1188,11 +1192,11 @@ func readMapStrIntfBytesDepth(b []byte, old map[string]interface{}, depth int) (
// ReadIntfBytes attempts to read
// the next object out of 'b' as a raw interface{} and
// return the remaining bytes.
func ReadIntfBytes(b []byte) (i interface{}, o []byte, err error) {
func ReadIntfBytes(b []byte) (i any, o []byte, err error) {
return readIntfBytesDepth(b, 0)
}
func readIntfBytesDepth(b []byte, depth int) (i interface{}, o []byte, err error) {
func readIntfBytesDepth(b []byte, depth int) (i any, o []byte, err error) {
if depth >= recursionLimit {
err = ErrRecursion
return
@@ -1215,7 +1219,12 @@ func readIntfBytesDepth(b []byte, depth int) (i interface{}, o []byte, err error
if err != nil {
return
}
j := make([]interface{}, int(sz))
// Each element will at least be 1 byte.
if uint32(len(o)) < sz {
err = ErrShortBytes
return
}
j := make([]any, int(sz))
i = j
for d := range j {
j[d], o, err = readIntfBytesDepth(o, depth+1)
@@ -1274,7 +1283,7 @@ func readIntfBytesDepth(b []byte, depth int) (i interface{}, o []byte, err error
}
// last resort is a raw extension
e := RawExtension{}
e.Type = int8(t)
e.Type = t
o, err = ReadExtensionBytes(b, &e)
i = &e
return
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
// Package setof allows serializing sets map[T]struct{} as arrays.
//
// Nil maps are preserved as a nil value on stream.
//
// A deterministic, sorted version is available, with slightly lower performance.
package setof
// ensure 'sz' extra bytes in 'b' can be appended without reallocating
func ensure(b []byte, sz int) []byte {
l := len(b)
c := cap(b)
if c-l < sz {
o := make([]byte, l, l+sz)
copy(o, b)
return o
}
return b
}
+9
View File
@@ -37,4 +37,13 @@ const (
BytesPrefixSize = 5
StringPrefixSize = 5
ExtensionPrefixSize = 6
// We cannot determine the exact size of the marshalled bytes,
// so we assume 32 bytes
BinaryMarshalerSize = BytesPrefixSize + 32
BinaryAppenderSize
TextMarshalerBinSize
TextAppenderBinSize
TextMarshalerStringSize = StringPrefixSize + 32
TextAppenderStringSize
)
-1
View File
@@ -1,5 +1,4 @@
//go:build (!purego && !appengine) || (!appengine && purego && unsafe)
// +build !purego,!appengine !appengine,purego,unsafe
package msgp
+74 -8
View File
@@ -1,9 +1,11 @@
package msgp
import (
"encoding"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"reflect"
@@ -33,7 +35,7 @@ var (
btsType = reflect.TypeOf(([]byte)(nil))
writerPool = sync.Pool{
New: func() interface{} {
New: func() any {
return &Writer{buf: make([]byte, 2048)}
},
}
@@ -430,7 +432,7 @@ func (mw *Writer) WriteUint64(u uint64) error {
}
// WriteByte is analogous to WriteUint8
func (mw *Writer) WriteByte(u byte) error { return mw.WriteUint8(uint8(u)) }
func (mw *Writer) WriteByte(u byte) error { return mw.WriteUint8(u) }
// WriteUint8 writes a uint8 to the writer
func (mw *Writer) WriteUint8(u uint8) error { return mw.WriteUint64(uint64(u)) }
@@ -446,6 +448,9 @@ func (mw *Writer) WriteUint(u uint) error { return mw.WriteUint64(uint64(u)) }
// WriteBytes writes binary as 'bin' to the writer
func (mw *Writer) WriteBytes(b []byte) error {
if uint64(len(b)) > math.MaxUint32 {
return ErrLimitExceeded
}
sz := uint32(len(b))
var err error
switch {
@@ -488,6 +493,10 @@ func (mw *Writer) WriteBool(b bool) error {
// WriteString writes a messagepack string to the writer.
// (This is NOT an implementation of io.StringWriter)
func (mw *Writer) WriteString(s string) error {
if uint64(len(s)) > math.MaxUint32 {
return ErrLimitExceeded
}
sz := uint32(len(s))
var err error
switch {
@@ -526,6 +535,9 @@ func (mw *Writer) WriteStringHeader(sz uint32) error {
// WriteStringFromBytes writes a 'str' object
// from a []byte.
func (mw *Writer) WriteStringFromBytes(str []byte) error {
if uint64(len(str)) > math.MaxUint32 {
return ErrLimitExceeded
}
sz := uint32(len(str))
var err error
switch {
@@ -591,7 +603,7 @@ func (mw *Writer) WriteMapStrStr(mp map[string]string) (err error) {
}
// WriteMapStrIntf writes a map[string]interface to the writer
func (mw *Writer) WriteMapStrIntf(mp map[string]interface{}) (err error) {
func (mw *Writer) WriteMapStrIntf(mp map[string]any) (err error) {
err = mw.WriteMapHeader(uint32(len(mp)))
if err != nil {
return
@@ -703,7 +715,7 @@ func (mw *Writer) WriteJSONNumber(n json.Number) error {
// - A pointer to a supported type
// - A type that satisfies the msgp.Encodable interface
// - A type that satisfies the msgp.Extension interface
func (mw *Writer) WriteIntf(v interface{}) error {
func (mw *Writer) WriteIntf(v any) error {
if v == nil {
return mw.WriteNil()
}
@@ -754,7 +766,7 @@ func (mw *Writer) WriteIntf(v interface{}) error {
return mw.WriteBytes(v)
case map[string]string:
return mw.WriteMapStrStr(v)
case map[string]interface{}:
case map[string]any:
return mw.WriteMapStrIntf(v)
case time.Time:
return mw.WriteTime(v)
@@ -817,7 +829,7 @@ func (mw *Writer) writeSlice(v reflect.Value) (err error) {
if err != nil {
return
}
for i := uint32(0); i < sz; i++ {
for i := range sz {
err = mw.WriteIntf(v.Index(int(i)).Interface())
if err != nil {
return
@@ -840,7 +852,7 @@ func isSupported(k reflect.Kind) bool {
// value of 'i'. If the underlying value is not
// a simple builtin (or []byte), GuessSize defaults
// to 512.
func GuessSize(i interface{}) int {
func GuessSize(i any) int {
if i == nil {
return NilSize
}
@@ -868,7 +880,7 @@ func GuessSize(i interface{}) int {
return Complex128Size
case bool:
return BoolSize
case map[string]interface{}:
case map[string]any:
s := MapHeaderSize
for key, val := range i {
s += StringPrefixSize + len(key) + GuessSize(val)
@@ -884,3 +896,57 @@ func GuessSize(i interface{}) int {
return 512
}
}
// Temporary buffer for reading/writing binary data.
var bytesPool = sync.Pool{New: func() any { return make([]byte, 0, 1024) }}
// WriteBinaryAppender will write the bytes from the given
// encoding.BinaryAppender as a bin array.
func (mw *Writer) WriteBinaryAppender(b encoding.BinaryAppender) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("msgp: panic during AppendBinary: %v", r)
}
}()
dst := bytesPool.Get().([]byte)
defer bytesPool.Put(dst) //nolint:staticcheck
dst, err = b.AppendBinary(dst[:0])
if err != nil {
return err
}
return mw.WriteBytes(dst)
}
// WriteTextAppender will write the bytes from the given
// encoding.TextAppender as a bin array.
func (mw *Writer) WriteTextAppender(b encoding.TextAppender) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("msgp: panic during AppendText: %v", r)
}
}()
dst := bytesPool.Get().([]byte)
defer bytesPool.Put(dst) //nolint:staticcheck
dst, err = b.AppendText(dst[:0])
if err != nil {
return err
}
return mw.WriteBytes(dst)
}
// WriteTextAppenderString will write the bytes from the given
// encoding.TextAppender as a string.
func (mw *Writer) WriteTextAppenderString(b encoding.TextAppender) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("msgp: panic during AppendText: %v", r)
}
}()
dst := bytesPool.Get().([]byte)
defer bytesPool.Put(dst) //nolint:staticcheck
dst, err = b.AppendText(dst[:0])
if err != nil {
return err
}
return mw.WriteStringFromBytes(dst)
}
+52 -6
View File
@@ -181,7 +181,7 @@ func AppendUint(b []byte, u uint) []byte { return AppendUint64(b, uint64(u)) }
func AppendUint8(b []byte, u uint8) []byte { return AppendUint64(b, uint64(u)) }
// AppendByte is analogous to AppendUint8
func AppendByte(b []byte, u byte) []byte { return AppendUint8(b, uint8(u)) }
func AppendByte(b []byte, u byte) []byte { return AppendUint8(b, u) }
// AppendUint16 appends a uint16 to the slice
func AppendUint16(b []byte, u uint16) []byte { return AppendUint64(b, uint64(u)) }
@@ -371,7 +371,7 @@ func AppendMapStrStr(b []byte, m map[string]string) []byte {
// AppendMapStrIntf appends a map[string]interface{} to the slice
// as a MessagePack map with 'str'-type keys.
func AppendMapStrIntf(b []byte, m map[string]interface{}) ([]byte, error) {
func AppendMapStrIntf(b []byte, m map[string]any) ([]byte, error) {
sz := uint32(len(m))
b = AppendMapHeader(b, sz)
var err error
@@ -394,7 +394,7 @@ func AppendMapStrIntf(b []byte, m map[string]interface{}) ([]byte, error) {
// - A *T, where T is another supported type
// - A type that satisfies the msgp.Marshaler interface
// - A type that satisfies the msgp.Extension interface
func AppendIntf(b []byte, i interface{}) ([]byte, error) {
func AppendIntf(b []byte, i any) ([]byte, error) {
if i == nil {
return AppendNil(b), nil
}
@@ -444,13 +444,13 @@ func AppendIntf(b []byte, i interface{}) ([]byte, error) {
return AppendTime(b, i), nil
case time.Duration:
return AppendDuration(b, i), nil
case map[string]interface{}:
case map[string]any:
return AppendMapStrIntf(b, i)
case map[string]string:
return AppendMapStrStr(b, i), nil
case json.Number:
return AppendJSONNumber(b, i)
case []interface{}:
case []any:
b = AppendArrayHeader(b, uint32(len(i)))
var err error
for _, k := range i {
@@ -483,7 +483,7 @@ func AppendIntf(b []byte, i interface{}) ([]byte, error) {
case reflect.Array, reflect.Slice:
l := v.Len()
b = AppendArrayHeader(b, uint32(l))
for i := 0; i < l; i++ {
for i := range l {
b, err = AppendIntf(b, v.Index(i).Interface())
if err != nil {
return b, err
@@ -518,3 +518,49 @@ func AppendJSONNumber(b []byte, n json.Number) ([]byte, error) {
}
return b, err
}
// AppendBytesTwoPrefixed will add the length to a bin section written with
// 2 bytes of space saved for a bin8 header.
// If the sz cannot fit inside a bin8, the data will be moved to make space for the header.
func AppendBytesTwoPrefixed(b []byte, sz int) []byte {
off := len(b) - sz - 2
switch {
case sz <= math.MaxUint8:
// Just write header...
prefixu8(b[off:], mbin8, uint8(sz))
case sz <= math.MaxUint16:
// Scoot one
b = append(b, 0)
copy(b[off+1:], b[off:])
prefixu16(b[off:], mbin16, uint16(sz))
default:
// Scoot three
b = append(b, 0, 0, 0)
copy(b[off+3:], b[off:])
prefixu32(b[off:], mbin32, uint32(sz))
}
return b
}
// AppendBytesStringTwoPrefixed will add the length to a string section written with
// 2 bytes of space saved for a str8 header.
// If the sz cannot fit inside a str8, the data will be moved to make space for the header.
func AppendBytesStringTwoPrefixed(b []byte, sz int) []byte {
off := len(b) - sz - 2
switch {
case sz <= math.MaxUint8:
// Just write header...
prefixu8(b[off:], mstr8, uint8(sz))
case sz <= math.MaxUint16:
// Scoot one
b = append(b, 0)
copy(b[off+1:], b[off:])
prefixu16(b[off:], mstr16, uint16(sz))
default:
// Scoot three
b = append(b, 0, 0, 0)
copy(b[off+3:], b[off:])
prefixu32(b[off:], mstr32, uint32(sz))
}
return b
}