Migrate package imports from next.orly.dev to new orly domain structure; add new varint and binary encoders with comprehensive tests; enhance existing tag and envelope implementations with additional methods, validations, and test coverage; introduce shared test.sh script for streamlined testing across modules.
This commit is contained in:
439
pkg/database/indexes/keys.go
Normal file
439
pkg/database/indexes/keys.go
Normal file
@@ -0,0 +1,439 @@
|
||||
package indexes
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"database.orly/indexes/types"
|
||||
"interfaces.orly/codec"
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
var counter int
|
||||
|
||||
func init() {
|
||||
// Initialize the counter to ensure it starts from 0
|
||||
counter = 0
|
||||
}
|
||||
|
||||
func next() int { counter++; return counter - 1 }
|
||||
|
||||
type P struct {
|
||||
val []byte
|
||||
}
|
||||
|
||||
func NewPrefix(prf ...int) (p *P) {
|
||||
if len(prf) > 0 {
|
||||
prefix := Prefix(prf[0])
|
||||
if prefix == "" {
|
||||
panic("unknown prefix")
|
||||
}
|
||||
return &P{[]byte(prefix)}
|
||||
} else {
|
||||
return &P{[]byte{0, 0, 0}}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P) Bytes() (b []byte) { return p.val }
|
||||
|
||||
func (p *P) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(p.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *P) UnmarshalRead(r io.Reader) (err error) {
|
||||
// Allocate a buffer for val if it's nil or empty
|
||||
if p.val == nil || len(p.val) == 0 {
|
||||
p.val = make([]byte, 3) // Prefixes are 3 bytes
|
||||
}
|
||||
_, err = r.Read(p.val)
|
||||
return
|
||||
}
|
||||
|
||||
type I string
|
||||
|
||||
func (i I) Write(w io.Writer) (n int, err error) { return w.Write([]byte(i)) }
|
||||
|
||||
const (
|
||||
EventPrefix = I("evt")
|
||||
IdPrefix = I("eid")
|
||||
FullIdPubkeyPrefix = I("fpc") // full id, pubkey, created at
|
||||
|
||||
CreatedAtPrefix = I("c--") // created at
|
||||
KindPrefix = I("kc-") // kind, created at
|
||||
PubkeyPrefix = I("pc-") // pubkey, created at
|
||||
KindPubkeyPrefix = I("kpc") // kind, pubkey, created at
|
||||
|
||||
TagPrefix = I("tc-") // tag, created at
|
||||
TagKindPrefix = I("tkc") // tag, kind, created at
|
||||
TagPubkeyPrefix = I("tpc") // tag, pubkey, created at
|
||||
TagKindPubkeyPrefix = I("tkp") // tag, kind, pubkey, created at
|
||||
|
||||
ExpirationPrefix = I("exp") // timestamp of expiration
|
||||
VersionPrefix = I("ver") // database version number, for triggering reindexes when new keys are added (policy is add-only).
|
||||
)
|
||||
|
||||
// Prefix returns the three byte human-readable prefixes that go in front of
|
||||
// database indexes.
|
||||
func Prefix(prf int) (i I) {
|
||||
switch prf {
|
||||
case Event:
|
||||
return EventPrefix
|
||||
case Id:
|
||||
return IdPrefix
|
||||
case FullIdPubkey:
|
||||
return FullIdPubkeyPrefix
|
||||
|
||||
case CreatedAt:
|
||||
return CreatedAtPrefix
|
||||
case Kind:
|
||||
return KindPrefix
|
||||
case Pubkey:
|
||||
return PubkeyPrefix
|
||||
case KindPubkey:
|
||||
return KindPubkeyPrefix
|
||||
|
||||
case Tag:
|
||||
return TagPrefix
|
||||
case TagKind:
|
||||
return TagKindPrefix
|
||||
case TagPubkey:
|
||||
return TagPubkeyPrefix
|
||||
case TagKindPubkey:
|
||||
return TagKindPubkeyPrefix
|
||||
|
||||
case Expiration:
|
||||
return ExpirationPrefix
|
||||
case Version:
|
||||
return VersionPrefix
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Identify(r io.Reader) (i int, err error) {
|
||||
// this is here for completeness; however, searches don't need to identify
|
||||
// this as they work via generated prefixes made using Prefix.
|
||||
var b [3]byte
|
||||
_, err = r.Read(b[:])
|
||||
if err != nil {
|
||||
i = -1
|
||||
return
|
||||
}
|
||||
switch I(b[:]) {
|
||||
case EventPrefix:
|
||||
i = Event
|
||||
case IdPrefix:
|
||||
i = Id
|
||||
case FullIdPubkeyPrefix:
|
||||
i = FullIdPubkey
|
||||
|
||||
case CreatedAtPrefix:
|
||||
i = CreatedAt
|
||||
case KindPrefix:
|
||||
i = Kind
|
||||
case PubkeyPrefix:
|
||||
i = Pubkey
|
||||
case KindPubkeyPrefix:
|
||||
i = KindPubkey
|
||||
|
||||
case TagPrefix:
|
||||
i = Tag
|
||||
case TagKindPrefix:
|
||||
i = TagKind
|
||||
case TagPubkeyPrefix:
|
||||
i = TagPubkey
|
||||
case TagKindPubkeyPrefix:
|
||||
i = TagKindPubkey
|
||||
|
||||
case ExpirationPrefix:
|
||||
i = Expiration
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Encs []codec.I
|
||||
|
||||
// T is a wrapper around an array of codec.I. The caller provides the Encs so
|
||||
// they can then call the accessor methods of the codec.I implementation.
|
||||
type T struct{ Encs }
|
||||
|
||||
// New creates a new indexes.T. The helper functions below have an encode and
|
||||
// decode variant, the decode variant doesn't add the prefix encoder because it
|
||||
// has been read by Identify or just is being read, and found because it was
|
||||
// written for the prefix in the iteration.
|
||||
func New(encoders ...codec.I) (i *T) { return &T{encoders} }
|
||||
func (t *T) MarshalWrite(w io.Writer) (err error) {
|
||||
for _, e := range t.Encs {
|
||||
if e == nil || reflect.ValueOf(e).IsNil() {
|
||||
// Skip nil encoders instead of returning early. This enables
|
||||
// generating search prefixes.
|
||||
continue
|
||||
}
|
||||
if err = e.MarshalWrite(w); chk.E(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func (t *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
for _, e := range t.Encs {
|
||||
if err = e.UnmarshalRead(r); chk.E(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Event is the whole event stored in binary format
|
||||
//
|
||||
// prefix|5 serial - event in binary format
|
||||
var Event = next()
|
||||
|
||||
func EventVars() (ser *types.Uint40) { return new(types.Uint40) }
|
||||
func EventEnc(ser *types.Uint40) (enc *T) {
|
||||
return New(NewPrefix(Event), ser)
|
||||
}
|
||||
func EventDec(ser *types.Uint40) (enc *T) { return New(NewPrefix(), ser) }
|
||||
|
||||
// Id contains a truncated 8-byte hash of an event index. This is the secondary
|
||||
// key of an event, the primary key is the serial found in the Event.
|
||||
//
|
||||
// 3 prefix|8 ID hash|5 serial
|
||||
var Id = next()
|
||||
|
||||
func IdVars() (id *types.IdHash, ser *types.Uint40) {
|
||||
return new(types.IdHash), new(types.Uint40)
|
||||
}
|
||||
func IdEnc(id *types.IdHash, ser *types.Uint40) (enc *T) {
|
||||
return New(NewPrefix(Id), id, ser)
|
||||
}
|
||||
func IdDec(id *types.IdHash, ser *types.Uint40) (enc *T) {
|
||||
return New(NewPrefix(), id, ser)
|
||||
}
|
||||
|
||||
// FullIdPubkey is an index designed to enable sorting and filtering of
|
||||
// results found via other indexes, without having to decode the event.
|
||||
//
|
||||
// 3 prefix|5 serial|32 ID|8 pubkey hash|8 timestamp
|
||||
var FullIdPubkey = next()
|
||||
|
||||
func FullIdPubkeyVars() (
|
||||
ser *types.Uint40, fid *types.Id, p *types.PubHash, ca *types.Uint64,
|
||||
) {
|
||||
return new(types.Uint40), new(types.Id), new(types.PubHash), new(types.Uint64)
|
||||
}
|
||||
func FullIdPubkeyEnc(
|
||||
ser *types.Uint40, fid *types.Id, p *types.PubHash, ca *types.Uint64,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(FullIdPubkey), ser, fid, p, ca)
|
||||
}
|
||||
func FullIdPubkeyDec(
|
||||
ser *types.Uint40, fid *types.Id, p *types.PubHash, ca *types.Uint64,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(), ser, fid, p, ca)
|
||||
}
|
||||
|
||||
// CreatedAt is an index that allows search for the timestamp on the event.
|
||||
//
|
||||
// 3 prefix|8 timestamp|5 serial
|
||||
var CreatedAt = next()
|
||||
|
||||
func CreatedAtVars() (ca *types.Uint64, ser *types.Uint40) {
|
||||
return new(types.Uint64), new(types.Uint40)
|
||||
}
|
||||
func CreatedAtEnc(ca *types.Uint64, ser *types.Uint40) (enc *T) {
|
||||
return New(NewPrefix(CreatedAt), ca, ser)
|
||||
}
|
||||
func CreatedAtDec(ca *types.Uint64, ser *types.Uint40) (enc *T) {
|
||||
return New(NewPrefix(), ca, ser)
|
||||
}
|
||||
|
||||
// Kind
|
||||
//
|
||||
// 3 prefix|2 kind|8 timestamp|5 serial
|
||||
var Kind = next()
|
||||
|
||||
func KindVars() (ki *types.Uint16, ca *types.Uint64, ser *types.Uint40) {
|
||||
return new(types.Uint16), new(types.Uint64), new(types.Uint40)
|
||||
}
|
||||
func KindEnc(ki *types.Uint16, ca *types.Uint64, ser *types.Uint40) (enc *T) {
|
||||
return New(NewPrefix(Kind), ki, ca, ser)
|
||||
}
|
||||
func KindDec(ki *types.Uint16, ca *types.Uint64, ser *types.Uint40) (enc *T) {
|
||||
return New(NewPrefix(), ki, ca, ser)
|
||||
}
|
||||
|
||||
// Pubkey is a composite index that allows search by pubkey
|
||||
// filtered by timestamp.
|
||||
//
|
||||
// 3 prefix|8 pubkey hash|8 timestamp|5 serial
|
||||
var Pubkey = next()
|
||||
|
||||
func PubkeyVars() (p *types.PubHash, ca *types.Uint64, ser *types.Uint40) {
|
||||
return new(types.PubHash), new(types.Uint64), new(types.Uint40)
|
||||
}
|
||||
func PubkeyEnc(p *types.PubHash, ca *types.Uint64, ser *types.Uint40) (enc *T) {
|
||||
return New(NewPrefix(Pubkey), p, ca, ser)
|
||||
}
|
||||
func PubkeyDec(p *types.PubHash, ca *types.Uint64, ser *types.Uint40) (enc *T) {
|
||||
return New(NewPrefix(), p, ca, ser)
|
||||
}
|
||||
|
||||
// KindPubkey
|
||||
//
|
||||
// 3 prefix|2 kind|8 pubkey hash|8 timestamp|5 serial
|
||||
var KindPubkey = next()
|
||||
|
||||
func KindPubkeyVars() (
|
||||
ki *types.Uint16, p *types.PubHash, ca *types.Uint64, ser *types.Uint40,
|
||||
) {
|
||||
return new(types.Uint16), new(types.PubHash), new(types.Uint64), new(types.Uint40)
|
||||
}
|
||||
func KindPubkeyEnc(
|
||||
ki *types.Uint16, p *types.PubHash, ca *types.Uint64, ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(KindPubkey), ki, p, ca, ser)
|
||||
}
|
||||
func KindPubkeyDec(
|
||||
ki *types.Uint16, p *types.PubHash, ca *types.Uint64, ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(), ki, p, ca, ser)
|
||||
}
|
||||
|
||||
// Tag allows searching for a tag and filter by timestamp.
|
||||
//
|
||||
// 3 prefix|1 key letter|8 value hash|8 timestamp|5 serial
|
||||
var Tag = next()
|
||||
|
||||
func TagVars() (
|
||||
k *types.Letter, v *types.Ident, ca *types.Uint64, ser *types.Uint40,
|
||||
) {
|
||||
return new(types.Letter), new(types.Ident), new(types.Uint64), new(types.Uint40)
|
||||
}
|
||||
func TagEnc(
|
||||
k *types.Letter, v *types.Ident, ca *types.Uint64, ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(Tag), k, v, ca, ser)
|
||||
}
|
||||
func TagDec(
|
||||
k *types.Letter, v *types.Ident, ca *types.Uint64, ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(), k, v, ca, ser)
|
||||
}
|
||||
|
||||
// TagKind
|
||||
//
|
||||
// 3 prefix|1 key letter|8 value hash|2 kind|8 timestamp|5 serial
|
||||
var TagKind = next()
|
||||
|
||||
func TagKindVars() (
|
||||
k *types.Letter, v *types.Ident, ki *types.Uint16, ca *types.Uint64,
|
||||
ser *types.Uint40,
|
||||
) {
|
||||
return new(types.Letter), new(types.Ident), new(types.Uint16), new(types.Uint64), new(types.Uint40)
|
||||
}
|
||||
func TagKindEnc(
|
||||
k *types.Letter, v *types.Ident, ki *types.Uint16, ca *types.Uint64,
|
||||
ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(TagKind), ki, k, v, ca, ser)
|
||||
}
|
||||
func TagKindDec(
|
||||
k *types.Letter, v *types.Ident, ki *types.Uint16, ca *types.Uint64,
|
||||
ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(), ki, k, v, ca, ser)
|
||||
}
|
||||
|
||||
// TagPubkey allows searching for a pubkey, tag and timestamp.
|
||||
//
|
||||
// 3 prefix|1 key letter|8 value hash|8 pubkey hash|8 timestamp|5 serial
|
||||
var TagPubkey = next()
|
||||
|
||||
func TagPubkeyVars() (
|
||||
k *types.Letter, v *types.Ident, p *types.PubHash, ca *types.Uint64,
|
||||
ser *types.Uint40,
|
||||
) {
|
||||
return new(types.Letter), new(types.Ident), new(types.PubHash), new(types.Uint64), new(types.Uint40)
|
||||
}
|
||||
func TagPubkeyEnc(
|
||||
k *types.Letter, v *types.Ident, p *types.PubHash, ca *types.Uint64,
|
||||
ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(TagPubkey), p, k, v, ca, ser)
|
||||
}
|
||||
func TagPubkeyDec(
|
||||
k *types.Letter, v *types.Ident, p *types.PubHash, ca *types.Uint64,
|
||||
ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(), p, k, v, ca, ser)
|
||||
}
|
||||
|
||||
// TagKindPubkey
|
||||
//
|
||||
// 3 prefix|1 key letter|8 value hash|2 kind|8 pubkey hash|8 bytes timestamp|5 serial
|
||||
var TagKindPubkey = next()
|
||||
|
||||
func TagKindPubkeyVars() (
|
||||
k *types.Letter, v *types.Ident, ki *types.Uint16, p *types.PubHash,
|
||||
ca *types.Uint64,
|
||||
ser *types.Uint40,
|
||||
) {
|
||||
return new(types.Letter), new(types.Ident), new(types.Uint16), new(types.PubHash), new(types.Uint64), new(types.Uint40)
|
||||
}
|
||||
func TagKindPubkeyEnc(
|
||||
k *types.Letter, v *types.Ident, ki *types.Uint16, p *types.PubHash,
|
||||
ca *types.Uint64,
|
||||
ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(TagKindPubkey), ki, p, k, v, ca, ser)
|
||||
}
|
||||
func TagKindPubkeyDec(
|
||||
k *types.Letter, v *types.Ident, ki *types.Uint16, p *types.PubHash,
|
||||
ca *types.Uint64,
|
||||
ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(), ki, p, k, v, ca, ser)
|
||||
}
|
||||
|
||||
// Expiration
|
||||
//
|
||||
// 3 prefix|8 timestamp|5 serial
|
||||
var Expiration = next()
|
||||
|
||||
func ExpirationVars() (
|
||||
exp *types.Uint64, ser *types.Uint40,
|
||||
) {
|
||||
return new(types.Uint64), new(types.Uint40)
|
||||
}
|
||||
func ExpirationEnc(
|
||||
exp *types.Uint64, ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(Expiration), exp, ser)
|
||||
}
|
||||
func ExpirationDec(
|
||||
exp *types.Uint64, ser *types.Uint40,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(), exp, ser)
|
||||
}
|
||||
|
||||
// Version
|
||||
//
|
||||
// 3 prefix|4 version
|
||||
var Version = next()
|
||||
|
||||
func VersionVars() (
|
||||
ver *types.Uint32,
|
||||
) {
|
||||
return new(types.Uint32)
|
||||
}
|
||||
func VersionEnc(
|
||||
ver *types.Uint32,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(Version), ver)
|
||||
}
|
||||
func VersionDec(
|
||||
ver *types.Uint32,
|
||||
) (enc *T) {
|
||||
return New(NewPrefix(), ver)
|
||||
}
|
||||
981
pkg/database/indexes/keys_test.go
Normal file
981
pkg/database/indexes/keys_test.go
Normal file
@@ -0,0 +1,981 @@
|
||||
package indexes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"database.orly/indexes/types"
|
||||
"lol.mleku.dev/chk"
|
||||
"utils.orly"
|
||||
)
|
||||
|
||||
// TestNewPrefix tests the NewPrefix function with and without arguments
|
||||
func TestNewPrefix(t *testing.T) {
|
||||
// Test with no arguments (default prefix)
|
||||
defaultPrefix := NewPrefix()
|
||||
if len(defaultPrefix.Bytes()) != 3 {
|
||||
t.Errorf(
|
||||
"Default prefix should be 3 bytes, got %d",
|
||||
len(defaultPrefix.Bytes()),
|
||||
)
|
||||
}
|
||||
|
||||
// Test with a valid prefix index
|
||||
validPrefix := NewPrefix(Event)
|
||||
if string(validPrefix.Bytes()) != string(EventPrefix) {
|
||||
t.Errorf("Expected prefix %q, got %q", EventPrefix, validPrefix.Bytes())
|
||||
}
|
||||
|
||||
// Test with an invalid prefix index (should panic)
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("NewPrefix should panic with invalid prefix index")
|
||||
}
|
||||
}()
|
||||
_ = NewPrefix(-1) // This should panic
|
||||
}
|
||||
|
||||
// TestPrefixMethods tests the methods of the P struct
|
||||
func TestPrefixMethods(t *testing.T) {
|
||||
// Create a prefix
|
||||
prefix := NewPrefix(Event)
|
||||
|
||||
// Test Bytes method
|
||||
if !utils.FastEqual(prefix.Bytes(), []byte(EventPrefix)) {
|
||||
t.Errorf(
|
||||
"Bytes method returned %v, expected %v", prefix.Bytes(),
|
||||
[]byte(EventPrefix),
|
||||
)
|
||||
}
|
||||
|
||||
// Test MarshalWrite method
|
||||
buf := new(bytes.Buffer)
|
||||
err := prefix.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
if !utils.FastEqual(buf.Bytes(), []byte(EventPrefix)) {
|
||||
t.Errorf(
|
||||
"MarshalWrite wrote %v, expected %v", buf.Bytes(),
|
||||
[]byte(EventPrefix),
|
||||
)
|
||||
}
|
||||
|
||||
// Test UnmarshalRead method
|
||||
newPrefix := &P{}
|
||||
err = newPrefix.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
if !utils.FastEqual(newPrefix.Bytes(), []byte(EventPrefix)) {
|
||||
t.Errorf(
|
||||
"UnmarshalRead read %v, expected %v", newPrefix.Bytes(),
|
||||
[]byte(EventPrefix),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TestPrefixFunction tests the Prefix function
|
||||
func TestPrefixFunction(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
index int
|
||||
expected I
|
||||
}{
|
||||
{"Event", Event, EventPrefix},
|
||||
{"ID", Id, IdPrefix},
|
||||
{"FullIdPubkey", FullIdPubkey, FullIdPubkeyPrefix},
|
||||
{"Pubkey", Pubkey, PubkeyPrefix},
|
||||
{"CreatedAt", CreatedAt, CreatedAtPrefix},
|
||||
{"TagPubkey", TagPubkey, TagPubkeyPrefix},
|
||||
{"Tag", Tag, TagPrefix},
|
||||
{"Kind", Kind, KindPrefix},
|
||||
{"KindPubkey", KindPubkey, KindPubkeyPrefix},
|
||||
{"TagKind", TagKind, TagKindPrefix},
|
||||
{
|
||||
"TagKindPubkey", TagKindPubkey,
|
||||
TagKindPubkeyPrefix,
|
||||
},
|
||||
{"Invalid", -1, ""},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(
|
||||
tc.name, func(t *testing.T) {
|
||||
result := Prefix(tc.index)
|
||||
if result != tc.expected {
|
||||
t.Errorf(
|
||||
"Prefix(%d) = %q, expected %q", tc.index, result,
|
||||
tc.expected,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TestIdentify tests the Identify function
|
||||
func TestIdentify(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
prefix I
|
||||
expected int
|
||||
}{
|
||||
{"Event", EventPrefix, Event},
|
||||
{"ID", IdPrefix, Id},
|
||||
{"FullIdPubkey", FullIdPubkeyPrefix, FullIdPubkey},
|
||||
{"Pubkey", PubkeyPrefix, Pubkey},
|
||||
{"CreatedAt", CreatedAtPrefix, CreatedAt},
|
||||
{"TagPubkey", TagPubkeyPrefix, TagPubkey},
|
||||
{"Tag", TagPrefix, Tag},
|
||||
{"Kind", KindPrefix, Kind},
|
||||
{"KindPubkey", KindPubkeyPrefix, KindPubkey},
|
||||
{"TagKind", TagKindPrefix, TagKind},
|
||||
{
|
||||
"TagKindPubkey", TagKindPubkeyPrefix,
|
||||
TagKindPubkey,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(
|
||||
tc.name, func(t *testing.T) {
|
||||
result, err := Identify(bytes.NewReader([]byte(tc.prefix)))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("Identify failed: %v", err)
|
||||
}
|
||||
if result != tc.expected {
|
||||
t.Errorf(
|
||||
"Identify(%q) = %d, expected %d", tc.prefix, result,
|
||||
tc.expected,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Test with invalid data
|
||||
t.Run(
|
||||
"Invalid", func(t *testing.T) {
|
||||
result, err := Identify(bytes.NewReader([]byte("xyz")))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("Identify failed: %v", err)
|
||||
}
|
||||
if result != 0 {
|
||||
t.Errorf(
|
||||
"Identify with invalid prefix should return 0, got %d",
|
||||
result,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test with error from reader
|
||||
t.Run(
|
||||
"ReaderError", func(t *testing.T) {
|
||||
errReader := &errorReader{}
|
||||
result, err := Identify(errReader)
|
||||
if err == nil {
|
||||
t.Errorf("Identify should return error with failing reader")
|
||||
}
|
||||
if result != -1 {
|
||||
t.Errorf(
|
||||
"Identify with reader error should return -1, got %d",
|
||||
result,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// errorReader is a mock reader that always returns an error
|
||||
type errorReader struct{}
|
||||
|
||||
func (e *errorReader) Read(p []byte) (n int, err error) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// TestTStruct tests the T struct and its methods
|
||||
func TestTStruct(t *testing.T) {
|
||||
// Create some test encoders
|
||||
prefix := NewPrefix(Event)
|
||||
ser := new(types.Uint40)
|
||||
ser.Set(12345)
|
||||
|
||||
// Test New function
|
||||
enc := New(prefix, ser)
|
||||
if len(enc.Encs) != 2 {
|
||||
t.Errorf("New should create T with 2 encoders, got %d", len(enc.Encs))
|
||||
}
|
||||
|
||||
// Test MarshalWrite
|
||||
buf := new(bytes.Buffer)
|
||||
err := enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Test UnmarshalRead
|
||||
dec := New(NewPrefix(), new(types.Uint40))
|
||||
err = dec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
decodedPrefix := dec.Encs[0].(*P)
|
||||
decodedSer := dec.Encs[1].(*types.Uint40)
|
||||
if !utils.FastEqual(decodedPrefix.Bytes(), prefix.Bytes()) {
|
||||
t.Errorf(
|
||||
"Decoded prefix %v, expected %v", decodedPrefix.Bytes(),
|
||||
prefix.Bytes(),
|
||||
)
|
||||
}
|
||||
if decodedSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", decodedSer.Get(), ser.Get())
|
||||
}
|
||||
|
||||
// Test with nil encoder
|
||||
encWithNil := New(prefix, nil, ser)
|
||||
buf.Reset()
|
||||
err = encWithNil.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite with nil encoder failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEventFunctions tests the Event-related functions
|
||||
func TestEventFunctions(t *testing.T) {
|
||||
// Test EventVars
|
||||
ser := EventVars()
|
||||
if ser == nil {
|
||||
t.Fatalf("EventVars should return non-nil *types.Uint40")
|
||||
}
|
||||
|
||||
// Set a value
|
||||
ser.Set(12345)
|
||||
|
||||
// Test EventEnc
|
||||
enc := EventEnc(ser)
|
||||
if len(enc.Encs) != 2 {
|
||||
t.Errorf(
|
||||
"EventEnc should create T with 2 encoders, got %d", len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test EventDec
|
||||
dec := EventDec(ser)
|
||||
if len(dec.Encs) != 2 {
|
||||
t.Errorf(
|
||||
"EventDec should create T with 2 encoders, got %d", len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err := enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newSer := new(types.Uint40)
|
||||
newDec := EventDec(newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded value
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestIdFunctions tests the Id-related functions
|
||||
func TestIdFunctions(t *testing.T) {
|
||||
// Test IdVars
|
||||
id, ser := IdVars()
|
||||
if id == nil || ser == nil {
|
||||
t.Fatalf("IdVars should return non-nil *types.IdHash and *types.Uint40")
|
||||
}
|
||||
|
||||
// Set values
|
||||
id.Set([]byte{1, 2, 3, 4, 5, 6, 7, 8})
|
||||
ser.Set(12345)
|
||||
|
||||
// Test IdEnc
|
||||
enc := IdEnc(id, ser)
|
||||
if len(enc.Encs) != 3 {
|
||||
t.Errorf("IdEnc should create T with 3 encoders, got %d", len(enc.Encs))
|
||||
}
|
||||
|
||||
// Test IdDec
|
||||
dec := IdDec(id, ser)
|
||||
if len(dec.Encs) != 3 {
|
||||
t.Errorf("IdDec should create T with 3 encoders, got %d", len(dec.Encs))
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err := enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newId, newSer := IdVars()
|
||||
newDec := IdDec(newId, newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if !utils.FastEqual(newId.Bytes(), id.Bytes()) {
|
||||
t.Errorf("Decoded id %v, expected %v", newId.Bytes(), id.Bytes())
|
||||
}
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestIdPubkeyFunctions tests the FullIdPubkey-related functions
|
||||
func TestIdPubkeyFunctions(t *testing.T) {
|
||||
// Test FullIdPubkeyVars
|
||||
ser, fid, p, ca := FullIdPubkeyVars()
|
||||
if ser == nil || fid == nil || p == nil || ca == nil {
|
||||
t.Fatalf("FullIdPubkeyVars should return non-nil values")
|
||||
}
|
||||
|
||||
// Set values
|
||||
ser.Set(12345)
|
||||
err := fid.FromId(
|
||||
[]byte{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
},
|
||||
)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromId failed: %v", err)
|
||||
}
|
||||
err = p.FromPubkey(
|
||||
[]byte{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
},
|
||||
)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkey failed: %v", err)
|
||||
}
|
||||
ca.Set(98765)
|
||||
|
||||
// Test FullIdPubkeyEnc
|
||||
enc := FullIdPubkeyEnc(ser, fid, p, ca)
|
||||
if len(enc.Encs) != 5 {
|
||||
t.Errorf(
|
||||
"FullIdPubkeyEnc should create T with 5 encoders, got %d",
|
||||
len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test FullIdPubkeyDec
|
||||
dec := FullIdPubkeyDec(ser, fid, p, ca)
|
||||
if len(dec.Encs) != 5 {
|
||||
t.Errorf(
|
||||
"FullIdPubkeyDec should create T with 5 encoders, got %d",
|
||||
len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err = enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newSer, newFid, newP, newCa := FullIdPubkeyVars()
|
||||
newDec := FullIdPubkeyDec(newSer, newFid, newP, newCa)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
if !utils.FastEqual(newFid.Bytes(), fid.Bytes()) {
|
||||
t.Errorf("Decoded id %v, expected %v", newFid.Bytes(), fid.Bytes())
|
||||
}
|
||||
if !utils.FastEqual(newP.Bytes(), p.Bytes()) {
|
||||
t.Errorf("Decoded pubkey hash %v, expected %v", newP.Bytes(), p.Bytes())
|
||||
}
|
||||
if newCa.Get() != ca.Get() {
|
||||
t.Errorf("Decoded created at %d, expected %d", newCa.Get(), ca.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreatedAtFunctions tests the CreatedAt-related functions
|
||||
func TestCreatedAtFunctions(t *testing.T) {
|
||||
// Test CreatedAtVars
|
||||
ca, ser := CreatedAtVars()
|
||||
if ca == nil || ser == nil {
|
||||
t.Fatalf("CreatedAtVars should return non-nil values")
|
||||
}
|
||||
|
||||
// Set values
|
||||
ca.Set(98765)
|
||||
ser.Set(12345)
|
||||
|
||||
// Test CreatedAtEnc
|
||||
enc := CreatedAtEnc(ca, ser)
|
||||
if len(enc.Encs) != 3 {
|
||||
t.Errorf(
|
||||
"CreatedAtEnc should create T with 3 encoders, got %d",
|
||||
len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test CreatedAtDec
|
||||
dec := CreatedAtDec(ca, ser)
|
||||
if len(dec.Encs) != 3 {
|
||||
t.Errorf(
|
||||
"CreatedAtDec should create T with 3 encoders, got %d",
|
||||
len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err := enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newCa, newSer := CreatedAtVars()
|
||||
newDec := CreatedAtDec(newCa, newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if newCa.Get() != ca.Get() {
|
||||
t.Errorf("Decoded created at %d, expected %d", newCa.Get(), ca.Get())
|
||||
}
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestPubkeyFunctions tests the Pubkey-related functions
|
||||
func TestPubkeyFunctions(t *testing.T) {
|
||||
// Test PubkeyVars
|
||||
p, ca, ser := PubkeyVars()
|
||||
if p == nil || ca == nil || ser == nil {
|
||||
t.Fatalf("PubkeyVars should return non-nil values")
|
||||
}
|
||||
|
||||
// Set values
|
||||
err := p.FromPubkey(
|
||||
[]byte{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
},
|
||||
)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkey failed: %v", err)
|
||||
}
|
||||
ca.Set(98765)
|
||||
ser.Set(12345)
|
||||
|
||||
// Test PubkeyEnc
|
||||
enc := PubkeyEnc(p, ca, ser)
|
||||
if len(enc.Encs) != 4 {
|
||||
t.Errorf(
|
||||
"PubkeyEnc should create T with 4 encoders, got %d",
|
||||
len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test PubkeyDec
|
||||
dec := PubkeyDec(p, ca, ser)
|
||||
if len(dec.Encs) != 4 {
|
||||
t.Errorf(
|
||||
"PubkeyDec should create T with 4 encoders, got %d",
|
||||
len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err = enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newP, newCa, newSer := PubkeyVars()
|
||||
newDec := PubkeyDec(newP, newCa, newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if !utils.FastEqual(newP.Bytes(), p.Bytes()) {
|
||||
t.Errorf("Decoded pubkey hash %v, expected %v", newP.Bytes(), p.Bytes())
|
||||
}
|
||||
if newCa.Get() != ca.Get() {
|
||||
t.Errorf("Decoded created at %d, expected %d", newCa.Get(), ca.Get())
|
||||
}
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestPubkeyTagFunctions tests the TagPubkey-related functions
|
||||
func TestPubkeyTagFunctions(t *testing.T) {
|
||||
// Test TagPubkeyVars
|
||||
k, v, p, ca, ser := TagPubkeyVars()
|
||||
if p == nil || k == nil || v == nil || ca == nil || ser == nil {
|
||||
t.Fatalf("TagPubkeyVars should return non-nil values")
|
||||
}
|
||||
|
||||
// Set values
|
||||
err := p.FromPubkey(
|
||||
[]byte{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
},
|
||||
)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkey failed: %v", err)
|
||||
}
|
||||
k.Set('e')
|
||||
v.FromIdent([]byte("test-value"))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdent failed: %v", err)
|
||||
}
|
||||
ca.Set(98765)
|
||||
ser.Set(12345)
|
||||
|
||||
// Test TagPubkeyEnc
|
||||
enc := TagPubkeyEnc(k, v, p, ca, ser)
|
||||
if len(enc.Encs) != 6 {
|
||||
t.Errorf(
|
||||
"TagPubkeyEnc should create T with 6 encoders, got %d",
|
||||
len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test TagPubkeyDec
|
||||
dec := TagPubkeyDec(k, v, p, ca, ser)
|
||||
if len(dec.Encs) != 6 {
|
||||
t.Errorf(
|
||||
"TagPubkeyDec should create T with 6 encoders, got %d",
|
||||
len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err = enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newK, newV, newP, newCa, newSer := TagPubkeyVars()
|
||||
newDec := TagPubkeyDec(newK, newV, newP, newCa, newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if !utils.FastEqual(newP.Bytes(), p.Bytes()) {
|
||||
t.Errorf("Decoded pubkey hash %v, expected %v", newP.Bytes(), p.Bytes())
|
||||
}
|
||||
if newK.Letter() != k.Letter() {
|
||||
t.Errorf(
|
||||
"Decoded key letter %c, expected %c", newK.Letter(), k.Letter(),
|
||||
)
|
||||
}
|
||||
if !utils.FastEqual(newV.Bytes(), v.Bytes()) {
|
||||
t.Errorf("Decoded value hash %v, expected %v", newV.Bytes(), v.Bytes())
|
||||
}
|
||||
if newCa.Get() != ca.Get() {
|
||||
t.Errorf("Decoded created at %d, expected %d", newCa.Get(), ca.Get())
|
||||
}
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestTagFunctions tests the Tag-related functions
|
||||
func TestTagFunctions(t *testing.T) {
|
||||
var err error
|
||||
// Test TagVars
|
||||
k, v, ca, ser := TagVars()
|
||||
if k == nil || v == nil || ca == nil || ser == nil {
|
||||
t.Fatalf("TagVars should return non-nil values")
|
||||
}
|
||||
|
||||
// Set values
|
||||
k.Set('e')
|
||||
v.FromIdent([]byte("test-value"))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdent failed: %v", err)
|
||||
}
|
||||
ca.Set(98765)
|
||||
ser.Set(12345)
|
||||
|
||||
// Test TagEnc
|
||||
enc := TagEnc(k, v, ca, ser)
|
||||
if len(enc.Encs) != 5 {
|
||||
t.Errorf(
|
||||
"TagEnc should create T with 5 encoders, got %d",
|
||||
len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test TagDec
|
||||
dec := TagDec(k, v, ca, ser)
|
||||
if len(dec.Encs) != 5 {
|
||||
t.Errorf(
|
||||
"TagDec should create T with 5 encoders, got %d",
|
||||
len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err = enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newK, newV, newCa, newSer := TagVars()
|
||||
newDec := TagDec(newK, newV, newCa, newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if newK.Letter() != k.Letter() {
|
||||
t.Errorf(
|
||||
"Decoded key letter %c, expected %c", newK.Letter(), k.Letter(),
|
||||
)
|
||||
}
|
||||
if !utils.FastEqual(newV.Bytes(), v.Bytes()) {
|
||||
t.Errorf("Decoded value hash %v, expected %v", newV.Bytes(), v.Bytes())
|
||||
}
|
||||
if newCa.Get() != ca.Get() {
|
||||
t.Errorf("Decoded created at %d, expected %d", newCa.Get(), ca.Get())
|
||||
}
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestKindFunctions tests the Kind-related functions
|
||||
func TestKindFunctions(t *testing.T) {
|
||||
// Test KindVars
|
||||
ki, ca, ser := KindVars()
|
||||
if ki == nil || ca == nil || ser == nil {
|
||||
t.Fatalf("KindVars should return non-nil values")
|
||||
}
|
||||
|
||||
// Set values
|
||||
ki.Set(1234)
|
||||
ca.Set(98765)
|
||||
ser.Set(12345)
|
||||
|
||||
// Test KindEnc
|
||||
enc := KindEnc(ki, ca, ser)
|
||||
if len(enc.Encs) != 4 {
|
||||
t.Errorf(
|
||||
"KindEnc should create T with 4 encoders, got %d",
|
||||
len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test KindDec
|
||||
dec := KindDec(ki, ca, ser)
|
||||
if len(dec.Encs) != 4 {
|
||||
t.Errorf(
|
||||
"KindDec should create T with 4 encoders, got %d",
|
||||
len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err := enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newKi, newCa, newSer := KindVars()
|
||||
newDec := KindDec(newKi, newCa, newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if newKi.Get() != ki.Get() {
|
||||
t.Errorf("Decoded kind %d, expected %d", newKi.Get(), ki.Get())
|
||||
}
|
||||
if newCa.Get() != ca.Get() {
|
||||
t.Errorf("Decoded created at %d, expected %d", newCa.Get(), ca.Get())
|
||||
}
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestKindTagFunctions tests the TagKind-related functions
|
||||
func TestKindTagFunctions(t *testing.T) {
|
||||
var err error
|
||||
// Test TagKindVars
|
||||
k, v, ki, ca, ser := TagKindVars()
|
||||
if ki == nil || k == nil || v == nil || ca == nil || ser == nil {
|
||||
t.Fatalf("TagKindVars should return non-nil values")
|
||||
}
|
||||
|
||||
// Set values
|
||||
ki.Set(1234)
|
||||
k.Set('e')
|
||||
v.FromIdent([]byte("test-value"))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdent failed: %v", err)
|
||||
}
|
||||
ca.Set(98765)
|
||||
ser.Set(12345)
|
||||
|
||||
// Test TagKindEnc
|
||||
enc := TagKindEnc(k, v, ki, ca, ser)
|
||||
if len(enc.Encs) != 6 {
|
||||
t.Errorf(
|
||||
"TagKindEnc should create T with 6 encoders, got %d",
|
||||
len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test TagKindDec
|
||||
dec := TagKindDec(k, v, ki, ca, ser)
|
||||
if len(dec.Encs) != 6 {
|
||||
t.Errorf(
|
||||
"TagKindDec should create T with 6 encoders, got %d",
|
||||
len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err = enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newK, newV, newKi, newCa, newSer := TagKindVars()
|
||||
newDec := TagKindDec(newK, newV, newKi, newCa, newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if newKi.Get() != ki.Get() {
|
||||
t.Errorf("Decoded kind %d, expected %d", newKi.Get(), ki.Get())
|
||||
}
|
||||
if newK.Letter() != k.Letter() {
|
||||
t.Errorf(
|
||||
"Decoded key letter %c, expected %c", newK.Letter(), k.Letter(),
|
||||
)
|
||||
}
|
||||
if !utils.FastEqual(newV.Bytes(), v.Bytes()) {
|
||||
t.Errorf("Decoded value hash %v, expected %v", newV.Bytes(), v.Bytes())
|
||||
}
|
||||
if newCa.Get() != ca.Get() {
|
||||
t.Errorf("Decoded created at %d, expected %d", newCa.Get(), ca.Get())
|
||||
}
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestKindPubkeyFunctions tests the KindPubkey-related functions
|
||||
func TestKindPubkeyFunctions(t *testing.T) {
|
||||
// Test KindPubkeyVars
|
||||
ki, p, ca, ser := KindPubkeyVars()
|
||||
if ki == nil || p == nil || ca == nil || ser == nil {
|
||||
t.Fatalf("KindPubkeyVars should return non-nil values")
|
||||
}
|
||||
|
||||
// Set values
|
||||
ki.Set(1234)
|
||||
err := p.FromPubkey(
|
||||
[]byte{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
},
|
||||
)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkey failed: %v", err)
|
||||
}
|
||||
ca.Set(98765)
|
||||
ser.Set(12345)
|
||||
|
||||
// Test KindPubkeyEnc
|
||||
enc := KindPubkeyEnc(ki, p, ca, ser)
|
||||
if len(enc.Encs) != 5 {
|
||||
t.Errorf(
|
||||
"KindPubkeyEnc should create T with 5 encoders, got %d",
|
||||
len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test KindPubkeyDec
|
||||
dec := KindPubkeyDec(ki, p, ca, ser)
|
||||
if len(dec.Encs) != 5 {
|
||||
t.Errorf(
|
||||
"KindPubkeyDec should create T with 5 encoders, got %d",
|
||||
len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err = enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newKi, newP, newCa, newSer := KindPubkeyVars()
|
||||
newDec := KindPubkeyDec(newKi, newP, newCa, newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if newKi.Get() != ki.Get() {
|
||||
t.Errorf("Decoded kind %d, expected %d", newKi.Get(), ki.Get())
|
||||
}
|
||||
if !utils.FastEqual(newP.Bytes(), p.Bytes()) {
|
||||
t.Errorf("Decoded pubkey hash %v, expected %v", newP.Bytes(), p.Bytes())
|
||||
}
|
||||
if newCa.Get() != ca.Get() {
|
||||
t.Errorf("Decoded created at %d, expected %d", newCa.Get(), ca.Get())
|
||||
}
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
|
||||
// TestKindPubkeyTagFunctions tests the TagKindPubkey-related functions
|
||||
func TestKindPubkeyTagFunctions(t *testing.T) {
|
||||
// Test TagKindPubkeyVars
|
||||
k, v, ki, p, ca, ser := TagKindPubkeyVars()
|
||||
if ki == nil || p == nil || k == nil || v == nil || ca == nil || ser == nil {
|
||||
t.Fatalf("TagKindPubkeyVars should return non-nil values")
|
||||
}
|
||||
|
||||
// Set values
|
||||
ki.Set(1234)
|
||||
err := p.FromPubkey(
|
||||
[]byte{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
},
|
||||
)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkey failed: %v", err)
|
||||
}
|
||||
k.Set('e')
|
||||
v.FromIdent([]byte("test-value"))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdent failed: %v", err)
|
||||
}
|
||||
ca.Set(98765)
|
||||
ser.Set(12345)
|
||||
|
||||
// Test TagKindPubkeyEnc
|
||||
enc := TagKindPubkeyEnc(k, v, ki, p, ca, ser)
|
||||
if len(enc.Encs) != 7 {
|
||||
t.Errorf(
|
||||
"TagKindPubkeyEnc should create T with 7 encoders, got %d",
|
||||
len(enc.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test TagKindPubkeyDec
|
||||
dec := TagKindPubkeyDec(k, v, ki, p, ca, ser)
|
||||
if len(dec.Encs) != 7 {
|
||||
t.Errorf(
|
||||
"TagKindPubkeyDec should create T with 7 encoders, got %d",
|
||||
len(dec.Encs),
|
||||
)
|
||||
}
|
||||
|
||||
// Test marshaling and unmarshaling
|
||||
buf := new(bytes.Buffer)
|
||||
err = enc.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Create new variables for decoding
|
||||
newK, newV, newKi, newP, newCa, newSer := TagKindPubkeyVars()
|
||||
newDec := TagKindPubkeyDec(newK, newV, newKi, newP, newCa, newSer)
|
||||
|
||||
err = newDec.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the decoded values
|
||||
if newKi.Get() != ki.Get() {
|
||||
t.Errorf("Decoded kind %d, expected %d", newKi.Get(), ki.Get())
|
||||
}
|
||||
if !utils.FastEqual(newP.Bytes(), p.Bytes()) {
|
||||
t.Errorf("Decoded pubkey hash %v, expected %v", newP.Bytes(), p.Bytes())
|
||||
}
|
||||
if newK.Letter() != k.Letter() {
|
||||
t.Errorf(
|
||||
"Decoded key letter %c, expected %c", newK.Letter(), k.Letter(),
|
||||
)
|
||||
}
|
||||
if !utils.FastEqual(newV.Bytes(), v.Bytes()) {
|
||||
t.Errorf("Decoded value hash %v, expected %v", newV.Bytes(), v.Bytes())
|
||||
}
|
||||
if newCa.Get() != ca.Get() {
|
||||
t.Errorf("Decoded created at %d, expected %d", newCa.Get(), ca.Get())
|
||||
}
|
||||
if newSer.Get() != ser.Get() {
|
||||
t.Errorf("Decoded serial %d, expected %d", newSer.Get(), ser.Get())
|
||||
}
|
||||
}
|
||||
419
pkg/database/indexes/types/endianness_test.go
Normal file
419
pkg/database/indexes/types/endianness_test.go
Normal file
@@ -0,0 +1,419 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestTypesSortLexicographically tests if the numeric types sort lexicographically
|
||||
// when using bytes.Compare after marshaling.
|
||||
func TestTypesSortLexicographically(t *testing.T) {
|
||||
// Test Uint16
|
||||
t.Run("Uint16", func(t *testing.T) {
|
||||
testUint16Sorting(t)
|
||||
})
|
||||
|
||||
// Test Uint24
|
||||
t.Run("Uint24", func(t *testing.T) {
|
||||
testUint24Sorting(t)
|
||||
})
|
||||
|
||||
// Test Uint32
|
||||
t.Run("Uint32", func(t *testing.T) {
|
||||
testUint32Sorting(t)
|
||||
})
|
||||
|
||||
// Test Uint40
|
||||
t.Run("Uint40", func(t *testing.T) {
|
||||
testUint40Sorting(t)
|
||||
})
|
||||
|
||||
// Test Uint64
|
||||
t.Run("Uint64", func(t *testing.T) {
|
||||
testUint64Sorting(t)
|
||||
})
|
||||
}
|
||||
|
||||
// TestEdgeCases tests sorting with edge cases like zero, max values, and adjacent values
|
||||
func TestEdgeCases(t *testing.T) {
|
||||
// Test Uint16 edge cases
|
||||
t.Run("Uint16EdgeCases", func(t *testing.T) {
|
||||
testUint16EdgeCases(t)
|
||||
})
|
||||
|
||||
// Test Uint24 edge cases
|
||||
t.Run("Uint24EdgeCases", func(t *testing.T) {
|
||||
testUint24EdgeCases(t)
|
||||
})
|
||||
|
||||
// Test Uint32 edge cases
|
||||
t.Run("Uint32EdgeCases", func(t *testing.T) {
|
||||
testUint32EdgeCases(t)
|
||||
})
|
||||
|
||||
// Test Uint40 edge cases
|
||||
t.Run("Uint40EdgeCases", func(t *testing.T) {
|
||||
testUint40EdgeCases(t)
|
||||
})
|
||||
|
||||
// Test Uint64 edge cases
|
||||
t.Run("Uint64EdgeCases", func(t *testing.T) {
|
||||
testUint64EdgeCases(t)
|
||||
})
|
||||
}
|
||||
|
||||
func testUint16Sorting(t *testing.T) {
|
||||
values := []uint16{1, 10, 100, 1000, 10000, 65535}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint16)
|
||||
u.Set(val)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint16 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint16 values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUint24Sorting(t *testing.T) {
|
||||
values := []uint32{1, 10, 100, 1000, 10000, 100000, 1000000, 16777215}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint24)
|
||||
err := u.Set(val)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set Uint24 %d: %v", val, err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err = u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint24 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint24 values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUint32Sorting(t *testing.T) {
|
||||
values := []uint32{1, 10, 100, 1000, 10000, 100000, 1000000, 4294967295}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint32)
|
||||
u.Set(val)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint32 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint32 values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUint40Sorting(t *testing.T) {
|
||||
values := []uint64{1, 10, 100, 1000, 10000, 100000, 1000000, 1099511627775}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint40)
|
||||
err := u.Set(val)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set Uint40 %d: %v", val, err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err = u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint40 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint40 values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUint64Sorting(t *testing.T) {
|
||||
values := []uint64{1, 10, 100, 1000, 10000, 100000, 1000000, 18446744073709551615}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint64)
|
||||
u.Set(val)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint64 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint64 values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Edge case test functions
|
||||
|
||||
func testUint16EdgeCases(t *testing.T) {
|
||||
// Test edge cases: 0, max value, and adjacent values
|
||||
values := []uint16{0, 1, 2, 65534, 65535}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint16)
|
||||
u.Set(val)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint16 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint16 edge case values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUint24EdgeCases(t *testing.T) {
|
||||
// Test edge cases: 0, max value, and adjacent values
|
||||
values := []uint32{0, 1, 2, 16777214, 16777215}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint24)
|
||||
err := u.Set(val)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set Uint24 %d: %v", val, err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err = u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint24 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint24 edge case values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUint32EdgeCases(t *testing.T) {
|
||||
// Test edge cases: 0, max value, and adjacent values
|
||||
values := []uint32{0, 1, 2, 4294967294, 4294967295}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint32)
|
||||
u.Set(val)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint32 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint32 edge case values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUint40EdgeCases(t *testing.T) {
|
||||
// Test edge cases: 0, max value, and adjacent values
|
||||
values := []uint64{0, 1, 2, 1099511627774, 1099511627775}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint40)
|
||||
err := u.Set(val)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set Uint40 %d: %v", val, err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err = u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint40 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint40 edge case values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUint64EdgeCases(t *testing.T) {
|
||||
// Test edge cases: 0, max value, and adjacent values
|
||||
values := []uint64{0, 1, 2, 18446744073709551614, 18446744073709551615}
|
||||
|
||||
// Marshal each value
|
||||
marshaledValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
u := new(Uint64)
|
||||
u.Set(val)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := u.MarshalWrite(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal Uint64 %d: %v", val, err)
|
||||
}
|
||||
|
||||
marshaledValues[i] = buf.Bytes()
|
||||
}
|
||||
|
||||
// Check if they sort correctly with bytes.Compare
|
||||
for i := 0; i < len(marshaledValues)-1; i++ {
|
||||
if bytes.Compare(marshaledValues[i], marshaledValues[i+1]) >= 0 {
|
||||
t.Errorf("Uint64 edge case values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", marshaledValues[i], marshaledValues[i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestEndianness demonstrates why BigEndian is used instead of LittleEndian
|
||||
// for lexicographical sorting with bytes.Compare
|
||||
func TestEndianness(t *testing.T) {
|
||||
// Test with uint32 values
|
||||
values := []uint32{1, 10, 100, 1000, 10000}
|
||||
|
||||
// Marshal each value using BigEndian
|
||||
bigEndianValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
buf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buf, val)
|
||||
bigEndianValues[i] = buf
|
||||
}
|
||||
|
||||
// Marshal each value using LittleEndian
|
||||
littleEndianValues := make([][]byte, len(values))
|
||||
for i, val := range values {
|
||||
buf := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(buf, val)
|
||||
littleEndianValues[i] = buf
|
||||
}
|
||||
|
||||
// Check if BigEndian values sort correctly with bytes.Compare
|
||||
t.Log("Testing BigEndian sorting:")
|
||||
for i := 0; i < len(bigEndianValues)-1; i++ {
|
||||
result := bytes.Compare(bigEndianValues[i], bigEndianValues[i+1])
|
||||
t.Logf("Compare %d with %d: result = %d", values[i], values[i+1], result)
|
||||
if result >= 0 {
|
||||
t.Errorf("BigEndian values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", bigEndianValues[i], bigEndianValues[i+1])
|
||||
}
|
||||
}
|
||||
|
||||
// Check if LittleEndian values sort correctly with bytes.Compare
|
||||
t.Log("Testing LittleEndian sorting:")
|
||||
correctOrder := true
|
||||
for i := 0; i < len(littleEndianValues)-1; i++ {
|
||||
result := bytes.Compare(littleEndianValues[i], littleEndianValues[i+1])
|
||||
t.Logf("Compare %d with %d: result = %d", values[i], values[i+1], result)
|
||||
if result >= 0 {
|
||||
correctOrder = false
|
||||
t.Logf("LittleEndian values don't sort correctly: %v should be less than %v",
|
||||
values[i], values[i+1])
|
||||
t.Logf("Bytes representation: %v vs %v", littleEndianValues[i], littleEndianValues[i+1])
|
||||
}
|
||||
}
|
||||
|
||||
// We expect LittleEndian to NOT sort correctly
|
||||
if correctOrder {
|
||||
t.Error("LittleEndian values unexpectedly sorted correctly")
|
||||
} else {
|
||||
t.Log("As expected, LittleEndian values don't sort correctly with bytes.Compare")
|
||||
}
|
||||
}
|
||||
38
pkg/database/indexes/types/fullid.go
Normal file
38
pkg/database/indexes/types/fullid.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"crypto.orly/sha256"
|
||||
"lol.mleku.dev/errorf"
|
||||
)
|
||||
|
||||
const IdLen = sha256.Size
|
||||
|
||||
type Id struct {
|
||||
val [IdLen]byte
|
||||
}
|
||||
|
||||
func (fi *Id) FromId(id []byte) (err error) {
|
||||
if len(id) != IdLen {
|
||||
err = errorf.E(
|
||||
"fullid.FromId: invalid ID length, got %d require %d", len(id),
|
||||
IdLen,
|
||||
)
|
||||
return
|
||||
}
|
||||
copy(fi.val[:], id)
|
||||
return
|
||||
}
|
||||
func (fi *Id) Bytes() (b []byte) { return fi.val[:] }
|
||||
|
||||
func (fi *Id) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(fi.val[:])
|
||||
return
|
||||
}
|
||||
|
||||
func (fi *Id) UnmarshalRead(r io.Reader) (err error) {
|
||||
copy(fi.val[:], fi.val[:IdLen])
|
||||
_, err = r.Read(fi.val[:])
|
||||
return
|
||||
}
|
||||
115
pkg/database/indexes/types/fullid_test.go
Normal file
115
pkg/database/indexes/types/fullid_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
"utils.orly"
|
||||
|
||||
"crypto.orly/sha256"
|
||||
)
|
||||
|
||||
func TestFromId(t *testing.T) {
|
||||
// Create a valid ID (32 bytes)
|
||||
validId := make([]byte, sha256.Size)
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
validId[i] = byte(i)
|
||||
}
|
||||
|
||||
// Create an invalid ID (wrong size)
|
||||
invalidId := make([]byte, sha256.Size-1)
|
||||
|
||||
// Test with valid ID
|
||||
fi := &Id{}
|
||||
err := fi.FromId(validId)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromId failed with valid ID: %v", err)
|
||||
}
|
||||
|
||||
// Verify the ID was set correctly
|
||||
if !utils.FastEqual(fi.Bytes(), validId) {
|
||||
t.Errorf(
|
||||
"FromId did not set the ID correctly: got %v, want %v", fi.Bytes(),
|
||||
validId,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with invalid ID
|
||||
fi = &Id{}
|
||||
err = fi.FromId(invalidId)
|
||||
if err == nil {
|
||||
t.Errorf("FromId should have failed with invalid ID size")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdMarshalWriteUnmarshalRead(t *testing.T) {
|
||||
// Create a ID with a known value
|
||||
fi1 := &Id{}
|
||||
validId := make([]byte, sha256.Size)
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
validId[i] = byte(i)
|
||||
}
|
||||
err := fi1.FromId(validId)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromId failed: %v", err)
|
||||
}
|
||||
|
||||
// Test MarshalWrite
|
||||
buf := new(bytes.Buffer)
|
||||
err = fi1.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the written bytes
|
||||
if !utils.FastEqual(buf.Bytes(), validId) {
|
||||
t.Errorf("MarshalWrite wrote %v, want %v", buf.Bytes(), validId)
|
||||
}
|
||||
|
||||
// Test UnmarshalRead
|
||||
fi2 := &Id{}
|
||||
err = fi2.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the read value
|
||||
if !utils.FastEqual(fi2.Bytes(), validId) {
|
||||
t.Errorf("UnmarshalRead read %v, want %v", fi2.Bytes(), validId)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdUnmarshalReadWithCorruptedData(t *testing.T) {
|
||||
// Create a ID with a known value
|
||||
fi1 := &Id{}
|
||||
validId := make([]byte, sha256.Size)
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
validId[i] = byte(i)
|
||||
}
|
||||
err := fi1.FromId(validId)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromId failed: %v", err)
|
||||
}
|
||||
|
||||
// Create a second ID with a different value
|
||||
fi2 := &Id{}
|
||||
differentId := make([]byte, sha256.Size)
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
differentId[i] = byte(sha256.Size - i - 1)
|
||||
}
|
||||
err = fi2.FromId(differentId)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromId failed: %v", err)
|
||||
}
|
||||
|
||||
// Test UnmarshalRead with corrupted data (less than Len bytes)
|
||||
corruptedData := make([]byte, sha256.Size/2)
|
||||
fi2.UnmarshalRead(bytes.NewBuffer(corruptedData))
|
||||
|
||||
// The UnmarshalRead method should not have copied the original data to itself
|
||||
// before reading, so the value should be partially overwritten
|
||||
if utils.FastEqual(fi2.Bytes(), differentId) {
|
||||
t.Errorf("UnmarshalRead did not modify the value as expected")
|
||||
}
|
||||
}
|
||||
31
pkg/database/indexes/types/identhash.go
Normal file
31
pkg/database/indexes/types/identhash.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"crypto.orly/sha256"
|
||||
)
|
||||
|
||||
const IdentLen = 8
|
||||
|
||||
type Ident struct{ val [IdentLen]byte }
|
||||
|
||||
func (i *Ident) FromIdent(id []byte) {
|
||||
idh := sha256.Sum256(id)
|
||||
copy(i.val[:], idh[:IdentLen])
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Ident) Bytes() (b []byte) { return i.val[:] }
|
||||
|
||||
func (i *Ident) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(i.val[:])
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Ident) UnmarshalRead(r io.Reader) (err error) {
|
||||
|
||||
copy(i.val[:], i.val[:IdentLen])
|
||||
_, err = r.Read(i.val[:])
|
||||
return
|
||||
}
|
||||
99
pkg/database/indexes/types/identhash_test.go
Normal file
99
pkg/database/indexes/types/identhash_test.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"crypto.orly/sha256"
|
||||
"lol.mleku.dev/chk"
|
||||
"utils.orly"
|
||||
)
|
||||
|
||||
func TestFromIdent(t *testing.T) {
|
||||
var err error
|
||||
// Create a test identity
|
||||
testIdent := []byte("test-identity")
|
||||
|
||||
// Calculate the expected hash
|
||||
idh := sha256.Sum256(testIdent)
|
||||
expected := idh[:IdentLen]
|
||||
|
||||
// Test FromIdent
|
||||
i := &Ident{}
|
||||
i.FromIdent(testIdent)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdent failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the hash was set correctly
|
||||
if !utils.FastEqual(i.Bytes(), expected) {
|
||||
t.Errorf(
|
||||
"FromIdent did not set the hash correctly: got %v, want %v",
|
||||
i.Bytes(), expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdent_MarshalWriteUnmarshalRead(t *testing.T) {
|
||||
var err error
|
||||
// Create a Ident with a known value
|
||||
i1 := &Ident{}
|
||||
testIdent := []byte("test-identity")
|
||||
i1.FromIdent(testIdent)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdent failed: %v", err)
|
||||
}
|
||||
|
||||
// Test MarshalWrite
|
||||
buf := new(bytes.Buffer)
|
||||
err = i1.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the written bytes
|
||||
if !utils.FastEqual(buf.Bytes(), i1.Bytes()) {
|
||||
t.Errorf("MarshalWrite wrote %v, want %v", buf.Bytes(), i1.Bytes())
|
||||
}
|
||||
|
||||
// Test UnmarshalRead
|
||||
i2 := &Ident{}
|
||||
err = i2.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the read value
|
||||
if !utils.FastEqual(i2.Bytes(), i1.Bytes()) {
|
||||
t.Errorf("UnmarshalRead read %v, want %v", i2.Bytes(), i1.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdent_UnmarshalReadWithCorruptedData(t *testing.T) {
|
||||
var err error
|
||||
// Create a Ident with a known value
|
||||
i1 := &Ident{}
|
||||
testIdent1 := []byte("test-identity-1")
|
||||
i1.FromIdent(testIdent1)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdent failed: %v", err)
|
||||
}
|
||||
|
||||
// Create a second Ident with a different value
|
||||
i2 := &Ident{}
|
||||
testIdent2 := []byte("test-identity-2")
|
||||
i2.FromIdent(testIdent2)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdent failed: %v", err)
|
||||
}
|
||||
|
||||
// Test UnmarshalRead with corrupted data (less than IdentLen bytes)
|
||||
corruptedData := make([]byte, IdentLen/2)
|
||||
i2.UnmarshalRead(bytes.NewBuffer(corruptedData))
|
||||
|
||||
// The UnmarshalRead method should not have copied the original data to itself
|
||||
// before reading, so the value should be partially overwritten
|
||||
if utils.FastEqual(i2.Bytes(), i1.Bytes()) {
|
||||
t.Errorf("UnmarshalRead did not modify the value as expected")
|
||||
}
|
||||
}
|
||||
87
pkg/database/indexes/types/idhash.go
Normal file
87
pkg/database/indexes/types/idhash.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
|
||||
"crypto.orly/sha256"
|
||||
"encoders.orly/hex"
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/errorf"
|
||||
)
|
||||
|
||||
const IdHashLen = 8
|
||||
|
||||
type IdHash struct{ val [IdHashLen]byte }
|
||||
|
||||
func (i *IdHash) Set(idh []byte) {
|
||||
if len(idh) != IdHashLen {
|
||||
panic("invalid IdHash length")
|
||||
}
|
||||
copy(i.val[:], idh)
|
||||
}
|
||||
|
||||
func (i *IdHash) FromId(id []byte) (err error) {
|
||||
if len(id) != sha256.Size {
|
||||
err = errorf.E(
|
||||
"FromId: invalid ID length, got %d require %d", len(id),
|
||||
sha256.Size,
|
||||
)
|
||||
return
|
||||
}
|
||||
idh := sha256.Sum256(id)
|
||||
copy(i.val[:], idh[:IdHashLen])
|
||||
return
|
||||
}
|
||||
|
||||
func (i *IdHash) FromIdBase64(idb64 string) (err error) {
|
||||
// Decode the base64 string
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(idb64)
|
||||
if chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the decoded ID has the correct length
|
||||
if len(decoded) != sha256.Size {
|
||||
err = errorf.E(
|
||||
"FromIdBase64: invalid ID length, got %d require %d", len(decoded),
|
||||
sha256.Size,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Hash the decoded ID and take the first IdHashLen bytes
|
||||
idh := sha256.Sum256(decoded)
|
||||
copy(i.val[:], idh[:IdHashLen])
|
||||
return
|
||||
}
|
||||
|
||||
func (i *IdHash) FromIdHex(idh string) (err error) {
|
||||
var id []byte
|
||||
if id, err = hex.Dec(idh); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if len(id) != sha256.Size {
|
||||
err = errorf.E(
|
||||
"FromIdHex: invalid ID length, got %d require %d", len(id),
|
||||
sha256.Size,
|
||||
)
|
||||
return
|
||||
}
|
||||
h := sha256.Sum256(id)
|
||||
copy(i.val[:], h[:IdHashLen])
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (i *IdHash) Bytes() (b []byte) { return i.val[:] }
|
||||
|
||||
func (i *IdHash) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(i.val[:])
|
||||
return
|
||||
}
|
||||
|
||||
func (i *IdHash) UnmarshalRead(r io.Reader) (err error) {
|
||||
_, err = r.Read(i.val[:])
|
||||
return
|
||||
}
|
||||
186
pkg/database/indexes/types/idhash_test.go
Normal file
186
pkg/database/indexes/types/idhash_test.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"crypto.orly/sha256"
|
||||
"encoders.orly/hex"
|
||||
"lol.mleku.dev/chk"
|
||||
"utils.orly"
|
||||
)
|
||||
|
||||
func TestFromIdHash(t *testing.T) {
|
||||
// Create a valid ID (32 bytes)
|
||||
validId := make([]byte, sha256.Size)
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
validId[i] = byte(i)
|
||||
}
|
||||
|
||||
// Create an invalid ID (wrong size)
|
||||
invalidId := make([]byte, sha256.Size-1)
|
||||
|
||||
// Test with valid ID
|
||||
i := new(IdHash)
|
||||
err := i.FromId(validId)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromId failed with valid ID: %v", err)
|
||||
}
|
||||
|
||||
// Calculate the expected hash
|
||||
idh := sha256.Sum256(validId)
|
||||
expected := idh[:IdHashLen]
|
||||
|
||||
// Verify the hash was set correctly
|
||||
if !utils.FastEqual(i.Bytes(), expected) {
|
||||
t.Errorf(
|
||||
"FromId did not set the hash correctly: got %v, want %v", i.Bytes(),
|
||||
expected,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with invalid ID
|
||||
i = new(IdHash)
|
||||
err = i.FromId(invalidId)
|
||||
if err == nil {
|
||||
t.Errorf("FromId should have failed with invalid ID size")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromIdBase64(t *testing.T) {
|
||||
// Create a valid ID (32 bytes)
|
||||
validId := make([]byte, sha256.Size)
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
validId[i] = byte(i)
|
||||
}
|
||||
|
||||
// Encode the ID as base64
|
||||
validIdBase64 := base64.RawURLEncoding.EncodeToString(validId)
|
||||
|
||||
// Test with valid base64 ID
|
||||
i := new(IdHash)
|
||||
err := i.FromIdBase64(validIdBase64)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdBase64 failed with valid ID: %v", err)
|
||||
}
|
||||
|
||||
// Calculate the expected hash
|
||||
idh := sha256.Sum256(validId)
|
||||
expected := idh[:IdHashLen]
|
||||
|
||||
// Verify the hash was set correctly
|
||||
if !utils.FastEqual(i.Bytes(), expected) {
|
||||
t.Errorf(
|
||||
"FromIdBase64 did not set the hash correctly: got %v, want %v",
|
||||
i.Bytes(), expected,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with invalid base64 ID
|
||||
i = new(IdHash)
|
||||
err = i.FromIdBase64("invalid-base64")
|
||||
if err == nil {
|
||||
t.Errorf("FromIdBase64 should have failed with invalid base64")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromIdHex(t *testing.T) {
|
||||
// Create a valid ID (32 bytes)
|
||||
validId := make([]byte, sha256.Size)
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
validId[i] = byte(i)
|
||||
}
|
||||
|
||||
// Encode the ID as hex
|
||||
validIdHex := hex.Enc(validId)
|
||||
|
||||
// Test with valid hex ID
|
||||
i := new(IdHash)
|
||||
err := i.FromIdHex(validIdHex)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromIdHex failed with valid ID: %v", err)
|
||||
}
|
||||
|
||||
// Calculate the expected hash
|
||||
idh := sha256.Sum256(validId)
|
||||
expected := idh[:IdHashLen]
|
||||
|
||||
// Verify the hash was set correctly
|
||||
if !utils.FastEqual(i.Bytes(), expected) {
|
||||
t.Errorf(
|
||||
"FromIdHex did not set the hash correctly: got %v, want %v",
|
||||
i.Bytes(), expected,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with invalid hex ID (wrong size)
|
||||
i = new(IdHash)
|
||||
err = i.FromIdHex(validIdHex[:len(validIdHex)-2])
|
||||
if err == nil {
|
||||
t.Errorf("FromIdHex should have failed with invalid ID size")
|
||||
}
|
||||
|
||||
// Test with invalid hex ID (not hex)
|
||||
i = new(IdHash)
|
||||
err = i.FromIdHex("invalid-hex")
|
||||
if err == nil {
|
||||
t.Errorf("FromIdHex should have failed with invalid hex")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdHashMarshalWriteUnmarshalRead(t *testing.T) {
|
||||
// Create a IdHash with a known value
|
||||
i1 := new(IdHash)
|
||||
validId := make([]byte, sha256.Size)
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
validId[i] = byte(i)
|
||||
}
|
||||
err := i1.FromId(validId)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromId failed: %v", err)
|
||||
}
|
||||
|
||||
// Test MarshalWrite
|
||||
buf := new(bytes.Buffer)
|
||||
err = i1.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the written bytes
|
||||
if !utils.FastEqual(buf.Bytes(), i1.Bytes()) {
|
||||
t.Errorf("MarshalWrite wrote %v, want %v", buf.Bytes(), i1.Bytes())
|
||||
}
|
||||
|
||||
// Test UnmarshalRead
|
||||
i2 := new(IdHash)
|
||||
err = i2.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the read value
|
||||
if !utils.FastEqual(i2.Bytes(), i1.Bytes()) {
|
||||
t.Errorf("UnmarshalRead read %v, want %v", i2.Bytes(), i1.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalReadWithEmptyVal(t *testing.T) {
|
||||
// Create a IdHash with an empty val
|
||||
i := new(IdHash)
|
||||
|
||||
// Create some test data
|
||||
testData := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
|
||||
// Test UnmarshalRead
|
||||
err := i.UnmarshalRead(bytes.NewBuffer(testData))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the read value
|
||||
if !utils.FastEqual(i.Bytes(), testData) {
|
||||
t.Errorf("UnmarshalRead read %v, want %v", i.Bytes(), testData)
|
||||
}
|
||||
}
|
||||
31
pkg/database/indexes/types/letter.go
Normal file
31
pkg/database/indexes/types/letter.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
const LetterLen = 1
|
||||
|
||||
type Letter struct {
|
||||
val byte
|
||||
}
|
||||
|
||||
func (p *Letter) Set(lb byte) { p.val = lb }
|
||||
|
||||
func (p *Letter) Letter() byte { return p.val }
|
||||
|
||||
func (p *Letter) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write([]byte{p.val})
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Letter) UnmarshalRead(r io.Reader) (err error) {
|
||||
val := make([]byte, 1)
|
||||
if _, err = r.Read(val); chk.E(err) {
|
||||
return
|
||||
}
|
||||
p.val = val[0]
|
||||
return
|
||||
}
|
||||
127
pkg/database/indexes/types/letter_test.go
Normal file
127
pkg/database/indexes/types/letter_test.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
func TestLetter_New(t *testing.T) {
|
||||
// Test with a valid letter
|
||||
l := new(Letter)
|
||||
l.Set('A')
|
||||
if l == nil {
|
||||
t.Fatal("New() returned nil")
|
||||
}
|
||||
if l.Letter() != 'A' {
|
||||
t.Errorf(
|
||||
"New('A') created a Letter with letter %c, want %c", l.Letter(),
|
||||
'A',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLetter_Set(t *testing.T) {
|
||||
// Create a Letter with a known value
|
||||
l := new(Letter)
|
||||
l.Set('A')
|
||||
|
||||
// Test Set
|
||||
l.Set('B')
|
||||
if l.Letter() != 'B' {
|
||||
t.Errorf(
|
||||
"Set('B') did not set the letter correctly: got %c, want %c",
|
||||
l.Letter(), 'B',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLetter(t *testing.T) {
|
||||
// Create a Letter with a known value
|
||||
l := new(Letter)
|
||||
l.Set('A')
|
||||
|
||||
// Test Letter
|
||||
if l.Letter() != 'A' {
|
||||
t.Errorf("Letter() returned %c, want %c", l.Letter(), 'A')
|
||||
}
|
||||
}
|
||||
|
||||
func TestLetter_MarshalWriteUnmarshalRead(t *testing.T) {
|
||||
// Create a Letter with a known value
|
||||
l1 := new(Letter)
|
||||
l1.Set('A')
|
||||
// Test MarshalWrite
|
||||
buf := new(bytes.Buffer)
|
||||
err := l1.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the written bytes
|
||||
if buf.Len() != 1 || buf.Bytes()[0] != 'A' {
|
||||
t.Errorf("MarshalWrite wrote %v, want [%d]", buf.Bytes(), 'A')
|
||||
}
|
||||
|
||||
// Test UnmarshalRead
|
||||
l2 := new(Letter)
|
||||
l2.Set('B') // Start with a different value
|
||||
err = l2.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the read value
|
||||
if l2.Letter() != 'A' {
|
||||
t.Errorf("UnmarshalRead read %c, want %c", l2.Letter(), 'A')
|
||||
}
|
||||
}
|
||||
|
||||
func TestLetter_UnmarshalReadWithEmptyReader(t *testing.T) {
|
||||
// Create a Letter with a known value
|
||||
l := new(Letter)
|
||||
l.Set('A')
|
||||
|
||||
// Test UnmarshalRead with an empty reader
|
||||
err := l.UnmarshalRead(bytes.NewBuffer([]byte{}))
|
||||
if err == nil {
|
||||
t.Errorf("UnmarshalRead should have failed with an empty reader")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLetter_EdgeCases(t *testing.T) {
|
||||
// Test with minimum value (0)
|
||||
l1 := new(Letter)
|
||||
if l1.Letter() != 0 {
|
||||
t.Errorf(
|
||||
"New(0) created a Letter with letter %d, want %d", l1.Letter(), 0,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with maximum value (255)
|
||||
l2 := new(Letter)
|
||||
l2.Set(255)
|
||||
if l2.Letter() != 255 {
|
||||
t.Errorf(
|
||||
"New(255) created a Letter with letter %d, want %d", l2.Letter(),
|
||||
255,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with special characters
|
||||
specialChars := []byte{
|
||||
'\n', '\t', '\r', ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')',
|
||||
'*', '+', ',', '-', '.', '/',
|
||||
}
|
||||
for _, c := range specialChars {
|
||||
l := new(Letter)
|
||||
l.Set(c)
|
||||
if l.Letter() != c {
|
||||
t.Errorf(
|
||||
"New(%d) created a Letter with letter %d, want %d", c,
|
||||
l.Letter(), c,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
58
pkg/database/indexes/types/pubhash.go
Normal file
58
pkg/database/indexes/types/pubhash.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"crypto.orly/ec/schnorr"
|
||||
"crypto.orly/sha256"
|
||||
"encoders.orly/hex"
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/errorf"
|
||||
)
|
||||
|
||||
const PubHashLen = 8
|
||||
|
||||
type PubHash struct{ val [PubHashLen]byte }
|
||||
|
||||
func (ph *PubHash) FromPubkey(pk []byte) (err error) {
|
||||
if len(pk) != schnorr.PubKeyBytesLen {
|
||||
err = errorf.E(
|
||||
"invalid Pubkey length, got %d require %d",
|
||||
len(pk), schnorr.PubKeyBytesLen,
|
||||
)
|
||||
return
|
||||
}
|
||||
pkh := sha256.Sum256(pk)
|
||||
copy(ph.val[:], pkh[:PubHashLen])
|
||||
return
|
||||
}
|
||||
|
||||
func (ph *PubHash) FromPubkeyHex(pk string) (err error) {
|
||||
if len(pk) != schnorr.PubKeyBytesLen*2 {
|
||||
err = errorf.E(
|
||||
"invalid Pubkey length, got %d require %d",
|
||||
len(pk), schnorr.PubKeyBytesLen*2,
|
||||
)
|
||||
return
|
||||
}
|
||||
var pkb []byte
|
||||
if pkb, err = hex.Dec(pk); chk.E(err) {
|
||||
return
|
||||
}
|
||||
h := sha256.Sum256(pkb)
|
||||
copy(ph.val[:], h[:PubHashLen])
|
||||
return
|
||||
}
|
||||
|
||||
func (ph *PubHash) Bytes() (b []byte) { return ph.val[:] }
|
||||
|
||||
func (ph *PubHash) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(ph.val[:])
|
||||
return
|
||||
}
|
||||
|
||||
func (ph *PubHash) UnmarshalRead(r io.Reader) (err error) {
|
||||
copy(ph.val[:], ph.val[:PubHashLen])
|
||||
_, err = r.Read(ph.val[:])
|
||||
return
|
||||
}
|
||||
164
pkg/database/indexes/types/pubhash_test.go
Normal file
164
pkg/database/indexes/types/pubhash_test.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"crypto.orly/ec/schnorr"
|
||||
"crypto.orly/sha256"
|
||||
"encoders.orly/hex"
|
||||
"lol.mleku.dev/chk"
|
||||
"utils.orly"
|
||||
)
|
||||
|
||||
func TestPubHash_FromPubkey(t *testing.T) {
|
||||
// Create a valid pubkey (32 bytes)
|
||||
validPubkey := make([]byte, schnorr.PubKeyBytesLen)
|
||||
for i := 0; i < schnorr.PubKeyBytesLen; i++ {
|
||||
validPubkey[i] = byte(i)
|
||||
}
|
||||
|
||||
// Create an invalid pubkey (wrong size)
|
||||
invalidPubkey := make([]byte, schnorr.PubKeyBytesLen-1)
|
||||
|
||||
// Test with valid pubkey
|
||||
ph := &PubHash{}
|
||||
err := ph.FromPubkey(validPubkey)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkey failed with valid pubkey: %v", err)
|
||||
}
|
||||
|
||||
// Calculate the expected hash
|
||||
pkh := sha256.Sum256(validPubkey)
|
||||
expected := pkh[:PubHashLen]
|
||||
|
||||
// Verify the hash was set correctly
|
||||
if !utils.FastEqual(ph.Bytes(), expected) {
|
||||
t.Errorf(
|
||||
"FromPubkey did not set the hash correctly: got %v, want %v",
|
||||
ph.Bytes(), expected,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with invalid pubkey
|
||||
ph = &PubHash{}
|
||||
err = ph.FromPubkey(invalidPubkey)
|
||||
if err == nil {
|
||||
t.Errorf("FromPubkey should have failed with invalid pubkey size")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPubHash_FromPubkeyHex(t *testing.T) {
|
||||
// Create a valid pubkey (32 bytes)
|
||||
validPubkey := make([]byte, schnorr.PubKeyBytesLen)
|
||||
for i := 0; i < schnorr.PubKeyBytesLen; i++ {
|
||||
validPubkey[i] = byte(i)
|
||||
}
|
||||
|
||||
// Encode the pubkey as hex
|
||||
validPubkeyHex := hex.Enc(validPubkey)
|
||||
|
||||
// Test with valid hex pubkey
|
||||
ph := &PubHash{}
|
||||
err := ph.FromPubkeyHex(validPubkeyHex)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkeyHex failed with valid pubkey: %v", err)
|
||||
}
|
||||
|
||||
// Calculate the expected hash
|
||||
pkh := sha256.Sum256(validPubkey)
|
||||
expected := pkh[:PubHashLen]
|
||||
|
||||
// Verify the hash was set correctly
|
||||
if !utils.FastEqual(ph.Bytes(), expected) {
|
||||
t.Errorf(
|
||||
"FromPubkeyHex did not set the hash correctly: got %v, want %v",
|
||||
ph.Bytes(), expected,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with invalid hex pubkey (wrong size)
|
||||
ph = &PubHash{}
|
||||
err = ph.FromPubkeyHex(validPubkeyHex[:len(validPubkeyHex)-2])
|
||||
if err == nil {
|
||||
t.Errorf("FromPubkeyHex should have failed with invalid pubkey size")
|
||||
}
|
||||
|
||||
// Test with invalid hex pubkey (not hex)
|
||||
ph = &PubHash{}
|
||||
err = ph.FromPubkeyHex("invalid-hex")
|
||||
if err == nil {
|
||||
t.Errorf("FromPubkeyHex should have failed with invalid hex")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPubHash_MarshalWriteUnmarshalRead(t *testing.T) {
|
||||
// Create a PubHash with a known value
|
||||
ph1 := &PubHash{}
|
||||
validPubkey := make([]byte, schnorr.PubKeyBytesLen)
|
||||
for i := 0; i < schnorr.PubKeyBytesLen; i++ {
|
||||
validPubkey[i] = byte(i)
|
||||
}
|
||||
err := ph1.FromPubkey(validPubkey)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkey failed: %v", err)
|
||||
}
|
||||
|
||||
// Test MarshalWrite
|
||||
buf := new(bytes.Buffer)
|
||||
err = ph1.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the written bytes
|
||||
if !utils.FastEqual(buf.Bytes(), ph1.Bytes()) {
|
||||
t.Errorf("MarshalWrite wrote %v, want %v", buf.Bytes(), ph1.Bytes())
|
||||
}
|
||||
|
||||
// Test UnmarshalRead
|
||||
ph2 := &PubHash{}
|
||||
err = ph2.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the read value
|
||||
if !utils.FastEqual(ph2.Bytes(), ph1.Bytes()) {
|
||||
t.Errorf("UnmarshalRead read %v, want %v", ph2.Bytes(), ph1.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPubHash_UnmarshalReadWithCorruptedData(t *testing.T) {
|
||||
// Create a PubHash with a known value
|
||||
ph1 := &PubHash{}
|
||||
validPubkey1 := make([]byte, schnorr.PubKeyBytesLen)
|
||||
for i := 0; i < schnorr.PubKeyBytesLen; i++ {
|
||||
validPubkey1[i] = byte(i)
|
||||
}
|
||||
err := ph1.FromPubkey(validPubkey1)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkey failed: %v", err)
|
||||
}
|
||||
|
||||
// Create a second PubHash with a different value
|
||||
ph2 := &PubHash{}
|
||||
validPubkey2 := make([]byte, schnorr.PubKeyBytesLen)
|
||||
for i := 0; i < schnorr.PubKeyBytesLen; i++ {
|
||||
validPubkey2[i] = byte(schnorr.PubKeyBytesLen - i - 1)
|
||||
}
|
||||
err = ph2.FromPubkey(validPubkey2)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromPubkey failed: %v", err)
|
||||
}
|
||||
|
||||
// Test UnmarshalRead with corrupted data (less than PubHashLen bytes)
|
||||
corruptedData := make([]byte, PubHashLen/2)
|
||||
ph2.UnmarshalRead(bytes.NewBuffer(corruptedData))
|
||||
|
||||
// The UnmarshalRead method should not have copied the original data to itself
|
||||
// before reading, so the value should be partially overwritten
|
||||
if utils.FastEqual(ph2.Bytes(), ph1.Bytes()) {
|
||||
t.Errorf("UnmarshalRead did not modify the value as expected")
|
||||
}
|
||||
}
|
||||
56
pkg/database/indexes/types/timestamp.go
Normal file
56
pkg/database/indexes/types/timestamp.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
const TimestampLen = 8
|
||||
|
||||
type Timestamp struct{ val int64 }
|
||||
|
||||
func (ts *Timestamp) FromInt(t int) { ts.val = int64(t) }
|
||||
func (ts *Timestamp) FromInt64(t int64) { ts.val = t }
|
||||
|
||||
func FromBytes(timestampBytes []byte) (ts *Timestamp, err error) {
|
||||
v := new(Uint64)
|
||||
if err = v.UnmarshalRead(bytes.NewBuffer(timestampBytes)); chk.E(err) {
|
||||
return
|
||||
}
|
||||
ts = &Timestamp{val: int64(v.Get())}
|
||||
return
|
||||
}
|
||||
|
||||
func (ts *Timestamp) ToTimestamp() (timestamp int64) {
|
||||
return ts.val
|
||||
}
|
||||
func (ts *Timestamp) Bytes() (b []byte, err error) {
|
||||
v := new(Uint64)
|
||||
v.Set(uint64(ts.val))
|
||||
buf := new(bytes.Buffer)
|
||||
if err = v.MarshalWrite(buf); chk.E(err) {
|
||||
return
|
||||
}
|
||||
b = buf.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
func (ts *Timestamp) MarshalWrite(w io.Writer) (err error) {
|
||||
v := new(Uint64)
|
||||
v.Set(uint64(ts.val))
|
||||
if err = v.MarshalWrite(w); chk.E(err) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ts *Timestamp) UnmarshalRead(r io.Reader) (err error) {
|
||||
v := new(Uint64)
|
||||
if err = v.UnmarshalRead(r); chk.E(err) {
|
||||
return
|
||||
}
|
||||
ts.val = int64(v.Get())
|
||||
return
|
||||
}
|
||||
243
pkg/database/indexes/types/timestamp_test.go
Normal file
243
pkg/database/indexes/types/timestamp_test.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
func TestTimestamp_FromInt(t *testing.T) {
|
||||
// Test with a positive value
|
||||
ts := &Timestamp{}
|
||||
ts.FromInt(12345)
|
||||
if ts.val != 12345 {
|
||||
t.Errorf(
|
||||
"FromInt(12345) did not set the value correctly: got %d, want %d",
|
||||
ts.val, 12345,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with a negative value
|
||||
ts = &Timestamp{}
|
||||
ts.FromInt(-12345)
|
||||
if ts.val != -12345 {
|
||||
t.Errorf(
|
||||
"FromInt(-12345) did not set the value correctly: got %d, want %d",
|
||||
ts.val, -12345,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with zero
|
||||
ts = &Timestamp{}
|
||||
ts.FromInt(0)
|
||||
if ts.val != 0 {
|
||||
t.Errorf(
|
||||
"FromInt(0) did not set the value correctly: got %d, want %d",
|
||||
ts.val, 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_FromInt64(t *testing.T) {
|
||||
// Test with a positive value
|
||||
ts := &Timestamp{}
|
||||
ts.FromInt64(12345)
|
||||
if ts.val != 12345 {
|
||||
t.Errorf(
|
||||
"FromInt64(12345) did not set the value correctly: got %d, want %d",
|
||||
ts.val, 12345,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with a negative value
|
||||
ts = &Timestamp{}
|
||||
ts.FromInt64(-12345)
|
||||
if ts.val != -12345 {
|
||||
t.Errorf(
|
||||
"FromInt64(-12345) did not set the value correctly: got %d, want %d",
|
||||
ts.val, -12345,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with zero
|
||||
ts = &Timestamp{}
|
||||
ts.FromInt64(0)
|
||||
if ts.val != 0 {
|
||||
t.Errorf(
|
||||
"FromInt64(0) did not set the value correctly: got %d, want %d",
|
||||
ts.val, 0,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with a large value
|
||||
ts = &Timestamp{}
|
||||
largeValue := int64(1) << 60
|
||||
ts.FromInt64(largeValue)
|
||||
if ts.val != largeValue {
|
||||
t.Errorf(
|
||||
"FromInt64(%d) did not set the value correctly: got %d, want %d",
|
||||
largeValue, ts.val, largeValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_FromBytes(t *testing.T) {
|
||||
// Create a number.Uint64 with a known value
|
||||
v := new(Uint64)
|
||||
v.Set(12345)
|
||||
|
||||
// Marshal it to bytes
|
||||
buf := new(bytes.Buffer)
|
||||
err := v.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Test FromBytes
|
||||
ts, err := FromBytes(buf.Bytes())
|
||||
if chk.E(err) {
|
||||
t.Fatalf("FromBytes failed: %v", err)
|
||||
}
|
||||
if ts.val != 12345 {
|
||||
t.Errorf(
|
||||
"FromBytes did not set the value correctly: got %d, want %d",
|
||||
ts.val, 12345,
|
||||
)
|
||||
}
|
||||
|
||||
// Test with invalid bytes
|
||||
_, err = FromBytes([]byte{1})
|
||||
if err == nil {
|
||||
t.Errorf("FromBytes should have failed with invalid bytes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_ToTimestamp(t *testing.T) {
|
||||
// Test with a positive value
|
||||
ts := &Timestamp{val: 12345}
|
||||
timestamp := ts.ToTimestamp()
|
||||
if timestamp != 12345 {
|
||||
t.Errorf("ToTimestamp() returned %d, want %d", timestamp, 12345)
|
||||
}
|
||||
|
||||
// Test with a negative value
|
||||
ts = &Timestamp{val: -12345}
|
||||
timestamp = ts.ToTimestamp()
|
||||
if timestamp != -12345 {
|
||||
t.Errorf("ToTimestamp() returned %d, want %d", timestamp, -12345)
|
||||
}
|
||||
|
||||
// Test with zero
|
||||
ts = &Timestamp{val: 0}
|
||||
timestamp = ts.ToTimestamp()
|
||||
if timestamp != 0 {
|
||||
t.Errorf("ToTimestamp() returned %d, want %d", timestamp, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_Bytes(t *testing.T) {
|
||||
// Test with a positive value
|
||||
ts := &Timestamp{val: 12345}
|
||||
b, err := ts.Bytes()
|
||||
if chk.E(err) {
|
||||
t.Fatalf("Bytes() failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the bytes
|
||||
v := new(Uint64)
|
||||
err = v.UnmarshalRead(bytes.NewBuffer(b))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
if v.Get() != 12345 {
|
||||
t.Errorf("Bytes() returned bytes for %d, want %d", v.Get(), 12345)
|
||||
}
|
||||
|
||||
// Skip negative value test for Bytes() since uint64 can't represent negative values
|
||||
// Instead, we'll test that MarshalWrite and UnmarshalRead work correctly with negative values
|
||||
// in the TestMarshalWriteUnmarshalRead function
|
||||
}
|
||||
|
||||
func TestTimestamp_MarshalWriteUnmarshalRead(t *testing.T) {
|
||||
// Test with a positive value
|
||||
ts1 := &Timestamp{val: 12345}
|
||||
buf := new(bytes.Buffer)
|
||||
err := ts1.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Test UnmarshalRead
|
||||
ts2 := &Timestamp{}
|
||||
err = ts2.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the read value
|
||||
if ts2.val != 12345 {
|
||||
t.Errorf("UnmarshalRead read %d, want %d", ts2.val, 12345)
|
||||
}
|
||||
|
||||
// Test with a negative value
|
||||
ts1 = &Timestamp{val: -12345}
|
||||
buf = new(bytes.Buffer)
|
||||
err = ts1.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Test UnmarshalRead
|
||||
ts2 = &Timestamp{}
|
||||
err = ts2.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the read value
|
||||
if ts2.val != -12345 {
|
||||
t.Errorf("UnmarshalRead read %d, want %d", ts2.val, -12345)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_WithCurrentTime(t *testing.T) {
|
||||
// Get the current time
|
||||
now := time.Now().Unix()
|
||||
|
||||
// Create a timestamp with the current time
|
||||
ts := &Timestamp{}
|
||||
ts.FromInt64(now)
|
||||
|
||||
// Verify the value
|
||||
if ts.val != now {
|
||||
t.Errorf(
|
||||
"FromInt64(%d) did not set the value correctly: got %d, want %d",
|
||||
now, ts.val, now,
|
||||
)
|
||||
}
|
||||
|
||||
// Test ToTimestamp
|
||||
timestamp := ts.ToTimestamp()
|
||||
if timestamp != now {
|
||||
t.Errorf("ToTimestamp() returned %d, want %d", timestamp, now)
|
||||
}
|
||||
|
||||
// Test MarshalWrite and UnmarshalRead
|
||||
buf := new(bytes.Buffer)
|
||||
err := ts.MarshalWrite(buf)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
ts2 := &Timestamp{}
|
||||
err = ts2.UnmarshalRead(bytes.NewBuffer(buf.Bytes()))
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
if ts2.val != now {
|
||||
t.Errorf("UnmarshalRead read %d, want %d", ts2.val, now)
|
||||
}
|
||||
}
|
||||
115
pkg/database/indexes/types/uint16.go
Normal file
115
pkg/database/indexes/types/uint16.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Uint16 is a codec for encoding and decoding 16-bit unsigned integers.
|
||||
type Uint16 struct {
|
||||
value uint16
|
||||
}
|
||||
|
||||
// Set sets the value as a uint16.
|
||||
func (c *Uint16) Set(value uint16) {
|
||||
c.value = value
|
||||
}
|
||||
|
||||
// Get gets the value as a uint16.
|
||||
func (c *Uint16) Get() uint16 {
|
||||
return c.value
|
||||
}
|
||||
|
||||
// SetInt sets the value as an int, converting it to uint16. Truncates values outside uint16 range (0-65535).
|
||||
func (c *Uint16) SetInt(value int) {
|
||||
c.value = uint16(value)
|
||||
}
|
||||
|
||||
// GetInt gets the value as an int, converted from uint16.
|
||||
func (c *Uint16) GetInt() int {
|
||||
return int(c.value)
|
||||
}
|
||||
|
||||
// MarshalWrite writes the uint16 value to the provided writer in BigEndian order.
|
||||
func (c *Uint16) MarshalWrite(w io.Writer) error {
|
||||
return binary.Write(w, binary.BigEndian, c.value)
|
||||
}
|
||||
|
||||
// UnmarshalRead reads a uint16 value from the provided reader in BigEndian order.
|
||||
func (c *Uint16) UnmarshalRead(r io.Reader) error {
|
||||
return binary.Read(r, binary.BigEndian, &c.value)
|
||||
}
|
||||
|
||||
type Uint16s []*Uint16
|
||||
|
||||
// Union computes the union of the current Uint16s slice with another Uint16s slice. The result
|
||||
// contains all unique elements from both slices.
|
||||
func (s Uint16s) Union(other Uint16s) Uint16s {
|
||||
valueMap := make(map[uint16]bool)
|
||||
var result Uint16s
|
||||
|
||||
// Add elements from the current Uint16s slice to the result
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Add elements from the other Uint16s slice to the result
|
||||
for _, item := range other {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersection computes the intersection of the current Uint16s slice with another Uint16s
|
||||
// slice. The result contains only the elements that exist in both slices.
|
||||
func (s Uint16s) Intersection(other Uint16s) Uint16s {
|
||||
valueMap := make(map[uint16]bool)
|
||||
var result Uint16s
|
||||
|
||||
// Add all elements from the other Uint16s slice to the map
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Check for common elements in the current Uint16s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Difference computes the difference of the current Uint16s slice with another Uint16s slice.
|
||||
// The result contains only the elements that are in the current slice but not in the other
|
||||
// slice.
|
||||
func (s Uint16s) Difference(other Uint16s) Uint16s {
|
||||
valueMap := make(map[uint16]bool)
|
||||
var result Uint16s
|
||||
|
||||
// Mark all elements in the other Uint16s slice
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Add elements from the current Uint16s slice that are not in the other Uint16s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
160
pkg/database/indexes/types/uint16_test.go
Normal file
160
pkg/database/indexes/types/uint16_test.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
"lukechampine.com/frand"
|
||||
"utils.orly"
|
||||
)
|
||||
|
||||
func TestUint16(t *testing.T) {
|
||||
// Helper function to generate random 16-bit integers
|
||||
generateRandomUint16 := func() uint16 {
|
||||
return uint16(frand.Intn(math.MaxUint16)) // math.MaxUint16 == 65535
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ { // Run test 100 times for random values
|
||||
// Generate a random value
|
||||
randomUint16 := generateRandomUint16()
|
||||
randomInt := int(randomUint16)
|
||||
|
||||
// Create a new encodedUint16
|
||||
encodedUint16 := new(Uint16)
|
||||
|
||||
// Test UInt16 setter and getter
|
||||
encodedUint16.Set(randomUint16)
|
||||
if encodedUint16.Get() != randomUint16 {
|
||||
t.Fatalf(
|
||||
"Get mismatch: got %d, expected %d", encodedUint16.Get(),
|
||||
randomUint16,
|
||||
)
|
||||
}
|
||||
|
||||
// Test GetInt setter and getter
|
||||
encodedUint16.SetInt(randomInt)
|
||||
if encodedUint16.GetInt() != randomInt {
|
||||
t.Fatalf(
|
||||
"GetInt mismatch: got %d, expected %d", encodedUint16.GetInt(),
|
||||
randomInt,
|
||||
)
|
||||
}
|
||||
|
||||
// Test encoding to []byte and decoding back
|
||||
bufEnc := new(bytes.Buffer)
|
||||
|
||||
// MarshalWrite
|
||||
err := encodedUint16.MarshalWrite(bufEnc)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
encoded := bufEnc.Bytes()
|
||||
|
||||
// Create a copy of encoded bytes before decoding
|
||||
bufDec := bytes.NewBuffer(encoded)
|
||||
|
||||
// Decode back the value
|
||||
decodedUint16 := new(Uint16)
|
||||
err = decodedUint16.UnmarshalRead(bufDec)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
if decodedUint16.Get() != randomUint16 {
|
||||
t.Fatalf(
|
||||
"Decoded value mismatch: got %d, expected %d",
|
||||
decodedUint16.Get(), randomUint16,
|
||||
)
|
||||
}
|
||||
|
||||
// Compare encoded bytes to ensure correctness
|
||||
if !utils.FastEqual(encoded, bufEnc.Bytes()) {
|
||||
t.Fatalf(
|
||||
"Byte encoding mismatch: got %v, expected %v", bufEnc.Bytes(),
|
||||
encoded,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint16sSetOperations(t *testing.T) {
|
||||
// Helper function to create a Uint16 with a specific value
|
||||
createUint16 := func(value uint16) *Uint16 {
|
||||
u := &Uint16{}
|
||||
u.Set(value)
|
||||
return u
|
||||
}
|
||||
|
||||
// Prepare test data
|
||||
a := createUint16(1)
|
||||
b := createUint16(2)
|
||||
c := createUint16(3)
|
||||
d := createUint16(4)
|
||||
e := createUint16(1) // Duplicate of a
|
||||
|
||||
// Define slices
|
||||
set1 := Uint16s{a, b, c} // [1, 2, 3]
|
||||
set2 := Uint16s{d, e, b} // [4, 1, 2]
|
||||
expectedUnion := Uint16s{a, b, c, d} // [1, 2, 3, 4]
|
||||
expectedIntersection := Uint16s{a, b} // [1, 2]
|
||||
expectedDifference := Uint16s{c} // [3]
|
||||
|
||||
// Test Union
|
||||
t.Run(
|
||||
"Union", func(t *testing.T) {
|
||||
result := set1.Union(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint16Values(result), getUint16Values(expectedUnion),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Union failed: expected %v, got %v",
|
||||
getUint16Values(expectedUnion), getUint16Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Intersection
|
||||
t.Run(
|
||||
"Intersection", func(t *testing.T) {
|
||||
result := set1.Intersection(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint16Values(result), getUint16Values(expectedIntersection),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Intersection failed: expected %v, got %v",
|
||||
getUint16Values(expectedIntersection),
|
||||
getUint16Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Difference
|
||||
t.Run(
|
||||
"Difference", func(t *testing.T) {
|
||||
result := set1.Difference(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint16Values(result), getUint16Values(expectedDifference),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Difference failed: expected %v, got %v",
|
||||
getUint16Values(expectedDifference),
|
||||
getUint16Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function to extract uint64 values from Uint16s
|
||||
func getUint16Values(slice Uint16s) []uint16 {
|
||||
var values []uint16
|
||||
for _, item := range slice {
|
||||
values = append(values, item.Get())
|
||||
}
|
||||
return values
|
||||
}
|
||||
154
pkg/database/indexes/types/uint24.go
Normal file
154
pkg/database/indexes/types/uint24.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
// MaxUint24 is the maximum value of a 24-bit unsigned integer: 2^24 - 1.
|
||||
const MaxUint24 uint32 = 1<<24 - 1
|
||||
|
||||
// Uint24 is a codec for encoding and decoding 24-bit unsigned integers.
|
||||
type Uint24 struct {
|
||||
value uint32
|
||||
}
|
||||
|
||||
// Set sets the value as a 24-bit unsigned integer.
|
||||
// If the value exceeds the maximum allowable value for 24 bits, it returns an error.
|
||||
func (c *Uint24) Set(value uint32) error {
|
||||
if value > MaxUint24 {
|
||||
return errors.New("value exceeds 24-bit range")
|
||||
}
|
||||
c.value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get gets the value as a 24-bit unsigned integer.
|
||||
func (c *Uint24) Get() uint32 {
|
||||
return c.value
|
||||
}
|
||||
|
||||
// SetInt sets the value as an int, converting it to a 24-bit unsigned integer.
|
||||
// If the value is out of the 24-bit range, it returns an error.
|
||||
func (c *Uint24) SetInt(value int) error {
|
||||
if value < 0 || uint32(value) > MaxUint24 {
|
||||
return errors.New("value exceeds 24-bit range")
|
||||
}
|
||||
c.value = uint32(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Int gets the value as an int, converted from the 24-bit unsigned integer.
|
||||
func (c *Uint24) Int() int {
|
||||
return int(c.value)
|
||||
}
|
||||
|
||||
// MarshalWrite encodes the 24-bit unsigned integer and writes it directly to the provided io.Writer.
|
||||
// The encoding uses 3 bytes in BigEndian order.
|
||||
func (c *Uint24) MarshalWrite(w io.Writer) error {
|
||||
if c.value > MaxUint24 {
|
||||
return errors.New("value exceeds 24-bit range")
|
||||
}
|
||||
|
||||
// Write the 3 bytes (BigEndian order) directly to the writer
|
||||
var buf [3]byte
|
||||
buf[0] = byte((c.value >> 16) & 0xFF) // Most significant byte
|
||||
buf[1] = byte((c.value >> 8) & 0xFF)
|
||||
buf[2] = byte(c.value & 0xFF) // Least significant byte
|
||||
|
||||
_, err := w.Write(buf[:]) // Write all 3 bytes to the writer
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalRead reads 3 bytes directly from the provided io.Reader and decodes it into a 24-bit unsigned integer.
|
||||
func (c *Uint24) UnmarshalRead(r io.Reader) error {
|
||||
// Read 3 bytes directly from the reader
|
||||
var buf [3]byte
|
||||
_, err := io.ReadFull(r, buf[:]) // Ensure exactly 3 bytes are read
|
||||
if chk.E(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode the 3 bytes into a 24-bit unsigned integer
|
||||
c.value = (uint32(buf[0]) << 16) |
|
||||
(uint32(buf[1]) << 8) |
|
||||
uint32(buf[2])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Uint24s []*Uint24
|
||||
|
||||
// Union computes the union of the current Uint24s slice with another Uint24s slice. The result
|
||||
// contains all unique elements from both slices.
|
||||
func (s Uint24s) Union(other Uint24s) Uint24s {
|
||||
valueMap := make(map[uint32]bool)
|
||||
var result Uint24s
|
||||
|
||||
// Add elements from the current Uint24s slice to the result
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Add elements from the other Uint24s slice to the result
|
||||
for _, item := range other {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersection computes the intersection of the current Uint24s slice with another Uint24s
|
||||
// slice. The result contains only the elements that exist in both slices.
|
||||
func (s Uint24s) Intersection(other Uint24s) Uint24s {
|
||||
valueMap := make(map[uint32]bool)
|
||||
var result Uint24s
|
||||
|
||||
// Add all elements from the other Uint24s slice to the map
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Check for common elements in the current Uint24s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Difference computes the difference of the current Uint24s slice with another Uint24s slice.
|
||||
// The result contains only the elements that are in the current slice but not in the other
|
||||
// slice.
|
||||
func (s Uint24s) Difference(other Uint24s) Uint24s {
|
||||
valueMap := make(map[uint32]bool)
|
||||
var result Uint24s
|
||||
|
||||
// Mark all elements in the other Uint24s slice
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Add elements from the current Uint24s slice that are not in the other Uint24s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
160
pkg/database/indexes/types/uint24_test.go
Normal file
160
pkg/database/indexes/types/uint24_test.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
func TestUint24(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value uint32
|
||||
expectedErr bool
|
||||
}{
|
||||
{"Minimum Value", 0, false},
|
||||
{"Maximum Value", MaxUint24, false},
|
||||
{"Value in Range", 8374263, false}, // Example value within the range
|
||||
{"Value Exceeds Range", MaxUint24 + 1, true}, // Exceeds 24-bit limit
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.name, func(t *testing.T) {
|
||||
codec := new(Uint24)
|
||||
|
||||
// Test Set
|
||||
err := codec.Set(tt.value)
|
||||
if tt.expectedErr {
|
||||
if !chk.E(err) {
|
||||
t.Errorf("expected error but got none")
|
||||
}
|
||||
return
|
||||
} else if chk.E(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Test Get getter
|
||||
if codec.Get() != tt.value {
|
||||
t.Errorf(
|
||||
"Get mismatch: got %d, expected %d", codec.Get(),
|
||||
tt.value,
|
||||
)
|
||||
}
|
||||
|
||||
// Test MarshalWrite and UnmarshalRead
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
// MarshalWrite directly to the buffer
|
||||
if err := codec.MarshalWrite(buf); chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Validate encoded size is 3 bytes
|
||||
encoded := buf.Bytes()
|
||||
if len(encoded) != 3 {
|
||||
t.Fatalf(
|
||||
"encoded size mismatch: got %d bytes, expected 3 bytes",
|
||||
len(encoded),
|
||||
)
|
||||
}
|
||||
|
||||
// Decode from the buffer
|
||||
decoded := new(Uint24)
|
||||
if err := decoded.UnmarshalRead(buf); chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Validate decoded value
|
||||
if decoded.Get() != tt.value {
|
||||
t.Errorf(
|
||||
"Decoded value mismatch: got %d, expected %d",
|
||||
decoded.Get(), tt.value,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint24sSetOperations(t *testing.T) {
|
||||
// Helper function to create a Uint24 with a specific value
|
||||
createUint24 := func(value uint32) *Uint24 {
|
||||
u := &Uint24{}
|
||||
u.Set(value)
|
||||
return u
|
||||
}
|
||||
|
||||
// Prepare test data
|
||||
a := createUint24(1)
|
||||
b := createUint24(2)
|
||||
c := createUint24(3)
|
||||
d := createUint24(4)
|
||||
e := createUint24(1) // Duplicate of a
|
||||
|
||||
// Define slices
|
||||
set1 := Uint24s{a, b, c} // [1, 2, 3]
|
||||
set2 := Uint24s{d, e, b} // [4, 1, 2]
|
||||
expectedUnion := Uint24s{a, b, c, d} // [1, 2, 3, 4]
|
||||
expectedIntersection := Uint24s{a, b} // [1, 2]
|
||||
expectedDifference := Uint24s{c} // [3]
|
||||
|
||||
// Test Union
|
||||
t.Run(
|
||||
"Union", func(t *testing.T) {
|
||||
result := set1.Union(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint24Values(result), getUint24Values(expectedUnion),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Union failed: expected %v, got %v",
|
||||
getUint24Values(expectedUnion), getUint24Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Intersection
|
||||
t.Run(
|
||||
"Intersection", func(t *testing.T) {
|
||||
result := set1.Intersection(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint24Values(result), getUint24Values(expectedIntersection),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Intersection failed: expected %v, got %v",
|
||||
getUint24Values(expectedIntersection),
|
||||
getUint24Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Difference
|
||||
t.Run(
|
||||
"Difference", func(t *testing.T) {
|
||||
result := set1.Difference(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint24Values(result), getUint24Values(expectedDifference),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Difference failed: expected %v, got %v",
|
||||
getUint24Values(expectedDifference),
|
||||
getUint24Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function to extract uint64 values from Uint24s
|
||||
func getUint24Values(slice Uint24s) []uint32 {
|
||||
var values []uint32
|
||||
for _, item := range slice {
|
||||
values = append(values, item.Get())
|
||||
}
|
||||
return values
|
||||
}
|
||||
116
pkg/database/indexes/types/uint32.go
Normal file
116
pkg/database/indexes/types/uint32.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Uint32 is a codec for encoding and decoding 32-bit unsigned integers.
|
||||
type Uint32 struct {
|
||||
value uint32
|
||||
}
|
||||
|
||||
// Set sets the value as a uint32.
|
||||
func (c *Uint32) Set(value uint32) {
|
||||
c.value = value
|
||||
}
|
||||
|
||||
// Get gets the value as a uint32.
|
||||
func (c *Uint32) Get() uint32 {
|
||||
return c.value
|
||||
}
|
||||
|
||||
// SetInt sets the value as an int, converting it to uint32.
|
||||
// Values outside the range of uint32 (0–4294967295) will be truncated.
|
||||
func (c *Uint32) SetInt(value int) {
|
||||
c.value = uint32(value)
|
||||
}
|
||||
|
||||
// Int gets the value as an int, converted from uint32.
|
||||
func (c *Uint32) Int() int {
|
||||
return int(c.value)
|
||||
}
|
||||
|
||||
// MarshalWrite writes the uint32 value to the provided writer in BigEndian order.
|
||||
func (c *Uint32) MarshalWrite(w io.Writer) error {
|
||||
return binary.Write(w, binary.BigEndian, c.value)
|
||||
}
|
||||
|
||||
// UnmarshalRead reads a uint32 value from the provided reader in BigEndian order.
|
||||
func (c *Uint32) UnmarshalRead(r io.Reader) error {
|
||||
return binary.Read(r, binary.BigEndian, &c.value)
|
||||
}
|
||||
|
||||
type Uint32s []*Uint32
|
||||
|
||||
// Union computes the union of the current Uint32s slice with another Uint32s slice. The result
|
||||
// contains all unique elements from both slices.
|
||||
func (s Uint32s) Union(other Uint32s) Uint32s {
|
||||
valueMap := make(map[uint32]bool)
|
||||
var result Uint32s
|
||||
|
||||
// Add elements from the current Uint32s slice to the result
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Add elements from the other Uint32s slice to the result
|
||||
for _, item := range other {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersection computes the intersection of the current Uint32s slice with another Uint32s
|
||||
// slice. The result contains only the elements that exist in both slices.
|
||||
func (s Uint32s) Intersection(other Uint32s) Uint32s {
|
||||
valueMap := make(map[uint32]bool)
|
||||
var result Uint32s
|
||||
|
||||
// Add all elements from the other Uint32s slice to the map
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Check for common elements in the current Uint32s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Difference computes the difference of the current Uint32s slice with another Uint32s slice.
|
||||
// The result contains only the elements that are in the current slice but not in the other
|
||||
// slice.
|
||||
func (s Uint32s) Difference(other Uint32s) Uint32s {
|
||||
valueMap := make(map[uint32]bool)
|
||||
var result Uint32s
|
||||
|
||||
// Mark all elements in the other Uint32s slice
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Add elements from the current Uint32s slice that are not in the other Uint32s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
159
pkg/database/indexes/types/uint32_test.go
Normal file
159
pkg/database/indexes/types/uint32_test.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
"lukechampine.com/frand"
|
||||
"utils.orly"
|
||||
)
|
||||
|
||||
func TestUint32(t *testing.T) {
|
||||
// Helper function to generate random 32-bit integers
|
||||
generateRandomUint32 := func() uint32 {
|
||||
return uint32(frand.Intn(math.MaxUint32)) // math.MaxUint32 == 4294967295
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ { // Run test 100 times for random values
|
||||
// Generate a random value
|
||||
randomUint32 := generateRandomUint32()
|
||||
randomInt := int(randomUint32)
|
||||
|
||||
// Create a new codec
|
||||
codec := new(Uint32)
|
||||
|
||||
// Test Uint32 setter and getter
|
||||
codec.Set(randomUint32)
|
||||
if codec.Get() != randomUint32 {
|
||||
t.Fatalf(
|
||||
"Uint32 mismatch: got %d, expected %d", codec.Get(),
|
||||
randomUint32,
|
||||
)
|
||||
}
|
||||
|
||||
// Test GetInt setter and getter
|
||||
codec.SetInt(randomInt)
|
||||
if codec.Int() != randomInt {
|
||||
t.Fatalf(
|
||||
"GetInt mismatch: got %d, expected %d", codec.Int(), randomInt,
|
||||
)
|
||||
}
|
||||
|
||||
// Test encoding to []byte and decoding back
|
||||
bufEnc := new(bytes.Buffer)
|
||||
|
||||
// MarshalWrite
|
||||
err := codec.MarshalWrite(bufEnc)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
encoded := bufEnc.Bytes()
|
||||
|
||||
// Create a copy of encoded bytes before decoding
|
||||
bufDec := bytes.NewBuffer(encoded)
|
||||
|
||||
// Decode back the value
|
||||
decoded := new(Uint32)
|
||||
err = decoded.UnmarshalRead(bufDec)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
if decoded.Get() != randomUint32 {
|
||||
t.Fatalf(
|
||||
"Decoded value mismatch: got %d, expected %d", decoded.Get(),
|
||||
randomUint32,
|
||||
)
|
||||
}
|
||||
|
||||
// Compare encoded bytes to ensure correctness
|
||||
if !utils.FastEqual(encoded, bufEnc.Bytes()) {
|
||||
t.Fatalf(
|
||||
"Byte encoding mismatch: got %v, expected %v", bufEnc.Bytes(),
|
||||
encoded,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint32sSetOperations(t *testing.T) {
|
||||
// Helper function to create a Uint32 with a specific value
|
||||
createUint32 := func(value uint32) *Uint32 {
|
||||
u := &Uint32{}
|
||||
u.Set(value)
|
||||
return u
|
||||
}
|
||||
|
||||
// Prepare test data
|
||||
a := createUint32(1)
|
||||
b := createUint32(2)
|
||||
c := createUint32(3)
|
||||
d := createUint32(4)
|
||||
e := createUint32(1) // Duplicate of a
|
||||
|
||||
// Define slices
|
||||
set1 := Uint32s{a, b, c} // [1, 2, 3]
|
||||
set2 := Uint32s{d, e, b} // [4, 1, 2]
|
||||
expectedUnion := Uint32s{a, b, c, d} // [1, 2, 3, 4]
|
||||
expectedIntersection := Uint32s{a, b} // [1, 2]
|
||||
expectedDifference := Uint32s{c} // [3]
|
||||
|
||||
// Test Union
|
||||
t.Run(
|
||||
"Union", func(t *testing.T) {
|
||||
result := set1.Union(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint32Values(result), getUint32Values(expectedUnion),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Union failed: expected %v, got %v",
|
||||
getUint32Values(expectedUnion), getUint32Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Intersection
|
||||
t.Run(
|
||||
"Intersection", func(t *testing.T) {
|
||||
result := set1.Intersection(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint32Values(result), getUint32Values(expectedIntersection),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Intersection failed: expected %v, got %v",
|
||||
getUint32Values(expectedIntersection),
|
||||
getUint32Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Difference
|
||||
t.Run(
|
||||
"Difference", func(t *testing.T) {
|
||||
result := set1.Difference(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint32Values(result), getUint32Values(expectedDifference),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Difference failed: expected %v, got %v",
|
||||
getUint32Values(expectedDifference),
|
||||
getUint32Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function to extract uint64 values from Uint32s
|
||||
func getUint32Values(slice Uint32s) []uint32 {
|
||||
var values []uint32
|
||||
for _, item := range slice {
|
||||
values = append(values, item.Get())
|
||||
}
|
||||
return values
|
||||
}
|
||||
151
pkg/database/indexes/types/uint40.go
Normal file
151
pkg/database/indexes/types/uint40.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
// MaxUint40 is the maximum value of a 40-bit unsigned integer: 2^40 - 1.
|
||||
const MaxUint40 uint64 = 1<<40 - 1
|
||||
|
||||
// Uint40 is a codec for encoding and decoding 40-bit unsigned integers.
|
||||
type Uint40 struct{ value uint64 }
|
||||
|
||||
// Set sets the value as a 40-bit unsigned integer.
|
||||
// If the value exceeds the maximum allowable value for 40 bits, it returns an error.
|
||||
func (c *Uint40) Set(value uint64) error {
|
||||
if value > MaxUint40 {
|
||||
return errors.New("value exceeds 40-bit range")
|
||||
}
|
||||
c.value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get gets the value as a 40-bit unsigned integer.
|
||||
func (c *Uint40) Get() uint64 { return c.value }
|
||||
|
||||
// SetInt sets the value as an int, converting it to a 40-bit unsigned integer.
|
||||
// If the value is out of the 40-bit range, it returns an error.
|
||||
func (c *Uint40) SetInt(value int) error {
|
||||
if value < 0 || uint64(value) > MaxUint40 {
|
||||
return errors.New("value exceeds 40-bit range")
|
||||
}
|
||||
c.value = uint64(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetInt gets the value as an int, converted from the 40-bit unsigned integer.
|
||||
// Note: If the value exceeds the int range, it will be truncated.
|
||||
func (c *Uint40) GetInt() int { return int(c.value) }
|
||||
|
||||
// MarshalWrite encodes the 40-bit unsigned integer and writes it to the provided writer.
|
||||
// The encoding uses 5 bytes in BigEndian order.
|
||||
func (c *Uint40) MarshalWrite(w io.Writer) (err error) {
|
||||
if c.value > MaxUint40 {
|
||||
return errors.New("value exceeds 40-bit range")
|
||||
}
|
||||
// Buffer for the 5 bytes
|
||||
buf := make([]byte, 5)
|
||||
// Write the upper 5 bytes (ignoring the most significant 3 bytes of uint64)
|
||||
buf[0] = byte((c.value >> 32) & 0xFF) // Most significant byte
|
||||
buf[1] = byte((c.value >> 24) & 0xFF)
|
||||
buf[2] = byte((c.value >> 16) & 0xFF)
|
||||
buf[3] = byte((c.value >> 8) & 0xFF)
|
||||
buf[4] = byte(c.value & 0xFF) // Least significant byte
|
||||
_, err = w.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalRead reads 5 bytes from the provided reader and decodes it into a 40-bit unsigned integer.
|
||||
func (c *Uint40) UnmarshalRead(r io.Reader) (err error) {
|
||||
// Buffer for the 5 bytes
|
||||
buf := make([]byte, 5)
|
||||
_, err = r.Read(buf)
|
||||
if chk.E(err) {
|
||||
return err
|
||||
}
|
||||
// Decode the 5 bytes into a 40-bit unsigned integer
|
||||
c.value = (uint64(buf[0]) << 32) |
|
||||
(uint64(buf[1]) << 24) |
|
||||
(uint64(buf[2]) << 16) |
|
||||
(uint64(buf[3]) << 8) |
|
||||
uint64(buf[4])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Uint40s []*Uint40
|
||||
|
||||
// Union computes the union of the current Uint40s slice with another Uint40s slice. The result
|
||||
// contains all unique elements from both slices.
|
||||
func (s Uint40s) Union(other Uint40s) Uint40s {
|
||||
valueMap := make(map[uint64]bool)
|
||||
var result Uint40s
|
||||
|
||||
// Add elements from the current Uint40s slice to the result
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Add elements from the other Uint40s slice to the result
|
||||
for _, item := range other {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersection computes the intersection of the current Uint40s slice with another Uint40s
|
||||
// slice. The result contains only the elements that exist in both slices.
|
||||
func (s Uint40s) Intersection(other Uint40s) Uint40s {
|
||||
valueMap := make(map[uint64]bool)
|
||||
var result Uint40s
|
||||
|
||||
// Add all elements from the other Uint40s slice to the map
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Check for common elements in the current Uint40s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Difference computes the difference of the current Uint40s slice with another Uint40s slice.
|
||||
// The result contains only the elements that are in the current slice but not in the other
|
||||
// slice.
|
||||
func (s Uint40s) Difference(other Uint40s) Uint40s {
|
||||
valueMap := make(map[uint64]bool)
|
||||
var result Uint40s
|
||||
|
||||
// Mark all elements in the other Uint40s slice
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Add elements from the current Uint40s slice that are not in the other Uint40s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
163
pkg/database/indexes/types/uint40_test.go
Normal file
163
pkg/database/indexes/types/uint40_test.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
func TestUint40(t *testing.T) {
|
||||
// Test cases for Get
|
||||
tests := []struct {
|
||||
name string
|
||||
value uint64
|
||||
expectedErr bool
|
||||
}{
|
||||
{"Minimum Value", 0, false},
|
||||
{"Maximum Value", MaxUint40, false},
|
||||
{
|
||||
"Value in Range", 109951162777, false,
|
||||
}, // Example value within the range
|
||||
{"Value Exceeds Range", MaxUint40 + 1, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.name, func(t *testing.T) {
|
||||
codec := new(Uint40)
|
||||
|
||||
// Test Set
|
||||
err := codec.Set(tt.value)
|
||||
if tt.expectedErr {
|
||||
if !chk.E(err) {
|
||||
t.Errorf("expected error but got none")
|
||||
}
|
||||
return
|
||||
} else if chk.E(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Test Get getter
|
||||
if codec.Get() != tt.value {
|
||||
t.Errorf(
|
||||
"Uint40 mismatch: got %d, expected %d", codec.Get(),
|
||||
tt.value,
|
||||
)
|
||||
}
|
||||
|
||||
// Test MarshalWrite and UnmarshalRead
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
// Marshal to a buffer
|
||||
if err = codec.MarshalWrite(buf); chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Validate encoded size is 5 bytes
|
||||
encoded := buf.Bytes()
|
||||
if len(encoded) != 5 {
|
||||
t.Fatalf(
|
||||
"encoded size mismatch: got %d bytes, expected 5 bytes",
|
||||
len(encoded),
|
||||
)
|
||||
}
|
||||
|
||||
// Decode from the buffer
|
||||
decoded := new(Uint40)
|
||||
if err = decoded.UnmarshalRead(buf); chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Validate decoded value
|
||||
if decoded.Get() != tt.value {
|
||||
t.Errorf(
|
||||
"Decoded value mismatch: got %d, expected %d",
|
||||
decoded.Get(), tt.value,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint40sSetOperations(t *testing.T) {
|
||||
// Helper function to create a Uint64 with a specific value
|
||||
createUint64 := func(value uint64) *Uint40 {
|
||||
u := &Uint40{}
|
||||
u.Set(value)
|
||||
return u
|
||||
}
|
||||
|
||||
// Prepare test data
|
||||
a := createUint64(1)
|
||||
b := createUint64(2)
|
||||
c := createUint64(3)
|
||||
d := createUint64(4)
|
||||
e := createUint64(1) // Duplicate of a
|
||||
|
||||
// Define slices
|
||||
set1 := Uint40s{a, b, c} // [1, 2, 3]
|
||||
set2 := Uint40s{d, e, b} // [4, 1, 2]
|
||||
expectedUnion := Uint40s{a, b, c, d} // [1, 2, 3, 4]
|
||||
expectedIntersection := Uint40s{a, b} // [1, 2]
|
||||
expectedDifference := Uint40s{c} // [3]
|
||||
|
||||
// Test Union
|
||||
t.Run(
|
||||
"Union", func(t *testing.T) {
|
||||
result := set1.Union(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint40Values(result), getUint40Values(expectedUnion),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Union failed: expected %v, got %v",
|
||||
getUint40Values(expectedUnion), getUint40Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Intersection
|
||||
t.Run(
|
||||
"Intersection", func(t *testing.T) {
|
||||
result := set1.Intersection(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint40Values(result), getUint40Values(expectedIntersection),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Intersection failed: expected %v, got %v",
|
||||
getUint40Values(expectedIntersection),
|
||||
getUint40Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Difference
|
||||
t.Run(
|
||||
"Difference", func(t *testing.T) {
|
||||
result := set1.Difference(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint40Values(result), getUint40Values(expectedDifference),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Difference failed: expected %v, got %v",
|
||||
getUint40Values(expectedDifference),
|
||||
getUint40Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function to extract uint64 values from Uint40s
|
||||
func getUint40Values(slice Uint40s) []uint64 {
|
||||
var values []uint64
|
||||
for _, item := range slice {
|
||||
values = append(values, item.Get())
|
||||
}
|
||||
return values
|
||||
}
|
||||
117
pkg/database/indexes/types/uint64.go
Normal file
117
pkg/database/indexes/types/uint64.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Uint64 is a codec for encoding and decoding 64-bit unsigned integers.
|
||||
type Uint64 struct {
|
||||
value uint64
|
||||
}
|
||||
|
||||
// Set sets the value as a uint64.
|
||||
func (c *Uint64) Set(value uint64) {
|
||||
c.value = value
|
||||
}
|
||||
|
||||
// Get gets the value as a uint64.
|
||||
func (c *Uint64) Get() uint64 {
|
||||
return c.value
|
||||
}
|
||||
|
||||
// SetInt sets the value as an int, converting it to uint64.
|
||||
// Values outside the range of uint64 are truncated.
|
||||
func (c *Uint64) SetInt(value int) {
|
||||
c.value = uint64(value)
|
||||
}
|
||||
|
||||
// Int gets the value as an int, converted from uint64. May truncate if the value exceeds the
|
||||
// range of int.
|
||||
func (c *Uint64) Int() int {
|
||||
return int(c.value)
|
||||
}
|
||||
|
||||
// MarshalWrite writes the uint64 value to the provided writer in BigEndian order.
|
||||
func (c *Uint64) MarshalWrite(w io.Writer) error {
|
||||
return binary.Write(w, binary.BigEndian, c.value)
|
||||
}
|
||||
|
||||
// UnmarshalRead reads a uint64 value from the provided reader in BigEndian order.
|
||||
func (c *Uint64) UnmarshalRead(r io.Reader) error {
|
||||
return binary.Read(r, binary.BigEndian, &c.value)
|
||||
}
|
||||
|
||||
type Uint64s []*Uint64
|
||||
|
||||
// Union computes the union of the current Uint64s slice with another Uint64s slice. The result
|
||||
// contains all unique elements from both slices.
|
||||
func (s Uint64s) Union(other Uint64s) Uint64s {
|
||||
valueMap := make(map[uint64]bool)
|
||||
var result Uint64s
|
||||
|
||||
// Add elements from the current Uint64s slice to the result
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Add elements from the other Uint64s slice to the result
|
||||
for _, item := range other {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
valueMap[val] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersection computes the intersection of the current Uint64s slice with another Uint64s
|
||||
// slice. The result contains only the elements that exist in both slices.
|
||||
func (s Uint64s) Intersection(other Uint64s) Uint64s {
|
||||
valueMap := make(map[uint64]bool)
|
||||
var result Uint64s
|
||||
|
||||
// Add all elements from the other Uint64s slice to the map
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Check for common elements in the current Uint64s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Difference computes the difference of the current Uint64s slice with another Uint64s slice.
|
||||
// The result contains only the elements that are in the current slice but not in the other
|
||||
// slice.
|
||||
func (s Uint64s) Difference(other Uint64s) Uint64s {
|
||||
valueMap := make(map[uint64]bool)
|
||||
var result Uint64s
|
||||
|
||||
// Mark all elements in the other Uint64s slice
|
||||
for _, item := range other {
|
||||
valueMap[item.Get()] = true
|
||||
}
|
||||
|
||||
// Add elements from the current Uint64s slice that are not in the other Uint64s slice
|
||||
for _, item := range s {
|
||||
val := item.Get()
|
||||
if !valueMap[val] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
159
pkg/database/indexes/types/uint64_test.go
Normal file
159
pkg/database/indexes/types/uint64_test.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
"lukechampine.com/frand"
|
||||
"utils.orly"
|
||||
)
|
||||
|
||||
func TestUint64(t *testing.T) {
|
||||
// Helper function to generate random 64-bit integers
|
||||
generateRandomUint64 := func() uint64 {
|
||||
return frand.Uint64n(math.MaxUint64) // math.MaxUint64 == 18446744073709551615
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ { // Run test 100 times for random values
|
||||
// Generate a random value
|
||||
randomUint64 := generateRandomUint64()
|
||||
randomInt := int(randomUint64)
|
||||
|
||||
// Create a new codec
|
||||
codec := new(Uint64)
|
||||
|
||||
// Test UInt64 setter and getter
|
||||
codec.Set(randomUint64)
|
||||
if codec.Get() != randomUint64 {
|
||||
t.Fatalf(
|
||||
"Uint64 mismatch: got %d, expected %d", codec.Get(),
|
||||
randomUint64,
|
||||
)
|
||||
}
|
||||
|
||||
// Test GetInt setter and getter
|
||||
codec.SetInt(randomInt)
|
||||
if codec.Int() != randomInt {
|
||||
t.Fatalf(
|
||||
"GetInt mismatch: got %d, expected %d", codec.Int(), randomInt,
|
||||
)
|
||||
}
|
||||
|
||||
// Test encoding to []byte and decoding back
|
||||
bufEnc := new(bytes.Buffer)
|
||||
|
||||
// MarshalWrite
|
||||
err := codec.MarshalWrite(bufEnc)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
encoded := bufEnc.Bytes()
|
||||
|
||||
// Create a buffer for decoding
|
||||
bufDec := bytes.NewBuffer(encoded)
|
||||
|
||||
// Decode back the value
|
||||
decoded := new(Uint64)
|
||||
err = decoded.UnmarshalRead(bufDec)
|
||||
if chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
if decoded.Get() != randomUint64 {
|
||||
t.Fatalf(
|
||||
"Decoded value mismatch: got %d, expected %d", decoded.Get(),
|
||||
randomUint64,
|
||||
)
|
||||
}
|
||||
|
||||
// Compare encoded bytes to ensure correctness
|
||||
if !utils.FastEqual(encoded, bufEnc.Bytes()) {
|
||||
t.Fatalf(
|
||||
"Byte encoding mismatch: got %v, expected %v", bufEnc.Bytes(),
|
||||
encoded,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint64sSetOperations(t *testing.T) {
|
||||
// Helper function to create a Uint64 with a specific value
|
||||
createUint64 := func(value uint64) *Uint64 {
|
||||
u := &Uint64{}
|
||||
u.Set(value)
|
||||
return u
|
||||
}
|
||||
|
||||
// Prepare test data
|
||||
a := createUint64(1)
|
||||
b := createUint64(2)
|
||||
c := createUint64(3)
|
||||
d := createUint64(4)
|
||||
e := createUint64(1) // Duplicate of a
|
||||
|
||||
// Define slices
|
||||
set1 := Uint64s{a, b, c} // [1, 2, 3]
|
||||
set2 := Uint64s{d, e, b} // [4, 1, 2]
|
||||
expectedUnion := Uint64s{a, b, c, d} // [1, 2, 3, 4]
|
||||
expectedIntersection := Uint64s{a, b} // [1, 2]
|
||||
expectedDifference := Uint64s{c} // [3]
|
||||
|
||||
// Test Union
|
||||
t.Run(
|
||||
"Union", func(t *testing.T) {
|
||||
result := set1.Union(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint64Values(result), getUint64Values(expectedUnion),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Union failed: expected %v, got %v",
|
||||
getUint64Values(expectedUnion), getUint64Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Intersection
|
||||
t.Run(
|
||||
"Intersection", func(t *testing.T) {
|
||||
result := set1.Intersection(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint64Values(result), getUint64Values(expectedIntersection),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Intersection failed: expected %v, got %v",
|
||||
getUint64Values(expectedIntersection),
|
||||
getUint64Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Test Difference
|
||||
t.Run(
|
||||
"Difference", func(t *testing.T) {
|
||||
result := set1.Difference(set2)
|
||||
if !reflect.DeepEqual(
|
||||
getUint64Values(result), getUint64Values(expectedDifference),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Difference failed: expected %v, got %v",
|
||||
getUint64Values(expectedDifference),
|
||||
getUint64Values(result),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function to extract uint64 values from Uint64s
|
||||
func getUint64Values(slice Uint64s) []uint64 {
|
||||
var values []uint64
|
||||
for _, item := range slice {
|
||||
values = append(values, item.Get())
|
||||
}
|
||||
return values
|
||||
}
|
||||
71
pkg/database/indexes/types/word.go
Normal file
71
pkg/database/indexes/types/word.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
)
|
||||
|
||||
var zero = []byte{0x00}
|
||||
|
||||
type Word struct {
|
||||
val []byte // Contains only the raw word (without the zero-byte marker)
|
||||
}
|
||||
|
||||
// FromWord stores the word without any modifications
|
||||
func (w *Word) FromWord(word []byte) {
|
||||
w.val = word // Only store the raw word
|
||||
}
|
||||
|
||||
// Bytes returns the raw word without any end-of-word marker
|
||||
func (w *Word) Bytes() []byte {
|
||||
return w.val
|
||||
}
|
||||
|
||||
// MarshalWrite writes the word to the writer, appending the zero-byte marker
|
||||
func (w *Word) MarshalWrite(wr io.Writer) (err error) {
|
||||
if _, err = wr.Write(w.val); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if _, err = wr.Write(zero); chk.E(err) {
|
||||
return
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalRead reads the word from the reader, stopping at the zero-byte marker
|
||||
func (w *Word) UnmarshalRead(r io.Reader) error {
|
||||
buf := new(bytes.Buffer)
|
||||
tmp := make([]byte, 1)
|
||||
foundEndMarker := false
|
||||
|
||||
// Read bytes until the zero byte is encountered
|
||||
for {
|
||||
n, err := r.Read(tmp)
|
||||
if n > 0 {
|
||||
if tmp[0] == 0x00 { // Stop on encountering the zero-byte marker
|
||||
foundEndMarker = true
|
||||
break
|
||||
}
|
||||
buf.WriteByte(tmp[0])
|
||||
}
|
||||
if err != nil {
|
||||
if chk.E(err) {
|
||||
return err // Handle unexpected errors
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Only store the word if we found a valid end marker
|
||||
if foundEndMarker {
|
||||
// Make a copy of the bytes to avoid them being zeroed when the buffer is returned to the pool
|
||||
bytes := buf.Bytes()
|
||||
w.val = make([]byte, len(bytes))
|
||||
copy(w.val, bytes)
|
||||
} else {
|
||||
w.val = []byte{} // Empty slice if no valid end marker was found
|
||||
}
|
||||
return nil
|
||||
}
|
||||
89
pkg/database/indexes/types/word_test.go
Normal file
89
pkg/database/indexes/types/word_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"database.orly/indexes/types"
|
||||
"lol.mleku.dev/chk"
|
||||
"utils.orly"
|
||||
)
|
||||
|
||||
func TestT(t *testing.T) {
|
||||
// Test cases: each contains inputs, expected serialized output, and expected result after deserialization.
|
||||
tests := []struct {
|
||||
word []byte // Input word
|
||||
expectedBytes []byte // Expected output from Bytes() (raw word)
|
||||
expectedEncoded []byte // Expected serialized (MarshalWrite) output (word + 0x00)
|
||||
}{
|
||||
{[]byte("example"), []byte("example"), []byte("example\x00")},
|
||||
{[]byte("golang"), []byte("golang"), []byte("golang\x00")},
|
||||
{[]byte(""), []byte(""), []byte("\x00")}, // Edge case: empty word
|
||||
{[]byte("123"), []byte("123"), []byte("123\x00")},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
// Create a new object and set the word
|
||||
ft := new(types.Word)
|
||||
ft.FromWord(tt.word)
|
||||
|
||||
// Ensure Bytes() returns the correct raw word
|
||||
if got := ft.Bytes(); !utils.FastEqual(tt.expectedBytes, got) {
|
||||
t.Errorf(
|
||||
"FromWord/Bytes failed: expected %q, got %q", tt.expectedBytes,
|
||||
got,
|
||||
)
|
||||
}
|
||||
|
||||
// Test MarshalWrite
|
||||
var buf bytes.Buffer
|
||||
if err := ft.MarshalWrite(&buf); chk.E(err) {
|
||||
t.Fatalf("MarshalWrite failed: %v", err)
|
||||
}
|
||||
|
||||
// Ensure the serialized output matches expectedEncoded
|
||||
if got := buf.Bytes(); !utils.FastEqual(tt.expectedEncoded, got) {
|
||||
t.Errorf(
|
||||
"MarshalWrite failed: expected %q, got %q", tt.expectedEncoded,
|
||||
got,
|
||||
)
|
||||
}
|
||||
|
||||
// Test UnmarshalRead
|
||||
newFt := new(types.Word)
|
||||
// Create a new reader from the buffer to reset the read position
|
||||
reader := bytes.NewReader(buf.Bytes())
|
||||
if err := newFt.UnmarshalRead(reader); chk.E(err) {
|
||||
t.Fatalf("UnmarshalRead failed: %v", err)
|
||||
}
|
||||
|
||||
// Ensure the word after decoding matches the original word
|
||||
if got := newFt.Bytes(); !utils.FastEqual(tt.expectedBytes, got) {
|
||||
t.Errorf(
|
||||
"UnmarshalRead failed: expected %q, got %q", tt.expectedBytes,
|
||||
got,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalReadHandlesMissingZeroByte(t *testing.T) {
|
||||
// Special case: what happens if the zero-byte marker is missing?
|
||||
data := []byte("incomplete") // No zero-byte at the end
|
||||
reader := bytes.NewReader(data)
|
||||
|
||||
ft := new(types.Word)
|
||||
err := ft.UnmarshalRead(reader)
|
||||
|
||||
// Expect an EOF or similar handling
|
||||
if !chk.E(err) {
|
||||
t.Errorf("UnmarshalRead should fail gracefully on missing zero-byte, but it didn't")
|
||||
}
|
||||
|
||||
// Ensure no data is stored in ft.val if no valid end-marker was encountered
|
||||
if got := ft.Bytes(); len(got) != 0 {
|
||||
t.Errorf(
|
||||
"UnmarshalRead stored incomplete data: got %q, expected empty", got,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user