go-pkg/lsm/marshal.go

139 lines
2.9 KiB
Go
Raw Permalink Normal View History

2024-06-10 21:12:11 -06:00
package lsm
import (
"bytes"
"encoding"
"encoding/binary"
"fmt"
)
type entry struct {
key string
value uint64
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (e *entry) MarshalBinary() (data []byte, err error) {
data = make([]byte, len(e.key), len(e.key)+binary.MaxVarintLen16)
copy(data, e.key)
data = binary.AppendUvarint(data, e.value)
reverse(data[len(e.key):])
return data, err
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (e *entry) UnmarshalBinary(data []byte) error {
// fmt.Println("unmarshal", data, string(data))
if len(data) < binary.MaxVarintLen16 {
return fmt.Errorf("%w: bad data", ErrDecode)
}
head := make([]byte, binary.MaxVarintLen16)
copy(head, data[max(0, len(data)-cap(head)):])
reverse(head)
size := 0
e.value, size = binary.Uvarint(head)
if size == 0 {
return fmt.Errorf("%w: invalid data", ErrDecode)
}
e.key = string(data[:len(data)-size])
return nil
}
var _ encoding.BinaryMarshaler = (*entry)(nil)
var _ encoding.BinaryUnmarshaler = (*entry)(nil)
type entries []entry
// MarshalBinary implements encoding.BinaryMarshaler.
func (lis *entries) MarshalBinary() (data []byte, err error) {
var buf bytes.Buffer
for _, e := range *lis {
d, err := e.MarshalBinary()
if err != nil {
return nil, err
}
_, err = buf.Write(d)
if err != nil {
return nil, err
}
_, err = buf.Write(reverse(binary.AppendUvarint(make([]byte, 0, binary.MaxVarintLen32), uint64(len(d)))))
if err != nil {
return nil, err
}
}
return buf.Bytes(), err
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (lis *entries) UnmarshalBinary(data []byte) error {
head := make([]byte, binary.MaxVarintLen16)
pos := uint64(len(data))
for pos > 0 {
copy(head, data[max(0, pos-uint64(cap(head))):])
length, size := binary.Uvarint(reverse(head))
e := entry{}
if err := e.UnmarshalBinary(data[max(0, pos-(length+uint64(size))) : pos-uint64(size)]); err != nil {
return err
}
*lis = append(*lis, e)
pos -= length + uint64(size)
}
reverse(*lis)
return nil
}
var _ encoding.BinaryMarshaler = (*entries)(nil)
var _ encoding.BinaryUnmarshaler = (*entries)(nil)
type segment struct {
entries entries
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (s *segment) MarshalBinary() (data []byte, err error) {
head := header{
entries: uint64(len(s.entries)),
}
data, err = s.entries.MarshalBinary()
if err != nil {
return nil, err
}
head.datalen = uint64(len(data))
h := hash()
h.Write(data)
head.sig = h.Sum(nil)
return head.Append(data), err
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (s *segment) UnmarshalBinary(data []byte) error {
head, err := ReadHead(data)
if err != nil {
return err
}
h := hash()
h.Write(data[:head.datalen])
if !bytes.Equal(head.sig, h.Sum(nil)) {
return fmt.Errorf("%w: invalid checksum", ErrDecode)
}
s.entries = make(entries, 0, head.entries)
return s.entries.UnmarshalBinary(data[:head.datalen])
}