diff --git a/refs/v2/marshal.go b/refs/v2/marshal.go new file mode 100644 index 0000000..e2abec1 --- /dev/null +++ b/refs/v2/marshal.go @@ -0,0 +1,49 @@ +package v2 + +import ( + "encoding/binary" + "math/bits" +) + +func (m *OwnerID) StableMarshal(buf []byte) ([]byte, error) { + if m == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, m.StableSize()) + } + + var ( + i, n, offset int + ) + + // Write key field. + + buf[i] = 0x0A // id:0x1 << 3 | wiretype:0x2 + offset = binary.PutUvarint(buf[i+1:], uint64(len(m.Value))) + n = copy(buf[i+1+offset:], m.Value) + i += 1 + offset + n + + return buf, nil +} + +func (m *OwnerID) StableSize() int { + if m == nil { + return 0 + } + + var ( + ln, size int + ) + + ln = len(m.Value) // size of key field + size += 1 + uvarIntSize(uint64(ln)) + ln // wiretype + size of string + string + + return size +} + +// uvarIntSize returns length of varint byte sequence for uint64 value 'x'. +func uvarIntSize(x uint64) int { + return (bits.Len64(x|1) + 6) / 7 +} diff --git a/refs/v2/marshal_test.go b/refs/v2/marshal_test.go new file mode 100644 index 0000000..c38b3ec --- /dev/null +++ b/refs/v2/marshal_test.go @@ -0,0 +1,44 @@ +package v2 + +import ( + "crypto/rand" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOwnerID_StableMarshal(t *testing.T) { + owner := make([]byte, 25) + _, err := rand.Read(owner) + require.NoError(t, err) + + expectedOwner := new(OwnerID) + expectedOwner.Value = owner + + gotOwner := new(OwnerID) + + t.Run("small buffer", func(t *testing.T) { + _, err = expectedOwner.StableMarshal(make([]byte, 1)) + require.Error(t, err) + }) + + t.Run("empty owner", func(t *testing.T) { + data, err := new(OwnerID).StableMarshal(nil) + require.NoError(t, err) + + err = gotOwner.Unmarshal(data) + require.NoError(t, err) + + require.Len(t, gotOwner.Value, 0) + }) + + t.Run("non empty owner", func(t *testing.T) { + data, err := expectedOwner.StableMarshal(nil) + require.NoError(t, err) + + err = gotOwner.Unmarshal(data) + require.NoError(t, err) + + require.Equal(t, expectedOwner, gotOwner) + }) +}