mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-06-30 02:20:37 +02:00
sonic ast visitor approach. now we're getting faster.
BenchmarkParseMessage/relay/jsonstdlib-4 206 8630635 ns/op BenchmarkParseMessage/relay/easyjson-4 278 4311793 ns/op BenchmarkParseMessage/relay/simdjson-4 422 2943387 ns/op BenchmarkParseMessage/relay/sonic-4 849 1576884 ns/op BenchmarkParseMessage/client/jsonstdlib-4 196 6140585 ns/op BenchmarkParseMessage/client/easyjson-4 385 2826706 ns/op BenchmarkParseMessage/client/simdjson-4 405 2628675 ns/op BenchmarkParseMessage/client/sonic-4 552 2413731 ns/op
This commit is contained in:
@ -42,10 +42,9 @@ func BenchmarkParseMessage(b *testing.B) {
|
||||
})
|
||||
|
||||
b.Run("sonic", func(b *testing.B) {
|
||||
smp := SonicMessageParser{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, msg := range messages {
|
||||
_, _ = smp.ParseMessage(msg)
|
||||
_, _ = ParseMessageSonic(msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -2,184 +2,472 @@ package nostr
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
stdlibjson "encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/bytedance/sonic/ast"
|
||||
)
|
||||
|
||||
type SonicMessageParser struct{}
|
||||
type sonicParserPosition int
|
||||
|
||||
func (smp *SonicMessageParser) ParseMessage(message []byte) (Envelope, error) {
|
||||
var err error
|
||||
const (
|
||||
inEnvelope sonicParserPosition = iota
|
||||
|
||||
tlarr, _ := ast.NewParser(string(message)).Parse()
|
||||
label, _ := tlarr.Index(0).StrictString()
|
||||
inEvent
|
||||
inReq
|
||||
inOk
|
||||
inEose
|
||||
inCount
|
||||
inAuth
|
||||
inClose
|
||||
inClosed
|
||||
inNotice
|
||||
|
||||
var v Envelope
|
||||
switch label {
|
||||
case "EVENT":
|
||||
env := &EventEnvelope{}
|
||||
sndN := tlarr.Index(1)
|
||||
var evtN *ast.Node
|
||||
switch sndN.TypeSafe() {
|
||||
case ast.V_STRING:
|
||||
subId, _ := sndN.StrictString()
|
||||
env.SubscriptionID = &subId
|
||||
evtN = tlarr.Index(2)
|
||||
case ast.V_OBJECT:
|
||||
evtN = sndN
|
||||
}
|
||||
err = eventFromSonicAst(&env.Event, evtN)
|
||||
v = env
|
||||
case "REQ":
|
||||
env := &ReqEnvelope{}
|
||||
nodes, _ := tlarr.ArrayUseNode()
|
||||
env.SubscriptionID, _ = nodes[1].StrictString()
|
||||
env.Filters = make(Filters, len(nodes)-2)
|
||||
for i, node := range nodes[2:] {
|
||||
err = filterFromSonicAst(&env.Filters[i], &node)
|
||||
}
|
||||
v = env
|
||||
case "COUNT":
|
||||
env := &CountEnvelope{}
|
||||
env.SubscriptionID, _ = tlarr.Index(1).StrictString()
|
||||
trdN := tlarr.Index(2)
|
||||
if countN := trdN.Get("count"); countN.Exists() {
|
||||
count, _ := countN.Int64()
|
||||
env.Count = &count
|
||||
hll, _ := trdN.Get("hll").StrictString()
|
||||
if len(hll) == 512 {
|
||||
env.HyperLogLog, _ = hex.DecodeString(hll)
|
||||
}
|
||||
} else {
|
||||
err = filterFromSonicAst(&env.Filter, trdN)
|
||||
}
|
||||
v = env
|
||||
case "NOTICE":
|
||||
notice, _ := tlarr.Index(1).StrictString()
|
||||
env := NoticeEnvelope(notice)
|
||||
v = &env
|
||||
case "EOSE":
|
||||
subId, _ := tlarr.Index(1).StrictString()
|
||||
env := EOSEEnvelope(subId)
|
||||
v = &env
|
||||
case "OK":
|
||||
env := &OKEnvelope{}
|
||||
env.EventID, _ = tlarr.Index(1).StrictString()
|
||||
env.OK, _ = tlarr.Index(2).Bool()
|
||||
env.Reason, _ = tlarr.Index(3).StrictString()
|
||||
v = env
|
||||
case "AUTH":
|
||||
env := &AuthEnvelope{}
|
||||
sndN := tlarr.Index(1)
|
||||
switch sndN.TypeSafe() {
|
||||
case ast.V_STRING:
|
||||
challenge, _ := sndN.StrictString()
|
||||
env.Challenge = &challenge
|
||||
case ast.V_OBJECT:
|
||||
err = eventFromSonicAst(&env.Event, sndN)
|
||||
}
|
||||
v = env
|
||||
case "CLOSED":
|
||||
env := &ClosedEnvelope{}
|
||||
env.SubscriptionID, _ = tlarr.Index(1).StrictString()
|
||||
env.Reason, _ = tlarr.Index(2).StrictString()
|
||||
v = env
|
||||
case "CLOSE":
|
||||
reason, _ := tlarr.Index(1).StrictString()
|
||||
env := CloseEnvelope(reason)
|
||||
v = &env
|
||||
inFilterObject
|
||||
inEventObject
|
||||
inCountObject
|
||||
|
||||
inSince
|
||||
inLimit
|
||||
inUntil
|
||||
inIds
|
||||
inAuthors
|
||||
inKinds
|
||||
inSearch
|
||||
inAFilterTag
|
||||
|
||||
inId
|
||||
inCreatedAt
|
||||
inKind
|
||||
inContent
|
||||
inPubkey
|
||||
inSig
|
||||
inTags
|
||||
inAnEventTag
|
||||
)
|
||||
|
||||
func (spp sonicParserPosition) String() string {
|
||||
switch spp {
|
||||
case inEnvelope:
|
||||
return "inEnvelope"
|
||||
case inEvent:
|
||||
return "inEvent"
|
||||
case inReq:
|
||||
return "inReq"
|
||||
case inOk:
|
||||
return "inOk"
|
||||
case inEose:
|
||||
return "inEose"
|
||||
case inCount:
|
||||
return "inCount"
|
||||
case inAuth:
|
||||
return "inAuth"
|
||||
case inClose:
|
||||
return "inClose"
|
||||
case inClosed:
|
||||
return "inClosed"
|
||||
case inNotice:
|
||||
return "inNotice"
|
||||
case inFilterObject:
|
||||
return "inFilterObject"
|
||||
case inEventObject:
|
||||
return "inEventObject"
|
||||
case inCountObject:
|
||||
return "inCountObject"
|
||||
case inSince:
|
||||
return "inSince"
|
||||
case inLimit:
|
||||
return "inLimit"
|
||||
case inUntil:
|
||||
return "inUntil"
|
||||
case inIds:
|
||||
return "inIds"
|
||||
case inAuthors:
|
||||
return "inAuthors"
|
||||
case inKinds:
|
||||
return "inKinds"
|
||||
case inAFilterTag:
|
||||
return "inAFilterTag"
|
||||
case inId:
|
||||
return "inId"
|
||||
case inCreatedAt:
|
||||
return "inCreatedAt"
|
||||
case inKind:
|
||||
return "inKind"
|
||||
case inContent:
|
||||
return "inContent"
|
||||
case inPubkey:
|
||||
return "inPubkey"
|
||||
case inSig:
|
||||
return "inSig"
|
||||
case inTags:
|
||||
return "inTags"
|
||||
case inAnEventTag:
|
||||
return "inAnEventTag"
|
||||
default:
|
||||
return nil, UnknownLabel
|
||||
return "<unexpected-spp>"
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
func eventFromSonicAst(evt *Event, node *ast.Node) error {
|
||||
evt.ID, _ = node.Get("id").StrictString()
|
||||
evt.PubKey, _ = node.Get("pubkey").StrictString()
|
||||
evt.Content, _ = node.Get("content").StrictString()
|
||||
evt.Sig, _ = node.Get("sig").StrictString()
|
||||
kind, _ := node.Get("kind").Int64()
|
||||
evt.Kind = int(kind)
|
||||
createdAt, _ := node.Get("created_at").Int64()
|
||||
evt.CreatedAt = Timestamp(createdAt)
|
||||
tagsN, err := node.Get("tags").ArrayUseNode()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid tags: %w", err)
|
||||
type SonicMessageParser struct {
|
||||
event *EventEnvelope
|
||||
req *ReqEnvelope
|
||||
ok *OKEnvelope
|
||||
eose *EOSEEnvelope
|
||||
count *CountEnvelope
|
||||
auth *AuthEnvelope
|
||||
close *CloseEnvelope
|
||||
closed *ClosedEnvelope
|
||||
notice *NoticeEnvelope
|
||||
|
||||
whereWeAre sonicParserPosition
|
||||
|
||||
currentEvent *Event
|
||||
currentEventTag Tag
|
||||
|
||||
currentFilter *Filter
|
||||
currentFilterTagList []string
|
||||
currentFilterTagName string
|
||||
|
||||
mainEnvelope Envelope
|
||||
}
|
||||
|
||||
func (smp *SonicMessageParser) OnArrayBegin(capacity int) error {
|
||||
// fmt.Println("***", "OnArrayBegin", "==", smp.whereWeAre)
|
||||
|
||||
switch smp.whereWeAre {
|
||||
case inTags:
|
||||
if smp.currentEvent.Tags == nil {
|
||||
smp.currentEvent.Tags = make(Tags, 0, 10)
|
||||
smp.currentEventTag = make(Tag, 0, 20)
|
||||
} else {
|
||||
smp.whereWeAre = inAnEventTag
|
||||
}
|
||||
case inAFilterTag:
|
||||
// we have already created this
|
||||
}
|
||||
evt.Tags = make(Tags, len(tagsN))
|
||||
for i, tagN := range tagsN {
|
||||
itemsN, err := tagN.ArrayUseNode()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid tag: %w", err)
|
||||
}
|
||||
tag := make(Tag, len(itemsN))
|
||||
|
||||
for j, itemN := range itemsN {
|
||||
tag[j], _ = itemN.StrictString()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
evt.Tags[i] = tag
|
||||
func (smp *SonicMessageParser) OnArrayEnd() error {
|
||||
// fmt.Println("***", "OnArrayEnd", "==", smp.whereWeAre)
|
||||
|
||||
switch smp.whereWeAre {
|
||||
// envelopes
|
||||
case inEvent:
|
||||
smp.mainEnvelope = smp.event
|
||||
case inReq:
|
||||
smp.mainEnvelope = smp.req
|
||||
case inOk:
|
||||
smp.mainEnvelope = smp.ok
|
||||
case inEose:
|
||||
smp.mainEnvelope = smp.eose
|
||||
case inCount:
|
||||
smp.mainEnvelope = smp.count
|
||||
case inAuth:
|
||||
smp.mainEnvelope = smp.auth
|
||||
case inClose:
|
||||
smp.mainEnvelope = smp.close
|
||||
case inClosed:
|
||||
smp.mainEnvelope = smp.closed
|
||||
case inNotice:
|
||||
smp.mainEnvelope = smp.notice
|
||||
|
||||
// filter object properties
|
||||
case inIds, inAuthors, inKinds, inSearch:
|
||||
smp.whereWeAre = inFilterObject
|
||||
case inAFilterTag:
|
||||
smp.currentFilter.Tags[smp.currentFilterTagName] = smp.currentFilterTagList
|
||||
// reuse the same underlying slice because we know nothing else will be appended to it
|
||||
smp.currentFilterTagList = smp.currentFilterTagList[len(smp.currentFilterTagList):]
|
||||
smp.whereWeAre = inFilterObject
|
||||
|
||||
// event object properties
|
||||
case inAnEventTag:
|
||||
smp.currentEvent.Tags = append(smp.currentEvent.Tags, smp.currentEventTag)
|
||||
// reuse the same underlying slice because we know nothing else will be appended to it
|
||||
smp.currentEventTag = smp.currentEventTag[len(smp.currentEventTag):]
|
||||
smp.whereWeAre = inTags
|
||||
case inTags:
|
||||
smp.whereWeAre = inEventObject
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unexpected array end at %v", smp.whereWeAre)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterFromSonicAst(filter *Filter, node *ast.Node) error {
|
||||
var err error
|
||||
func (smp *SonicMessageParser) OnObjectBegin(capacity int) error {
|
||||
// fmt.Println("***", "OnObjectBegin", "==", smp.whereWeAre)
|
||||
|
||||
node.ForEach(func(path ast.Sequence, node *ast.Node) bool {
|
||||
switch *path.Key {
|
||||
case "limit":
|
||||
limit, _ := node.Int64()
|
||||
filter.Limit = int(limit)
|
||||
filter.LimitZero = filter.Limit == 0
|
||||
case "since":
|
||||
since, _ := node.Int64()
|
||||
filter.Since = (*Timestamp)(&since)
|
||||
case "until":
|
||||
until, _ := node.Int64()
|
||||
filter.Until = (*Timestamp)(&until)
|
||||
case "search":
|
||||
filter.Search, _ = node.StrictString()
|
||||
case "ids":
|
||||
idsN, _ := node.ArrayUseNode()
|
||||
filter.IDs = make([]string, len(idsN))
|
||||
for i, idN := range idsN {
|
||||
filter.IDs[i], _ = idN.StrictString()
|
||||
}
|
||||
case "authors":
|
||||
authorsN, _ := node.ArrayUseNode()
|
||||
filter.Authors = make([]string, len(authorsN))
|
||||
for i, authorN := range authorsN {
|
||||
filter.Authors[i], _ = authorN.StrictString()
|
||||
}
|
||||
case "kinds":
|
||||
kindsN, _ := node.ArrayUseNode()
|
||||
filter.Kinds = make([]int, len(kindsN))
|
||||
for i, kindN := range kindsN {
|
||||
kind, _ := kindN.Int64()
|
||||
filter.Kinds[i] = int(kind)
|
||||
}
|
||||
switch smp.whereWeAre {
|
||||
case inEvent:
|
||||
smp.whereWeAre = inEventObject
|
||||
smp.currentEvent = &Event{}
|
||||
case inAuth:
|
||||
smp.whereWeAre = inEventObject
|
||||
smp.currentEvent = &Event{}
|
||||
case inReq:
|
||||
smp.whereWeAre = inFilterObject
|
||||
smp.currentFilter = &Filter{}
|
||||
case inCount:
|
||||
// set this temporarily, we will switch to a filterObject if we see "count" or "hll"
|
||||
smp.whereWeAre = inFilterObject
|
||||
smp.currentFilter = &Filter{}
|
||||
default:
|
||||
return fmt.Errorf("unexpected object begin at %v", smp.whereWeAre)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (smp *SonicMessageParser) OnObjectKey(key string) error {
|
||||
// fmt.Println("***", "OnObjectKey", key, "==", smp.whereWeAre)
|
||||
|
||||
switch smp.whereWeAre {
|
||||
case inEventObject:
|
||||
switch key {
|
||||
case "id":
|
||||
smp.whereWeAre = inId
|
||||
case "sig":
|
||||
smp.whereWeAre = inSig
|
||||
case "pubkey":
|
||||
smp.whereWeAre = inPubkey
|
||||
case "content":
|
||||
smp.whereWeAre = inContent
|
||||
case "created_at":
|
||||
smp.whereWeAre = inCreatedAt
|
||||
case "kind":
|
||||
smp.whereWeAre = inKind
|
||||
case "tags":
|
||||
smp.whereWeAre = inTags
|
||||
default:
|
||||
if len(*path.Key) > 1 && (*path.Key)[0] == '#' {
|
||||
if filter.Tags == nil {
|
||||
filter.Tags = make(TagMap, 2)
|
||||
return fmt.Errorf("unexpected event attr %s", key)
|
||||
}
|
||||
case inFilterObject:
|
||||
switch key {
|
||||
case "limit":
|
||||
smp.whereWeAre = inLimit
|
||||
case "since":
|
||||
smp.whereWeAre = inSince
|
||||
case "until":
|
||||
smp.whereWeAre = inUntil
|
||||
case "ids":
|
||||
smp.whereWeAre = inIds
|
||||
smp.currentFilter.IDs = make([]string, 0, 25)
|
||||
case "authors":
|
||||
smp.whereWeAre = inAuthors
|
||||
smp.currentFilter.Authors = make([]string, 0, 25)
|
||||
case "kinds":
|
||||
smp.whereWeAre = inKinds
|
||||
smp.currentFilter.IDs = make([]string, 0, 12)
|
||||
case "search":
|
||||
smp.whereWeAre = inSearch
|
||||
case "count", "hll":
|
||||
// oops, switch to a countObject
|
||||
smp.whereWeAre = inCountObject
|
||||
default:
|
||||
if len(key) > 1 && key[0] == '#' {
|
||||
if smp.currentFilter.Tags == nil {
|
||||
smp.currentFilter.Tags = make(TagMap, 1)
|
||||
smp.currentFilterTagList = make([]string, 0, 25)
|
||||
}
|
||||
tagsN, _ := node.ArrayUseNode()
|
||||
tags := make([]string, len(tagsN))
|
||||
for i, authorN := range tagsN {
|
||||
tags[i], _ = authorN.StrictString()
|
||||
}
|
||||
filter.Tags[(*path.Key)[1:]] = tags
|
||||
smp.whereWeAre = inAFilterTag
|
||||
smp.currentFilterTagName = key[1:]
|
||||
} else {
|
||||
err = fmt.Errorf("unexpected field '%s'", *path.Key)
|
||||
return false
|
||||
return fmt.Errorf("unexpected filter attr %s", key)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
case inCountObject:
|
||||
// we'll judge by the shape of the value so ignore this
|
||||
default:
|
||||
return fmt.Errorf("unexpected object key %s at %s", key, smp.whereWeAre)
|
||||
}
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (smp *SonicMessageParser) OnObjectEnd() error {
|
||||
// fmt.Println("***", "OnObjectEnd", "==", smp.whereWeAre)
|
||||
|
||||
switch smp.whereWeAre {
|
||||
case inEventObject:
|
||||
if smp.event != nil {
|
||||
smp.event.Event = *smp.currentEvent
|
||||
smp.whereWeAre = inEvent
|
||||
} else {
|
||||
smp.auth.Event = *smp.currentEvent
|
||||
smp.whereWeAre = inAuth
|
||||
}
|
||||
case inFilterObject:
|
||||
if smp.req != nil {
|
||||
smp.req.Filters = append(smp.req.Filters, *smp.currentFilter)
|
||||
smp.whereWeAre = inReq
|
||||
} else {
|
||||
smp.count.Filter = *smp.currentFilter
|
||||
smp.whereWeAre = inCount
|
||||
}
|
||||
case inCountObject:
|
||||
smp.whereWeAre = inCount
|
||||
default:
|
||||
return fmt.Errorf("unexpected object end at %s", smp.whereWeAre)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (smp *SonicMessageParser) OnString(v string) error {
|
||||
// fmt.Println("***", "OnString", v, "==", smp.whereWeAre)
|
||||
|
||||
switch smp.whereWeAre {
|
||||
case inEnvelope:
|
||||
switch v {
|
||||
case "EVENT":
|
||||
smp.event = &EventEnvelope{}
|
||||
smp.whereWeAre = inEvent
|
||||
case "REQ":
|
||||
smp.req = &ReqEnvelope{Filters: make(Filters, 0, 1)}
|
||||
smp.whereWeAre = inReq
|
||||
case "OK":
|
||||
smp.ok = &OKEnvelope{}
|
||||
smp.whereWeAre = inOk
|
||||
case "EOSE":
|
||||
smp.whereWeAre = inEose
|
||||
case "COUNT":
|
||||
smp.count = &CountEnvelope{}
|
||||
smp.whereWeAre = inCount
|
||||
case "AUTH":
|
||||
smp.auth = &AuthEnvelope{}
|
||||
smp.whereWeAre = inAuth
|
||||
case "CLOSE":
|
||||
smp.whereWeAre = inClose
|
||||
case "CLOSED":
|
||||
smp.closed = &ClosedEnvelope{}
|
||||
smp.whereWeAre = inClosed
|
||||
case "NOTICE":
|
||||
smp.whereWeAre = inNotice
|
||||
}
|
||||
|
||||
// in an envelope
|
||||
case inEvent:
|
||||
smp.event.SubscriptionID = &v
|
||||
case inReq:
|
||||
smp.req.SubscriptionID = v
|
||||
case inOk:
|
||||
if smp.ok.EventID == "" {
|
||||
smp.ok.EventID = v
|
||||
} else {
|
||||
smp.ok.Reason = v
|
||||
}
|
||||
case inEose:
|
||||
smp.eose = (*EOSEEnvelope)(&v)
|
||||
case inCount:
|
||||
smp.count.SubscriptionID = v
|
||||
case inAuth:
|
||||
smp.auth.Challenge = &v
|
||||
case inClose:
|
||||
smp.close = (*CloseEnvelope)(&v)
|
||||
case inClosed:
|
||||
if smp.closed.SubscriptionID == "" {
|
||||
smp.closed.SubscriptionID = v
|
||||
} else {
|
||||
smp.closed.Reason = v
|
||||
}
|
||||
case inNotice:
|
||||
smp.notice = (*NoticeEnvelope)(&v)
|
||||
|
||||
// filter object properties
|
||||
case inIds:
|
||||
smp.currentFilter.IDs = append(smp.currentFilter.IDs, v)
|
||||
case inAuthors:
|
||||
smp.currentFilter.Authors = append(smp.currentFilter.Authors, v)
|
||||
case inSearch:
|
||||
smp.currentFilter.Search = v
|
||||
smp.whereWeAre = inFilterObject
|
||||
case inAFilterTag:
|
||||
smp.currentFilterTagList = append(smp.currentFilterTagList, v)
|
||||
|
||||
// id object properties
|
||||
case inId:
|
||||
smp.currentEvent.ID = v
|
||||
smp.whereWeAre = inEventObject
|
||||
case inContent:
|
||||
smp.currentEvent.Content = v
|
||||
smp.whereWeAre = inEventObject
|
||||
case inPubkey:
|
||||
smp.currentEvent.PubKey = v
|
||||
smp.whereWeAre = inEventObject
|
||||
case inSig:
|
||||
smp.currentEvent.Sig = v
|
||||
smp.whereWeAre = inEventObject
|
||||
case inAnEventTag:
|
||||
smp.currentEventTag = append(smp.currentEventTag, v)
|
||||
|
||||
// count object properties
|
||||
case inCountObject:
|
||||
smp.count.HyperLogLog, _ = hex.DecodeString(v)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unexpected string %s at %v", v, smp.whereWeAre)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (smp *SonicMessageParser) OnInt64(v int64, _ stdlibjson.Number) error {
|
||||
// fmt.Println("***", "OnInt64", v, "==", smp.whereWeAre)
|
||||
|
||||
switch smp.whereWeAre {
|
||||
// event object
|
||||
case inCreatedAt:
|
||||
smp.currentEvent.CreatedAt = Timestamp(v)
|
||||
smp.whereWeAre = inEventObject
|
||||
case inKind:
|
||||
smp.currentEvent.Kind = int(v)
|
||||
smp.whereWeAre = inEventObject
|
||||
|
||||
// filter object
|
||||
case inLimit:
|
||||
smp.currentFilter.Limit = int(v)
|
||||
smp.currentFilter.LimitZero = v == 0
|
||||
case inSince:
|
||||
smp.currentFilter.Since = (*Timestamp)(&v)
|
||||
case inUntil:
|
||||
smp.currentFilter.Until = (*Timestamp)(&v)
|
||||
case inKinds:
|
||||
smp.currentFilter.Kinds = append(smp.currentFilter.Kinds, int(v))
|
||||
|
||||
// count object
|
||||
case inCountObject:
|
||||
smp.count.Count = &v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (smp *SonicMessageParser) OnBool(v bool) error {
|
||||
// fmt.Println("***", "OnBool", v, "==", smp.whereWeAre)
|
||||
|
||||
if smp.whereWeAre == inOk {
|
||||
smp.ok.OK = v
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("unexpected boolean")
|
||||
}
|
||||
}
|
||||
|
||||
func (_ SonicMessageParser) OnNull() error {
|
||||
return fmt.Errorf("null shouldn't be anywhere in a message")
|
||||
}
|
||||
|
||||
func (_ SonicMessageParser) OnFloat64(v float64, n stdlibjson.Number) error {
|
||||
return fmt.Errorf("float shouldn't be anywhere in a message")
|
||||
}
|
||||
|
||||
func ParseMessageSonic(message []byte) (Envelope, error) {
|
||||
smp := &SonicMessageParser{}
|
||||
smp.whereWeAre = inEnvelope
|
||||
|
||||
err := ast.Preorder(string(message), smp, nil)
|
||||
|
||||
return smp.mainEnvelope, err
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/minio/simdjson-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -41,8 +40,8 @@ func TestParseMessage(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "EVENT envelope with tags",
|
||||
Message: []byte(`["EVENT",{"kind":3,"id":"9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2","pubkey":"373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7","created_at":1644844224,"tags":[["p","3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"],["p","75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"],["p","46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"]],"content":"{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}","sig":"811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be"}]`),
|
||||
ExpectedEnvelope: &EventEnvelope{Event: Event{Kind: 3, ID: "9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2", PubKey: "373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7", CreatedAt: 1644844224, Tags: Tags{Tag{"p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"}, Tag{"p", "75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"}, Tag{"p", "46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"}}, Content: "{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}", Sig: "811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be"}},
|
||||
Message: []byte(`["EVENT",{"kind":3,"id":"9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2","pubkey":"373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7","created_at":1644844224,"tags":[["p","3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"],["e","75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"],["p","46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"]],"content":"{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}","sig":"811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be"}]`),
|
||||
ExpectedEnvelope: &EventEnvelope{Event: Event{Kind: 3, ID: "9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2", PubKey: "373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7", CreatedAt: 1644844224, Tags: Tags{Tag{"p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"}, Tag{"e", "75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"}, Tag{"p", "46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"}}, Content: "{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}", Sig: "811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be"}},
|
||||
},
|
||||
{
|
||||
Name: "NOTICE envelope",
|
||||
@ -115,12 +114,12 @@ func TestParseMessage(t *testing.T) {
|
||||
}
|
||||
|
||||
if testCase.ExpectedEnvelope == nil {
|
||||
assert.Nil(t, envelope, "expected nil but got %v", envelope)
|
||||
require.Nil(t, envelope, "expected nil but got %v", envelope)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NotNil(t, envelope, "expected non-nil envelope but got nil")
|
||||
assert.Equal(t, testCase.ExpectedEnvelope.String(), envelope.String())
|
||||
require.NotNil(t, envelope, "expected non-nil envelope but got nil")
|
||||
require.Equal(t, testCase.ExpectedEnvelope.String(), envelope.String())
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -148,10 +147,9 @@ func TestParseMessage(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("sonic", func(t *testing.T) {
|
||||
smp := SonicMessageParser{}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.Name, func(t *testing.T) {
|
||||
envelope, err := smp.ParseMessage(testCase.Message)
|
||||
envelope, err := ParseMessageSonic(testCase.Message)
|
||||
|
||||
if testCase.ExpectedEnvelope == nil && envelope == nil {
|
||||
return
|
||||
@ -164,7 +162,7 @@ func TestParseMessage(t *testing.T) {
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, envelope, "expected non-nil envelope but got nil")
|
||||
require.Equal(t, testCase.ExpectedEnvelope.String(), envelope.String())
|
||||
require.Equal(t, testCase.ExpectedEnvelope, envelope)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
Reference in New Issue
Block a user