binary encoding and some changes to nson benchmarks.

This commit is contained in:
fiatjaf
2023-11-02 15:28:01 -03:00
parent 1789d43d51
commit 4c72e16f3e
7 changed files with 484 additions and 29 deletions

18
binary/README.md Normal file
View File

@@ -0,0 +1,18 @@
# The simplest binary encoding for Nostr events
Some benchmarks:
goos: linux
goarch: amd64
pkg: github.com/nbd-wtf/go-nostr/binary
cpu: AMD Ryzen 3 3200G with Radeon Vega Graphics
BenchmarkBinaryEncoding/easyjson.Marshal-4 24488 53274 ns/op 35191 B/op 102 allocs/op
BenchmarkBinaryEncoding/binary.Marshal-4 5066 218284 ns/op 1282116 B/op 88 allocs/op
BenchmarkBinaryEncoding/binary.MarshalBinary-4 5743 191603 ns/op 1277763 B/op 37 allocs/op
BenchmarkBinaryDecoding/easyjson.Unmarshal-4 32701 38647 ns/op 45832 B/op 124 allocs/op
BenchmarkBinaryDecoding/binary.Unmarshal-4 85705 14249 ns/op 25488 B/op 141 allocs/op
BenchmarkBinaryDecoding/binary.UnmarshalBinary-4 213438 5451 ns/op 16784 B/op 39 allocs/op
BenchmarkBinaryDecoding/easyjson.Unmarshal+sig-4 307 3971993 ns/op 131639 B/op 404 allocs/op
BenchmarkBinaryDecoding/binary.Unmarshal+sig-4 310 3924042 ns/op 111277 B/op 421 allocs/op
PASS
ok github.com/nbd-wtf/go-nostr/binary 11.444s

76
binary/binary.go Normal file
View File

@@ -0,0 +1,76 @@
package binary
import (
"encoding/binary"
"fmt"
"github.com/nbd-wtf/go-nostr"
)
func UnmarshalBinary(data []byte, evt *Event) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("failed to decode leaner: %v", r)
}
}()
copy(evt.ID[:], data[0:32])
copy(evt.PubKey[:], data[32:64])
copy(evt.Sig[:], data[64:128])
evt.CreatedAt = nostr.Timestamp(binary.BigEndian.Uint32(data[128:132]))
evt.Kind = binary.BigEndian.Uint16(data[132:134])
contentLength := int(binary.BigEndian.Uint16(data[134:136]))
evt.Content = string(data[136 : 136+contentLength])
curr := 136 + contentLength
ntags := int(data[curr])
evt.Tags = make(nostr.Tags, ntags)
for t := range evt.Tags {
curr = curr + 1
nItems := int(data[curr])
tag := make(nostr.Tag, nItems)
for i := range tag {
curr = curr + 1
itemSize := int(binary.BigEndian.Uint16(data[curr : curr+2]))
itemStart := curr + 2
itemEnd := itemStart + itemSize
item := string(data[itemStart:itemEnd])
tag[i] = item
curr = itemEnd
}
evt.Tags[t] = tag
}
return err
}
func MarshalBinary(evt *Event) []byte {
content := []byte(evt.Content)
buf := make([]byte, 32+32+64+4+2+2+len(content)+65536 /* blergh */)
copy(buf[0:32], evt.ID[:])
copy(buf[32:64], evt.PubKey[:])
copy(buf[64:128], evt.Sig[:])
binary.BigEndian.PutUint32(buf[128:132], uint32(evt.CreatedAt))
binary.BigEndian.PutUint16(buf[132:134], evt.Kind)
binary.BigEndian.PutUint16(buf[134:136], uint16(len(content)))
copy(buf[136:], content)
curr := 136 + len(content)
buf[curr] = uint8(len(evt.Tags))
for _, tag := range evt.Tags {
curr++
buf[curr] = uint8(len(tag))
for _, item := range tag {
curr++
itemb := []byte(item)
itemSize := len(itemb)
binary.BigEndian.PutUint16(buf[curr:curr+2], uint16(itemSize))
itemEnd := curr + 2 + itemSize
copy(buf[curr+2:itemEnd], itemb)
curr = itemEnd
}
}
buf = buf[0 : curr+1]
return buf
}

239
binary/binary_test.go Normal file

File diff suppressed because one or more lines are too long

44
binary/event.go Normal file
View File

@@ -0,0 +1,44 @@
package binary
import (
"encoding/hex"
"github.com/nbd-wtf/go-nostr"
)
type Event struct {
PubKey [32]byte
Sig [64]byte
ID [32]byte
Kind uint16
CreatedAt nostr.Timestamp
Content string
Tags nostr.Tags
}
func BinaryEvent(evt *nostr.Event) *Event {
bevt := Event{
Tags: evt.Tags,
Content: evt.Content,
Kind: uint16(evt.Kind),
CreatedAt: evt.CreatedAt,
}
hex.Decode(bevt.ID[:], []byte(evt.ID))
hex.Decode(bevt.PubKey[:], []byte(evt.PubKey))
hex.Decode(bevt.Sig[:], []byte(evt.Sig))
return &bevt
}
func (bevt *Event) ToNormalEvent() *nostr.Event {
return &nostr.Event{
Tags: bevt.Tags,
Content: bevt.Content,
Kind: int(bevt.Kind),
CreatedAt: bevt.CreatedAt,
ID: hex.EncodeToString(bevt.ID[:]),
PubKey: hex.EncodeToString(bevt.PubKey[:]),
Sig: hex.EncodeToString(bevt.Sig[:]),
}
}

79
binary/hybrid.go Normal file
View File

@@ -0,0 +1,79 @@
package binary
import (
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/nbd-wtf/go-nostr"
)
func Unmarshal(data []byte, evt *nostr.Event) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("failed to decode leaner: %v", r)
}
}()
evt.ID = hex.EncodeToString(data[0:32])
evt.PubKey = hex.EncodeToString(data[32:64])
evt.Sig = hex.EncodeToString(data[64:128])
evt.CreatedAt = nostr.Timestamp(binary.BigEndian.Uint32(data[128:132]))
evt.Kind = int(binary.BigEndian.Uint16(data[132:134]))
contentLength := int(binary.BigEndian.Uint16(data[134:136]))
evt.Content = string(data[136 : 136+contentLength])
curr := 136 + contentLength
ntags := int(data[curr])
evt.Tags = make(nostr.Tags, ntags)
for t := range evt.Tags {
curr = curr + 1
nItems := int(data[curr])
tag := make(nostr.Tag, nItems)
for i := range tag {
curr = curr + 1
itemSize := int(binary.BigEndian.Uint16(data[curr : curr+2]))
itemStart := curr + 2
itemEnd := itemStart + itemSize
item := string(data[itemStart:itemEnd])
tag[i] = item
curr = itemEnd
}
evt.Tags[t] = tag
}
return err
}
func Marshal(evt *nostr.Event) ([]byte, error) {
content := []byte(evt.Content)
buf := make([]byte, 32+32+64+4+2+2+len(content)+65536 /* blergh */)
hex.Decode(buf[0:32], []byte(evt.ID))
hex.Decode(buf[32:64], []byte(evt.PubKey))
hex.Decode(buf[64:128], []byte(evt.Sig))
binary.BigEndian.PutUint32(buf[128:132], uint32(evt.CreatedAt))
binary.BigEndian.PutUint16(buf[132:134], uint16(evt.Kind))
binary.BigEndian.PutUint16(buf[134:136], uint16(len(content)))
copy(buf[136:], content)
curr := 136 + len(content)
buf[curr] = uint8(len(evt.Tags))
for _, tag := range evt.Tags {
curr++
buf[curr] = uint8(len(tag))
for _, item := range tag {
curr++
itemb := []byte(item)
itemSize := len(itemb)
binary.BigEndian.PutUint16(buf[curr:curr+2], uint16(itemSize))
itemEnd := curr + 2 + itemSize
copy(buf[curr+2:itemEnd], itemb)
curr = itemEnd
}
}
buf = buf[0 : curr+1]
return buf, nil
}