Browse Source

Use stack-allocated arrays for IdPkTs to reduce GC pressure (v0.56.6)

- Change IdPkTs Id/Pub fields from []byte to [32]byte (ntypes.EventID/Pubkey)
- Add NewIdPkTs() constructor for copying byte slices into fixed arrays
- Add IDSlice() and PubSlice() methods for slice access when needed
- Update protobuf converters to handle array-to-slice conversion
- Update all database and neo4j creation sites to use NewIdPkTs()
- Fix test files to use bytes.Equal() for array comparisons
- Benchmark confirms 0 B/op, 0 allocs/op for copy operations

Files modified:
- pkg/interfaces/store/store_interface.go: Core type change to fixed arrays
- pkg/interfaces/store/store_interface_test.go: Tests for new API
- pkg/proto/orlydb/v1/converters.go: Protobuf conversion with ntypes
- pkg/database/get-fullidpubkey-by-serial.go: Use NewIdPkTs constructor
- pkg/database/get-fullidpubkey-by-serials.go: Use NewIdPkTs constructor
- pkg/database/process-delete.go: Slice arrays for DeleteEvent calls
- pkg/neo4j/fetch-event.go: Use NewIdPkTs constructor
- pkg/neo4j/query-events.go: Use NewIdPkTs constructor
- pkg/database/query-for-*_test.go: Use bytes.Equal for comparisons

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
main v0.56.6
woikos 4 months ago
parent
commit
8c0e7d418a
No known key found for this signature in database
  1. 12
      pkg/database/get-fullidpubkey-by-serial.go
  2. 13
      pkg/database/get-fullidpubkey-by-serials.go
  3. 8
      pkg/database/process-delete.go
  4. 10
      pkg/database/query-for-authors-tags_test.go
  5. 8
      pkg/database/query-for-created-at_test.go
  6. 42
      pkg/database/query-for-ids_test.go
  7. 10
      pkg/database/query-for-kinds-authors-tags_test.go
  8. 6
      pkg/database/query-for-kinds-authors_test.go
  9. 8
      pkg/database/query-for-kinds-tags_test.go
  10. 4
      pkg/database/query-for-kinds_test.go
  11. 8
      pkg/database/query-for-tags_test.go
  12. 56
      pkg/interfaces/store/store_interface.go
  13. 175
      pkg/interfaces/store/store_interface_test.go
  14. 16
      pkg/neo4j/fetch-event.go
  15. 8
      pkg/neo4j/query-events.go
  16. 9
      pkg/proto/orlydb/v1/converters.go
  17. 2
      pkg/version/version

12
pkg/database/get-fullidpubkey-by-serial.go

@ -41,12 +41,12 @@ func (d *D) GetFullIdPubkeyBySerial(ser *types.Uint40) ( @@ -41,12 +41,12 @@ func (d *D) GetFullIdPubkeyBySerial(ser *types.Uint40) (
).UnmarshalRead(buf2); chk.E(err) {
return
}
idpkts := store.IdPkTs{
Id: fid.Bytes(),
Pub: p.Bytes(),
Ts: int64(ca.Get()),
Ser: ser.Get(),
}
idpkts := store.NewIdPkTs(
fid.Bytes(),
p.Bytes(),
int64(ca.Get()),
ser.Get(),
)
fidpk = &idpkts
}
return

13
pkg/database/get-fullidpubkey-by-serials.go

@ -59,14 +59,13 @@ func (d *D) GetFullIdPubkeyBySerials(sers []*types.Uint40) ( @@ -59,14 +59,13 @@ func (d *D) GetFullIdPubkeyBySerials(sers []*types.Uint40) (
).UnmarshalRead(bytes.NewBuffer(key)); chk.E(err) {
return
}
fidpks = append(
fidpks, &store.IdPkTs{
Id: fid.Bytes(),
Pub: p.Bytes(),
Ts: int64(ca.Get()),
Ser: ser.Get(),
},
idpkts := store.NewIdPkTs(
fid.Bytes(),
p.Bytes(),
int64(ca.Get()),
ser.Get(),
)
fidpks = append(fidpks, &idpkts)
}
}
return

8
pkg/database/process-delete.go

@ -129,11 +129,11 @@ func (d *D) ProcessDelete(ev *event.E, admins [][]byte) (err error) { @@ -129,11 +129,11 @@ func (d *D) ProcessDelete(ev *event.E, admins [][]byte) (err error) {
})
for _, v := range idPkTss {
if v.Ts < ev.CreatedAt {
if err = d.DeleteEvent(context.Background(), v.Id); chk.E(err) {
log.W.F("failed to delete event %x via a-tag: %v", v.Id, err)
if err = d.DeleteEvent(context.Background(), v.Id[:]); chk.E(err) {
log.W.F("failed to delete event %x via a-tag: %v", v.Id[:], err)
continue
}
log.D.F("deleted event %x via a-tag deletion", v.Id)
log.D.F("deleted event %x via a-tag deletion", v.Id[:])
}
}
}
@ -187,7 +187,7 @@ func (d *D) ProcessDelete(ev *event.E, admins [][]byte) (err error) { @@ -187,7 +187,7 @@ func (d *D) ProcessDelete(ev *event.E, admins [][]byte) (err error) {
for _, v := range idPkTss {
if v.Ts < ev.CreatedAt {
if err = d.DeleteEvent(
context.Background(), v.Id,
context.Background(), v.Id[:],
); chk.E(err) {
continue
}

10
pkg/database/query-for-authors-tags_test.go

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
package database
import (
"bytes"
"testing"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/tag"
"next.orly.dev/pkg/utils"
)
func TestQueryForAuthorsTags(t *testing.T) {
@ -56,10 +56,10 @@ func TestQueryForAuthorsTags(t *testing.T) { @@ -56,10 +56,10 @@ func TestQueryForAuthorsTags(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if !utils.FastEqual(ev.Pubkey, testEvent.Pubkey) {
if !bytes.Equal(ev.Pubkey[:], testEvent.Pubkey[:]) {
t.Fatalf(
"result %d has incorrect author, got %x, expected %x",
i, ev.Pubkey, testEvent.Pubkey,
@ -70,9 +70,9 @@ func TestQueryForAuthorsTags(t *testing.T) { @@ -70,9 +70,9 @@ func TestQueryForAuthorsTags(t *testing.T) {
var hasTag bool
for _, tg := range *ev.Tags {
if tg.Len() >= 2 && len(tg.Key()) == 1 {
if utils.FastEqual(
if bytes.Equal(
tg.Key(), testTag.Key(),
) && utils.FastEqual(tg.Value(), testTag.Value()) {
) && bytes.Equal(tg.Value(), testTag.Value()) {
hasTag = true
break
}

8
pkg/database/query-for-created-at_test.go

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
package database
import (
"bytes"
"testing"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/timestamp"
"next.orly.dev/pkg/utils"
)
func TestQueryForCreatedAt(t *testing.T) {
@ -50,7 +50,7 @@ func TestQueryForCreatedAt(t *testing.T) { @@ -50,7 +50,7 @@ func TestQueryForCreatedAt(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
break
}
@ -88,7 +88,7 @@ func TestQueryForCreatedAt(t *testing.T) { @@ -88,7 +88,7 @@ func TestQueryForCreatedAt(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
break
}
@ -126,7 +126,7 @@ func TestQueryForCreatedAt(t *testing.T) { @@ -126,7 +126,7 @@ func TestQueryForCreatedAt(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
break
}

42
pkg/database/query-for-ids_test.go

@ -1,13 +1,13 @@ @@ -1,13 +1,13 @@
package database
import (
"bytes"
"testing"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/kind"
"git.mleku.dev/mleku/nostr/encoders/tag"
"git.mleku.dev/mleku/nostr/encoders/timestamp"
"next.orly.dev/pkg/utils"
)
func TestQueryForIds(t *testing.T) {
@ -39,9 +39,9 @@ func TestQueryForIds(t *testing.T) { @@ -39,9 +39,9 @@ func TestQueryForIds(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if !utils.FastEqual(ev.Pubkey, events[1].Pubkey) {
if !bytes.Equal(ev.Pubkey[:], events[1].Pubkey[:]) {
t.Fatalf(
"result %d has incorrect author, got %x, expected %x",
i, ev.Pubkey, events[1].Pubkey,
@ -79,7 +79,7 @@ func TestQueryForIds(t *testing.T) { @@ -79,7 +79,7 @@ func TestQueryForIds(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if ev.Kind != testKind.K {
t.Fatalf(
@ -131,16 +131,16 @@ func TestQueryForIds(t *testing.T) { @@ -131,16 +131,16 @@ func TestQueryForIds(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
// Check if the event has the tag we're looking for
var hasTag bool
for _, tg := range *ev.Tags {
if tg.Len() >= 2 && len(tg.Key()) == 1 {
if utils.FastEqual(
if bytes.Equal(
tg.Key(), testTag.Key(),
) && utils.FastEqual(tg.Value(), testTag.Value()) {
) && bytes.Equal(tg.Value(), testTag.Value()) {
hasTag = true
break
}
@ -182,7 +182,7 @@ func TestQueryForIds(t *testing.T) { @@ -182,7 +182,7 @@ func TestQueryForIds(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if ev.Kind != testKind.K {
t.Fatalf(
@ -190,7 +190,7 @@ func TestQueryForIds(t *testing.T) { @@ -190,7 +190,7 @@ func TestQueryForIds(t *testing.T) {
i, ev.Kind, testKind.K,
)
}
if !utils.FastEqual(ev.Pubkey, events[1].Pubkey) {
if !bytes.Equal(ev.Pubkey[:], events[1].Pubkey[:]) {
t.Fatalf(
"result %d has incorrect author, got %x, expected %x",
i, ev.Pubkey, events[1].Pubkey,
@ -229,7 +229,7 @@ func TestQueryForIds(t *testing.T) { @@ -229,7 +229,7 @@ func TestQueryForIds(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if ev.Kind != testEventForTag.Kind {
t.Fatalf(
@ -242,9 +242,9 @@ func TestQueryForIds(t *testing.T) { @@ -242,9 +242,9 @@ func TestQueryForIds(t *testing.T) {
var hasTag bool
for _, tg := range *ev.Tags {
if tg.Len() >= 2 && len(tg.Key()) == 1 {
if utils.FastEqual(
if bytes.Equal(
tg.Key(), testTag.Key(),
) && utils.FastEqual(tg.Value(), testTag.Value()) {
) && bytes.Equal(tg.Value(), testTag.Value()) {
hasTag = true
break
}
@ -290,7 +290,7 @@ func TestQueryForIds(t *testing.T) { @@ -290,7 +290,7 @@ func TestQueryForIds(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if ev.Kind != testEventForTag.Kind {
t.Fatalf(
@ -299,7 +299,7 @@ func TestQueryForIds(t *testing.T) { @@ -299,7 +299,7 @@ func TestQueryForIds(t *testing.T) {
)
}
if !utils.FastEqual(ev.Pubkey, testEventForTag.Pubkey) {
if !bytes.Equal(ev.Pubkey[:], testEventForTag.Pubkey[:]) {
t.Fatalf(
"result %d has incorrect author, got %x, expected %x",
i, ev.Pubkey, testEventForTag.Pubkey,
@ -310,9 +310,9 @@ func TestQueryForIds(t *testing.T) { @@ -310,9 +310,9 @@ func TestQueryForIds(t *testing.T) {
var hasTag bool
for _, tg := range *ev.Tags {
if tg.Len() >= 2 && len(tg.Key()) == 1 {
if utils.FastEqual(
if bytes.Equal(
tg.Key(), testTag.Key(),
) && utils.FastEqual(tg.Value(), testTag.Value()) {
) && bytes.Equal(tg.Value(), testTag.Value()) {
hasTag = true
break
}
@ -357,10 +357,10 @@ func TestQueryForIds(t *testing.T) { @@ -357,10 +357,10 @@ func TestQueryForIds(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if !utils.FastEqual(ev.Pubkey, testEventForTag.Pubkey) {
if !bytes.Equal(ev.Pubkey[:], testEventForTag.Pubkey[:]) {
t.Fatalf(
"result %d has incorrect author, got %x, expected %x",
i, ev.Pubkey, testEventForTag.Pubkey,
@ -371,9 +371,9 @@ func TestQueryForIds(t *testing.T) { @@ -371,9 +371,9 @@ func TestQueryForIds(t *testing.T) {
var hasTag bool
for _, tg := range *ev.Tags {
if tg.Len() >= 2 && len(tg.Key()) == 1 {
if utils.FastEqual(
if bytes.Equal(
tg.Key(), testTag.Key(),
) && utils.FastEqual(tg.Value(), testTag.Value()) {
) && bytes.Equal(tg.Value(), testTag.Value()) {
hasTag = true
break
}
@ -430,7 +430,7 @@ func TestQueryForIds(t *testing.T) { @@ -430,7 +430,7 @@ func TestQueryForIds(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
break
}

10
pkg/database/query-for-kinds-authors-tags_test.go

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
package database
import (
"bytes"
"testing"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/kind"
"git.mleku.dev/mleku/nostr/encoders/tag"
"next.orly.dev/pkg/utils"
)
func TestQueryForKindsAuthorsTags(t *testing.T) {
@ -62,7 +62,7 @@ func TestQueryForKindsAuthorsTags(t *testing.T) { @@ -62,7 +62,7 @@ func TestQueryForKindsAuthorsTags(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if ev.Kind != testKind {
t.Fatalf(
@ -71,7 +71,7 @@ func TestQueryForKindsAuthorsTags(t *testing.T) { @@ -71,7 +71,7 @@ func TestQueryForKindsAuthorsTags(t *testing.T) {
)
}
if !utils.FastEqual(ev.Pubkey, testEvent.Pubkey) {
if !bytes.Equal(ev.Pubkey[:], testEvent.Pubkey[:]) {
t.Fatalf(
"result %d has incorrect author, got %x, expected %x",
i, ev.Pubkey, testEvent.Pubkey,
@ -82,9 +82,9 @@ func TestQueryForKindsAuthorsTags(t *testing.T) { @@ -82,9 +82,9 @@ func TestQueryForKindsAuthorsTags(t *testing.T) {
var hasTag bool
for _, tg := range *ev.Tags {
if tg.Len() >= 2 && len(tg.Key()) == 1 {
if utils.FastEqual(
if bytes.Equal(
tg.Key(), testTag.Key(),
) && utils.FastEqual(tg.Value(), testTag.Value()) {
) && bytes.Equal(tg.Value(), testTag.Value()) {
hasTag = true
break
}

6
pkg/database/query-for-kinds-authors_test.go

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
package database
import (
"bytes"
"testing"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/kind"
"git.mleku.dev/mleku/nostr/encoders/tag"
"next.orly.dev/pkg/utils"
)
func TestQueryForKindsAuthors(t *testing.T) {
@ -46,7 +46,7 @@ func TestQueryForKindsAuthors(t *testing.T) { @@ -46,7 +46,7 @@ func TestQueryForKindsAuthors(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if ev.Kind != testKind.K {
t.Fatalf(
@ -54,7 +54,7 @@ func TestQueryForKindsAuthors(t *testing.T) { @@ -54,7 +54,7 @@ func TestQueryForKindsAuthors(t *testing.T) {
i, ev.Kind, testKind.K,
)
}
if !utils.FastEqual(ev.Pubkey, events[1].Pubkey) {
if !bytes.Equal(ev.Pubkey[:], events[1].Pubkey[:]) {
t.Fatalf(
"result %d has incorrect author, got %x, expected %x",
i, ev.Pubkey, events[1].Pubkey,

8
pkg/database/query-for-kinds-tags_test.go

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
package database
import (
"bytes"
"testing"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/kind"
"git.mleku.dev/mleku/nostr/encoders/tag"
"next.orly.dev/pkg/utils"
)
func TestQueryForKindsTags(t *testing.T) {
@ -58,7 +58,7 @@ func TestQueryForKindsTags(t *testing.T) { @@ -58,7 +58,7 @@ func TestQueryForKindsTags(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if ev.Kind != testKind {
t.Fatalf(
@ -71,9 +71,9 @@ func TestQueryForKindsTags(t *testing.T) { @@ -71,9 +71,9 @@ func TestQueryForKindsTags(t *testing.T) {
var hasTag bool
for _, tg := range *ev.Tags {
if tg.Len() >= 2 && len(tg.Key()) == 1 {
if utils.FastEqual(
if bytes.Equal(
tg.Key(), testTag.Key(),
) && utils.FastEqual(tg.Value(), testTag.Value()) {
) && bytes.Equal(tg.Value(), testTag.Value()) {
hasTag = true
break
}

4
pkg/database/query-for-kinds_test.go

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
package database
import (
"bytes"
"testing"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/kind"
"next.orly.dev/pkg/utils"
)
func TestQueryForKinds(t *testing.T) {
@ -41,7 +41,7 @@ func TestQueryForKinds(t *testing.T) { @@ -41,7 +41,7 @@ func TestQueryForKinds(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
if ev.Kind != testKind.K {
t.Fatalf(

8
pkg/database/query-for-tags_test.go

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
package database
import (
"bytes"
"testing"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/tag"
"next.orly.dev/pkg/utils"
)
func TestQueryForTags(t *testing.T) {
@ -52,16 +52,16 @@ func TestQueryForTags(t *testing.T) { @@ -52,16 +52,16 @@ func TestQueryForTags(t *testing.T) {
// Find the event with this ID
var found bool
for _, ev := range events {
if utils.FastEqual(result.Id, ev.ID) {
if bytes.Equal(result.Id[:], ev.ID[:]) {
found = true
// Check if the event has the tag we're looking for
var hasTag bool
for _, tg := range *ev.Tags {
if tg.Len() >= 2 && len(tg.Key()) == 1 {
if utils.FastEqual(
if bytes.Equal(
tg.Key(), testTag.Key(),
) && utils.FastEqual(tg.Value(), testTag.Value()) {
) && bytes.Equal(tg.Value(), testTag.Value()) {
hasTag = true
break
}

56
pkg/interfaces/store/store_interface.go

@ -61,39 +61,54 @@ type Accountant interface { @@ -61,39 +61,54 @@ 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.
// IdPkTs holds event reference data using fixed-size arrays for stack allocation.
// Total size: 80 bytes (32+32+8+8), fits in a cache line.
// Copies of this struct stay on the stack and are value-safe.
type IdPkTs struct {
Id []byte
Pub []byte
Ts int64
Ser uint64
Id ntypes.EventID // 32 bytes - event ID
Pub ntypes.Pubkey // 32 bytes - author pubkey
Ts int64 // 8 bytes - created_at timestamp
Ser uint64 // 8 bytes - database serial number
}
// 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)
// NewIdPkTs creates an IdPkTs from byte slices (copies into fixed arrays).
func NewIdPkTs(id, pub []byte, ts int64, ser uint64) IdPkTs {
return IdPkTs{
Id: ntypes.EventIDFromBytes(id),
Pub: ntypes.PubkeyFromBytes(pub),
Ts: ts,
Ser: ser,
}
}
// IDSlice returns the event ID as a byte slice (shares memory with array).
func (i *IdPkTs) IDSlice() []byte {
return 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)
// PubSlice returns the pubkey as a byte slice (shares memory with array).
func (i *IdPkTs) PubSlice() []byte {
return i.Pub[:]
}
// IDHex returns the event ID as a lowercase hex string.
func (i *IdPkTs) IDHex() string {
return ntypes.EventIDFromBytes(i.Id).Hex()
return i.Id.Hex()
}
// PubHex returns the pubkey as a lowercase hex string.
func (i *IdPkTs) PubHex() string {
return ntypes.PubkeyFromBytes(i.Pub).Hex()
return i.Pub.Hex()
}
// ToEventRef converts IdPkTs to an EventRef (fully stack-allocated).
// ToEventRef converts IdPkTs to an EventRef.
func (i *IdPkTs) ToEventRef() EventRef {
return NewEventRef(i.Id, i.Pub, i.Ts, i.Ser)
return EventRef{
id: i.Id,
pub: i.Pub,
ts: i.Ts,
ser: i.Ser,
}
}
// EventRef is a stack-friendly event reference using fixed-size arrays.
@ -141,12 +156,11 @@ func (r *EventRef) IDSlice() []byte { return r.id.Bytes() } @@ -141,12 +156,11 @@ 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.
// ToIdPkTs converts EventRef to IdPkTs.
func (r EventRef) ToIdPkTs() *IdPkTs {
return &IdPkTs{
Id: r.id.Copy(),
Pub: r.pub.Copy(),
Id: r.id,
Pub: r.pub,
Ts: r.ts,
Ser: r.ser,
}

175
pkg/interfaces/store/store_interface_test.go

@ -7,8 +7,8 @@ import ( @@ -7,8 +7,8 @@ import (
ntypes "git.mleku.dev/mleku/nostr/types"
)
func TestIdPkTsFixedMethods(t *testing.T) {
// Create an IdPkTs with sample data
func TestNewIdPkTs(t *testing.T) {
// Create sample data
id := make([]byte, 32)
pub := make([]byte, 32)
for i := 0; i < 32; i++ {
@ -16,29 +16,49 @@ func TestIdPkTsFixedMethods(t *testing.T) { @@ -16,29 +16,49 @@ func TestIdPkTsFixedMethods(t *testing.T) {
pub[i] = byte(i + 32)
}
ipk := &IdPkTs{
Id: id,
Pub: pub,
Ts: 1234567890,
Ser: 42,
}
ipk := NewIdPkTs(id, pub, 1234567890, 42)
// Test IDFixed returns correct data
idFixed := ipk.IDFixed()
if !bytes.Equal(idFixed[:], id) {
t.Errorf("IDFixed: got %x, want %x", idFixed[:], id)
// Test that data was copied correctly
if !bytes.Equal(ipk.Id[:], id) {
t.Errorf("Id: got %x, want %x", ipk.Id[:], id)
}
if !bytes.Equal(ipk.Pub[:], pub) {
t.Errorf("Pub: got %x, want %x", ipk.Pub[:], pub)
}
if ipk.Ts != 1234567890 {
t.Errorf("Ts: got %d, want 1234567890", ipk.Ts)
}
if ipk.Ser != 42 {
t.Errorf("Ser: got %d, want 42", ipk.Ser)
}
// Test IDFixed returns a copy
idFixed[0] = 0xFF
// Test that Id is a copy (modifying original doesn't affect struct)
id[0] = 0xFF
if ipk.Id[0] == 0xFF {
t.Error("IDFixed should return a copy, not a reference")
t.Error("NewIdPkTs should copy data, not reference")
}
}
func TestIdPkTsSliceMethods(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 + 32)
}
ipk := NewIdPkTs(id, pub, 1234567890, 42)
// Test IDSlice returns correct data
idSlice := ipk.IDSlice()
if !bytes.Equal(idSlice, id) {
t.Errorf("IDSlice: got %x, want %x", idSlice, id)
}
// Test PubFixed returns correct data
pubFixed := ipk.PubFixed()
if !bytes.Equal(pubFixed[:], pub) {
t.Errorf("PubFixed: got %x, want %x", pubFixed[:], pub)
// Test PubSlice returns correct data
pubSlice := ipk.PubSlice()
if !bytes.Equal(pubSlice, pub) {
t.Errorf("PubSlice: got %x, want %x", pubSlice, pub)
}
// Test hex methods
@ -49,6 +69,26 @@ func TestIdPkTsFixedMethods(t *testing.T) { @@ -49,6 +69,26 @@ func TestIdPkTsFixedMethods(t *testing.T) {
}
}
func TestIdPkTsCopyOnAssignment(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 + 32)
}
ipk1 := NewIdPkTs(id, pub, 1234567890, 42)
ipk2 := ipk1 // Copy
// Modify the copy
ipk2.Id[0] = 0xFF
// Original should be unchanged (arrays are copied on assignment)
if ipk1.Id[0] == 0xFF {
t.Error("IdPkTs should copy on assignment")
}
}
func TestEventRef(t *testing.T) {
id := make([]byte, 32)
pub := make([]byte, 32)
@ -103,10 +143,10 @@ func TestEventRefToIdPkTs(t *testing.T) { @@ -103,10 +143,10 @@ func TestEventRefToIdPkTs(t *testing.T) {
ipk := ref.ToIdPkTs()
// Verify conversion
if !bytes.Equal(ipk.Id, id) {
if !bytes.Equal(ipk.Id[:], id) {
t.Error("ToIdPkTs: Id mismatch")
}
if !bytes.Equal(ipk.Pub, pub) {
if !bytes.Equal(ipk.Pub[:], pub) {
t.Error("ToIdPkTs: Pub mismatch")
}
if ipk.Ts != 1234567890 {
@ -131,13 +171,7 @@ func TestIdPkTsToEventRef(t *testing.T) { @@ -131,13 +171,7 @@ func TestIdPkTsToEventRef(t *testing.T) {
pub[i] = byte(i + 100)
}
ipk := &IdPkTs{
Id: id,
Pub: pub,
Ts: 1234567890,
Ser: 42,
}
ipk := NewIdPkTs(id, pub, 1234567890, 42)
ref := ipk.ToEventRef()
// Verify conversion - need addressable values for slicing
@ -157,6 +191,23 @@ func TestIdPkTsToEventRef(t *testing.T) { @@ -157,6 +191,23 @@ func TestIdPkTsToEventRef(t *testing.T) {
}
}
func BenchmarkIdPkTsCopy(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 := NewIdPkTs(id, pub, 1234567890, 42)
b.ResetTimer()
for i := 0; i < b.N; i++ {
ipk2 := ipk // Copy (should stay on stack)
_ = ipk2
}
}
func BenchmarkEventRefCopy(b *testing.B) {
id := make([]byte, 32)
pub := make([]byte, 32)
@ -182,12 +233,7 @@ func BenchmarkIdPkTsToEventRef(b *testing.B) { @@ -182,12 +233,7 @@ func BenchmarkIdPkTsToEventRef(b *testing.B) {
pub[i] = byte(i + 100)
}
ipk := &IdPkTs{
Id: id,
Pub: pub,
Ts: 1234567890,
Ser: 42,
}
ipk := NewIdPkTs(id, pub, 1234567890, 42)
b.ResetTimer()
for i := 0; i < b.N; i++ {
@ -215,7 +261,7 @@ func BenchmarkEventRefAccess(b *testing.B) { @@ -215,7 +261,7 @@ func BenchmarkEventRefAccess(b *testing.B) {
}
}
func BenchmarkIdPkTsFixedAccess(b *testing.B) {
func BenchmarkIdPkTsAccess(b *testing.B) {
id := make([]byte, 32)
pub := make([]byte, 32)
for i := 0; i < 32; i++ {
@ -223,26 +269,55 @@ func BenchmarkIdPkTsFixedAccess(b *testing.B) { @@ -223,26 +269,55 @@ func BenchmarkIdPkTsFixedAccess(b *testing.B) {
pub[i] = byte(i + 100)
}
ipk := &IdPkTs{
Id: id,
Pub: pub,
Ts: 1234567890,
Ser: 42,
}
ipk := NewIdPkTs(id, pub, 1234567890, 42)
b.ResetTimer()
for i := 0; i < b.N; i++ {
idCopy := ipk.IDFixed()
pubCopy := ipk.PubFixed()
idCopy := ipk.Id
pubCopy := ipk.Pub
_ = idCopy
_ = pubCopy
}
}
// Ensure EventRef implements expected interface at compile time
var _ interface {
ID() ntypes.EventID
Pub() ntypes.Pubkey
Ts() int64
Ser() uint64
} = EventRef{}
// Ensure types satisfy expected size for stack allocation
func TestStructSizes(t *testing.T) {
var ipk IdPkTs
var ref EventRef
// Both should be exactly 80 bytes (32+32+8+8)
// This is not directly testable in Go, but we can verify the fields exist
_ = ipk.Id
_ = ipk.Pub
_ = ipk.Ts
_ = ipk.Ser
_ = ref
}
// Ensure ntypes.EventID and ntypes.Pubkey are used correctly
func TestNtypesCompatibility(t *testing.T) {
var id ntypes.EventID
var pub ntypes.Pubkey
// Fill with test data
for i := 0; i < 32; i++ {
id[i] = byte(i)
pub[i] = byte(i + 32)
}
// Create IdPkTs directly with ntypes
ipk := IdPkTs{
Id: id,
Pub: pub,
Ts: 1234567890,
Ser: 42,
}
// Verify
if ipk.Id != id {
t.Error("ntypes.EventID should be directly assignable to IdPkTs.Id")
}
if ipk.Pub != pub {
t.Error("ntypes.Pubkey should be directly assignable to IdPkTs.Pub")
}
}

16
pkg/neo4j/fetch-event.go

@ -336,12 +336,8 @@ RETURN e.id AS id, @@ -336,12 +336,8 @@ RETURN e.id AS id,
return nil, err
}
fidpk = &store.IdPkTs{
Id: id,
Pub: pubkey,
Ts: createdAt,
Ser: serial,
}
idpkts := store.NewIdPkTs(id, pubkey, createdAt, serial)
fidpk = &idpkts
return fidpk, nil
}
@ -421,12 +417,8 @@ RETURN e.id AS id, @@ -421,12 +417,8 @@ RETURN e.id AS id,
continue
}
fidpks = append(fidpks, &store.IdPkTs{
Id: id,
Pub: pubkey,
Ts: createdAt,
Ser: uint64(serialVal),
})
idpkts := store.NewIdPkTs(id, pubkey, createdAt, uint64(serialVal))
fidpks = append(fidpks, &idpkts)
}
return fidpks, nil

8
pkg/neo4j/query-events.go

@ -595,12 +595,8 @@ func (n *N) QueryForIds(c context.Context, f *filter.F) ( @@ -595,12 +595,8 @@ func (n *N) QueryForIds(c context.Context, f *filter.F) (
continue
}
idPkTs = append(idPkTs, &store.IdPkTs{
Id: id,
Pub: pubkey,
Ts: createdAt,
Ser: uint64(serialVal),
})
ipkts := store.NewIdPkTs(id, pubkey, createdAt, uint64(serialVal))
idPkTs = append(idPkTs, &ipkts)
}
return idPkTs, nil

9
pkg/proto/orlydb/v1/converters.go

@ -9,6 +9,7 @@ import ( @@ -9,6 +9,7 @@ import (
"git.mleku.dev/mleku/nostr/encoders/kind"
"git.mleku.dev/mleku/nostr/encoders/tag"
"git.mleku.dev/mleku/nostr/encoders/timestamp"
ntypes "git.mleku.dev/mleku/nostr/types"
"next.orly.dev/pkg/database"
indextypes "next.orly.dev/pkg/database/indexes/types"
"next.orly.dev/pkg/interfaces/store"
@ -271,8 +272,8 @@ func IdPkTsToProto(i *store.IdPkTs) *IdPkTs { @@ -271,8 +272,8 @@ func IdPkTsToProto(i *store.IdPkTs) *IdPkTs {
return nil
}
return &IdPkTs{
Id: i.Id,
Pubkey: i.Pub,
Id: i.Id[:],
Pubkey: i.Pub[:],
Timestamp: i.Ts,
Serial: i.Ser,
}
@ -284,8 +285,8 @@ func ProtoToIdPkTs(pb *IdPkTs) *store.IdPkTs { @@ -284,8 +285,8 @@ func ProtoToIdPkTs(pb *IdPkTs) *store.IdPkTs {
return nil
}
return &store.IdPkTs{
Id: pb.Id,
Pub: pb.Pubkey,
Id: ntypes.EventIDFromBytes(pb.Id),
Pub: ntypes.PubkeyFromBytes(pb.Pubkey),
Ts: pb.Timestamp,
Ser: pb.Serial,
}

2
pkg/version/version

@ -1 +1 @@ @@ -1 +1 @@
v0.56.5
v0.56.6

Loading…
Cancel
Save