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