Bump reva to pull in the latest fixes and improvements
This commit is contained in:
-1
@@ -1,5 +1,4 @@
|
||||
//go:build linux && !appengine && !tinygo
|
||||
// +build linux,!appengine,!tinygo
|
||||
|
||||
package msgp
|
||||
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
//go:build (!linux && !tinygo && !windows) || appengine
|
||||
// +build !linux,!tinygo,!windows appengine
|
||||
|
||||
package msgp
|
||||
|
||||
|
||||
+159
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,5 +1,4 @@
|
||||
//go:build tinygo
|
||||
// +build tinygo
|
||||
|
||||
package msgp
|
||||
|
||||
|
||||
+12
-3
@@ -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
@@ -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
@@ -1,5 +1,4 @@
|
||||
//go:build tinygo
|
||||
// +build tinygo
|
||||
|
||||
package msgp
|
||||
|
||||
|
||||
+10
-4
@@ -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
@@ -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
@@ -1,5 +1,4 @@
|
||||
//go:build windows || appengine || tinygo
|
||||
// +build windows appengine tinygo
|
||||
|
||||
package msgp
|
||||
|
||||
|
||||
+3
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,5 +1,4 @@
|
||||
//go:build (purego && !unsafe) || appengine
|
||||
// +build purego,!unsafe appengine
|
||||
|
||||
package msgp
|
||||
|
||||
|
||||
+141
-16
@@ -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
@@ -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
|
||||
|
||||
+3989
File diff suppressed because it is too large
Load Diff
+19
@@ -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
@@ -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
@@ -1,5 +1,4 @@
|
||||
//go:build (!purego && !appengine) || (!appengine && purego && unsafe)
|
||||
// +build !purego,!appengine !appengine,purego,unsafe
|
||||
|
||||
package msgp
|
||||
|
||||
|
||||
+74
-8
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user