diff --git a/.github/workflows/gosec.yml b/.github/workflows/gosec.yml new file mode 100644 index 0000000..c022777 --- /dev/null +++ b/.github/workflows/gosec.yml @@ -0,0 +1,58 @@ +name: gosec + +on: + push: + branches: + - main + pull_request: + branches: + - main + schedule: + - cron: "0 0 * * 0" + workflow_dispatch: + +permissions: + contents: read + +jobs: + scan: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run Gosec Security Scanner + uses: securego/gosec@de65614d10a6b84029e3e1215567b8ce7e490f23 # master, gosec 2.26.1 + with: + args: "-no-fail -fmt sarif -out results.sarif ./..." + + - name: Upload SARIF artifact + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: gosec-sarif + path: results.sarif + if-no-files-found: error + retention-days: 7 + + upload-sarif: + needs: scan + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Download SARIF artifact + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + with: + name: gosec-sarif + path: . + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@7c1e4cf0b20d7c1872b26569c00ba908797a59bf # v4 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index cc4bd0e..a2cd8cb 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ .DS_Store .idea .vscode +.gocache/ diff --git a/def/error.go b/def/error.go index 5bdcf8d..6932995 100644 --- a/def/error.go +++ b/def/error.go @@ -19,6 +19,7 @@ var ( ErrCanNotDecode = fmt.Errorf("%winvalid code", ErrMsgpack) ErrCanNotSetSliceAsMapKey = fmt.Errorf("%wcan not set slice as map key", ErrMsgpack) ErrCanNotSetMapAsMapKey = fmt.Errorf("%wcan not set map as map key", ErrMsgpack) + ErrValueOutOfRange = fmt.Errorf("%wvalue out of range", ErrMsgpack) // encoding errors diff --git a/ext/encode.go b/ext/encode.go index 7fc22c8..b76e0be 100644 --- a/ext/encode.go +++ b/ext/encode.go @@ -1,6 +1,7 @@ package ext import ( + "encoding/binary" "reflect" ) @@ -28,117 +29,92 @@ type Encoder interface { // as well as methods to write raw byte slices into a target byte slice. type EncoderCommon struct{} +func setUint64Bytes(value uint64, offset int, size int, d *[]byte) int { + switch size { + case 1: + (*d)[offset] = byte(value) // #nosec G115 -- MessagePack writes the selected low-order byte. + case 2: + binary.BigEndian.PutUint16((*d)[offset:], uint16(value)) // #nosec G115 -- MessagePack writes the selected low-order bytes. + case 4: + binary.BigEndian.PutUint32((*d)[offset:], uint32(value)) // #nosec G115 -- MessagePack writes the selected low-order bytes. + case 8: + binary.BigEndian.PutUint64((*d)[offset:], value) + default: + panic("invalid uint64 byte size") + } + return offset + size +} + // 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 + return setUint64Bytes(uint64(value), offset, 1, d) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } // 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 + return setUint64Bytes(uint64(value), offset, 2, d) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } // 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) - (*d)[offset+2] = byte(value >> 8) - (*d)[offset+3] = byte(value) - return offset + 4 + return setUint64Bytes(uint64(value), offset, 4, d) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } // 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) - (*d)[offset+2] = byte(value >> 40) - (*d)[offset+3] = byte(value >> 32) - (*d)[offset+4] = byte(value >> 24) - (*d)[offset+5] = byte(value >> 16) - (*d)[offset+6] = byte(value >> 8) - (*d)[offset+7] = byte(value) - return offset + 8 + return setUint64Bytes(uint64(value), offset, 8, d) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } // 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 + return setUint64Bytes(value, offset, 1, d) } // 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 + return setUint64Bytes(value, offset, 2, d) } // 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) - (*d)[offset+2] = byte(value >> 8) - (*d)[offset+3] = byte(value) - return offset + 4 + return setUint64Bytes(value, offset, 4, d) } // 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) - (*d)[offset+2] = byte(value >> 40) - (*d)[offset+3] = byte(value >> 32) - (*d)[offset+4] = byte(value >> 24) - (*d)[offset+5] = byte(value >> 16) - (*d)[offset+6] = byte(value >> 8) - (*d)[offset+7] = byte(value) - return offset + 8 + return setUint64Bytes(value, offset, 8, d) } // 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 + return setUint64Bytes(uint64(code), offset, 1, d) // #nosec G115 -- callers pass bounded MessagePack code or length values. } // 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 + return setUint64Bytes(uint64(value), offset, 2, d) // #nosec G115 -- callers pass bounded MessagePack length values. } // 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) - (*d)[offset+2] = byte(value >> 8) - (*d)[offset+3] = byte(value) - return offset + 4 + return setUint64Bytes(uint64(value), offset, 4, d) // #nosec G115 -- callers pass bounded MessagePack length values. } // 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) - (*d)[offset+2] = byte(value >> 8) - (*d)[offset+3] = byte(value) - return offset + 4 + return setUint64Bytes(uint64(value), offset, 4, d) } // SetBytes writes the given byte slice `bs` into the target byte slice at the specified offset. diff --git a/ext/encode_stream.go b/ext/encode_stream.go index 0df7927..3acf036 100644 --- a/ext/encode_stream.go +++ b/ext/encode_stream.go @@ -32,115 +32,62 @@ func CreateStreamWriter(w io.Writer, buf *common.Buffer) StreamWriter { // 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), - ) + return w.buf.WriteUint64(w.w, uint64(value), 1) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } // 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), - ) + return w.buf.WriteUint64(w.w, uint64(value), 2) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } // 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), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return w.buf.WriteUint64(w.w, uint64(value), 4) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } // 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), - byte(value>>48), - byte(value>>40), - byte(value>>32), - byte(value>>24), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return w.buf.WriteUint64(w.w, uint64(value), 8) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } // 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), - ) + return w.buf.WriteUint64(w.w, value, 1) } // 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), - ) + return w.buf.WriteUint64(w.w, value, 2) } // 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), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return w.buf.WriteUint64(w.w, value, 4) } // 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), - byte(value>>48), - byte(value>>40), - byte(value>>32), - byte(value>>24), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return w.buf.WriteUint64(w.w, value, 8) } // 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), - ) + return w.buf.WriteUint64(w.w, uint64(value), 1) // #nosec G115 -- callers pass bounded MessagePack code or length values. } // 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), - ) + return w.buf.WriteUint64(w.w, uint64(value), 2) // #nosec G115 -- callers pass bounded MessagePack length values. } // 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), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return w.buf.WriteUint64(w.w, uint64(value), 4) // #nosec G115 -- callers pass bounded MessagePack length values. } // 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), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return w.buf.WriteUint64(w.w, uint64(value), 4) } // WriteBytes writes a slice of bytes to the underlying writer. diff --git a/internal/common/buffer.go b/internal/common/buffer.go index 6ddec30..3fc44df 100644 --- a/internal/common/buffer.go +++ b/internal/common/buffer.go @@ -1,6 +1,7 @@ package common import ( + "encoding/binary" "io" "sync" ) @@ -16,20 +17,45 @@ type Buffer struct { } func (b *Buffer) Write(w io.Writer, vs ...byte) error { - if len(b.Data) < b.offset+len(vs) { - _, err := w.Write(b.Data[:b.offset]) - b.offset = 0 - if err != nil { - return err - } - if len(b.Data) < len(vs) { - b.Data = append(b.Data, make([]byte, len(vs)-len(b.Data))...) + if err := b.ensure(w, len(vs)); err != nil { + return err + } + copy(b.Data[b.offset:], vs) + b.offset += len(vs) + return nil +} + +func (b *Buffer) WriteString(w io.Writer, s string) error { + for len(s) > 0 { + if b.offset == len(b.Data) { + if err := b.flush(w); err != nil { + return err + } } + n := copy(b.Data[b.offset:], s) + b.offset += n + s = s[n:] } - for i := range vs { - b.Data[b.offset+i] = vs[i] + return nil +} + +func (b *Buffer) WriteUint64(w io.Writer, value uint64, size int) error { + if err := b.ensure(w, size); err != nil { + return err } - b.offset += len(vs) + switch size { + case 1: + b.Data[b.offset] = byte(value) // #nosec G115 -- MessagePack writes the selected low-order byte. + case 2: + binary.BigEndian.PutUint16(b.Data[b.offset:], uint16(value)) // #nosec G115 -- MessagePack writes the selected low-order bytes. + case 4: + binary.BigEndian.PutUint32(b.Data[b.offset:], uint32(value)) // #nosec G115 -- MessagePack writes the selected low-order bytes. + case 8: + binary.BigEndian.PutUint64(b.Data[b.offset:], value) + default: + panic("invalid uint64 byte size") + } + b.offset += size return nil } @@ -38,6 +64,25 @@ func (b *Buffer) Flush(w io.Writer) error { return err } +func (b *Buffer) ensure(w io.Writer, size int) error { + if len(b.Data) >= b.offset+size { + return nil + } + if err := b.flush(w); err != nil { + return err + } + if len(b.Data) < size { + b.Data = append(b.Data, make([]byte, size-len(b.Data))...) + } + return nil +} + +func (b *Buffer) flush(w io.Writer) error { + _, err := w.Write(b.Data[:b.offset]) + b.offset = 0 + return err +} + var bufPool = sync.Pool{ New: func() interface{} { data := make([]byte, 64) diff --git a/internal/common/decodingutil/range.go b/internal/common/decodingutil/range.go new file mode 100644 index 0000000..0f38bd2 --- /dev/null +++ b/internal/common/decodingutil/range.go @@ -0,0 +1,189 @@ +package decodingutil + +import ( + "fmt" + "math" + "reflect" + + "github.com/shamaton/msgpack/v3/def" +) + +func RangeError(value interface{}, k reflect.Kind) error { + return fmt.Errorf("%w %v decoding as %v", def.ErrValueOutOfRange, value, k) +} + +func Int64FromUint64(v uint64, k reflect.Kind) (int64, error) { + if v > math.MaxInt64 { + return 0, RangeError(v, k) + } + return int64(v), nil +} + +func Int64FromFloat64(v float64, k reflect.Kind) (int64, error) { + if math.IsNaN(v) || math.IsInf(v, 0) || v < -1<<63 || v >= 1<<63 { + return 0, RangeError(v, k) + } + return int64(v), nil +} + +func Int64FromInt8Byte(v byte) int64 { + if v <= math.MaxInt8 { + return int64(v) + } + return int64(v) - (math.MaxUint8 + 1) +} + +func Int64FromInt16Bits(v uint16) int64 { + if v <= math.MaxInt16 { + return int64(v) + } + return int64(v) - (math.MaxUint16 + 1) +} + +func Int64FromInt32Bits(v uint32) int64 { + if v <= math.MaxInt32 { + return int64(v) + } + return int64(v) - (math.MaxUint32 + 1) +} + +func Int64FromInt64Bits(v uint64) int64 { + if v <= math.MaxInt64 { + return int64(v) + } + if v == 1<<63 { + return math.MinInt64 + } + return -int64(^v + 1) // #nosec G115 -- value is checked to fit before converting the two's-complement magnitude. +} + +func Uint64FromInt64(v int64, k reflect.Kind) (uint64, error) { + if v < 0 { + return 0, RangeError(v, k) + } + return uint64(v), nil +} + +func Int8FromByte(v byte) int8 { + return int8(v) // #nosec G115 -- MessagePack ext type codes are signed one-byte values. +} + +func Int8FromInt64(v int64, k reflect.Kind) (int8, error) { + if v < math.MinInt8 || v > math.MaxInt8 { + return 0, RangeError(v, k) + } + return int8(v), nil +} + +func IntFromInt64(v int64, k reflect.Kind) (int, error) { + if def.IsIntSize32 && (v < math.MinInt32 || v > math.MaxInt32) { + return 0, RangeError(v, k) + } + return int(v), nil +} + +func Int16FromInt64(v int64, k reflect.Kind) (int16, error) { + if v < math.MinInt16 || v > math.MaxInt16 { + return 0, RangeError(v, k) + } + return int16(v), nil +} + +func Int32FromInt64(v int64, k reflect.Kind) (int32, error) { + if v < math.MinInt32 || v > math.MaxInt32 { + return 0, RangeError(v, k) + } + return int32(v), nil +} + +func Uint8FromUint64(v uint64, k reflect.Kind) (uint8, error) { + if v > math.MaxUint8 { + return 0, RangeError(v, k) + } + return uint8(v), nil +} + +func UintFromUint64(v uint64, k reflect.Kind) (uint, error) { + if def.IsIntSize32 && v > math.MaxUint32 { + return 0, RangeError(v, k) + } + return uint(v), nil +} + +func IntValueForKind(v int64, k reflect.Kind) (int64, error) { + switch k { + case reflect.Int: + vv, err := IntFromInt64(v, k) + if err != nil { + return 0, err + } + return int64(vv), nil + case reflect.Int8: + vv, err := Int8FromInt64(v, k) + if err != nil { + return 0, err + } + return int64(vv), nil + case reflect.Int16: + vv, err := Int16FromInt64(v, k) + if err != nil { + return 0, err + } + return int64(vv), nil + case reflect.Int32: + vv, err := Int32FromInt64(v, k) + if err != nil { + return 0, err + } + return int64(vv), nil + case reflect.Int64: + return v, nil + } + return 0, RangeError(v, k) +} + +func Uint16FromUint64(v uint64, k reflect.Kind) (uint16, error) { + if v > math.MaxUint16 { + return 0, RangeError(v, k) + } + return uint16(v), nil +} + +func Uint32FromUint64(v uint64, k reflect.Kind) (uint32, error) { + if v > math.MaxUint32 { + return 0, RangeError(v, k) + } + return uint32(v), nil +} + +func UintValueForKind(v uint64, k reflect.Kind) (uint64, error) { + switch k { + case reflect.Uint: + vv, err := UintFromUint64(v, k) + if err != nil { + return 0, err + } + return uint64(vv), nil + case reflect.Uint8: + vv, err := Uint8FromUint64(v, k) + if err != nil { + return 0, err + } + return uint64(vv), nil + case reflect.Uint16: + vv, err := Uint16FromUint64(v, k) + if err != nil { + return 0, err + } + return uint64(vv), nil + case reflect.Uint32: + vv, err := Uint32FromUint64(v, k) + if err != nil { + return 0, err + } + return uint64(vv), nil + case reflect.Uint64: + return v, nil + } + return 0, RangeError(v, k) +} diff --git a/internal/common/testutil/struct.go b/internal/common/testutil/struct.go index 366e2db..a7eff80 100644 --- a/internal/common/testutil/struct.go +++ b/internal/common/testutil/struct.go @@ -31,14 +31,12 @@ func CreateStruct(fieldNum int) (v any, asMapBytes []byte, asArrayBytes []byte) // set encoded bytes if len(name) < 32 { - asMapBytes = append(asMapBytes, def.FixStr+byte(len(name))) + asMapBytes = append(asMapBytes, def.FixStr+byte(len(name))) // #nosec G115 -- name length is checked to fit fixstr. } else if len(name) < math.MaxUint8 { asMapBytes = append(asMapBytes, def.Str8) - asMapBytes = append(asMapBytes, byte(len(name))) - } - for _, c := range name { - asMapBytes = append(asMapBytes, byte(c)) + asMapBytes = append(asMapBytes, byte(len(name))) // #nosec G115 -- name length is checked to fit str8. } + asMapBytes = append(asMapBytes, name...) value := byte(i % 0x7f) asMapBytes = append(asMapBytes, value) asArrayBytes = append(asArrayBytes, value) diff --git a/internal/decoding/bin.go b/internal/decoding/bin.go index 6feef55..4397120 100644 --- a/internal/decoding/bin.go +++ b/internal/decoding/bin.go @@ -3,7 +3,6 @@ package decoding import ( "encoding/binary" "reflect" - "unsafe" "github.com/shamaton/msgpack/v3/def" ) @@ -60,5 +59,5 @@ func (d *decoder) asBin(offset int, k reflect.Kind) ([]byte, int, error) { func (d *decoder) asBinString(offset int, k reflect.Kind) (string, int, error) { bs, offset, err := d.asBin(offset, k) - return *(*string)(unsafe.Pointer(&bs)), offset, err + return string(bs), offset, err } diff --git a/internal/decoding/complex.go b/internal/decoding/complex.go index 4990342..8af6af6 100644 --- a/internal/decoding/complex.go +++ b/internal/decoding/complex.go @@ -7,6 +7,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) func (d *decoder) asComplex64(offset int, k reflect.Kind) (complex64, int, error) { @@ -21,7 +22,7 @@ func (d *decoder) asComplex64(offset int, k reflect.Kind) (complex64, int, error if err != nil { return complex(0, 0), 0, err } - if int8(t) != def.ComplexTypeCode() { + if decodingutil.Int8FromByte(t) != def.ComplexTypeCode() { return complex(0, 0), 0, fmt.Errorf("fixext8. complex type is diffrent %d, %d", t, def.ComplexTypeCode()) } rb, offset, err := d.readSize4(offset) @@ -41,7 +42,7 @@ func (d *decoder) asComplex64(offset int, k reflect.Kind) (complex64, int, error if err != nil { return complex(0, 0), 0, err } - if int8(t) != def.ComplexTypeCode() { + if decodingutil.Int8FromByte(t) != def.ComplexTypeCode() { return complex(0, 0), 0, fmt.Errorf("fixext16. complex type is diffrent %d, %d", t, def.ComplexTypeCode()) } rb, offset, err := d.readSize8(offset) @@ -73,7 +74,7 @@ func (d *decoder) asComplex128(offset int, k reflect.Kind) (complex128, int, err if err != nil { return complex(0, 0), 0, err } - if int8(t) != def.ComplexTypeCode() { + if decodingutil.Int8FromByte(t) != def.ComplexTypeCode() { return complex(0, 0), 0, fmt.Errorf("fixext8. complex type is diffrent %d, %d", t, def.ComplexTypeCode()) } rb, offset, err := d.readSize4(offset) @@ -93,7 +94,7 @@ func (d *decoder) asComplex128(offset int, k reflect.Kind) (complex128, int, err if err != nil { return complex(0, 0), 0, err } - if int8(t) != def.ComplexTypeCode() { + if decodingutil.Int8FromByte(t) != def.ComplexTypeCode() { return complex(0, 0), 0, fmt.Errorf("fixext16. complex type is diffrent %d, %d", t, def.ComplexTypeCode()) } rb, offset, err := d.readSize8(offset) diff --git a/internal/decoding/decoding.go b/internal/decoding/decoding.go index 27f6b3b..535a017 100644 --- a/internal/decoding/decoding.go +++ b/internal/decoding/decoding.go @@ -6,6 +6,7 @@ import ( "github.com/shamaton/msgpack/v3/def" "github.com/shamaton/msgpack/v3/internal/common" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) type decoder struct { @@ -47,6 +48,10 @@ func (d *decoder) decode(rv reflect.Value, offset int) (int, error) { if err != nil { return 0, err } + v, err = decodingutil.IntValueForKind(v, k) + if err != nil { + return 0, err + } rv.SetInt(v) offset = o @@ -55,6 +60,10 @@ func (d *decoder) decode(rv reflect.Value, offset int) (int, error) { if err != nil { return 0, err } + v, err = decodingutil.UintValueForKind(v, k) + if err != nil { + return 0, err + } rv.SetUint(v) offset = o diff --git a/internal/decoding/decoding_test.go b/internal/decoding/decoding_test.go index 46823c1..e1a6935 100644 --- a/internal/decoding/decoding_test.go +++ b/internal/decoding/decoding_test.go @@ -78,6 +78,36 @@ func TestDecoding(t *testing.T) { }) } +func TestDecodeIntegerOutOfRange(t *testing.T) { + t.Run("scalar uint8", func(t *testing.T) { + var v uint8 + err := Decode([]byte{def.Uint16, 0x01, 0x00}, &v, false) + tu.IsError(t, err, def.ErrValueOutOfRange) + }) + + t.Run("struct field int8", func(t *testing.T) { + var v struct { + Value int8 + } + err := Decode([]byte{def.FixArray + 1, def.Uint16, 0x00, 0x80}, &v, true) + tu.IsError(t, err, def.ErrValueOutOfRange) + }) + + t.Run("dynamic slice int8", func(t *testing.T) { + type smallInt int8 + var v []smallInt + err := Decode([]byte{def.FixArray + 1, def.Uint16, 0x00, 0x80}, &v, false) + tu.IsError(t, err, def.ErrValueOutOfRange) + }) + + t.Run("dynamic map uint8", func(t *testing.T) { + type smallUint uint8 + var v map[string]smallUint + err := Decode([]byte{def.FixMap + 1, def.FixStr + 1, 'a', def.Uint16, 0x01, 0x00}, &v, false) + tu.IsError(t, err, def.ErrValueOutOfRange) + }) +} + func Test_decodeWithCode(t *testing.T) { var target any method := func(d *decoder) func(offset int, _ reflect.Kind) (bool, int, error) { diff --git a/internal/decoding/int.go b/internal/decoding/int.go index f14f3d8..222cb02 100644 --- a/internal/decoding/int.go +++ b/internal/decoding/int.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) func (d *decoder) isPositiveFixNum(v byte) bool { @@ -12,7 +13,8 @@ func (d *decoder) isPositiveFixNum(v byte) bool { } func (d *decoder) isNegativeFixNum(v byte) bool { - return def.NegativeFixintMin <= int8(v) && int8(v) <= def.NegativeFixintMax + // MessagePack negative fixint is encoded as a single byte in 0xe0..0xff (-32..-1). + return v >= 0xe0 } func (d *decoder) asInt(offset int, k reflect.Kind) (int64, int, error) { @@ -34,7 +36,7 @@ func (d *decoder) asInt(offset int, k reflect.Kind) (int64, int, error) { if err != nil { return 0, 0, err } - return int64(int8(b)), offset, nil + return decodingutil.Int64FromInt8Byte(b), offset, nil case code == def.Uint8: offset++ @@ -50,7 +52,7 @@ func (d *decoder) asInt(offset int, k reflect.Kind) (int64, int, error) { if err != nil { return 0, 0, err } - return int64(int8(b)), offset, nil + return decodingutil.Int64FromInt8Byte(b), offset, nil case code == def.Uint16: offset++ @@ -67,8 +69,7 @@ func (d *decoder) asInt(offset int, k reflect.Kind) (int64, int, error) { if err != nil { return 0, 0, err } - v := int16(binary.BigEndian.Uint16(bs)) - return int64(v), offset, nil + return decodingutil.Int64FromInt16Bits(binary.BigEndian.Uint16(bs)), offset, nil case code == def.Uint32: offset++ @@ -85,8 +86,7 @@ func (d *decoder) asInt(offset int, k reflect.Kind) (int64, int, error) { if err != nil { return 0, 0, err } - v := int32(binary.BigEndian.Uint32(bs)) - return int64(v), offset, nil + return decodingutil.Int64FromInt32Bits(binary.BigEndian.Uint32(bs)), offset, nil case code == def.Uint64: offset++ @@ -94,7 +94,11 @@ func (d *decoder) asInt(offset int, k reflect.Kind) (int64, int, error) { if err != nil { return 0, 0, err } - return int64(binary.BigEndian.Uint64(bs)), offset, nil + v, err := decodingutil.Int64FromUint64(binary.BigEndian.Uint64(bs), k) + if err != nil { + return 0, 0, err + } + return v, offset, nil case code == def.Int64: offset++ @@ -102,21 +106,29 @@ func (d *decoder) asInt(offset int, k reflect.Kind) (int64, int, error) { if err != nil { return 0, 0, err } - return int64(binary.BigEndian.Uint64(bs)), offset, nil + return decodingutil.Int64FromInt64Bits(binary.BigEndian.Uint64(bs)), offset, nil case code == def.Float32: v, offset, err := d.asFloat32(offset, k) if err != nil { return 0, 0, err } - return int64(v), offset, nil + vv, err := decodingutil.Int64FromFloat64(float64(v), k) + if err != nil { + return 0, 0, err + } + return vv, offset, nil case code == def.Float64: v, offset, err := d.asFloat64(offset, k) if err != nil { return 0, 0, err } - return int64(v), offset, nil + vv, err := decodingutil.Int64FromFloat64(v, k) + if err != nil { + return 0, 0, err + } + return vv, offset, nil case code == def.Nil: offset++ diff --git a/internal/decoding/int_test.go b/internal/decoding/int_test.go index 086dd7a..239a3c4 100644 --- a/internal/decoding/int_test.go +++ b/internal/decoding/int_test.go @@ -138,6 +138,18 @@ func Test_asInt(t *testing.T) { Expected: int64(1), MethodAs: method, }, + { + Name: "Float32.nan", + Data: []byte{def.Float32, 0x7f, 0xc0, 0x00, 0x00}, + Error: def.ErrValueOutOfRange, + MethodAs: method, + }, + { + Name: "Float32.inf", + Data: []byte{def.Float32, 0x7f, 0x80, 0x00, 0x00}, + Error: def.ErrValueOutOfRange, + MethodAs: method, + }, { Name: "Float64.error", Data: []byte{def.Float64}, @@ -150,6 +162,12 @@ func Test_asInt(t *testing.T) { Expected: int64(1), MethodAs: method, }, + { + Name: "Float64.outOfRange", + Data: []byte{def.Float64, 0x43, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Error: def.ErrValueOutOfRange, + MethodAs: method, + }, { Name: "Nil.ok", Data: []byte{def.Nil}, diff --git a/internal/decoding/interface.go b/internal/decoding/interface.go index 5c10d5a..dc19c43 100644 --- a/internal/decoding/interface.go +++ b/internal/decoding/interface.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) func (d *decoder) asInterface(offset int, k reflect.Kind) (interface{}, int, error) { @@ -30,19 +31,31 @@ func (d *decoder) asInterface(offset int, k reflect.Kind) (interface{}, int, err if err != nil { return nil, 0, err } - return uint8(v), offset, err + vv, err := decodingutil.Uint8FromUint64(v, k) + if err != nil { + return nil, 0, err + } + return vv, offset, nil case code == def.Uint16: v, offset, err := d.asUint(offset, k) if err != nil { return nil, 0, err } - return uint16(v), offset, err + vv, err := decodingutil.Uint16FromUint64(v, k) + if err != nil { + return nil, 0, err + } + return vv, offset, nil case code == def.Uint32: v, offset, err := d.asUint(offset, k) if err != nil { return nil, 0, err } - return uint32(v), offset, err + vv, err := decodingutil.Uint32FromUint64(v, k) + if err != nil { + return nil, 0, err + } + return vv, offset, nil case code == def.Uint64: v, offset, err := d.asUint(offset, k) if err != nil { @@ -55,19 +68,31 @@ func (d *decoder) asInterface(offset int, k reflect.Kind) (interface{}, int, err if err != nil { return nil, 0, err } - return int8(v), offset, err + vv, err := decodingutil.Int8FromInt64(v, k) + if err != nil { + return nil, 0, err + } + return vv, offset, nil case code == def.Int16: v, offset, err := d.asInt(offset, k) if err != nil { return nil, 0, err } - return int16(v), offset, err + vv, err := decodingutil.Int16FromInt64(v, k) + if err != nil { + return nil, 0, err + } + return vv, offset, nil case code == def.Int32: v, offset, err := d.asInt(offset, k) if err != nil { return nil, 0, err } - return int32(v), offset, err + vv, err := decodingutil.Int32FromInt64(v, k) + if err != nil { + return nil, 0, err + } + return vv, offset, nil case code == def.Int64: v, offset, err := d.asInt(offset, k) if err != nil { diff --git a/internal/decoding/map.go b/internal/decoding/map.go index 4cc1194..5054b67 100644 --- a/internal/decoding/map.go +++ b/internal/decoding/map.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) var ( @@ -110,7 +111,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[k] = int(v) + vv, err := decodingutil.IntFromInt64(v, valueKind) + if err != nil { + return 0, false, err + } + m[k] = vv offset = o } rv.Set(reflect.ValueOf(m)) @@ -127,7 +132,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[k] = uint(v) + vv, err := decodingutil.UintFromUint64(v, valueKind) + if err != nil { + return 0, false, err + } + m[k] = vv offset = o } rv.Set(reflect.ValueOf(m)) @@ -212,7 +221,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[k] = int8(v) + vv, err := decodingutil.Int8FromInt64(v, valueKind) + if err != nil { + return 0, false, err + } + m[k] = vv offset = o } rv.Set(reflect.ValueOf(m)) @@ -229,7 +242,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[k] = int16(v) + vv, err := decodingutil.Int16FromInt64(v, valueKind) + if err != nil { + return 0, false, err + } + m[k] = vv offset = o } rv.Set(reflect.ValueOf(m)) @@ -246,7 +263,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[k] = int32(v) + vv, err := decodingutil.Int32FromInt64(v, valueKind) + if err != nil { + return 0, false, err + } + m[k] = vv offset = o } rv.Set(reflect.ValueOf(m)) @@ -280,7 +301,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[k] = uint8(v) + vv, err := decodingutil.Uint8FromUint64(v, valueKind) + if err != nil { + return 0, false, err + } + m[k] = vv offset = o } rv.Set(reflect.ValueOf(m)) @@ -296,7 +321,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[k] = uint16(v) + vv, err := decodingutil.Uint16FromUint64(v, valueKind) + if err != nil { + return 0, false, err + } + m[k] = vv offset = o } rv.Set(reflect.ValueOf(m)) @@ -313,7 +342,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[k] = uint32(v) + vv, err := decodingutil.Uint32FromUint64(v, valueKind) + if err != nil { + return 0, false, err + } + m[k] = vv offset = o } rv.Set(reflect.ValueOf(m)) @@ -347,7 +380,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[int(k)] = v + kk, err := decodingutil.IntFromInt64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -364,7 +401,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[int8(k)] = v + kk, err := decodingutil.Int8FromInt64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -381,7 +422,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[int16(k)] = v + kk, err := decodingutil.Int16FromInt64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -398,7 +443,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[int32(k)] = v + kk, err := decodingutil.Int32FromInt64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -432,7 +481,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[int(k)] = v + kk, err := decodingutil.IntFromInt64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -449,7 +502,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[int8(k)] = v + kk, err := decodingutil.Int8FromInt64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -466,7 +523,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[int16(k)] = v + kk, err := decodingutil.Int16FromInt64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -483,7 +544,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[int32(k)] = v + kk, err := decodingutil.Int32FromInt64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -517,7 +582,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[uint(k)] = v + kk, err := decodingutil.UintFromUint64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -534,7 +603,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[uint8(k)] = v + kk, err := decodingutil.Uint8FromUint64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -551,7 +624,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[uint16(k)] = v + kk, err := decodingutil.Uint16FromUint64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -568,7 +645,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[uint32(k)] = v + kk, err := decodingutil.Uint32FromUint64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -602,7 +683,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[uint(k)] = v + kk, err := decodingutil.UintFromUint64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -619,7 +704,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[uint8(k)] = v + kk, err := decodingutil.Uint8FromUint64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -636,7 +725,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[uint16(k)] = v + kk, err := decodingutil.Uint16FromUint64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) @@ -653,7 +746,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, offset int, l int) (int, bool, er if err != nil { return 0, false, err } - m[uint32(k)] = v + kk, err := decodingutil.Uint32FromUint64(k, keyKind) + if err != nil { + return 0, false, err + } + m[kk] = v offset = o } rv.Set(reflect.ValueOf(m)) diff --git a/internal/decoding/slice.go b/internal/decoding/slice.go index f577b9c..9953e31 100644 --- a/internal/decoding/slice.go +++ b/internal/decoding/slice.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) var ( @@ -77,7 +78,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, offset int, l int) (int, bool, if err != nil { return 0, false, err } - sli[i] = int(v) + vv, err := decodingutil.IntFromInt64(v, k) + if err != nil { + return 0, false, err + } + sli[i] = vv offset = o } rv.Set(reflect.ValueOf(sli)) @@ -90,7 +95,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, offset int, l int) (int, bool, if err != nil { return 0, false, err } - sli[i] = uint(v) + vv, err := decodingutil.UintFromUint64(v, k) + if err != nil { + return 0, false, err + } + sli[i] = vv offset = o } rv.Set(reflect.ValueOf(sli)) @@ -155,7 +164,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, offset int, l int) (int, bool, if err != nil { return 0, false, err } - sli[i] = int8(v) + vv, err := decodingutil.Int8FromInt64(v, k) + if err != nil { + return 0, false, err + } + sli[i] = vv offset = o } rv.Set(reflect.ValueOf(sli)) @@ -168,7 +181,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, offset int, l int) (int, bool, if err != nil { return 0, false, err } - sli[i] = int16(v) + vv, err := decodingutil.Int16FromInt64(v, k) + if err != nil { + return 0, false, err + } + sli[i] = vv offset = o } rv.Set(reflect.ValueOf(sli)) @@ -181,7 +198,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, offset int, l int) (int, bool, if err != nil { return 0, false, err } - sli[i] = int32(v) + vv, err := decodingutil.Int32FromInt64(v, k) + if err != nil { + return 0, false, err + } + sli[i] = vv offset = o } rv.Set(reflect.ValueOf(sli)) @@ -207,7 +228,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, offset int, l int) (int, bool, if err != nil { return 0, false, err } - sli[i] = uint8(v) + vv, err := decodingutil.Uint8FromUint64(v, k) + if err != nil { + return 0, false, err + } + sli[i] = vv offset = o } rv.Set(reflect.ValueOf(sli)) @@ -220,7 +245,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, offset int, l int) (int, bool, if err != nil { return 0, false, err } - sli[i] = uint16(v) + vv, err := decodingutil.Uint16FromUint64(v, k) + if err != nil { + return 0, false, err + } + sli[i] = vv offset = o } rv.Set(reflect.ValueOf(sli)) @@ -233,7 +262,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, offset int, l int) (int, bool, if err != nil { return 0, false, err } - sli[i] = uint32(v) + vv, err := decodingutil.Uint32FromUint64(v, k) + if err != nil { + return 0, false, err + } + sli[i] = vv offset = o } rv.Set(reflect.ValueOf(sli)) diff --git a/internal/decoding/uint.go b/internal/decoding/uint.go index ea4dc19..8703640 100644 --- a/internal/decoding/uint.go +++ b/internal/decoding/uint.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) func (d *decoder) asUint(offset int, k reflect.Kind) (uint64, int, error) { @@ -26,7 +27,11 @@ func (d *decoder) asUint(offset int, k reflect.Kind) (uint64, int, error) { if err != nil { return 0, 0, err } - return uint64(int8(b)), offset, nil + v, err := decodingutil.Uint64FromInt64(decodingutil.Int64FromInt8Byte(b), k) + if err != nil { + return 0, 0, err + } + return v, offset, nil case code == def.Uint8: offset++ @@ -42,7 +47,11 @@ func (d *decoder) asUint(offset int, k reflect.Kind) (uint64, int, error) { if err != nil { return 0, 0, err } - return uint64(int8(b)), offset, nil + v, err := decodingutil.Uint64FromInt64(decodingutil.Int64FromInt8Byte(b), k) + if err != nil { + return 0, 0, err + } + return v, offset, nil case code == def.Uint16: offset++ @@ -59,8 +68,11 @@ func (d *decoder) asUint(offset int, k reflect.Kind) (uint64, int, error) { if err != nil { return 0, 0, err } - v := int16(binary.BigEndian.Uint16(bs)) - return uint64(v), offset, nil + v, err := decodingutil.Uint64FromInt64(decodingutil.Int64FromInt16Bits(binary.BigEndian.Uint16(bs)), k) + if err != nil { + return 0, 0, err + } + return v, offset, nil case code == def.Uint32: offset++ @@ -77,8 +89,11 @@ func (d *decoder) asUint(offset int, k reflect.Kind) (uint64, int, error) { if err != nil { return 0, 0, err } - v := int32(binary.BigEndian.Uint32(bs)) - return uint64(v), offset, nil + v, err := decodingutil.Uint64FromInt64(decodingutil.Int64FromInt32Bits(binary.BigEndian.Uint32(bs)), k) + if err != nil { + return 0, 0, err + } + return v, offset, nil case code == def.Uint64: offset++ @@ -94,7 +109,11 @@ func (d *decoder) asUint(offset int, k reflect.Kind) (uint64, int, error) { if err != nil { return 0, 0, err } - return binary.BigEndian.Uint64(bs), offset, nil + v, err := decodingutil.Uint64FromInt64(decodingutil.Int64FromInt64Bits(binary.BigEndian.Uint64(bs)), k) + if err != nil { + return 0, 0, err + } + return v, offset, nil case code == def.Nil: offset++ diff --git a/internal/decoding/uint_test.go b/internal/decoding/uint_test.go index 29c8e27..87df108 100644 --- a/internal/decoding/uint_test.go +++ b/internal/decoding/uint_test.go @@ -1,7 +1,6 @@ package decoding import ( - "math" "reflect" "testing" @@ -74,9 +73,9 @@ func Test_asUint(t *testing.T) { MethodAs: method, }, { - Name: "NegativeFixNum.ok", + Name: "NegativeFixNum.error.out_of_range", Data: []byte{0xff}, - Expected: uint64(math.MaxUint64), + Error: def.ErrValueOutOfRange, MethodAs: method, }, { @@ -86,9 +85,9 @@ func Test_asUint(t *testing.T) { MethodAs: method, }, { - Name: "Int8.ok", + Name: "Int8.error.out_of_range", Data: []byte{def.Int8, 0xff}, - Expected: uint64(math.MaxUint64), + Error: def.ErrValueOutOfRange, MethodAs: method, }, { @@ -98,9 +97,9 @@ func Test_asUint(t *testing.T) { MethodAs: method, }, { - Name: "Int16.ok", + Name: "Int16.error.out_of_range", Data: []byte{def.Int16, 0xff, 0xff}, - Expected: uint64(math.MaxUint64), + Error: def.ErrValueOutOfRange, MethodAs: method, }, { @@ -110,9 +109,9 @@ func Test_asUint(t *testing.T) { MethodAs: method, }, { - Name: "Int32.ok", + Name: "Int32.error.out_of_range", Data: []byte{def.Int32, 0xff, 0xff, 0xff, 0xff}, - Expected: uint64(math.MaxUint64), + Error: def.ErrValueOutOfRange, MethodAs: method, }, { @@ -122,9 +121,9 @@ func Test_asUint(t *testing.T) { MethodAs: method, }, { - Name: "Int64.ok", + Name: "Int64.error.out_of_range", Data: []byte{def.Int64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - Expected: uint64(math.MaxUint64), + Error: def.ErrValueOutOfRange, MethodAs: method, }, { diff --git a/internal/encoding/set.go b/internal/encoding/set.go index d3407f2..42d5f31 100644 --- a/internal/encoding/set.go +++ b/internal/encoding/set.go @@ -1,84 +1,65 @@ package encoding +import "encoding/binary" + +func (e *encoder) setUint64Bytes(value uint64, offset int, size int) int { + switch size { + case 1: + e.d[offset] = byte(value) // #nosec G115 -- MessagePack writes the selected low-order byte. + case 2: + binary.BigEndian.PutUint16(e.d[offset:], uint16(value)) // #nosec G115 -- MessagePack writes the selected low-order bytes. + case 4: + binary.BigEndian.PutUint32(e.d[offset:], uint32(value)) // #nosec G115 -- MessagePack writes the selected low-order bytes. + case 8: + binary.BigEndian.PutUint64(e.d[offset:], value) + default: + panic("invalid uint64 byte size") + } + return offset + size +} + func (e *encoder) setByte1Int64(value int64, offset int) int { - e.d[offset] = byte(value) - return offset + 1 + return e.setUint64Bytes(uint64(value), offset, 1) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } func (e *encoder) setByte2Int64(value int64, offset int) int { - e.d[offset+0] = byte(value >> 8) - e.d[offset+1] = byte(value) - return offset + 2 + return e.setUint64Bytes(uint64(value), offset, 2) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } func (e *encoder) setByte4Int64(value int64, offset int) int { - e.d[offset+0] = byte(value >> 24) - e.d[offset+1] = byte(value >> 16) - e.d[offset+2] = byte(value >> 8) - e.d[offset+3] = byte(value) - return offset + 4 + return e.setUint64Bytes(uint64(value), offset, 4) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } func (e *encoder) setByte8Int64(value int64, offset int) int { - e.d[offset] = byte(value >> 56) - e.d[offset+1] = byte(value >> 48) - e.d[offset+2] = byte(value >> 40) - e.d[offset+3] = byte(value >> 32) - e.d[offset+4] = byte(value >> 24) - e.d[offset+5] = byte(value >> 16) - e.d[offset+6] = byte(value >> 8) - e.d[offset+7] = byte(value) - return offset + 8 + return e.setUint64Bytes(uint64(value), offset, 8) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } func (e *encoder) setByte1Uint64(value uint64, offset int) int { - e.d[offset] = byte(value) - return offset + 1 + return e.setUint64Bytes(value, offset, 1) } func (e *encoder) setByte2Uint64(value uint64, offset int) int { - e.d[offset] = byte(value >> 8) - e.d[offset+1] = byte(value) - return offset + 2 + return e.setUint64Bytes(value, offset, 2) } func (e *encoder) setByte4Uint64(value uint64, offset int) int { - e.d[offset] = byte(value >> 24) - e.d[offset+1] = byte(value >> 16) - e.d[offset+2] = byte(value >> 8) - e.d[offset+3] = byte(value) - return offset + 4 + return e.setUint64Bytes(value, offset, 4) } func (e *encoder) setByte8Uint64(value uint64, offset int) int { - e.d[offset] = byte(value >> 56) - e.d[offset+1] = byte(value >> 48) - e.d[offset+2] = byte(value >> 40) - e.d[offset+3] = byte(value >> 32) - e.d[offset+4] = byte(value >> 24) - e.d[offset+5] = byte(value >> 16) - e.d[offset+6] = byte(value >> 8) - e.d[offset+7] = byte(value) - return offset + 8 + return e.setUint64Bytes(value, offset, 8) } func (e *encoder) setByte1Int(code, offset int) int { - e.d[offset] = byte(code) - return offset + 1 + return e.setUint64Bytes(uint64(code), offset, 1) // #nosec G115 -- callers pass bounded MessagePack code or length values. } func (e *encoder) setByte2Int(value int, offset int) int { - e.d[offset] = byte(value >> 8) - e.d[offset+1] = byte(value) - return offset + 2 + return e.setUint64Bytes(uint64(value), offset, 2) // #nosec G115 -- callers pass bounded MessagePack length values. } func (e *encoder) setByte4Int(value int, offset int) int { - e.d[offset] = byte(value >> 24) - e.d[offset+1] = byte(value >> 16) - e.d[offset+2] = byte(value >> 8) - e.d[offset+3] = byte(value) - return offset + 4 + return e.setUint64Bytes(uint64(value), offset, 4) // #nosec G115 -- callers pass bounded MessagePack length values. } func (e *encoder) setBytes(bs []byte, offset int) int { diff --git a/internal/encoding/string.go b/internal/encoding/string.go index 3e91990..b340443 100644 --- a/internal/encoding/string.go +++ b/internal/encoding/string.go @@ -2,15 +2,12 @@ package encoding import ( "math" - "unsafe" "github.com/shamaton/msgpack/v3/def" ) func (e *encoder) calcString(v string) int { - // NOTE : unsafe - strBytes := *(*[]byte)(unsafe.Pointer(&v)) - l := len(strBytes) + l := len(v) if l < 32 { return def.Byte1 + l } else if l <= math.MaxUint8 { @@ -23,9 +20,7 @@ func (e *encoder) calcString(v string) int { } func (e *encoder) writeString(str string, offset int) int { - // NOTE : unsafe - strBytes := *(*[]byte)(unsafe.Pointer(&str)) - l := len(strBytes) + l := len(str) if l < 32 { offset = e.setByte1Int(def.FixStr+l, offset) } else if l <= math.MaxUint8 { diff --git a/internal/stream/decoding/bin.go b/internal/stream/decoding/bin.go index 6ba5e88..4e787b8 100644 --- a/internal/stream/decoding/bin.go +++ b/internal/stream/decoding/bin.go @@ -3,7 +3,6 @@ package decoding import ( "encoding/binary" "reflect" - "unsafe" "github.com/shamaton/msgpack/v3/def" ) @@ -48,7 +47,7 @@ func (d *decoder) asBinWithCode(code byte, k reflect.Kind) ([]byte, error) { func (d *decoder) asBinStringWithCode(code byte, k reflect.Kind) (string, error) { bs, err := d.asBinWithCode(code, k) - return *(*string)(unsafe.Pointer(&bs)), err + return string(bs), err } func (d *decoder) copySizeN(n int) ([]byte, error) { diff --git a/internal/stream/decoding/complex.go b/internal/stream/decoding/complex.go index a943d78..51c09e5 100644 --- a/internal/stream/decoding/complex.go +++ b/internal/stream/decoding/complex.go @@ -7,6 +7,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) func (d *decoder) asComplex64(code byte, k reflect.Kind) (complex64, error) { @@ -16,7 +17,7 @@ func (d *decoder) asComplex64(code byte, k reflect.Kind) (complex64, error) { if err != nil { return complex(0, 0), err } - if int8(t) != def.ComplexTypeCode() { + if decodingutil.Int8FromByte(t) != def.ComplexTypeCode() { return complex(0, 0), fmt.Errorf("fixext8. complex type is diffrent %d, %d", t, def.ComplexTypeCode()) } rb, err := d.readSize4() @@ -37,7 +38,7 @@ func (d *decoder) asComplex64(code byte, k reflect.Kind) (complex64, error) { if err != nil { return complex(0, 0), err } - if int8(t) != def.ComplexTypeCode() { + if decodingutil.Int8FromByte(t) != def.ComplexTypeCode() { return complex(0, 0), fmt.Errorf("fixext16. complex type is diffrent %d, %d", t, def.ComplexTypeCode()) } rb, err := d.readSize8() @@ -65,7 +66,7 @@ func (d *decoder) asComplex128(code byte, k reflect.Kind) (complex128, error) { if err != nil { return complex(0, 0), err } - if int8(t) != def.ComplexTypeCode() { + if decodingutil.Int8FromByte(t) != def.ComplexTypeCode() { return complex(0, 0), fmt.Errorf("fixext8. complex type is diffrent %d, %d", t, def.ComplexTypeCode()) } rb, err := d.readSize4() @@ -86,7 +87,7 @@ func (d *decoder) asComplex128(code byte, k reflect.Kind) (complex128, error) { if err != nil { return complex(0, 0), err } - if int8(t) != def.ComplexTypeCode() { + if decodingutil.Int8FromByte(t) != def.ComplexTypeCode() { return complex(0, 0), fmt.Errorf("fixext16. complex type is diffrent %d, %d", t, def.ComplexTypeCode()) } rb, err := d.readSize8() diff --git a/internal/stream/decoding/decoding.go b/internal/stream/decoding/decoding.go index cdf685b..c7939db 100644 --- a/internal/stream/decoding/decoding.go +++ b/internal/stream/decoding/decoding.go @@ -7,6 +7,7 @@ import ( "github.com/shamaton/msgpack/v3/def" "github.com/shamaton/msgpack/v3/internal/common" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) type decoder struct { @@ -55,6 +56,10 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { if err != nil { return err } + v, err = decodingutil.IntValueForKind(v, k) + if err != nil { + return err + } rv.SetInt(v) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: @@ -62,6 +67,10 @@ func (d *decoder) decodeWithCode(code byte, rv reflect.Value) error { if err != nil { return err } + v, err = decodingutil.UintValueForKind(v, k) + if err != nil { + return err + } rv.SetUint(v) case reflect.Float32: diff --git a/internal/stream/decoding/decoding_test.go b/internal/stream/decoding/decoding_test.go index bc2c41e..182b350 100644 --- a/internal/stream/decoding/decoding_test.go +++ b/internal/stream/decoding/decoding_test.go @@ -1,6 +1,7 @@ package decoding import ( + "bytes" "io" "reflect" "testing" @@ -90,6 +91,36 @@ func TestDecoding(t *testing.T) { }) } +func TestDecodeIntegerOutOfRange(t *testing.T) { + t.Run("scalar uint8", func(t *testing.T) { + var v uint8 + err := Decode(bytes.NewReader([]byte{def.Uint16, 0x01, 0x00}), &v, false) + tu.IsError(t, err, def.ErrValueOutOfRange) + }) + + t.Run("struct field int8", func(t *testing.T) { + var v struct { + Value int8 + } + err := Decode(bytes.NewReader([]byte{def.FixArray + 1, def.Uint16, 0x00, 0x80}), &v, true) + tu.IsError(t, err, def.ErrValueOutOfRange) + }) + + t.Run("dynamic slice int8", func(t *testing.T) { + type smallInt int8 + var v []smallInt + err := Decode(bytes.NewReader([]byte{def.FixArray + 1, def.Uint16, 0x00, 0x80}), &v, false) + tu.IsError(t, err, def.ErrValueOutOfRange) + }) + + t.Run("dynamic map uint8", func(t *testing.T) { + type smallUint uint8 + var v map[string]smallUint + err := Decode(bytes.NewReader([]byte{def.FixMap + 1, def.FixStr + 1, 'a', def.Uint16, 0x01, 0x00}), &v, false) + tu.IsError(t, err, def.ErrValueOutOfRange) + }) +} + func Test_decodeWithCode(t *testing.T) { var target any method := func(d *decoder) func(code byte, _ reflect.Kind) (bool, error) { diff --git a/internal/stream/decoding/ext.go b/internal/stream/decoding/ext.go index 470302e..cd69b58 100644 --- a/internal/stream/decoding/ext.go +++ b/internal/stream/decoding/ext.go @@ -5,6 +5,7 @@ import ( "github.com/shamaton/msgpack/v3/def" "github.com/shamaton/msgpack/v3/ext" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" "github.com/shamaton/msgpack/v3/time" ) @@ -61,7 +62,7 @@ func (d *decoder) readIfExtType(code byte) (innerType int8, data []byte, err err if err != nil { return 0, nil, err } - return int8(typ), []byte{v}, nil + return decodingutil.Int8FromByte(typ), []byte{v}, nil case def.Fixext2: typ, err := d.readSize1() @@ -72,7 +73,7 @@ func (d *decoder) readIfExtType(code byte) (innerType int8, data []byte, err err if err != nil { return 0, nil, err } - return int8(typ), data, nil + return decodingutil.Int8FromByte(typ), data, nil case def.Fixext4: typ, err := d.readSize1() @@ -83,7 +84,7 @@ func (d *decoder) readIfExtType(code byte) (innerType int8, data []byte, err err if err != nil { return 0, nil, err } - return int8(typ), data, nil + return decodingutil.Int8FromByte(typ), data, nil case def.Fixext8: typ, err := d.readSize1() @@ -94,7 +95,7 @@ func (d *decoder) readIfExtType(code byte) (innerType int8, data []byte, err err if err != nil { return 0, nil, err } - return int8(typ), data, nil + return decodingutil.Int8FromByte(typ), data, nil case def.Fixext16: typ, err := d.readSize1() @@ -105,7 +106,7 @@ func (d *decoder) readIfExtType(code byte) (innerType int8, data []byte, err err if err != nil { return 0, nil, err } - return int8(typ), data, nil + return decodingutil.Int8FromByte(typ), data, nil case def.Ext8: bs, err := d.readSize1() @@ -122,7 +123,7 @@ func (d *decoder) readIfExtType(code byte) (innerType int8, data []byte, err err if err != nil { return 0, nil, err } - return int8(typ), data, nil + return decodingutil.Int8FromByte(typ), data, nil case def.Ext16: bs, err := d.readSize2() @@ -139,7 +140,7 @@ func (d *decoder) readIfExtType(code byte) (innerType int8, data []byte, err err if err != nil { return 0, nil, err } - return int8(typ), data, nil + return decodingutil.Int8FromByte(typ), data, nil case def.Ext32: bs, err := d.readSize4() @@ -156,7 +157,7 @@ func (d *decoder) readIfExtType(code byte) (innerType int8, data []byte, err err if err != nil { return 0, nil, err } - return int8(typ), data, nil + return decodingutil.Int8FromByte(typ), data, nil } return 0, nil, nil diff --git a/internal/stream/decoding/int.go b/internal/stream/decoding/int.go index 8c505d4..2dfd7ca 100644 --- a/internal/stream/decoding/int.go +++ b/internal/stream/decoding/int.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) func (d *decoder) isPositiveFixNum(v byte) bool { @@ -12,7 +13,8 @@ func (d *decoder) isPositiveFixNum(v byte) bool { } func (d *decoder) isNegativeFixNum(v byte) bool { - return def.NegativeFixintMin <= int8(v) && int8(v) <= def.NegativeFixintMax + // MessagePack negative fixint is encoded as a single byte in 0xe0..0xff (-32..-1). + return v >= 0xe0 } func (d *decoder) asInt(k reflect.Kind) (int64, error) { @@ -29,7 +31,7 @@ func (d *decoder) asIntWithCode(code byte, k reflect.Kind) (int64, error) { return int64(code), nil case d.isNegativeFixNum(code): - return int64(int8(code)), nil + return decodingutil.Int64FromInt8Byte(code), nil case code == def.Uint8: b, err := d.readSize1() @@ -43,7 +45,7 @@ func (d *decoder) asIntWithCode(code byte, k reflect.Kind) (int64, error) { if err != nil { return 0, err } - return int64(int8(b)), nil + return decodingutil.Int64FromInt8Byte(b), nil case code == def.Uint16: bs, err := d.readSize2() @@ -58,8 +60,7 @@ func (d *decoder) asIntWithCode(code byte, k reflect.Kind) (int64, error) { if err != nil { return 0, err } - v := int16(binary.BigEndian.Uint16(bs)) - return int64(v), nil + return decodingutil.Int64FromInt16Bits(binary.BigEndian.Uint16(bs)), nil case code == def.Uint32: bs, err := d.readSize4() @@ -74,36 +75,35 @@ func (d *decoder) asIntWithCode(code byte, k reflect.Kind) (int64, error) { if err != nil { return 0, err } - v := int32(binary.BigEndian.Uint32(bs)) - return int64(v), nil + return decodingutil.Int64FromInt32Bits(binary.BigEndian.Uint32(bs)), nil case code == def.Uint64: bs, err := d.readSize8() if err != nil { return 0, err } - return int64(binary.BigEndian.Uint64(bs)), nil + return decodingutil.Int64FromUint64(binary.BigEndian.Uint64(bs), k) case code == def.Int64: bs, err := d.readSize8() if err != nil { return 0, err } - return int64(binary.BigEndian.Uint64(bs)), nil + return decodingutil.Int64FromInt64Bits(binary.BigEndian.Uint64(bs)), nil case code == def.Float32: v, err := d.asFloat32WithCode(code, k) if err != nil { return 0, err } - return int64(v), nil + return decodingutil.Int64FromFloat64(float64(v), k) case code == def.Float64: v, err := d.asFloat64WithCode(code, k) if err != nil { return 0, err } - return int64(v), nil + return decodingutil.Int64FromFloat64(v, k) case code == def.Nil: return 0, nil diff --git a/internal/stream/decoding/int_test.go b/internal/stream/decoding/int_test.go index b57f05c..06a06c7 100644 --- a/internal/stream/decoding/int_test.go +++ b/internal/stream/decoding/int_test.go @@ -165,6 +165,22 @@ func Test_asIntWithCode(t *testing.T) { ReadCount: 1, MethodAsWithCode: method, }, + { + Name: "Float32.nan", + Code: def.Float32, + Data: []byte{0x7f, 0xc0, 0x00, 0x00}, + Error: def.ErrValueOutOfRange, + ReadCount: 1, + MethodAsWithCode: method, + }, + { + Name: "Float32.inf", + Code: def.Float32, + Data: []byte{0x7f, 0x80, 0x00, 0x00}, + Error: def.ErrValueOutOfRange, + ReadCount: 1, + MethodAsWithCode: method, + }, { Name: "Float64.error", Code: def.Float64, @@ -179,6 +195,14 @@ func Test_asIntWithCode(t *testing.T) { ReadCount: 1, MethodAsWithCode: method, }, + { + Name: "Float64.outOfRange", + Code: def.Float64, + Data: []byte{0x43, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Error: def.ErrValueOutOfRange, + ReadCount: 1, + MethodAsWithCode: method, + }, { Name: "Nil", Code: def.Nil, diff --git a/internal/stream/decoding/interface.go b/internal/stream/decoding/interface.go index 692e916..0746f4a 100644 --- a/internal/stream/decoding/interface.go +++ b/internal/stream/decoding/interface.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) func (d *decoder) asInterface(k reflect.Kind) (interface{}, error) { @@ -32,19 +33,19 @@ func (d *decoder) asInterfaceWithCode(code byte, k reflect.Kind) (interface{}, e if err != nil { return nil, err } - return uint8(v), err + return decodingutil.Uint8FromUint64(v, k) case code == def.Uint16: v, err := d.asUintWithCode(code, k) if err != nil { return nil, err } - return uint16(v), err + return decodingutil.Uint16FromUint64(v, k) case code == def.Uint32: v, err := d.asUintWithCode(code, k) if err != nil { return nil, err } - return uint32(v), err + return decodingutil.Uint32FromUint64(v, k) case code == def.Uint64: v, err := d.asUintWithCode(code, k) if err != nil { @@ -57,19 +58,19 @@ func (d *decoder) asInterfaceWithCode(code byte, k reflect.Kind) (interface{}, e if err != nil { return nil, err } - return int8(v), err + return decodingutil.Int8FromInt64(v, k) case code == def.Int16: v, err := d.asIntWithCode(code, k) if err != nil { return nil, err } - return int16(v), err + return decodingutil.Int16FromInt64(v, k) case code == def.Int32: v, err := d.asIntWithCode(code, k) if err != nil { return nil, err } - return int32(v), err + return decodingutil.Int32FromInt64(v, k) case code == def.Int64: v, err := d.asIntWithCode(code, k) if err != nil { diff --git a/internal/stream/decoding/map.go b/internal/stream/decoding/map.go index ce2f10b..a54ee85 100644 --- a/internal/stream/decoding/map.go +++ b/internal/stream/decoding/map.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) var ( @@ -97,7 +98,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[k] = int(v) + vv, err := decodingutil.IntFromInt64(v, valueKind) + if err != nil { + return false, err + } + m[k] = vv } rv.Set(reflect.ValueOf(m)) return true, nil @@ -113,7 +118,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[k] = uint(v) + vv, err := decodingutil.UintFromUint64(v, valueKind) + if err != nil { + return false, err + } + m[k] = vv } rv.Set(reflect.ValueOf(m)) return true, nil @@ -193,7 +202,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[k] = int8(v) + vv, err := decodingutil.Int8FromInt64(v, valueKind) + if err != nil { + return false, err + } + m[k] = vv } rv.Set(reflect.ValueOf(m)) return true, nil @@ -209,7 +222,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[k] = int16(v) + vv, err := decodingutil.Int16FromInt64(v, valueKind) + if err != nil { + return false, err + } + m[k] = vv } rv.Set(reflect.ValueOf(m)) return true, nil @@ -225,7 +242,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[k] = int32(v) + vv, err := decodingutil.Int32FromInt64(v, valueKind) + if err != nil { + return false, err + } + m[k] = vv } rv.Set(reflect.ValueOf(m)) return true, nil @@ -257,7 +278,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[k] = uint8(v) + vv, err := decodingutil.Uint8FromUint64(v, valueKind) + if err != nil { + return false, err + } + m[k] = vv } rv.Set(reflect.ValueOf(m)) return true, nil @@ -272,7 +297,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[k] = uint16(v) + vv, err := decodingutil.Uint16FromUint64(v, valueKind) + if err != nil { + return false, err + } + m[k] = vv } rv.Set(reflect.ValueOf(m)) return true, nil @@ -288,7 +317,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[k] = uint32(v) + vv, err := decodingutil.Uint32FromUint64(v, valueKind) + if err != nil { + return false, err + } + m[k] = vv } rv.Set(reflect.ValueOf(m)) return true, nil @@ -320,7 +353,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[int(k)] = v + kk, err := decodingutil.IntFromInt64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -336,7 +373,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[int8(k)] = v + kk, err := decodingutil.Int8FromInt64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -352,7 +393,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[int16(k)] = v + kk, err := decodingutil.Int16FromInt64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -368,7 +413,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[int32(k)] = v + kk, err := decodingutil.Int32FromInt64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -400,7 +449,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[int(k)] = v + kk, err := decodingutil.IntFromInt64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -416,7 +469,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[int8(k)] = v + kk, err := decodingutil.Int8FromInt64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -432,7 +489,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[int16(k)] = v + kk, err := decodingutil.Int16FromInt64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -448,7 +509,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[int32(k)] = v + kk, err := decodingutil.Int32FromInt64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -480,7 +545,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[uint(k)] = v + kk, err := decodingutil.UintFromUint64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -496,7 +565,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[uint8(k)] = v + kk, err := decodingutil.Uint8FromUint64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -512,7 +585,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[uint16(k)] = v + kk, err := decodingutil.Uint16FromUint64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -528,7 +605,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[uint32(k)] = v + kk, err := decodingutil.Uint32FromUint64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -560,7 +641,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[uint(k)] = v + kk, err := decodingutil.UintFromUint64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -576,7 +661,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[uint8(k)] = v + kk, err := decodingutil.Uint8FromUint64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -592,7 +681,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[uint16(k)] = v + kk, err := decodingutil.Uint16FromUint64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil @@ -608,7 +701,11 @@ func (d *decoder) asFixedMap(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - m[uint32(k)] = v + kk, err := decodingutil.Uint32FromUint64(k, keyKind) + if err != nil { + return false, err + } + m[kk] = v } rv.Set(reflect.ValueOf(m)) return true, nil diff --git a/internal/stream/decoding/slice.go b/internal/stream/decoding/slice.go index f0aceed..0affc95 100644 --- a/internal/stream/decoding/slice.go +++ b/internal/stream/decoding/slice.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) var ( @@ -64,7 +65,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - sli[i] = int(v) + vv, err := decodingutil.IntFromInt64(v, k) + if err != nil { + return false, err + } + sli[i] = vv } rv.Set(reflect.ValueOf(sli)) return true, nil @@ -76,7 +81,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - sli[i] = uint(v) + vv, err := decodingutil.UintFromUint64(v, k) + if err != nil { + return false, err + } + sli[i] = vv } rv.Set(reflect.ValueOf(sli)) return true, nil @@ -136,7 +145,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - sli[i] = int8(v) + vv, err := decodingutil.Int8FromInt64(v, k) + if err != nil { + return false, err + } + sli[i] = vv } rv.Set(reflect.ValueOf(sli)) return true, nil @@ -148,7 +161,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - sli[i] = int16(v) + vv, err := decodingutil.Int16FromInt64(v, k) + if err != nil { + return false, err + } + sli[i] = vv } rv.Set(reflect.ValueOf(sli)) return true, nil @@ -160,7 +177,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - sli[i] = int32(v) + vv, err := decodingutil.Int32FromInt64(v, k) + if err != nil { + return false, err + } + sli[i] = vv } rv.Set(reflect.ValueOf(sli)) return true, nil @@ -184,7 +205,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - sli[i] = uint8(v) + vv, err := decodingutil.Uint8FromUint64(v, k) + if err != nil { + return false, err + } + sli[i] = vv } rv.Set(reflect.ValueOf(sli)) return true, nil @@ -196,7 +221,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - sli[i] = uint16(v) + vv, err := decodingutil.Uint16FromUint64(v, k) + if err != nil { + return false, err + } + sli[i] = vv } rv.Set(reflect.ValueOf(sli)) return true, nil @@ -208,7 +237,11 @@ func (d *decoder) asFixedSlice(rv reflect.Value, l int) (bool, error) { if err != nil { return false, err } - sli[i] = uint32(v) + vv, err := decodingutil.Uint32FromUint64(v, k) + if err != nil { + return false, err + } + sli[i] = vv } rv.Set(reflect.ValueOf(sli)) return true, nil diff --git a/internal/stream/decoding/uint.go b/internal/stream/decoding/uint.go index 3a62092..c90635a 100644 --- a/internal/stream/decoding/uint.go +++ b/internal/stream/decoding/uint.go @@ -5,6 +5,7 @@ import ( "reflect" "github.com/shamaton/msgpack/v3/def" + "github.com/shamaton/msgpack/v3/internal/common/decodingutil" ) func (d *decoder) asUint(k reflect.Kind) (uint64, error) { @@ -21,7 +22,7 @@ func (d *decoder) asUintWithCode(code byte, k reflect.Kind) (uint64, error) { return uint64(code), nil case d.isNegativeFixNum(code): - return uint64(int8(code)), nil + return decodingutil.Uint64FromInt64(decodingutil.Int64FromInt8Byte(code), k) case code == def.Uint8: b, err := d.readSize1() @@ -35,7 +36,7 @@ func (d *decoder) asUintWithCode(code byte, k reflect.Kind) (uint64, error) { if err != nil { return 0, err } - return uint64(int8(b)), nil + return decodingutil.Uint64FromInt64(decodingutil.Int64FromInt8Byte(b), k) case code == def.Uint16: bs, err := d.readSize2() @@ -50,8 +51,7 @@ func (d *decoder) asUintWithCode(code byte, k reflect.Kind) (uint64, error) { if err != nil { return 0, err } - v := int16(binary.BigEndian.Uint16(bs)) - return uint64(v), nil + return decodingutil.Uint64FromInt64(decodingutil.Int64FromInt16Bits(binary.BigEndian.Uint16(bs)), k) case code == def.Uint32: bs, err := d.readSize4() @@ -66,8 +66,7 @@ func (d *decoder) asUintWithCode(code byte, k reflect.Kind) (uint64, error) { if err != nil { return 0, err } - v := int32(binary.BigEndian.Uint32(bs)) - return uint64(v), nil + return decodingutil.Uint64FromInt64(decodingutil.Int64FromInt32Bits(binary.BigEndian.Uint32(bs)), k) case code == def.Uint64: bs, err := d.readSize8() @@ -81,7 +80,7 @@ func (d *decoder) asUintWithCode(code byte, k reflect.Kind) (uint64, error) { if err != nil { return 0, err } - return binary.BigEndian.Uint64(bs), nil + return decodingutil.Uint64FromInt64(decodingutil.Int64FromInt64Bits(binary.BigEndian.Uint64(bs)), k) case code == def.Nil: return 0, nil diff --git a/internal/stream/decoding/uint_test.go b/internal/stream/decoding/uint_test.go index d09fc68..4fb5166 100644 --- a/internal/stream/decoding/uint_test.go +++ b/internal/stream/decoding/uint_test.go @@ -40,6 +40,7 @@ func Test_asUintWithCode(t *testing.T) { length int expected uint64 errSkip bool + wantErr bool }{ { name: "Uint8", @@ -48,10 +49,10 @@ func Test_asUintWithCode(t *testing.T) { expected: math.MaxUint8, }, { - name: "Int8", - code: def.Int8, - length: 1, - expected: math.MaxUint64, + name: "Int8", + code: def.Int8, + length: 1, + wantErr: true, }, { name: "Uint16", @@ -60,10 +61,10 @@ func Test_asUintWithCode(t *testing.T) { expected: math.MaxUint16, }, { - name: "Int16", - code: def.Int16, - length: 2, - expected: math.MaxUint64, + name: "Int16", + code: def.Int16, + length: 2, + wantErr: true, }, { name: "Uint32", @@ -72,10 +73,10 @@ func Test_asUintWithCode(t *testing.T) { expected: math.MaxUint32, }, { - name: "Int32", - code: def.Int32, - length: 4, - expected: math.MaxUint64, + name: "Int32", + code: def.Int32, + length: 4, + wantErr: true, }, { name: "Uint64", @@ -84,10 +85,10 @@ func Test_asUintWithCode(t *testing.T) { expected: math.MaxUint64, }, { - name: "Int64", - code: def.Int64, - length: 8, - expected: math.MaxUint64, + name: "Int64", + code: def.Int64, + length: 8, + wantErr: true, }, { name: "Nil", @@ -124,6 +125,10 @@ func Test_asUintWithCode(t *testing.T) { } defer common.PutBuffer(d.buf) v, err := d.asUintWithCode(tc.code, reflect.String) + if tc.wantErr { + tu.IsError(t, err, def.ErrValueOutOfRange) + return + } tu.NoError(t, err) tu.Equal(t, v, tc.expected) diff --git a/internal/stream/encoding/encoding_test.go b/internal/stream/encoding/encoding_test.go index fb18002..c241bee 100644 --- a/internal/stream/encoding/encoding_test.go +++ b/internal/stream/encoding/encoding_test.go @@ -79,9 +79,16 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { t.Run(tc.Name, func(t *testing.T) { w := NewTestWriter() + buf := common.GetBuffer() + data := buf.Data + defer func() { + buf.Data = data + common.PutBuffer(buf) + }() + e := encoder{ w: w, - buf: common.GetBuffer(), + buf: buf, Common: common.Common{}, asArray: tc.AsArray, } @@ -90,7 +97,9 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { t.Fatal("buffer size must be greater than pre write size") } - e.buf.Data = make([]byte, tc.BufferSize) + if tc.BufferSize > 0 { + e.buf.Data = make([]byte, tc.BufferSize) + } if tc.PreWriteSize > 0 { for i := 0; i < tc.PreWriteSize; i++ { _ = e.buf.Write(e.w, dummyByte) @@ -99,7 +108,6 @@ func (tc *AsXXXTestCase[T]) Run(t *testing.T) { err := method(&e) _ = e.buf.Flush(w) - common.PutBuffer(e.buf) if tc.PreWriteSize > 0 { tu.IsError(t, err, ErrTestWriter) diff --git a/internal/stream/encoding/set.go b/internal/stream/encoding/set.go index 787acec..0e00e89 100644 --- a/internal/stream/encoding/set.go +++ b/internal/stream/encoding/set.go @@ -1,93 +1,53 @@ package encoding func (e *encoder) setByte1Int64(value int64) error { - return e.buf.Write(e.w, byte(value)) + return e.buf.WriteUint64(e.w, uint64(value), 1) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } func (e *encoder) setByte2Int64(value int64) error { - return e.buf.Write(e.w, - byte(value>>8), - byte(value), - ) + return e.buf.WriteUint64(e.w, uint64(value), 2) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } func (e *encoder) setByte4Int64(value int64) error { - return e.buf.Write(e.w, - byte(value>>24), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return e.buf.WriteUint64(e.w, uint64(value), 4) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } func (e *encoder) setByte8Int64(value int64) error { - return e.buf.Write(e.w, - byte(value>>56), - byte(value>>48), - byte(value>>40), - byte(value>>32), - byte(value>>24), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return e.buf.WriteUint64(e.w, uint64(value), 8) // #nosec G115 -- MessagePack encodes signed integers as two's-complement bytes. } func (e *encoder) setByte1Uint64(value uint64) error { - return e.buf.Write(e.w, byte(value)) + return e.buf.WriteUint64(e.w, value, 1) } func (e *encoder) setByte2Uint64(value uint64) error { - return e.buf.Write(e.w, - byte(value>>8), - byte(value), - ) + return e.buf.WriteUint64(e.w, value, 2) } func (e *encoder) setByte4Uint64(value uint64) error { - return e.buf.Write(e.w, - byte(value>>24), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return e.buf.WriteUint64(e.w, value, 4) } func (e *encoder) setByte8Uint64(value uint64) error { - return e.buf.Write(e.w, - byte(value>>56), - byte(value>>48), - byte(value>>40), - byte(value>>32), - byte(value>>24), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return e.buf.WriteUint64(e.w, value, 8) } func (e *encoder) setByte1Int(value int) error { - return e.buf.Write(e.w, - byte(value), - ) + return e.buf.WriteUint64(e.w, uint64(value), 1) // #nosec G115 -- callers pass bounded MessagePack code or length values. } func (e *encoder) setByte2Int(value int) error { - return e.buf.Write(e.w, - byte(value>>8), - byte(value), - ) + return e.buf.WriteUint64(e.w, uint64(value), 2) // #nosec G115 -- callers pass bounded MessagePack length values. } func (e *encoder) setByte4Int(value int) error { - return e.buf.Write(e.w, - byte(value>>24), - byte(value>>16), - byte(value>>8), - byte(value), - ) + return e.buf.WriteUint64(e.w, uint64(value), 4) // #nosec G115 -- callers pass bounded MessagePack length values. } func (e *encoder) setBytes(bs []byte) error { return e.buf.Write(e.w, bs...) } + +func (e *encoder) setString(s string) error { + return e.buf.WriteString(e.w, s) +} diff --git a/internal/stream/encoding/string.go b/internal/stream/encoding/string.go index 39c0c9c..a454985 100644 --- a/internal/stream/encoding/string.go +++ b/internal/stream/encoding/string.go @@ -2,15 +2,12 @@ package encoding import ( "math" - "unsafe" "github.com/shamaton/msgpack/v3/def" ) func (e *encoder) writeString(str string) error { - // NOTE : unsafe - strBytes := *(*[]byte)(unsafe.Pointer(&str)) - l := len(strBytes) + l := len(str) if l < 32 { if err := e.setByte1Int(def.FixStr + l); err != nil { return err @@ -37,5 +34,5 @@ func (e *encoder) writeString(str string) error { return err } } - return e.setBytes(strBytes) + return e.setString(str) } diff --git a/msgpack_test.go b/msgpack_test.go index 726559a..71a3b0e 100644 --- a/msgpack_test.go +++ b/msgpack_test.go @@ -79,7 +79,7 @@ func TestInt(t *testing.T) { c: func(d []byte) bool { return def.NegativeFixintMin <= int8(d[0]) && int8(d[0]) <= def.NegativeFixintMax }, - e: "value different", + e: "value out of range", } encdec(t, arg) } @@ -90,7 +90,7 @@ func TestInt(t *testing.T) { c: func(d []byte) bool { return d[0] == def.Int64 }, - e: "value different", + e: "value out of range", } encdec(t, arg) } diff --git a/time/decode.go b/time/decode.go index b9803e8..ce13398 100644 --- a/time/decode.go +++ b/time/decode.go @@ -20,6 +20,10 @@ type timeDecoder struct { var _ ext.Decoder = (*timeDecoder)(nil) +func timeCodeFromByte(v byte) int8 { + return int8(v) // #nosec G115 -- MessagePack timestamp ext type is a signed one-byte value. +} + func (td *timeDecoder) Code() int8 { return def.TimeStamp } @@ -57,14 +61,14 @@ func (td *timeDecoder) IsType(offset int, d *[]byte) bool { switch code { case def.Fixext4: t, _, ok := td.readSize1Safe(offset, d) - if !ok || int8(t) != td.Code() { + if !ok || timeCodeFromByte(t) != td.Code() { return false } _, _, ok = td.readSize4Safe(offset+def.Byte1, d) return ok case def.Fixext8: t, _, ok := td.readSize1Safe(offset, d) - if !ok || int8(t) != td.Code() { + if !ok || timeCodeFromByte(t) != td.Code() { return false } _, _, ok = td.readSize8Safe(offset+def.Byte1, d) @@ -75,7 +79,7 @@ func (td *timeDecoder) IsType(offset int, d *[]byte) bool { return false } t, _, ok := td.readSize1Safe(offset, d) - if !ok || l != 12 || int8(t) != td.Code() { + if !ok || l != 12 || timeCodeFromByte(t) != td.Code() { return false } _, _, ok = td.readSize4Safe(offset+def.Byte1, d) @@ -152,7 +156,7 @@ func (td *timeDecoder) AsValue(offset int, k reflect.Kind, d *[]byte) (interface return zero, 0, fmt.Errorf("in timestamp 96 formats, nanoseconds must not be larger than 999999999 : %d", nano) } sec := binary.BigEndian.Uint64(secbs) - v := time.Unix(int64(sec), int64(nano)) + v := time.Unix(int64(sec), int64(nano)) // #nosec G115 -- timestamp96 seconds are encoded as signed two's-complement bytes. if decodeAsLocal { return v, offset, nil } diff --git a/time/decode_stream.go b/time/decode_stream.go index 75239cb..309eae7 100644 --- a/time/decode_stream.go +++ b/time/decode_stream.go @@ -66,7 +66,7 @@ func (td *timeStreamDecoder) ToValue(code byte, data []byte, k reflect.Kind) (in return zero, fmt.Errorf("in timestamp 96 formats, nanoseconds must not be larger than 999999999 : %d", nano) } sec := binary.BigEndian.Uint64(data[4:12]) - v := time.Unix(int64(sec), int64(nano)) + v := time.Unix(int64(sec), int64(nano)) // #nosec G115 -- timestamp96 seconds are encoded as signed two's-complement bytes. if decodeAsLocal { return v, nil } diff --git a/time/encode.go b/time/encode.go index 06683aa..4f5dba5 100644 --- a/time/encode.go +++ b/time/encode.go @@ -26,13 +26,16 @@ func (s *timeEncoder) Type() reflect.Type { func (s *timeEncoder) CalcByteSize(value reflect.Value) (int, error) { t := value.Interface().(time.Time) - secs := uint64(t.Unix()) - if secs>>34 == 0 { - data := uint64(t.Nanosecond())<<34 | secs - if data&0xffffffff00000000 == 0 { - return def.Byte1 + def.Byte1 + def.Byte4, nil + sec := t.Unix() + if sec >= 0 { + secs := uint64(sec) // #nosec G115 -- non-negative Unix seconds are checked before timestamp64 packing. + if secs>>34 == 0 { + data := uint64(t.Nanosecond())<<34 | secs // #nosec G115 -- time.Nanosecond is always in [0, 999999999]. + if data&0xffffffff00000000 == 0 { + return def.Byte1 + def.Byte1 + def.Byte4, nil + } + return def.Byte1 + def.Byte1 + def.Byte8, nil } - return def.Byte1 + def.Byte1 + def.Byte8, nil } return def.Byte1 + def.Byte1 + def.Byte1 + def.Byte4 + def.Byte8, nil @@ -41,26 +44,29 @@ func (s *timeEncoder) CalcByteSize(value reflect.Value) (int, error) { func (s *timeEncoder) WriteToBytes(value reflect.Value, offset int, bytes *[]byte) int { t := value.Interface().(time.Time) - secs := uint64(t.Unix()) - if secs>>34 == 0 { - data := uint64(t.Nanosecond())<<34 | secs - if data&0xffffffff00000000 == 0 { - offset = s.SetByte1Int(def.Fixext4, offset, bytes) + sec := t.Unix() + if sec >= 0 { + secs := uint64(sec) // #nosec G115 -- non-negative Unix seconds are checked before timestamp64 packing. + if secs>>34 == 0 { + data := uint64(t.Nanosecond())<<34 | secs // #nosec G115 -- time.Nanosecond is always in [0, 999999999]. + if data&0xffffffff00000000 == 0 { + offset = s.SetByte1Int(def.Fixext4, offset, bytes) + offset = s.SetByte1Int(def.TimeStamp, offset, bytes) + offset = s.SetByte4Uint64(data, offset, bytes) + return offset + } + + offset = s.SetByte1Int(def.Fixext8, offset, bytes) offset = s.SetByte1Int(def.TimeStamp, offset, bytes) - offset = s.SetByte4Uint64(data, offset, bytes) + offset = s.SetByte8Uint64(data, offset, bytes) return offset } - - offset = s.SetByte1Int(def.Fixext8, offset, bytes) - offset = s.SetByte1Int(def.TimeStamp, offset, bytes) - offset = s.SetByte8Uint64(data, offset, bytes) - return offset } offset = s.SetByte1Int(def.Ext8, offset, bytes) offset = s.SetByte1Int(12, offset, bytes) offset = s.SetByte1Int(def.TimeStamp, offset, bytes) offset = s.SetByte4Int(t.Nanosecond(), offset, bytes) - offset = s.SetByte8Uint64(secs, offset, bytes) + offset = s.SetByte8Int64(sec, offset, bytes) return offset } diff --git a/time/encode_stream.go b/time/encode_stream.go index 48b74c9..641e88f 100644 --- a/time/encode_stream.go +++ b/time/encode_stream.go @@ -25,32 +25,35 @@ func (timeStreamEncoder) Type() reflect.Type { func (e timeStreamEncoder) Write(w ext.StreamWriter, value reflect.Value) error { t := value.Interface().(time.Time) - secs := uint64(t.Unix()) - if secs>>34 == 0 { - data := uint64(t.Nanosecond())<<34 | secs - if data&0xffffffff00000000 == 0 { - if err := w.WriteByte1Int(def.Fixext4); err != nil { + sec := t.Unix() + if sec >= 0 { + secs := uint64(sec) // #nosec G115 -- non-negative Unix seconds are checked before timestamp64 packing. + if secs>>34 == 0 { + data := uint64(t.Nanosecond())<<34 | secs // #nosec G115 -- time.Nanosecond is always in [0, 999999999]. + if data&0xffffffff00000000 == 0 { + if err := w.WriteByte1Int(def.Fixext4); err != nil { + return err + } + if err := w.WriteByte1Int(def.TimeStamp); err != nil { + return err + } + if err := w.WriteByte4Uint64(data); err != nil { + return err + } + return nil + } + + if err := w.WriteByte1Int(def.Fixext8); err != nil { return err } if err := w.WriteByte1Int(def.TimeStamp); err != nil { return err } - if err := w.WriteByte4Uint64(data); err != nil { + if err := w.WriteByte8Uint64(data); err != nil { return err } return nil } - - if err := w.WriteByte1Int(def.Fixext8); err != nil { - return err - } - if err := w.WriteByte1Int(def.TimeStamp); err != nil { - return err - } - if err := w.WriteByte8Uint64(data); err != nil { - return err - } - return nil } if err := w.WriteByte1Int(def.Ext8); err != nil { @@ -65,7 +68,7 @@ func (e timeStreamEncoder) Write(w ext.StreamWriter, value reflect.Value) error if err := w.WriteByte4Int(t.Nanosecond()); err != nil { return err } - if err := w.WriteByte8Uint64(secs); err != nil { + if err := w.WriteByte8Int64(sec); err != nil { return err } return nil diff --git a/time/encode_stream_test.go b/time/encode_stream_test.go index 48a6e7f..0702164 100644 --- a/time/encode_stream_test.go +++ b/time/encode_stream_test.go @@ -223,6 +223,36 @@ func TestStreamEncodedDataAccuracy(t *testing.T) { } } +func TestStreamWriteNegativeTimestamp(t *testing.T) { + encoder := StreamEncoder + original := time.Unix(-1, 123456789) + value := reflect.ValueOf(original) + buf := &bytes.Buffer{} + buffer := common.GetBuffer() + defer common.PutBuffer(buffer) + w := ext.CreateStreamWriter(buf, buffer) + + err := encoder.Write(w, value) + tu.NoError(t, err) + err = buffer.Flush(buf) + tu.NoError(t, err) + + b := buf.Bytes() + tu.Equal(t, len(b), 15) + tu.Equal(t, b[0], def.Ext8) + tu.Equal(t, b[1], byte(12)) + tu.Equal(t, int8(b[2]), def.TimeStamp) + tu.Equal(t, binary.BigEndian.Uint32(b[3:7]), uint32(original.Nanosecond())) + tu.Equal(t, binary.BigEndian.Uint64(b[7:15]), ^uint64(0)) + + decoded, err := StreamDecoder.ToValue(def.Ext8, b[3:15], reflect.TypeOf(time.Time{}).Kind()) + tu.NoError(t, err) + + decodedTime := decoded.(time.Time) + tu.Equal(t, decodedTime.Unix(), original.Unix()) + tu.Equal(t, decodedTime.Nanosecond(), original.Nanosecond()) +} + func TestStreamWriteWithVariousNanoseconds(t *testing.T) { encoder := StreamEncoder diff --git a/time/encode_test.go b/time/encode_test.go index 634faf0..5ebe7da 100644 --- a/time/encode_test.go +++ b/time/encode_test.go @@ -127,6 +127,33 @@ func TestEncodedDataAccuracy(t *testing.T) { } } +func TestWriteToBytesNegativeTimestamp(t *testing.T) { + encoder := Encoder + original := time.Unix(-1, 123456789) + value := reflect.ValueOf(original) + + size, err := encoder.CalcByteSize(value) + tu.NoError(t, err) + tu.Equal(t, size, 15) + + bytes := make([]byte, size) + offset := encoder.WriteToBytes(value, 0, &bytes) + tu.Equal(t, offset, size) + tu.Equal(t, bytes[0], def.Ext8) + tu.Equal(t, bytes[1], byte(12)) + tu.Equal(t, int8(bytes[2]), def.TimeStamp) + tu.Equal(t, binary.BigEndian.Uint32(bytes[3:7]), uint32(original.Nanosecond())) + tu.Equal(t, binary.BigEndian.Uint64(bytes[7:15]), ^uint64(0)) + + decoded, offset, err := Decoder.AsValue(0, reflect.TypeOf(time.Time{}).Kind(), &bytes) + tu.NoError(t, err) + tu.Equal(t, offset, size) + + decodedTime := decoded.(time.Time) + tu.Equal(t, decodedTime.Unix(), original.Unix()) + tu.Equal(t, decodedTime.Nanosecond(), original.Nanosecond()) +} + func TestWriteToBytes(t *testing.T) { tests := []struct { name string