binary.go raw
1 package event
2
3 import (
4 "bytes"
5 "io"
6
7 "next.orly.dev/pkg/nostr/crypto/ec/schnorr"
8 "next.orly.dev/pkg/nostr/encoders/tag"
9 "next.orly.dev/pkg/nostr/encoders/varint"
10 "next.orly.dev/pkg/lol/chk"
11 )
12
13 // MarshalBinary writes a binary encoding of an event.
14 //
15 // [ 32 bytes ID ]
16 // [ 32 bytes Pubkey ]
17 // [ varint CreatedAt ]
18 // [ 2 bytes Kind ]
19 // [ varint Tags length ]
20 //
21 // [ varint tag length ]
22 // [ varint tag element length ]
23 // [ tag element data ]
24 // ...
25 //
26 // [ varint Content length ]
27 // [ 64 bytes Sig ]
28 func (ev *E) MarshalBinary(w io.Writer) {
29 _, _ = w.Write(ev.ID)
30 _, _ = w.Write(ev.Pubkey)
31 varint.Encode(w, uint64(ev.CreatedAt))
32 varint.Encode(w, uint64(ev.Kind))
33 if ev.Tags == nil {
34 varint.Encode(w, 0)
35 } else {
36 varint.Encode(w, uint64(ev.Tags.Len()))
37 for _, x := range *ev.Tags {
38 varint.Encode(w, uint64(x.Len()))
39 for _, y := range x.T {
40 varint.Encode(w, uint64(len(y)))
41 _, _ = w.Write(y)
42 }
43 }
44 }
45 varint.Encode(w, uint64(len(ev.Content)))
46 _, _ = w.Write(ev.Content)
47 _, _ = w.Write(ev.Sig)
48 }
49
50 // MarshalBinaryToBytes writes the binary encoding to a byte slice, reusing dst if provided.
51 // This is more efficient than MarshalBinary when you need the result as []byte.
52 func (ev *E) MarshalBinaryToBytes(dst []byte) []byte {
53 var buf *bytes.Buffer
54 if dst == nil {
55 // Estimate size: fixed fields + varints + tags + content
56 estimatedSize := 32 + 32 + 10 + 10 + 64 // ID + Pubkey + varints + Sig
57 if ev.Tags != nil {
58 for _, tag := range *ev.Tags {
59 estimatedSize += 10 // varint for tag length
60 for _, elem := range tag.T {
61 estimatedSize += 10 + len(elem) // varint + data
62 }
63 }
64 }
65 estimatedSize += 10 + len(ev.Content) // content varint + content
66 buf = bytes.NewBuffer(make([]byte, 0, estimatedSize))
67 } else {
68 buf = bytes.NewBuffer(dst[:0])
69 }
70 ev.MarshalBinary(buf)
71 return buf.Bytes()
72 }
73
74 func (ev *E) UnmarshalBinary(r io.Reader) (err error) {
75 ev.ID = make([]byte, 32)
76 if _, err = r.Read(ev.ID); chk.E(err) {
77 return
78 }
79 ev.Pubkey = make([]byte, 32)
80 if _, err = r.Read(ev.Pubkey); chk.E(err) {
81 return
82 }
83 var ca uint64
84 if ca, err = varint.Decode(r); chk.E(err) {
85 return
86 }
87 ev.CreatedAt = int64(ca)
88 var k uint64
89 if k, err = varint.Decode(r); chk.E(err) {
90 return
91 }
92 ev.Kind = uint16(k)
93 var nTags uint64
94 if nTags, err = varint.Decode(r); chk.E(err) {
95 return
96 }
97 if nTags == 0 {
98 ev.Tags = nil
99 } else {
100 ev.Tags = tag.NewSWithCap(int(nTags))
101 for range nTags {
102 var nField uint64
103 if nField, err = varint.Decode(r); chk.E(err) {
104 return
105 }
106 t := tag.NewWithCap(int(nField))
107 for range nField {
108 var lenField uint64
109 if lenField, err = varint.Decode(r); chk.E(err) {
110 return
111 }
112 field := make([]byte, lenField)
113 if _, err = r.Read(field); chk.E(err) {
114 return
115 }
116 t.T = append(t.T, field)
117 }
118 *ev.Tags = append(*ev.Tags, t)
119 }
120 }
121 var cLen uint64
122 if cLen, err = varint.Decode(r); chk.E(err) {
123 return
124 }
125 ev.Content = make([]byte, cLen)
126 if _, err = r.Read(ev.Content); chk.E(err) {
127 return
128 }
129 ev.Sig = make([]byte, schnorr.SignatureSize)
130 if _, err = r.Read(ev.Sig); chk.E(err) {
131 return
132 }
133 return
134 }
135