Initial commit

This commit is contained in:
jdl
2023-10-13 11:43:27 +02:00
commit 71eb6b0c7e
121 changed files with 11493 additions and 0 deletions

25
mdb/change/binary.go Normal file
View File

@@ -0,0 +1,25 @@
package change
import (
"encoding/binary"
"io"
"git.crumpington.com/public/jldb/lib/errs"
)
func writeBin(w io.Writer, data ...any) error {
for _, value := range data {
if err := binary.Write(w, binary.LittleEndian, value); err != nil {
return errs.IO.WithErr(err)
}
}
return nil
}
func readBin(r io.Reader, ptrs ...any) error {
for _, ptr := range ptrs {
if err := binary.Read(r, binary.LittleEndian, ptr); err != nil {
return errs.IO.WithErr(err)
}
}
return nil
}

98
mdb/change/change.go Normal file
View File

@@ -0,0 +1,98 @@
package change
import (
"io"
"git.crumpington.com/public/jldb/lib/errs"
)
// ----------------------------------------------------------------------------
// Change
// ----------------------------------------------------------------------------
// The Change type encodes a change (store / delete) to be applied to a
// pagefile.
type Change struct {
CollectionID uint64
ItemID uint64
Store bool
Data []byte
WritePageIDs []uint64
ClearPageIDs []uint64
}
func (ch Change) writeTo(w io.Writer) error {
dataSize := int64(len(ch.Data))
if !ch.Store {
dataSize = -1
}
err := writeBin(w,
ch.CollectionID,
ch.ItemID,
dataSize,
uint64(len(ch.WritePageIDs)),
uint64(len(ch.ClearPageIDs)),
ch.WritePageIDs,
ch.ClearPageIDs)
if err != nil {
return err
}
if ch.Store {
if _, err := w.Write(ch.Data); err != nil {
return errs.IO.WithErr(err)
}
}
return nil
}
func (ch *Change) readFrom(r io.Reader) error {
var pageCount, clearCount uint64
var dataSize int64
err := readBin(r,
&ch.CollectionID,
&ch.ItemID,
&dataSize,
&pageCount,
&clearCount)
if err != nil {
return err
}
if uint64(cap(ch.WritePageIDs)) < pageCount {
ch.WritePageIDs = make([]uint64, pageCount)
}
ch.WritePageIDs = ch.WritePageIDs[:pageCount]
if uint64(cap(ch.ClearPageIDs)) < clearCount {
ch.ClearPageIDs = make([]uint64, clearCount)
}
ch.ClearPageIDs = ch.ClearPageIDs[:clearCount]
if err = readBin(r, ch.WritePageIDs); err != nil {
return err
}
if err = readBin(r, ch.ClearPageIDs); err != nil {
return err
}
ch.Store = dataSize != -1
if ch.Store {
if int64(cap(ch.Data)) < dataSize {
ch.Data = make([]byte, dataSize)
}
ch.Data = ch.Data[:dataSize]
if _, err := r.Read(ch.Data); err != nil {
return errs.IO.WithErr(err)
}
} else {
ch.Data = ch.Data[:0]
}
return nil
}

67
mdb/change/change_test.go Normal file
View File

@@ -0,0 +1,67 @@
package change
import (
"bytes"
"reflect"
"testing"
)
func (lhs Change) AssertEqual(t *testing.T, rhs Change) {
if lhs.CollectionID != rhs.CollectionID {
t.Fatal(lhs.CollectionID, rhs.CollectionID)
}
if lhs.ItemID != rhs.ItemID {
t.Fatal(lhs.ItemID, rhs.ItemID)
}
if lhs.Store != rhs.Store {
t.Fatal(lhs.Store, rhs.Store)
}
if len(lhs.Data) != len(rhs.Data) {
t.Fatal(len(lhs.Data), len(rhs.Data))
}
if len(lhs.Data) != 0 {
if !reflect.DeepEqual(lhs.Data, rhs.Data) {
t.Fatal(lhs.Data, rhs.Data)
}
}
if len(lhs.WritePageIDs) != len(rhs.WritePageIDs) {
t.Fatal(len(lhs.WritePageIDs), len(rhs.WritePageIDs))
}
if len(lhs.WritePageIDs) != 0 {
if !reflect.DeepEqual(lhs.WritePageIDs, rhs.WritePageIDs) {
t.Fatal(lhs.WritePageIDs, rhs.WritePageIDs)
}
}
if len(lhs.ClearPageIDs) != len(rhs.ClearPageIDs) {
t.Fatal(len(lhs.ClearPageIDs), len(rhs.ClearPageIDs))
}
if len(lhs.ClearPageIDs) != 0 {
if !reflect.DeepEqual(lhs.ClearPageIDs, rhs.ClearPageIDs) {
t.Fatal(lhs.ClearPageIDs, rhs.ClearPageIDs)
}
}
}
func TestChangeWriteToReadFrom(t *testing.T) {
out := Change{}
for i := 0; i < 100; i++ {
in := randChange()
buf := &bytes.Buffer{}
if err := in.writeTo(buf); err != nil {
t.Fatal(err)
}
if err := out.readFrom(buf); err != nil {
t.Fatal(err)
}
in.AssertEqual(t, out)
}
}

35
mdb/change/encoding.go Normal file
View File

@@ -0,0 +1,35 @@
package change
import "io"
func Write(changes []Change, w io.Writer) error {
count := uint64(len(changes))
if err := writeBin(w, count); err != nil {
return err
}
for _, c := range changes {
if err := c.writeTo(w); err != nil {
return err
}
}
return nil
}
func Read(changes []Change, r io.Reader) ([]Change, error) {
var count uint64
if err := readBin(r, &count); err != nil {
return changes, err
}
if uint64(len(changes)) < count {
changes = make([]Change, count)
}
changes = changes[:count]
for i := range changes {
if err := changes[i].readFrom(r); err != nil {
return changes, err
}
}
return changes, nil
}

View File

@@ -0,0 +1,64 @@
package change
import (
"bytes"
crand "crypto/rand"
"math/rand"
"testing"
)
func randChange() Change {
c := Change{
CollectionID: rand.Uint64(),
ItemID: rand.Uint64(),
Store: rand.Float32() < 0.5,
}
if c.Store {
data := make([]byte, 1+rand.Intn(100))
crand.Read(data)
c.Data = data
}
c.WritePageIDs = make([]uint64, rand.Intn(10))
for i := range c.WritePageIDs {
c.WritePageIDs[i] = rand.Uint64()
}
c.ClearPageIDs = make([]uint64, rand.Intn(10))
for i := range c.ClearPageIDs {
c.ClearPageIDs[i] = rand.Uint64()
}
return c
}
func randChangeSlice() []Change {
changes := make([]Change, 1+rand.Intn(10))
for i := range changes {
changes[i] = randChange()
}
return changes
}
func TestWriteRead(t *testing.T) {
in := randChangeSlice()
var out []Change
buf := &bytes.Buffer{}
if err := Write(in, buf); err != nil {
t.Fatal(err)
}
out, err := Read(out, buf)
if err != nil {
t.Fatal(err)
}
if len(in) != len(out) {
t.Fatal(len(in), len(out))
}
for i := range in {
in[i].AssertEqual(t, out[i])
}
}