Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Masayuki Shamoto
Copyright (c) 2025 Masayuki Shamoto

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* Renaming fields via `msgpack:"field_name"`
* Omitting fields via `msgpack:"-"`
* Omitting empty fields via `msgpack:"field_name,omitempty"`
* Supports extend encoder / decoder
* Supports extend encoder / decoder [(example)](./msgpack_example_test.go)
* Can also Encoding / Decoding struct as array

## Installation
Expand Down
21 changes: 21 additions & 0 deletions ext/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,56 @@ import (
"github.com/shamaton/msgpack/v2/def"
)

// Decoder defines an interface for decoding values from bytes.
// It provides methods to get the decoder type, check if the data matches the type,
// and convert the data into a Go value.
type Decoder interface {
// Code returns the unique code representing the decoder type.
Code() int8

// IsType checks if the data at the given offset matches the expected type.
// Returns true if the type matches, false otherwise.
IsType(offset int, d *[]byte) bool

// AsValue decodes the data at the given offset into a Go value of the specified kind.
// Returns the decoded value, the new offset, and an error if decoding fails.
AsValue(offset int, k reflect.Kind, d *[]byte) (interface{}, int, error)
}

// DecoderCommon provides common utility methods for decoding data from bytes.
type DecoderCommon struct {
}

// ReadSize1 reads a single byte from the given index in the byte slice.
// Returns the byte and the new index after reading.
func (cd *DecoderCommon) ReadSize1(index int, d *[]byte) (byte, int) {
rb := def.Byte1
return (*d)[index], index + rb
}

// ReadSize2 reads two bytes from the given index in the byte slice.
// Returns the bytes as a slice and the new index after reading.
func (cd *DecoderCommon) ReadSize2(index int, d *[]byte) ([]byte, int) {
rb := def.Byte2
return (*d)[index : index+rb], index + rb
}

// ReadSize4 reads four bytes from the given index in the byte slice.
// Returns the bytes as a slice and the new index after reading.
func (cd *DecoderCommon) ReadSize4(index int, d *[]byte) ([]byte, int) {
rb := def.Byte4
return (*d)[index : index+rb], index + rb
}

// ReadSize8 reads eight bytes from the given index in the byte slice.
// Returns the bytes as a slice and the new index after reading.
func (cd *DecoderCommon) ReadSize8(index int, d *[]byte) ([]byte, int) {
rb := def.Byte8
return (*d)[index : index+rb], index + rb
}

// ReadSizeN reads a specified number of bytes (n) from the given index in the byte slice.
// Returns the bytes as a slice and the new index after reading.
func (cd *DecoderCommon) ReadSizeN(index, n int, d *[]byte) ([]byte, int) {
return (*d)[index : index+n], index + n
}
11 changes: 11 additions & 0 deletions ext/decoder_stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@ import (
"reflect"
)

// StreamDecoder defines an interface for decoding streams of data.
// It provides methods to retrieve the decoder's code, check type compatibility,
// and convert raw data into a Go value of a specified kind.
type StreamDecoder interface {
// Code returns the unique identifier for the decoder.
Code() int8

// IsType checks if the provided code, inner type, and data length match the expected type.
// Returns true if the type matches, otherwise false.
IsType(code byte, innerType int8, dataLength int) bool

// ToValue converts the raw data into a Go value of the specified kind.
// Takes the code, raw data, and the target kind as input.
// Returns the decoded value or an error if the conversion fails.
ToValue(code byte, data []byte, k reflect.Kind) (any, error)
}
41 changes: 41 additions & 0 deletions ext/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,48 @@ import (
"reflect"
)

// Encoder defines an interface for encoding values into bytes.
// It provides methods to get the encoding type, calculate the byte size of a value,
// and write the encoded value into a byte slice.
type Encoder interface {
// Code returns the unique code representing the encoder type.
Code() int8

// Type returns the reflect.Type of the value that the encoder handles.
Type() reflect.Type

// CalcByteSize calculates the number of bytes required to encode the given value.
// Returns the size and an error if the calculation fails.
CalcByteSize(value reflect.Value) (int, error)

// WriteToBytes encodes the given value into a byte slice starting at the specified offset.
// Returns the new offset after writing the bytes.
WriteToBytes(value reflect.Value, offset int, bytes *[]byte) int
}

// EncoderCommon provides utility methods for encoding various types of values into bytes.
// It includes methods to encode integers and unsigned integers of different sizes,
// as well as methods to write raw byte slices into a target byte slice.
type EncoderCommon struct {
}

// SetByte1Int64 encodes a single byte from the given int64 value into the byte slice at the specified offset.
// Returns the new offset after writing the byte.
func (c *EncoderCommon) SetByte1Int64(value int64, offset int, d *[]byte) int {
(*d)[offset] = byte(value)
return offset + 1
}

// SetByte2Int64 encodes the lower two bytes of the given int64 value into the byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetByte2Int64(value int64, offset int, d *[]byte) int {
(*d)[offset+0] = byte(value >> 8)
(*d)[offset+1] = byte(value)
return offset + 2
}

// SetByte4Int64 encodes the lower four bytes of the given int64 value into the byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetByte4Int64(value int64, offset int, d *[]byte) int {
(*d)[offset+0] = byte(value >> 24)
(*d)[offset+1] = byte(value >> 16)
Expand All @@ -33,6 +54,8 @@ func (c *EncoderCommon) SetByte4Int64(value int64, offset int, d *[]byte) int {
return offset + 4
}

// SetByte8Int64 encodes all eight bytes of the given int64 value into the byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetByte8Int64(value int64, offset int, d *[]byte) int {
(*d)[offset] = byte(value >> 56)
(*d)[offset+1] = byte(value >> 48)
Expand All @@ -45,17 +68,23 @@ func (c *EncoderCommon) SetByte8Int64(value int64, offset int, d *[]byte) int {
return offset + 8
}

// SetByte1Uint64 encodes a single byte from the given uint64 value into the byte slice at the specified offset.
// Returns the new offset after writing the byte.
func (c *EncoderCommon) SetByte1Uint64(value uint64, offset int, d *[]byte) int {
(*d)[offset] = byte(value)
return offset + 1
}

// SetByte2Uint64 encodes the lower two bytes of the given uint64 value into the byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetByte2Uint64(value uint64, offset int, d *[]byte) int {
(*d)[offset] = byte(value >> 8)
(*d)[offset+1] = byte(value)
return offset + 2
}

// SetByte4Uint64 encodes the lower four bytes of the given uint64 value into the byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetByte4Uint64(value uint64, offset int, d *[]byte) int {
(*d)[offset] = byte(value >> 24)
(*d)[offset+1] = byte(value >> 16)
Expand All @@ -64,6 +93,8 @@ func (c *EncoderCommon) SetByte4Uint64(value uint64, offset int, d *[]byte) int
return offset + 4
}

// SetByte8Uint64 encodes all eight bytes of the given uint64 value into the byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetByte8Uint64(value uint64, offset int, d *[]byte) int {
(*d)[offset] = byte(value >> 56)
(*d)[offset+1] = byte(value >> 48)
Expand All @@ -76,17 +107,23 @@ func (c *EncoderCommon) SetByte8Uint64(value uint64, offset int, d *[]byte) int
return offset + 8
}

// SetByte1Int encodes a single byte from the given int value into the byte slice at the specified offset.
// Returns the new offset after writing the byte.
func (c *EncoderCommon) SetByte1Int(code, offset int, d *[]byte) int {
(*d)[offset] = byte(code)
return offset + 1
}

// SetByte2Int encodes the lower two bytes of the given int value into the byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetByte2Int(value int, offset int, d *[]byte) int {
(*d)[offset] = byte(value >> 8)
(*d)[offset+1] = byte(value)
return offset + 2
}

// SetByte4Int encodes the lower four bytes of the given int value into the byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetByte4Int(value int, offset int, d *[]byte) int {
(*d)[offset] = byte(value >> 24)
(*d)[offset+1] = byte(value >> 16)
Expand All @@ -95,6 +132,8 @@ func (c *EncoderCommon) SetByte4Int(value int, offset int, d *[]byte) int {
return offset + 4
}

// SetByte4Uint32 encodes the lower four bytes of the given uint32 value into the byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetByte4Uint32(value uint32, offset int, d *[]byte) int {
(*d)[offset] = byte(value >> 24)
(*d)[offset+1] = byte(value >> 16)
Expand All @@ -103,6 +142,8 @@ func (c *EncoderCommon) SetByte4Uint32(value uint32, offset int, d *[]byte) int
return offset + 4
}

// SetBytes writes the given byte slice `bs` into the target byte slice at the specified offset.
// Returns the new offset after writing the bytes.
func (c *EncoderCommon) SetBytes(bs []byte, offset int, d *[]byte) int {
for i := range bs {
(*d)[offset+i] = bs[i]
Expand Down
27 changes: 23 additions & 4 deletions ext/encode_stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,45 @@ import (
"github.com/shamaton/msgpack/v2/internal/common"
)

// StreamEncoder is interface that extended encoder should implement
// StreamEncoder is an interface that extended encoders should implement.
// It defines methods for encoding data into a stream.
type StreamEncoder interface {
// Code returns the unique code for the encoder.
Code() int8
// Type returns the reflect.Type of the value being encoded.
Type() reflect.Type
// Write encodes the given value and writes it to the provided StreamWriter.
Write(w StreamWriter, value reflect.Value) error
}

// StreamWriter is provided some writing functions for extended format by user
// StreamWriter provides methods for writing data in extended formats.
// It wraps an io.Writer and a buffer for efficient writing.
type StreamWriter struct {
w io.Writer
buf *common.Buffer
w io.Writer // The underlying writer to write data to.
buf *common.Buffer // A buffer used for temporary storage during writing.
}

// CreateStreamWriter creates and returns a new StreamWriter instance.
func CreateStreamWriter(w io.Writer, buf *common.Buffer) StreamWriter {
return StreamWriter{w, buf}
}

// WriteByte1Int64 writes a single byte representation of an int64 value.
func (w *StreamWriter) WriteByte1Int64(value int64) error {
return w.buf.Write(w.w,
byte(value),
)
}

// WriteByte2Int64 writes a two-byte representation of an int64 value.
func (w *StreamWriter) WriteByte2Int64(value int64) error {
return w.buf.Write(w.w,
byte(value>>8),
byte(value),
)
}

// WriteByte4Int64 writes a four-byte representation of an int64 value.
func (w *StreamWriter) WriteByte4Int64(value int64) error {
return w.buf.Write(w.w,
byte(value>>24),
Expand All @@ -46,6 +55,7 @@ func (w *StreamWriter) WriteByte4Int64(value int64) error {
)
}

// WriteByte8Int64 writes an eight-byte representation of an int64 value.
func (w *StreamWriter) WriteByte8Int64(value int64) error {
return w.buf.Write(w.w,
byte(value>>56),
Expand All @@ -59,19 +69,22 @@ func (w *StreamWriter) WriteByte8Int64(value int64) error {
)
}

// WriteByte1Uint64 writes a single byte representation of a uint64 value.
func (w *StreamWriter) WriteByte1Uint64(value uint64) error {
return w.buf.Write(w.w,
byte(value),
)
}

// WriteByte2Uint64 writes a two-byte representation of a uint64 value.
func (w *StreamWriter) WriteByte2Uint64(value uint64) error {
return w.buf.Write(w.w,
byte(value>>8),
byte(value),
)
}

// WriteByte4Uint64 writes a four-byte representation of a uint64 value.
func (w *StreamWriter) WriteByte4Uint64(value uint64) error {
return w.buf.Write(w.w,
byte(value>>24),
Expand All @@ -81,6 +94,7 @@ func (w *StreamWriter) WriteByte4Uint64(value uint64) error {
)
}

// WriteByte8Uint64 writes an eight-byte representation of a uint64 value.
func (w *StreamWriter) WriteByte8Uint64(value uint64) error {
return w.buf.Write(w.w,
byte(value>>56),
Expand All @@ -94,19 +108,22 @@ func (w *StreamWriter) WriteByte8Uint64(value uint64) error {
)
}

// WriteByte1Int writes a single byte representation of an int value.
func (w *StreamWriter) WriteByte1Int(value int) error {
return w.buf.Write(w.w,
byte(value),
)
}

// WriteByte2Int writes a two-byte representation of an int value.
func (w *StreamWriter) WriteByte2Int(value int) error {
return w.buf.Write(w.w,
byte(value>>8),
byte(value),
)
}

// WriteByte4Int writes a four-byte representation of an int value.
func (w *StreamWriter) WriteByte4Int(value int) error {
return w.buf.Write(w.w,
byte(value>>24),
Expand All @@ -116,6 +133,7 @@ func (w *StreamWriter) WriteByte4Int(value int) error {
)
}

// WriteByte4Uint32 writes a four-byte representation of a uint32 value.
func (w *StreamWriter) WriteByte4Uint32(value uint32) error {
return w.buf.Write(w.w,
byte(value>>24),
Expand All @@ -125,6 +143,7 @@ func (w *StreamWriter) WriteByte4Uint32(value uint32) error {
)
}

// WriteBytes writes a slice of bytes to the underlying writer.
func (w *StreamWriter) WriteBytes(bs []byte) error {
return w.buf.Write(w.w, bs...)
}
6 changes: 3 additions & 3 deletions internal/encoding/byte.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ func (e *encoder) isByteSlice(rv reflect.Value) bool {

func (e *encoder) calcByteSlice(l int) (int, error) {
if l <= math.MaxUint8 {
return def.Byte1 + l, nil
return def.Byte1 + def.Byte1 + l, nil
} else if l <= math.MaxUint16 {
return def.Byte2 + l, nil
return def.Byte1 + def.Byte2 + l, nil
} else if uint(l) <= math.MaxUint32 {
return def.Byte4 + l, nil
return def.Byte1 + def.Byte4 + l, nil
}
// not supported error
return 0, fmt.Errorf("%w slice length : %d", def.ErrUnsupportedType, l)
Expand Down
6 changes: 3 additions & 3 deletions internal/encoding/byte_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ func Test_calcByteSlice(t *testing.T) {
{
name: "u8",
value: math.MaxUint8,
result: def.Byte1 + math.MaxUint8,
result: def.Byte1 + def.Byte1 + math.MaxUint8,
},
{
name: "u16",
value: math.MaxUint16,
result: def.Byte2 + math.MaxUint16,
result: def.Byte1 + def.Byte2 + math.MaxUint16,
},
{
name: "u32",
value: math.MaxUint32,
result: def.Byte4 + math.MaxUint32,
result: def.Byte1 + def.Byte4 + math.MaxUint32,
},
{
name: "u32over",
Expand Down
Loading
Loading