Skip to content

Commit

Permalink
Add support for decimal128 to bsonproto (#44)
Browse files Browse the repository at this point in the history
AlekSi authored Apr 4, 2024
1 parent 140d6a0 commit 60edcbe
Showing 3 changed files with 54 additions and 1 deletion.
8 changes: 7 additions & 1 deletion bsonproto/bsonproto.go
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import (
//
// CString is not included as it is not a real BSON type.
type ScalarType interface {
float64 | string | Binary | ObjectID | bool | time.Time | NullType | Regex | int32 | Timestamp | int64
float64 | string | Binary | ObjectID | bool | time.Time | NullType | Regex | int32 | Timestamp | int64 | Decimal128
}

// Size returns a size of the encoding of value v in bytes.
@@ -46,6 +46,8 @@ func SizeAny(v any) int {
return SizeTimestamp
case int64:
return SizeInt64
case Decimal128:
return SizeDecimal128
default:
panic(fmt.Sprintf("unsupported type %T", v))
}
@@ -89,6 +91,8 @@ func EncodeAny(b []byte, v any) {
EncodeTimestamp(b, v)
case int64:
EncodeInt64(b, v)
case Decimal128:
EncodeDecimal128(b, v)
default:
panic(fmt.Sprintf("unsupported type %T", v))
}
@@ -133,6 +137,8 @@ func DecodeAny(b []byte, v any) error {
*v, err = DecodeTimestamp(b)
case *int64:
*v, err = DecodeInt64(b)
case *Decimal128:
*v, err = DecodeDecimal128(b)
default:
panic(fmt.Sprintf("unsupported type %T", v))
}
7 changes: 7 additions & 0 deletions bsonproto/bsonproto_test.go
Original file line number Diff line number Diff line change
@@ -71,6 +71,9 @@ func TestScalars(t *testing.T) {
}, {
v: int64(1234567890123456789),
b: []byte{0x15, 0x81, 0xe9, 0x7d, 0xf4, 0x10, 0x22, 0x11},
}, {
v: Decimal128{H: 2024, L: 404},
b: []byte{0x94, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
}} {
t.Run(fmt.Sprintf("%[1]d_%[2]T(%[2]v)", i, tc.v), func(t *testing.T) {
s := SizeAny(tc.v)
@@ -269,6 +272,10 @@ func TestScalarsDecodeErrors(t *testing.T) {
b: []byte{0x42},
v: int64(0),
err: ErrDecodeShortInput,
}, {
b: []byte{0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42},
v: Decimal128{},
err: ErrDecodeShortInput,
}} {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
v := reflect.New(reflect.TypeOf(tc.v)).Interface() // v := new(T)
40 changes: 40 additions & 0 deletions bsonproto/decimal128.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package bsonproto

import (
"encoding/binary"
"fmt"
)

// Decimal128 represents BSON scalar type decimal128.
type Decimal128 struct {
L uint64
H uint64
}

// SizeDecimal128 is a size of the encoding of [Decimal128] in bytes.
const SizeDecimal128 = 16

// EncodeDecimal128 encodes [Decimal128] value v into b.
//
// b must be at least 16 ([SizeDecimal128]) bytes long; otherwise, EncodeDecimal128 will panic.
// Only b[0:16] bytes are modified.
func EncodeDecimal128(b []byte, v Decimal128) {
binary.LittleEndian.PutUint64(b, uint64(v.L))
binary.LittleEndian.PutUint64(b[8:], uint64(v.H))
}

// DecodeDecimal128 decodes [Decimal128] value from b.
//
// If there is not enough bytes, DecodeDecimal128 will return a wrapped [ErrDecodeShortInput].
func DecodeDecimal128(b []byte) (Decimal128, error) {
var res Decimal128

if len(b) < SizeDecimal128 {
return res, fmt.Errorf("DecodeDecimal128: expected at least %d bytes, got %d: %w", SizeDecimal128, len(b), ErrDecodeShortInput)
}

res.L = binary.LittleEndian.Uint64(b[:8])
res.H = binary.LittleEndian.Uint64(b[8:])

return res, nil
}

0 comments on commit 60edcbe

Please sign in to comment.