From 06063750e76df68911a7daef310614a542dcdb99 Mon Sep 17 00:00:00 2001 From: mleku Date: Tue, 23 Dec 2025 14:47:50 +0100 Subject: [PATCH] Add fixed-size type support for IdPkTs and EventRef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update nostr dependency to v1.0.11 with new types package - Add IDFixed(), PubFixed(), IDHex(), PubHex() methods to IdPkTs - Add EventRef type: 80-byte stack-allocated event reference - Add ToEventRef()/ToIdPkTs() conversion methods - Update tests to use IDHex() instead of hex.Enc(r.Id) EventRef provides: - Copy-on-assignment semantics (arrays vs slices) - Zero heap allocations for event reference passing - Type-safe fixed-size fields (EventID, Pubkey) Files modified: - go.mod, go.sum: Update nostr to v1.0.11 - pkg/interfaces/store/store_interface.go: Add methods and EventRef type - pkg/interfaces/store/store_interface_test.go: New test file - pkg/database/binary_tag_filter_test.go: Use IDHex() - pkg/neo4j/fetch-event_test.go: Use IDHex(), PubHex() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- go.mod | 18 +- go.sum | 36 +-- pkg/database/binary_tag_filter_test.go | 8 +- pkg/interfaces/store/store_interface.go | 85 +++++++ pkg/interfaces/store/store_interface_test.go | 248 +++++++++++++++++++ pkg/neo4j/fetch-event_test.go | 4 +- 6 files changed, 366 insertions(+), 33 deletions(-) create mode 100644 pkg/interfaces/store/store_interface_test.go diff --git a/go.mod b/go.mod index 45c86a0..5574032 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module next.orly.dev go 1.25.3 require ( - git.mleku.dev/mleku/nostr v1.0.9 + git.mleku.dev/mleku/nostr v1.0.11 github.com/adrg/xdg v0.5.3 github.com/aperturerobotics/go-indexeddb v0.2.3 github.com/dgraph-io/badger/v4 v4.8.0 @@ -21,7 +21,7 @@ require ( github.com/vertex-lab/nostr-sqlite v0.3.2 go-simpler.org/env v0.12.0 go.uber.org/atomic v1.11.0 - golang.org/x/crypto v0.45.0 + golang.org/x/crypto v0.46.0 golang.org/x/lint v0.0.0-20241112194109-818c5a804067 honnef.co/go/tools v0.6.1 lol.mleku.dev v1.0.5 @@ -69,14 +69,14 @@ require ( go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect golang.org/x/arch v0.15.0 // indirect - golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect + golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 // indirect golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 // indirect - golang.org/x/mod v0.30.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect - golang.org/x/tools v0.39.0 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect p256k1.mleku.dev v1.0.3 // indirect diff --git a/go.sum b/go.sum index 0447612..b7e71e1 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.mleku.dev/mleku/nostr v1.0.9 h1:aiN0ihnXzEpboXjW4u8qr5XokLQqg4P0XSZ1Y273qM0= -git.mleku.dev/mleku/nostr v1.0.9/go.mod h1:iYTlg2WKJXJ0kcsM6QBGOJ0UDiJidMgL/i64cHyPjZc= +git.mleku.dev/mleku/nostr v1.0.11 h1:xQ+rKPzTblerX/kRLDimOsH3rQK7/n9wYdG4DBKGcsg= +git.mleku.dev/mleku/nostr v1.0.11/go.mod h1:kJwSMmLRnAJ7QJtgXDv2wGgceFU0luwVqrgAL3MI93M= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 h1:ClzzXMDDuUbWfNNZqGeYq4PnYOlwlOVIvSyNaIy0ykg= @@ -166,37 +166,37 @@ golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= -golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7VJHZO84hejP9Jmp0MM= +golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 h1:HDjDiATsGqvuqvkDvgJjD1IgPrVekcSXVVE21JwvzGE= golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms= golang.org/x/lint v0.0.0-20241112194109-818c5a804067 h1:adDmSQyFTCiv19j015EGKJBoaa7ElV0Q1Wovb/4G7NA= golang.org/x/lint v0.0.0-20241112194109-818c5a804067/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/database/binary_tag_filter_test.go b/pkg/database/binary_tag_filter_test.go index 72af6d9..0882572 100644 --- a/pkg/database/binary_tag_filter_test.go +++ b/pkg/database/binary_tag_filter_test.go @@ -120,7 +120,7 @@ func TestBinaryTagFilterRegression(t *testing.T) { // Verify we got the correct event found := false for _, r := range results { - if hex.Enc(r.Id) == testEventIdHex { + if r.IDHex() == testEventIdHex { found = true break } @@ -156,7 +156,7 @@ func TestBinaryTagFilterRegression(t *testing.T) { // Verify we got the correct event found := false for _, r := range results { - if hex.Enc(r.Id) == testEventIdHex { + if r.IDHex() == testEventIdHex { found = true break } @@ -192,7 +192,7 @@ func TestBinaryTagFilterRegression(t *testing.T) { // Verify we got the correct event found := false for _, r := range results { - if hex.Enc(r.Id) == testEventIdHex { + if r.IDHex() == testEventIdHex { found = true break } @@ -229,7 +229,7 @@ func TestBinaryTagFilterRegression(t *testing.T) { // Verify we got the correct event found := false for _, r := range results { - if hex.Enc(r.Id) == testEventIdHex { + if r.IDHex() == testEventIdHex { found = true break } diff --git a/pkg/interfaces/store/store_interface.go b/pkg/interfaces/store/store_interface.go index f05f1c8..c75b550 100644 --- a/pkg/interfaces/store/store_interface.go +++ b/pkg/interfaces/store/store_interface.go @@ -15,6 +15,7 @@ import ( "git.mleku.dev/mleku/nostr/encoders/event" "git.mleku.dev/mleku/nostr/encoders/filter" "git.mleku.dev/mleku/nostr/encoders/tag" + ntypes "git.mleku.dev/mleku/nostr/types" ) // I am a type for a persistence layer for nostr events handled by a relay. @@ -60,6 +61,9 @@ type Accountant interface { EventCount() (count uint64, err error) } +// IdPkTs holds event reference data with slice fields for backward compatibility. +// For new code preferring stack-allocated, copy-on-assignment semantics, +// use the IDFixed() and PubFixed() methods or convert to EventRef. type IdPkTs struct { Id []byte Pub []byte @@ -67,6 +71,87 @@ type IdPkTs struct { Ser uint64 } +// IDFixed returns the event ID as a fixed-size array (stack-allocated, copied on assignment). +func (i *IdPkTs) IDFixed() ntypes.EventID { + return ntypes.EventIDFromBytes(i.Id) +} + +// PubFixed returns the pubkey as a fixed-size array (stack-allocated, copied on assignment). +func (i *IdPkTs) PubFixed() ntypes.Pubkey { + return ntypes.PubkeyFromBytes(i.Pub) +} + +// IDHex returns the event ID as a lowercase hex string. +func (i *IdPkTs) IDHex() string { + return ntypes.EventIDFromBytes(i.Id).Hex() +} + +// PubHex returns the pubkey as a lowercase hex string. +func (i *IdPkTs) PubHex() string { + return ntypes.PubkeyFromBytes(i.Pub).Hex() +} + +// ToEventRef converts IdPkTs to an EventRef (fully stack-allocated). +func (i *IdPkTs) ToEventRef() EventRef { + return NewEventRef(i.Id, i.Pub, i.Ts, i.Ser) +} + +// EventRef is a stack-friendly event reference using fixed-size arrays. +// Total size: 80 bytes (32+32+8+8), fits in a cache line, copies stay on stack. +// Use this type when you need safe, immutable event references. +type EventRef struct { + id ntypes.EventID // 32 bytes + pub ntypes.Pubkey // 32 bytes + ts int64 // 8 bytes + ser uint64 // 8 bytes +} + +// NewEventRef creates an EventRef from byte slices. +// The slices are copied into fixed-size arrays. +func NewEventRef(id, pub []byte, ts int64, ser uint64) EventRef { + return EventRef{ + id: ntypes.EventIDFromBytes(id), + pub: ntypes.PubkeyFromBytes(pub), + ts: ts, + ser: ser, + } +} + +// ID returns the event ID (copy, stays on stack). +func (r EventRef) ID() ntypes.EventID { return r.id } + +// Pub returns the pubkey (copy, stays on stack). +func (r EventRef) Pub() ntypes.Pubkey { return r.pub } + +// Ts returns the timestamp. +func (r EventRef) Ts() int64 { return r.ts } + +// Ser returns the serial number. +func (r EventRef) Ser() uint64 { return r.ser } + +// IDHex returns the event ID as lowercase hex. +func (r EventRef) IDHex() string { return r.id.Hex() } + +// PubHex returns the pubkey as lowercase hex. +func (r EventRef) PubHex() string { return r.pub.Hex() } + +// IDSlice returns a slice view of the ID (shares memory, use carefully). +func (r *EventRef) IDSlice() []byte { return r.id.Bytes() } + +// PubSlice returns a slice view of the pubkey (shares memory, use carefully). +func (r *EventRef) PubSlice() []byte { return r.pub.Bytes() } + +// ToIdPkTs converts EventRef to IdPkTs for backward compatibility. +// Note: This allocates new slices. +func (r EventRef) ToIdPkTs() *IdPkTs { + return &IdPkTs{ + Id: r.id.Copy(), + Pub: r.pub.Copy(), + Ts: r.ts, + Ser: r.ser, + } +} + type Querier interface { QueryForIds(c context.Context, f *filter.F) (evs []*IdPkTs, err error) } diff --git a/pkg/interfaces/store/store_interface_test.go b/pkg/interfaces/store/store_interface_test.go new file mode 100644 index 0000000..ce3a09e --- /dev/null +++ b/pkg/interfaces/store/store_interface_test.go @@ -0,0 +1,248 @@ +package store + +import ( + "bytes" + "testing" + + ntypes "git.mleku.dev/mleku/nostr/types" +) + +func TestIdPkTsFixedMethods(t *testing.T) { + // Create an IdPkTs with sample data + id := make([]byte, 32) + pub := make([]byte, 32) + for i := 0; i < 32; i++ { + id[i] = byte(i) + pub[i] = byte(i + 32) + } + + ipk := &IdPkTs{ + Id: id, + Pub: pub, + Ts: 1234567890, + Ser: 42, + } + + // Test IDFixed returns correct data + idFixed := ipk.IDFixed() + if !bytes.Equal(idFixed[:], id) { + t.Errorf("IDFixed: got %x, want %x", idFixed[:], id) + } + + // Test IDFixed returns a copy + idFixed[0] = 0xFF + if ipk.Id[0] == 0xFF { + t.Error("IDFixed should return a copy, not a reference") + } + + // Test PubFixed returns correct data + pubFixed := ipk.PubFixed() + if !bytes.Equal(pubFixed[:], pub) { + t.Errorf("PubFixed: got %x, want %x", pubFixed[:], pub) + } + + // Test hex methods + idHex := ipk.IDHex() + expectedIDHex := "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + if idHex != expectedIDHex { + t.Errorf("IDHex: got %s, want %s", idHex, expectedIDHex) + } +} + +func TestEventRef(t *testing.T) { + id := make([]byte, 32) + pub := make([]byte, 32) + for i := 0; i < 32; i++ { + id[i] = byte(i) + pub[i] = byte(i + 100) + } + + // Create EventRef + ref := NewEventRef(id, pub, 1234567890, 42) + + // Test accessors - need to get addressable values for slicing + refID := ref.ID() + refPub := ref.Pub() + if !bytes.Equal(refID[:], id) { + t.Error("ID() mismatch") + } + if !bytes.Equal(refPub[:], pub) { + t.Error("Pub() mismatch") + } + if ref.Ts() != 1234567890 { + t.Error("Ts() mismatch") + } + if ref.Ser() != 42 { + t.Error("Ser() mismatch") + } + + // Test copy-on-assignment + ref2 := ref + testID := ref.ID() + testID[0] = 0xFF + ref2ID := ref2.ID() + if ref2ID[0] == 0xFF { + t.Error("EventRef should copy on assignment") + } + + // Test hex methods + if len(ref.IDHex()) != 64 { + t.Errorf("IDHex length: got %d, want 64", len(ref.IDHex())) + } +} + +func TestEventRefToIdPkTs(t *testing.T) { + id := make([]byte, 32) + pub := make([]byte, 32) + for i := 0; i < 32; i++ { + id[i] = byte(i) + pub[i] = byte(i + 100) + } + + ref := NewEventRef(id, pub, 1234567890, 42) + ipk := ref.ToIdPkTs() + + // Verify conversion + if !bytes.Equal(ipk.Id, id) { + t.Error("ToIdPkTs: Id mismatch") + } + if !bytes.Equal(ipk.Pub, pub) { + t.Error("ToIdPkTs: Pub mismatch") + } + if ipk.Ts != 1234567890 { + t.Error("ToIdPkTs: Ts mismatch") + } + if ipk.Ser != 42 { + t.Error("ToIdPkTs: Ser mismatch") + } + + // Verify independence (modifications don't affect original) + ipk.Id[0] = 0xFF + if ref.ID()[0] == 0xFF { + t.Error("ToIdPkTs should create independent copy") + } +} + +func TestIdPkTsToEventRef(t *testing.T) { + id := make([]byte, 32) + pub := make([]byte, 32) + for i := 0; i < 32; i++ { + id[i] = byte(i) + pub[i] = byte(i + 100) + } + + ipk := &IdPkTs{ + Id: id, + Pub: pub, + Ts: 1234567890, + Ser: 42, + } + + ref := ipk.ToEventRef() + + // Verify conversion - need addressable values for slicing + refID := ref.ID() + refPub := ref.Pub() + if !bytes.Equal(refID[:], id) { + t.Error("ToEventRef: ID mismatch") + } + if !bytes.Equal(refPub[:], pub) { + t.Error("ToEventRef: Pub mismatch") + } + if ref.Ts() != 1234567890 { + t.Error("ToEventRef: Ts mismatch") + } + if ref.Ser() != 42 { + t.Error("ToEventRef: Ser mismatch") + } +} + +func BenchmarkEventRefCopy(b *testing.B) { + id := make([]byte, 32) + pub := make([]byte, 32) + for i := 0; i < 32; i++ { + id[i] = byte(i) + pub[i] = byte(i + 100) + } + + ref := NewEventRef(id, pub, 1234567890, 42) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + ref2 := ref // Copy (should stay on stack) + _ = ref2 + } +} + +func BenchmarkIdPkTsToEventRef(b *testing.B) { + id := make([]byte, 32) + pub := make([]byte, 32) + for i := 0; i < 32; i++ { + id[i] = byte(i) + pub[i] = byte(i + 100) + } + + ipk := &IdPkTs{ + Id: id, + Pub: pub, + Ts: 1234567890, + Ser: 42, + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + ref := ipk.ToEventRef() + _ = ref + } +} + +func BenchmarkEventRefAccess(b *testing.B) { + id := make([]byte, 32) + pub := make([]byte, 32) + for i := 0; i < 32; i++ { + id[i] = byte(i) + pub[i] = byte(i + 100) + } + + ref := NewEventRef(id, pub, 1234567890, 42) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + idCopy := ref.ID() + pubCopy := ref.Pub() + _ = idCopy + _ = pubCopy + } +} + +func BenchmarkIdPkTsFixedAccess(b *testing.B) { + id := make([]byte, 32) + pub := make([]byte, 32) + for i := 0; i < 32; i++ { + id[i] = byte(i) + pub[i] = byte(i + 100) + } + + ipk := &IdPkTs{ + Id: id, + Pub: pub, + Ts: 1234567890, + Ser: 42, + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + idCopy := ipk.IDFixed() + pubCopy := ipk.PubFixed() + _ = idCopy + _ = pubCopy + } +} + +// Ensure EventRef implements expected interface at compile time +var _ interface { + ID() ntypes.EventID + Pub() ntypes.Pubkey + Ts() int64 + Ser() uint64 +} = EventRef{} diff --git a/pkg/neo4j/fetch-event_test.go b/pkg/neo4j/fetch-event_test.go index a5da4e0..bc33141 100644 --- a/pkg/neo4j/fetch-event_test.go +++ b/pkg/neo4j/fetch-event_test.go @@ -339,11 +339,11 @@ func TestGetFullIdPubkeyBySerial(t *testing.T) { t.Fatal("Expected non-nil result") } - if hex.Enc(idPkTs.Id) != hex.Enc(ev.ID[:]) { + if idPkTs.IDHex() != hex.Enc(ev.ID[:]) { t.Fatalf("ID mismatch") } - if hex.Enc(idPkTs.Pub) != hex.Enc(ev.Pubkey[:]) { + if idPkTs.PubHex() != hex.Enc(ev.Pubkey[:]) { t.Fatalf("Pubkey mismatch") }