- Introduced benchmark tests for JSON and binary marshaling/unmarshaling, canonical encoding, and ID generation to assess performance. - Implemented optimizations to reduce memory allocations and CPU processing time across various encoding methods. - Enhanced `Marshal`, `ToCanonical`, and `MarshalBinary` methods with pre-allocation strategies to minimize reallocations. - Added handling for nil tags to avoid unnecessary allocations during binary encoding. - Documented performance improvements in the new PERFORMANCE_REPORT.md file, highlighting significant reductions in execution time and memory usage.
135 lines
3.1 KiB
Go
135 lines
3.1 KiB
Go
package event
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
|
|
"lol.mleku.dev/chk"
|
|
"next.orly.dev/pkg/crypto/ec/schnorr"
|
|
"next.orly.dev/pkg/encoders/tag"
|
|
"next.orly.dev/pkg/encoders/varint"
|
|
)
|
|
|
|
// MarshalBinary writes a binary encoding of an event.
|
|
//
|
|
// [ 32 bytes ID ]
|
|
// [ 32 bytes Pubkey ]
|
|
// [ varint CreatedAt ]
|
|
// [ 2 bytes Kind ]
|
|
// [ varint Tags length ]
|
|
//
|
|
// [ varint tag length ]
|
|
// [ varint tag element length ]
|
|
// [ tag element data ]
|
|
// ...
|
|
//
|
|
// [ varint Content length ]
|
|
// [ 64 bytes Sig ]
|
|
func (ev *E) MarshalBinary(w io.Writer) {
|
|
_, _ = w.Write(ev.ID)
|
|
_, _ = w.Write(ev.Pubkey)
|
|
varint.Encode(w, uint64(ev.CreatedAt))
|
|
varint.Encode(w, uint64(ev.Kind))
|
|
if ev.Tags == nil {
|
|
varint.Encode(w, 0)
|
|
} else {
|
|
varint.Encode(w, uint64(ev.Tags.Len()))
|
|
for _, x := range *ev.Tags {
|
|
varint.Encode(w, uint64(x.Len()))
|
|
for _, y := range x.T {
|
|
varint.Encode(w, uint64(len(y)))
|
|
_, _ = w.Write(y)
|
|
}
|
|
}
|
|
}
|
|
varint.Encode(w, uint64(len(ev.Content)))
|
|
_, _ = w.Write(ev.Content)
|
|
_, _ = w.Write(ev.Sig)
|
|
}
|
|
|
|
// MarshalBinaryToBytes writes the binary encoding to a byte slice, reusing dst if provided.
|
|
// This is more efficient than MarshalBinary when you need the result as []byte.
|
|
func (ev *E) MarshalBinaryToBytes(dst []byte) []byte {
|
|
var buf *bytes.Buffer
|
|
if dst == nil {
|
|
// Estimate size: fixed fields + varints + tags + content
|
|
estimatedSize := 32 + 32 + 10 + 10 + 64 // ID + Pubkey + varints + Sig
|
|
if ev.Tags != nil {
|
|
for _, tag := range *ev.Tags {
|
|
estimatedSize += 10 // varint for tag length
|
|
for _, elem := range tag.T {
|
|
estimatedSize += 10 + len(elem) // varint + data
|
|
}
|
|
}
|
|
}
|
|
estimatedSize += 10 + len(ev.Content) // content varint + content
|
|
buf = bytes.NewBuffer(make([]byte, 0, estimatedSize))
|
|
} else {
|
|
buf = bytes.NewBuffer(dst[:0])
|
|
}
|
|
ev.MarshalBinary(buf)
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func (ev *E) UnmarshalBinary(r io.Reader) (err error) {
|
|
ev.ID = make([]byte, 32)
|
|
if _, err = r.Read(ev.ID); chk.E(err) {
|
|
return
|
|
}
|
|
ev.Pubkey = make([]byte, 32)
|
|
if _, err = r.Read(ev.Pubkey); chk.E(err) {
|
|
return
|
|
}
|
|
var ca uint64
|
|
if ca, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
ev.CreatedAt = int64(ca)
|
|
var k uint64
|
|
if k, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
ev.Kind = uint16(k)
|
|
var nTags uint64
|
|
if nTags, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
if nTags == 0 {
|
|
ev.Tags = nil
|
|
} else {
|
|
ev.Tags = tag.NewSWithCap(int(nTags))
|
|
for range nTags {
|
|
var nField uint64
|
|
if nField, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
t := tag.NewWithCap(int(nField))
|
|
for range nField {
|
|
var lenField uint64
|
|
if lenField, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
field := make([]byte, lenField)
|
|
if _, err = r.Read(field); chk.E(err) {
|
|
return
|
|
}
|
|
t.T = append(t.T, field)
|
|
}
|
|
*ev.Tags = append(*ev.Tags, t)
|
|
}
|
|
}
|
|
var cLen uint64
|
|
if cLen, err = varint.Decode(r); chk.E(err) {
|
|
return
|
|
}
|
|
ev.Content = make([]byte, cLen)
|
|
if _, err = r.Read(ev.Content); chk.E(err) {
|
|
return
|
|
}
|
|
ev.Sig = make([]byte, schnorr.SignatureSize)
|
|
if _, err = r.Read(ev.Sig); chk.E(err) {
|
|
return
|
|
}
|
|
return
|
|
}
|