bring in khatru and eventstore.
This commit is contained in:
139
eventstore/mmm/betterbinary/codec.go
Normal file
139
eventstore/mmm/betterbinary/codec.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package betterbinary
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxKind = math.MaxUint16
|
||||
MaxCreatedAt = math.MaxUint32
|
||||
MaxContentSize = math.MaxUint16
|
||||
MaxTagCount = math.MaxUint16
|
||||
MaxTagItemCount = math.MaxUint8
|
||||
MaxTagItemSize = math.MaxUint16
|
||||
)
|
||||
|
||||
func Measure(evt nostr.Event) int {
|
||||
n := 135 // static base
|
||||
|
||||
n += 2 + // tag section length
|
||||
2 + // number of tags
|
||||
len(evt.Tags)*3 // each tag offset + each tag item count
|
||||
for _, tag := range evt.Tags {
|
||||
n += len(tag) * 2 // item length for each item in this tag
|
||||
for _, item := range tag {
|
||||
n += len(item) // actual tag item
|
||||
}
|
||||
}
|
||||
|
||||
// content length and actual content
|
||||
n += 2 + len(evt.Content)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func Marshal(evt nostr.Event, buf []byte) error {
|
||||
buf[0] = 0
|
||||
|
||||
if evt.Kind > MaxKind {
|
||||
return fmt.Errorf("kind is too big: %d, max is %d", evt.Kind, MaxKind)
|
||||
}
|
||||
binary.LittleEndian.PutUint16(buf[1:3], uint16(evt.Kind))
|
||||
|
||||
if evt.CreatedAt > MaxCreatedAt {
|
||||
return fmt.Errorf("created_at is too big: %d, max is %d", evt.CreatedAt, MaxCreatedAt)
|
||||
}
|
||||
binary.LittleEndian.PutUint32(buf[3:7], uint32(evt.CreatedAt))
|
||||
|
||||
hex.Decode(buf[7:39], []byte(evt.ID))
|
||||
hex.Decode(buf[39:71], []byte(evt.PubKey))
|
||||
hex.Decode(buf[71:135], []byte(evt.Sig))
|
||||
|
||||
tagBase := 135
|
||||
// buf[135:137] (tagsSectionLength) will be set later when we know the absolute size of the tags section
|
||||
|
||||
ntags := len(evt.Tags)
|
||||
if ntags > MaxTagCount {
|
||||
return fmt.Errorf("can't encode too many tags: %d, max is %d", ntags, MaxTagCount)
|
||||
}
|
||||
binary.LittleEndian.PutUint16(buf[137:139], uint16(ntags))
|
||||
|
||||
tagOffset := 2 + 2 + ntags*2
|
||||
for t, tag := range evt.Tags {
|
||||
binary.LittleEndian.PutUint16(buf[tagBase+2+2+t*2:], uint16(tagOffset))
|
||||
|
||||
itemCount := len(tag)
|
||||
if itemCount > MaxTagItemCount {
|
||||
return fmt.Errorf("can't encode a tag with so many items: %d, max is %d", itemCount, MaxTagItemCount)
|
||||
}
|
||||
buf[tagBase+tagOffset] = uint8(itemCount)
|
||||
|
||||
itemOffset := 1
|
||||
for _, item := range tag {
|
||||
itemSize := len(item)
|
||||
if itemSize > MaxTagItemSize {
|
||||
return fmt.Errorf("tag item is too large: %d, max is %d", itemSize, MaxTagItemSize)
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint16(buf[tagBase+tagOffset+itemOffset:], uint16(itemSize))
|
||||
copy(buf[tagBase+tagOffset+itemOffset+2:], []byte(item))
|
||||
itemOffset += 2 + len(item)
|
||||
}
|
||||
tagOffset += itemOffset
|
||||
}
|
||||
|
||||
tagsSectionLength := tagOffset
|
||||
binary.LittleEndian.PutUint16(buf[tagBase:], uint16(tagsSectionLength))
|
||||
|
||||
// content
|
||||
if contentLength := len(evt.Content); contentLength > MaxContentSize {
|
||||
return fmt.Errorf("content is too large: %d, max is %d", contentLength, MaxContentSize)
|
||||
} else {
|
||||
binary.LittleEndian.PutUint16(buf[tagBase+tagsSectionLength:], uint16(contentLength))
|
||||
}
|
||||
copy(buf[tagBase+tagsSectionLength+2:], []byte(evt.Content))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Unmarshal(data []byte, evt *nostr.Event) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("failed to decode binary: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
evt.Kind = int(binary.LittleEndian.Uint16(data[1:3]))
|
||||
evt.CreatedAt = nostr.Timestamp(binary.LittleEndian.Uint32(data[3:7]))
|
||||
evt.ID = hex.EncodeToString(data[7:39])
|
||||
evt.PubKey = hex.EncodeToString(data[39:71])
|
||||
evt.Sig = hex.EncodeToString(data[71:135])
|
||||
|
||||
const tagbase = 135
|
||||
tagsSectionLength := binary.LittleEndian.Uint16(data[tagbase:])
|
||||
ntags := binary.LittleEndian.Uint16(data[tagbase+2:])
|
||||
evt.Tags = make(nostr.Tags, ntags)
|
||||
for t := range evt.Tags {
|
||||
offset := binary.LittleEndian.Uint16(data[tagbase+4+t*2:])
|
||||
nitems := int(data[tagbase+offset])
|
||||
tag := make(nostr.Tag, nitems)
|
||||
|
||||
curr := tagbase + offset + 1
|
||||
for i := range tag {
|
||||
length := binary.LittleEndian.Uint16(data[curr:])
|
||||
tag[i] = string(data[curr+2 : curr+2+length])
|
||||
curr += 2 + length
|
||||
}
|
||||
evt.Tags[t] = tag
|
||||
}
|
||||
|
||||
contentLength := binary.LittleEndian.Uint16(data[tagbase+tagsSectionLength:])
|
||||
evt.Content = string(data[tagbase+tagsSectionLength+2 : tagbase+tagsSectionLength+2+contentLength])
|
||||
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user