package event import ( "bytes" "io" "crypto/secp256k1" "git.smesh.lol/nostr/pkg/tag" "git.smesh.lol/nostr/pkg/varint" "git.smesh.lol/nostr/pkg/lol/chk" ) 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.T { 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) } func (ev *E) MarshalBinaryToBytes(dst []byte) (out []byte) { var buf *bytes.Buffer if dst == nil { size := 32 + 32 + 10 + 10 + 64 if ev.Tags != nil { for _, t := range ev.Tags.T { size += 10 for _, elem := range t.T { size += 10 + len(elem) } } } size += 10 + len(ev.Content) b := []byte{:0:size} buf = bytes.NewBuffer(b) } else { buf = bytes.NewBuffer(dst[:0]) } ev.MarshalBinary(buf) return buf.Bytes() } // readFull reads exactly len(buf) bytes. io.Reader.Read may return a short // count with a nil error, so a truncated frame used to decode into a // zero-padded field and look like a valid event; io.ReadFull turns that into // ErrUnexpectedEOF. func readFull(r io.Reader, buf []byte) (err error) { if _, err = io.ReadFull(r, buf); chk.E(err) { return } return } func (ev *E) UnmarshalBinary(r io.Reader) (err error) { ev.ID = []byte{:32} if err = readFull(r, ev.ID); chk.E(err) { return } ev.Pubkey = []byte{:32} if err = readFull(r, 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 } // Declared outside the loops: a declaration in the body of a self-mutating // method allocates in the sovereign arena on every iteration. var nField uint64 var t *tag.T var lenField uint64 var field []byte if nTags == 0 { ev.Tags = nil } else { ev.Tags = tag.NewSWithCap(int32(nTags)) for range nTags { if nField, err = varint.Decode(r); chk.E(err) { return } t = tag.NewWithCap(int32(nField)) for range nField { if lenField, err = varint.Decode(r); chk.E(err) { return } field = []byte{:lenField} if err = readFull(r, field); chk.E(err) { return } t.T = push(t.T, field) } ev.Tags.T = push(ev.Tags.T, t) } } var cLen uint64 if cLen, err = varint.Decode(r); chk.E(err) { return } ev.Content = []byte{:cLen} if err = readFull(r, ev.Content); chk.E(err) { return } ev.Sig = []byte{:secp256k1.SignatureSize} if err = readFull(r, ev.Sig); chk.E(err) { return } return }