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 }