mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-09-19 03:58:12 +02:00
some fixes to sonic and make it faster by reusing arrays.
BenchmarkParseMessage/relay/jsonstdlib-4 216 5651451 ns/op BenchmarkParseMessage/relay/easyjson-4 313 3703457 ns/op BenchmarkParseMessage/relay/simdjson-4 469 2606309 ns/op BenchmarkParseMessage/relay/sonic-4 966 1268594 ns/op BenchmarkParseMessage/client/jsonstdlib-4 216 5524239 ns/op BenchmarkParseMessage/client/easyjson-4 422 2756370 ns/op BenchmarkParseMessage/client/simdjson-4 519 2298477 ns/op BenchmarkParseMessage/client/sonic-4 644 1860583 ns/op
This commit is contained in:
@@ -42,9 +42,10 @@ func BenchmarkParseMessage(b *testing.B) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
b.Run("sonic", func(b *testing.B) {
|
b.Run("sonic", func(b *testing.B) {
|
||||||
|
smp := NewSonicMessageParser()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, msg := range messages {
|
for _, msg := range messages {
|
||||||
_, _ = ParseMessageSonic(msg)
|
_, _ = smp.ParseMessage(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -57,7 +58,7 @@ func generateTestMessages(typ string) [][]byte {
|
|||||||
|
|
||||||
setup := map[string]map[int]func() []byte{
|
setup := map[string]map[int]func() []byte{
|
||||||
"client": {
|
"client": {
|
||||||
500: generateEventMessage,
|
600: generateEventMessage,
|
||||||
5: generateEOSEMessage,
|
5: generateEOSEMessage,
|
||||||
9: generateNoticeMessage,
|
9: generateNoticeMessage,
|
||||||
14: generateCountMessage,
|
14: generateCountMessage,
|
||||||
@@ -65,6 +66,7 @@ func generateTestMessages(typ string) [][]byte {
|
|||||||
},
|
},
|
||||||
"relay": {
|
"relay": {
|
||||||
500: generateReqMessage,
|
500: generateReqMessage,
|
||||||
|
50: generateEventMessage,
|
||||||
10: generateCountMessage,
|
10: generateCountMessage,
|
||||||
},
|
},
|
||||||
}[typ]
|
}[typ]
|
||||||
|
@@ -4,14 +4,15 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
stdlibjson "encoding/json"
|
stdlibjson "encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/bytedance/sonic/ast"
|
"github.com/bytedance/sonic/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
type sonicParserPosition int
|
type sonicVisitorPosition int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
inEnvelope sonicParserPosition = iota
|
inEnvelope sonicVisitorPosition = iota
|
||||||
|
|
||||||
inEvent
|
inEvent
|
||||||
inReq
|
inReq
|
||||||
@@ -42,11 +43,12 @@ const (
|
|||||||
inContent
|
inContent
|
||||||
inPubkey
|
inPubkey
|
||||||
inSig
|
inSig
|
||||||
inTags
|
inTags // we just saw the "tags" object key
|
||||||
inAnEventTag
|
inTagsList // we have just seen the first `[` of the tags
|
||||||
|
inAnEventTag // we are inside an actual tag, i.e we have just seen `[[`, or `].[`
|
||||||
)
|
)
|
||||||
|
|
||||||
func (spp sonicParserPosition) String() string {
|
func (spp sonicVisitorPosition) String() string {
|
||||||
switch spp {
|
switch spp {
|
||||||
case inEnvelope:
|
case inEnvelope:
|
||||||
return "inEnvelope"
|
return "inEnvelope"
|
||||||
@@ -102,6 +104,8 @@ func (spp sonicParserPosition) String() string {
|
|||||||
return "inSig"
|
return "inSig"
|
||||||
case inTags:
|
case inTags:
|
||||||
return "inTags"
|
return "inTags"
|
||||||
|
case inTagsList:
|
||||||
|
return "inTagsList"
|
||||||
case inAnEventTag:
|
case inAnEventTag:
|
||||||
return "inAnEventTag"
|
return "inAnEventTag"
|
||||||
default:
|
default:
|
||||||
@@ -109,7 +113,7 @@ func (spp sonicParserPosition) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SonicMessageParser struct {
|
type sonicVisitor struct {
|
||||||
event *EventEnvelope
|
event *EventEnvelope
|
||||||
req *ReqEnvelope
|
req *ReqEnvelope
|
||||||
ok *OKEnvelope
|
ok *OKEnvelope
|
||||||
@@ -120,7 +124,7 @@ type SonicMessageParser struct {
|
|||||||
closed *ClosedEnvelope
|
closed *ClosedEnvelope
|
||||||
notice *NoticeEnvelope
|
notice *NoticeEnvelope
|
||||||
|
|
||||||
whereWeAre sonicParserPosition
|
whereWeAre sonicVisitorPosition
|
||||||
|
|
||||||
currentEvent *Event
|
currentEvent *Event
|
||||||
currentEventTag Tag
|
currentEventTag Tag
|
||||||
@@ -129,20 +133,20 @@ type SonicMessageParser struct {
|
|||||||
currentFilterTagList []string
|
currentFilterTagList []string
|
||||||
currentFilterTagName string
|
currentFilterTagName string
|
||||||
|
|
||||||
|
smp *sonicMessageParser
|
||||||
mainEnvelope Envelope
|
mainEnvelope Envelope
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *SonicMessageParser) OnArrayBegin(capacity int) error {
|
func (sv *sonicVisitor) OnArrayBegin(capacity int) error {
|
||||||
// fmt.Println("***", "OnArrayBegin", "==", smp.whereWeAre)
|
// fmt.Println("***", "OnArrayBegin", "==", sv.whereWeAre)
|
||||||
|
|
||||||
switch smp.whereWeAre {
|
switch sv.whereWeAre {
|
||||||
case inTags:
|
case inTags:
|
||||||
if smp.currentEvent.Tags == nil {
|
sv.whereWeAre = inTagsList
|
||||||
smp.currentEvent.Tags = make(Tags, 0, 10)
|
sv.currentEvent.Tags = sv.smp.reusableTagArray
|
||||||
smp.currentEventTag = make(Tag, 0, 20)
|
case inTagsList:
|
||||||
} else {
|
sv.whereWeAre = inAnEventTag
|
||||||
smp.whereWeAre = inAnEventTag
|
sv.currentEventTag = sv.smp.reusableStringArray
|
||||||
}
|
|
||||||
case inAFilterTag:
|
case inAFilterTag:
|
||||||
// we have already created this
|
// we have already created this
|
||||||
}
|
}
|
||||||
@@ -150,131 +154,138 @@ func (smp *SonicMessageParser) OnArrayBegin(capacity int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *SonicMessageParser) OnArrayEnd() error {
|
func (sv *sonicVisitor) OnArrayEnd() error {
|
||||||
// fmt.Println("***", "OnArrayEnd", "==", smp.whereWeAre)
|
// fmt.Println("***", "OnArrayEnd", "==", sv.whereWeAre)
|
||||||
|
|
||||||
switch smp.whereWeAre {
|
switch sv.whereWeAre {
|
||||||
// envelopes
|
// envelopes
|
||||||
case inEvent:
|
case inEvent:
|
||||||
smp.mainEnvelope = smp.event
|
sv.mainEnvelope = sv.event
|
||||||
case inReq:
|
case inReq:
|
||||||
smp.mainEnvelope = smp.req
|
sv.mainEnvelope = sv.req
|
||||||
|
sv.smp.doneWithFilterSlice(sv.req.Filters)
|
||||||
case inOk:
|
case inOk:
|
||||||
smp.mainEnvelope = smp.ok
|
sv.mainEnvelope = sv.ok
|
||||||
case inEose:
|
case inEose:
|
||||||
smp.mainEnvelope = smp.eose
|
sv.mainEnvelope = sv.eose
|
||||||
case inCount:
|
case inCount:
|
||||||
smp.mainEnvelope = smp.count
|
sv.mainEnvelope = sv.count
|
||||||
case inAuth:
|
case inAuth:
|
||||||
smp.mainEnvelope = smp.auth
|
sv.mainEnvelope = sv.auth
|
||||||
case inClose:
|
case inClose:
|
||||||
smp.mainEnvelope = smp.close
|
sv.mainEnvelope = sv.close
|
||||||
case inClosed:
|
case inClosed:
|
||||||
smp.mainEnvelope = smp.closed
|
sv.mainEnvelope = sv.closed
|
||||||
case inNotice:
|
case inNotice:
|
||||||
smp.mainEnvelope = smp.notice
|
sv.mainEnvelope = sv.notice
|
||||||
|
|
||||||
// filter object properties
|
// filter object properties
|
||||||
case inIds, inAuthors, inKinds, inSearch:
|
case inIds:
|
||||||
smp.whereWeAre = inFilterObject
|
sv.whereWeAre = inFilterObject
|
||||||
|
sv.smp.doneWithStringSlice(sv.currentFilter.IDs)
|
||||||
|
case inAuthors:
|
||||||
|
sv.whereWeAre = inFilterObject
|
||||||
|
sv.smp.doneWithStringSlice(sv.currentFilter.Authors)
|
||||||
|
case inKinds:
|
||||||
|
sv.whereWeAre = inFilterObject
|
||||||
|
sv.smp.doneWithIntSlice(sv.currentFilter.Kinds)
|
||||||
case inAFilterTag:
|
case inAFilterTag:
|
||||||
smp.currentFilter.Tags[smp.currentFilterTagName] = smp.currentFilterTagList
|
sv.currentFilter.Tags[sv.currentFilterTagName] = sv.currentFilterTagList
|
||||||
// reuse the same underlying slice because we know nothing else will be appended to it
|
sv.whereWeAre = inFilterObject
|
||||||
smp.currentFilterTagList = smp.currentFilterTagList[len(smp.currentFilterTagList):]
|
sv.smp.doneWithStringSlice(sv.currentFilterTagList)
|
||||||
smp.whereWeAre = inFilterObject
|
|
||||||
|
|
||||||
// event object properties
|
// event object properties
|
||||||
case inAnEventTag:
|
case inAnEventTag:
|
||||||
smp.currentEvent.Tags = append(smp.currentEvent.Tags, smp.currentEventTag)
|
sv.currentEvent.Tags = append(sv.currentEvent.Tags, sv.currentEventTag)
|
||||||
// reuse the same underlying slice because we know nothing else will be appended to it
|
sv.whereWeAre = inTagsList
|
||||||
smp.currentEventTag = smp.currentEventTag[len(smp.currentEventTag):]
|
sv.smp.doneWithStringSlice(sv.currentEventTag)
|
||||||
smp.whereWeAre = inTags
|
case inTags, inTagsList:
|
||||||
case inTags:
|
sv.whereWeAre = inEventObject
|
||||||
smp.whereWeAre = inEventObject
|
sv.smp.doneWithTagSlice(sv.currentEvent.Tags)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected array end at %v", smp.whereWeAre)
|
return fmt.Errorf("unexpected array end at %v", sv.whereWeAre)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *SonicMessageParser) OnObjectBegin(capacity int) error {
|
func (sv *sonicVisitor) OnObjectBegin(capacity int) error {
|
||||||
// fmt.Println("***", "OnObjectBegin", "==", smp.whereWeAre)
|
// fmt.Println("***", "OnObjectBegin", "==", sv.whereWeAre)
|
||||||
|
|
||||||
switch smp.whereWeAre {
|
switch sv.whereWeAre {
|
||||||
case inEvent:
|
case inEvent:
|
||||||
smp.whereWeAre = inEventObject
|
sv.whereWeAre = inEventObject
|
||||||
smp.currentEvent = &Event{}
|
sv.currentEvent = &Event{}
|
||||||
case inAuth:
|
case inAuth:
|
||||||
smp.whereWeAre = inEventObject
|
sv.whereWeAre = inEventObject
|
||||||
smp.currentEvent = &Event{}
|
sv.currentEvent = &Event{}
|
||||||
case inReq:
|
case inReq:
|
||||||
smp.whereWeAre = inFilterObject
|
sv.whereWeAre = inFilterObject
|
||||||
smp.currentFilter = &Filter{}
|
sv.currentFilter = &Filter{}
|
||||||
case inCount:
|
case inCount:
|
||||||
// set this temporarily, we will switch to a filterObject if we see "count" or "hll"
|
// set this temporarily, we will switch to a filterObject if we see "count" or "hll"
|
||||||
smp.whereWeAre = inFilterObject
|
sv.whereWeAre = inFilterObject
|
||||||
smp.currentFilter = &Filter{}
|
sv.currentFilter = &Filter{}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected object begin at %v", smp.whereWeAre)
|
return fmt.Errorf("unexpected object begin at %v", sv.whereWeAre)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *SonicMessageParser) OnObjectKey(key string) error {
|
func (sv *sonicVisitor) OnObjectKey(key string) error {
|
||||||
// fmt.Println("***", "OnObjectKey", key, "==", smp.whereWeAre)
|
// fmt.Println("***", "OnObjectKey", key, "==", sv.whereWeAre)
|
||||||
|
|
||||||
switch smp.whereWeAre {
|
switch sv.whereWeAre {
|
||||||
case inEventObject:
|
case inEventObject:
|
||||||
switch key {
|
switch key {
|
||||||
case "id":
|
case "id":
|
||||||
smp.whereWeAre = inId
|
sv.whereWeAre = inId
|
||||||
case "sig":
|
case "sig":
|
||||||
smp.whereWeAre = inSig
|
sv.whereWeAre = inSig
|
||||||
case "pubkey":
|
case "pubkey":
|
||||||
smp.whereWeAre = inPubkey
|
sv.whereWeAre = inPubkey
|
||||||
case "content":
|
case "content":
|
||||||
smp.whereWeAre = inContent
|
sv.whereWeAre = inContent
|
||||||
case "created_at":
|
case "created_at":
|
||||||
smp.whereWeAre = inCreatedAt
|
sv.whereWeAre = inCreatedAt
|
||||||
case "kind":
|
case "kind":
|
||||||
smp.whereWeAre = inKind
|
sv.whereWeAre = inKind
|
||||||
case "tags":
|
case "tags":
|
||||||
smp.whereWeAre = inTags
|
sv.whereWeAre = inTags
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected event attr %s", key)
|
return fmt.Errorf("unexpected event attr %s", key)
|
||||||
}
|
}
|
||||||
case inFilterObject:
|
case inFilterObject:
|
||||||
switch key {
|
switch key {
|
||||||
case "limit":
|
case "limit":
|
||||||
smp.whereWeAre = inLimit
|
sv.whereWeAre = inLimit
|
||||||
case "since":
|
case "since":
|
||||||
smp.whereWeAre = inSince
|
sv.whereWeAre = inSince
|
||||||
case "until":
|
case "until":
|
||||||
smp.whereWeAre = inUntil
|
sv.whereWeAre = inUntil
|
||||||
case "ids":
|
case "ids":
|
||||||
smp.whereWeAre = inIds
|
sv.whereWeAre = inIds
|
||||||
smp.currentFilter.IDs = make([]string, 0, 25)
|
sv.currentFilter.IDs = sv.smp.reusableStringArray
|
||||||
case "authors":
|
case "authors":
|
||||||
smp.whereWeAre = inAuthors
|
sv.whereWeAre = inAuthors
|
||||||
smp.currentFilter.Authors = make([]string, 0, 25)
|
sv.currentFilter.Authors = sv.smp.reusableStringArray
|
||||||
case "kinds":
|
case "kinds":
|
||||||
smp.whereWeAre = inKinds
|
sv.whereWeAre = inKinds
|
||||||
smp.currentFilter.IDs = make([]string, 0, 12)
|
sv.currentFilter.Kinds = sv.smp.reusableIntArray
|
||||||
case "search":
|
case "search":
|
||||||
smp.whereWeAre = inSearch
|
sv.whereWeAre = inSearch
|
||||||
case "count", "hll":
|
case "count", "hll":
|
||||||
// oops, switch to a countObject
|
// oops, switch to a countObject
|
||||||
smp.whereWeAre = inCountObject
|
sv.whereWeAre = inCountObject
|
||||||
default:
|
default:
|
||||||
if len(key) > 1 && key[0] == '#' {
|
if len(key) > 1 && key[0] == '#' {
|
||||||
if smp.currentFilter.Tags == nil {
|
if sv.currentFilter.Tags == nil {
|
||||||
smp.currentFilter.Tags = make(TagMap, 1)
|
sv.currentFilter.Tags = make(TagMap, 1)
|
||||||
smp.currentFilterTagList = make([]string, 0, 25)
|
|
||||||
}
|
}
|
||||||
smp.whereWeAre = inAFilterTag
|
sv.currentFilterTagList = sv.smp.reusableStringArray
|
||||||
smp.currentFilterTagName = key[1:]
|
sv.currentFilterTagName = key[1:]
|
||||||
|
sv.whereWeAre = inAFilterTag
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("unexpected filter attr %s", key)
|
return fmt.Errorf("unexpected filter attr %s", key)
|
||||||
}
|
}
|
||||||
@@ -282,192 +293,254 @@ func (smp *SonicMessageParser) OnObjectKey(key string) error {
|
|||||||
case inCountObject:
|
case inCountObject:
|
||||||
// we'll judge by the shape of the value so ignore this
|
// we'll judge by the shape of the value so ignore this
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected object key %s at %s", key, smp.whereWeAre)
|
return fmt.Errorf("unexpected object key %s at %s", key, sv.whereWeAre)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *SonicMessageParser) OnObjectEnd() error {
|
func (sv *sonicVisitor) OnObjectEnd() error {
|
||||||
// fmt.Println("***", "OnObjectEnd", "==", smp.whereWeAre)
|
// fmt.Println("***", "OnObjectEnd", "==", sv.whereWeAre)
|
||||||
|
|
||||||
switch smp.whereWeAre {
|
switch sv.whereWeAre {
|
||||||
case inEventObject:
|
case inEventObject:
|
||||||
if smp.event != nil {
|
if sv.event != nil {
|
||||||
smp.event.Event = *smp.currentEvent
|
sv.event.Event = *sv.currentEvent
|
||||||
smp.whereWeAre = inEvent
|
sv.whereWeAre = inEvent
|
||||||
} else {
|
} else {
|
||||||
smp.auth.Event = *smp.currentEvent
|
sv.auth.Event = *sv.currentEvent
|
||||||
smp.whereWeAre = inAuth
|
sv.whereWeAre = inAuth
|
||||||
}
|
}
|
||||||
|
sv.currentEvent = nil
|
||||||
case inFilterObject:
|
case inFilterObject:
|
||||||
if smp.req != nil {
|
if sv.req != nil {
|
||||||
smp.req.Filters = append(smp.req.Filters, *smp.currentFilter)
|
sv.req.Filters = append(sv.req.Filters, *sv.currentFilter)
|
||||||
smp.whereWeAre = inReq
|
sv.whereWeAre = inReq
|
||||||
} else {
|
} else {
|
||||||
smp.count.Filter = *smp.currentFilter
|
sv.count.Filter = *sv.currentFilter
|
||||||
smp.whereWeAre = inCount
|
sv.whereWeAre = inCount
|
||||||
}
|
}
|
||||||
|
sv.currentFilter = nil
|
||||||
case inCountObject:
|
case inCountObject:
|
||||||
smp.whereWeAre = inCount
|
sv.whereWeAre = inCount
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected object end at %s", smp.whereWeAre)
|
return fmt.Errorf("unexpected object end at %s", sv.whereWeAre)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *SonicMessageParser) OnString(v string) error {
|
func (sv *sonicVisitor) OnString(v string) error {
|
||||||
// fmt.Println("***", "OnString", v, "==", smp.whereWeAre)
|
// fmt.Println("***", "OnString", v, "==", sv.whereWeAre)
|
||||||
|
|
||||||
switch smp.whereWeAre {
|
switch sv.whereWeAre {
|
||||||
case inEnvelope:
|
case inEnvelope:
|
||||||
switch v {
|
switch v {
|
||||||
case "EVENT":
|
case "EVENT":
|
||||||
smp.event = &EventEnvelope{}
|
sv.event = &EventEnvelope{}
|
||||||
smp.whereWeAre = inEvent
|
sv.whereWeAre = inEvent
|
||||||
case "REQ":
|
case "REQ":
|
||||||
smp.req = &ReqEnvelope{Filters: make(Filters, 0, 1)}
|
sv.req = &ReqEnvelope{Filters: sv.smp.reusableFilterArray}
|
||||||
smp.whereWeAre = inReq
|
sv.whereWeAre = inReq
|
||||||
case "OK":
|
case "OK":
|
||||||
smp.ok = &OKEnvelope{}
|
sv.ok = &OKEnvelope{}
|
||||||
smp.whereWeAre = inOk
|
sv.whereWeAre = inOk
|
||||||
case "EOSE":
|
case "EOSE":
|
||||||
smp.whereWeAre = inEose
|
sv.whereWeAre = inEose
|
||||||
case "COUNT":
|
case "COUNT":
|
||||||
smp.count = &CountEnvelope{}
|
sv.count = &CountEnvelope{}
|
||||||
smp.whereWeAre = inCount
|
sv.whereWeAre = inCount
|
||||||
case "AUTH":
|
case "AUTH":
|
||||||
smp.auth = &AuthEnvelope{}
|
sv.auth = &AuthEnvelope{}
|
||||||
smp.whereWeAre = inAuth
|
sv.whereWeAre = inAuth
|
||||||
case "CLOSE":
|
case "CLOSE":
|
||||||
smp.whereWeAre = inClose
|
sv.whereWeAre = inClose
|
||||||
case "CLOSED":
|
case "CLOSED":
|
||||||
smp.closed = &ClosedEnvelope{}
|
sv.closed = &ClosedEnvelope{}
|
||||||
smp.whereWeAre = inClosed
|
sv.whereWeAre = inClosed
|
||||||
case "NOTICE":
|
case "NOTICE":
|
||||||
smp.whereWeAre = inNotice
|
sv.whereWeAre = inNotice
|
||||||
}
|
}
|
||||||
|
|
||||||
// in an envelope
|
// in an envelope
|
||||||
case inEvent:
|
case inEvent:
|
||||||
smp.event.SubscriptionID = &v
|
sv.event.SubscriptionID = &v
|
||||||
case inReq:
|
case inReq:
|
||||||
smp.req.SubscriptionID = v
|
sv.req.SubscriptionID = v
|
||||||
case inOk:
|
case inOk:
|
||||||
if smp.ok.EventID == "" {
|
if sv.ok.EventID == "" {
|
||||||
smp.ok.EventID = v
|
sv.ok.EventID = v
|
||||||
} else {
|
} else {
|
||||||
smp.ok.Reason = v
|
sv.ok.Reason = v
|
||||||
}
|
}
|
||||||
case inEose:
|
case inEose:
|
||||||
smp.eose = (*EOSEEnvelope)(&v)
|
sv.eose = (*EOSEEnvelope)(&v)
|
||||||
case inCount:
|
case inCount:
|
||||||
smp.count.SubscriptionID = v
|
sv.count.SubscriptionID = v
|
||||||
case inAuth:
|
case inAuth:
|
||||||
smp.auth.Challenge = &v
|
sv.auth.Challenge = &v
|
||||||
case inClose:
|
case inClose:
|
||||||
smp.close = (*CloseEnvelope)(&v)
|
sv.close = (*CloseEnvelope)(&v)
|
||||||
case inClosed:
|
case inClosed:
|
||||||
if smp.closed.SubscriptionID == "" {
|
if sv.closed.SubscriptionID == "" {
|
||||||
smp.closed.SubscriptionID = v
|
sv.closed.SubscriptionID = v
|
||||||
} else {
|
} else {
|
||||||
smp.closed.Reason = v
|
sv.closed.Reason = v
|
||||||
}
|
}
|
||||||
case inNotice:
|
case inNotice:
|
||||||
smp.notice = (*NoticeEnvelope)(&v)
|
sv.notice = (*NoticeEnvelope)(&v)
|
||||||
|
|
||||||
// filter object properties
|
// filter object properties
|
||||||
case inIds:
|
case inIds:
|
||||||
smp.currentFilter.IDs = append(smp.currentFilter.IDs, v)
|
sv.currentFilter.IDs = append(sv.currentFilter.IDs, v)
|
||||||
case inAuthors:
|
case inAuthors:
|
||||||
smp.currentFilter.Authors = append(smp.currentFilter.Authors, v)
|
sv.currentFilter.Authors = append(sv.currentFilter.Authors, v)
|
||||||
case inSearch:
|
case inSearch:
|
||||||
smp.currentFilter.Search = v
|
sv.currentFilter.Search = v
|
||||||
smp.whereWeAre = inFilterObject
|
sv.whereWeAre = inFilterObject
|
||||||
case inAFilterTag:
|
case inAFilterTag:
|
||||||
smp.currentFilterTagList = append(smp.currentFilterTagList, v)
|
sv.currentFilterTagList = append(sv.currentFilterTagList, v)
|
||||||
|
|
||||||
// id object properties
|
// id object properties
|
||||||
case inId:
|
case inId:
|
||||||
smp.currentEvent.ID = v
|
sv.currentEvent.ID = v
|
||||||
smp.whereWeAre = inEventObject
|
sv.whereWeAre = inEventObject
|
||||||
case inContent:
|
case inContent:
|
||||||
smp.currentEvent.Content = v
|
sv.currentEvent.Content = v
|
||||||
smp.whereWeAre = inEventObject
|
sv.whereWeAre = inEventObject
|
||||||
case inPubkey:
|
case inPubkey:
|
||||||
smp.currentEvent.PubKey = v
|
sv.currentEvent.PubKey = v
|
||||||
smp.whereWeAre = inEventObject
|
sv.whereWeAre = inEventObject
|
||||||
case inSig:
|
case inSig:
|
||||||
smp.currentEvent.Sig = v
|
sv.currentEvent.Sig = v
|
||||||
smp.whereWeAre = inEventObject
|
sv.whereWeAre = inEventObject
|
||||||
case inAnEventTag:
|
case inAnEventTag:
|
||||||
smp.currentEventTag = append(smp.currentEventTag, v)
|
sv.currentEventTag = append(sv.currentEventTag, v)
|
||||||
|
|
||||||
// count object properties
|
// count object properties
|
||||||
case inCountObject:
|
case inCountObject:
|
||||||
smp.count.HyperLogLog, _ = hex.DecodeString(v)
|
sv.count.HyperLogLog, _ = hex.DecodeString(v)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected string %s at %v", v, smp.whereWeAre)
|
return fmt.Errorf("unexpected string %s at %v", v, sv.whereWeAre)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *SonicMessageParser) OnInt64(v int64, _ stdlibjson.Number) error {
|
func (sv *sonicVisitor) OnInt64(v int64, _ stdlibjson.Number) error {
|
||||||
// fmt.Println("***", "OnInt64", v, "==", smp.whereWeAre)
|
// fmt.Println("***", "OnInt64", v, "==", sv.whereWeAre)
|
||||||
|
|
||||||
switch smp.whereWeAre {
|
switch sv.whereWeAre {
|
||||||
// event object
|
// event object
|
||||||
case inCreatedAt:
|
case inCreatedAt:
|
||||||
smp.currentEvent.CreatedAt = Timestamp(v)
|
sv.currentEvent.CreatedAt = Timestamp(v)
|
||||||
smp.whereWeAre = inEventObject
|
sv.whereWeAre = inEventObject
|
||||||
case inKind:
|
case inKind:
|
||||||
smp.currentEvent.Kind = int(v)
|
sv.currentEvent.Kind = int(v)
|
||||||
smp.whereWeAre = inEventObject
|
sv.whereWeAre = inEventObject
|
||||||
|
|
||||||
// filter object
|
// filter object
|
||||||
case inLimit:
|
case inLimit:
|
||||||
smp.currentFilter.Limit = int(v)
|
sv.currentFilter.Limit = int(v)
|
||||||
smp.currentFilter.LimitZero = v == 0
|
sv.currentFilter.LimitZero = v == 0
|
||||||
case inSince:
|
case inSince:
|
||||||
smp.currentFilter.Since = (*Timestamp)(&v)
|
sv.currentFilter.Since = (*Timestamp)(&v)
|
||||||
case inUntil:
|
case inUntil:
|
||||||
smp.currentFilter.Until = (*Timestamp)(&v)
|
sv.currentFilter.Until = (*Timestamp)(&v)
|
||||||
case inKinds:
|
case inKinds:
|
||||||
smp.currentFilter.Kinds = append(smp.currentFilter.Kinds, int(v))
|
sv.currentFilter.Kinds = append(sv.currentFilter.Kinds, int(v))
|
||||||
|
|
||||||
// count object
|
// count object
|
||||||
case inCountObject:
|
case inCountObject:
|
||||||
smp.count.Count = &v
|
sv.count.Count = &v
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *SonicMessageParser) OnBool(v bool) error {
|
func (sv *sonicVisitor) OnBool(v bool) error {
|
||||||
// fmt.Println("***", "OnBool", v, "==", smp.whereWeAre)
|
// fmt.Println("***", "OnBool", v, "==", sv.whereWeAre)
|
||||||
|
|
||||||
if smp.whereWeAre == inOk {
|
if sv.whereWeAre == inOk {
|
||||||
smp.ok.OK = v
|
sv.ok.OK = v
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("unexpected boolean")
|
return fmt.Errorf("unexpected boolean")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ SonicMessageParser) OnNull() error {
|
func (_ sonicVisitor) OnNull() error {
|
||||||
return fmt.Errorf("null shouldn't be anywhere in a message")
|
return fmt.Errorf("null shouldn't be anywhere in a message")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ SonicMessageParser) OnFloat64(v float64, n stdlibjson.Number) error {
|
func (_ sonicVisitor) OnFloat64(v float64, n stdlibjson.Number) error {
|
||||||
return fmt.Errorf("float shouldn't be anywhere in a message")
|
return fmt.Errorf("float shouldn't be anywhere in a message")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseMessageSonic(message []byte) (Envelope, error) {
|
type sonicMessageParser struct {
|
||||||
smp := &SonicMessageParser{}
|
reusableFilterArray []Filter
|
||||||
smp.whereWeAre = inEnvelope
|
reusableTagArray []Tag
|
||||||
|
reusableStringArray []string
|
||||||
err := ast.Preorder(string(message), smp, nil)
|
reusableIntArray []int
|
||||||
|
}
|
||||||
return smp.mainEnvelope, err
|
|
||||||
|
func NewSonicMessageParser() sonicMessageParser {
|
||||||
|
return sonicMessageParser{
|
||||||
|
reusableFilterArray: make([]Filter, 0, 1000),
|
||||||
|
reusableTagArray: make([]Tag, 0, 10000),
|
||||||
|
reusableStringArray: make([]string, 0, 10000),
|
||||||
|
reusableIntArray: make([]int, 0, 10000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (smp *sonicMessageParser) doneWithFilterSlice(slice []Filter) {
|
||||||
|
if unsafe.SliceData(smp.reusableFilterArray) == unsafe.SliceData(slice) {
|
||||||
|
smp.reusableFilterArray = slice[len(slice):]
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(smp.reusableFilterArray) < 7 {
|
||||||
|
// create a new one
|
||||||
|
smp.reusableFilterArray = make([]Filter, 0, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (smp *sonicMessageParser) doneWithTagSlice(slice []Tag) {
|
||||||
|
if unsafe.SliceData(smp.reusableTagArray) == unsafe.SliceData(slice) {
|
||||||
|
smp.reusableTagArray = slice[len(slice):]
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(smp.reusableTagArray) < 7 {
|
||||||
|
// create a new one
|
||||||
|
smp.reusableTagArray = make([]Tag, 0, 10000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (smp *sonicMessageParser) doneWithStringSlice(slice []string) {
|
||||||
|
if unsafe.SliceData(smp.reusableStringArray) == unsafe.SliceData(slice) {
|
||||||
|
smp.reusableStringArray = slice[len(slice):]
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(smp.reusableStringArray) < 15 {
|
||||||
|
// create a new one
|
||||||
|
smp.reusableStringArray = make([]string, 0, 10000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (smp *sonicMessageParser) doneWithIntSlice(slice []int) {
|
||||||
|
if unsafe.SliceData(smp.reusableIntArray) == unsafe.SliceData(slice) {
|
||||||
|
smp.reusableIntArray = slice[len(slice):]
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(smp.reusableIntArray) < 8 {
|
||||||
|
// create a new one
|
||||||
|
smp.reusableIntArray = make([]int, 0, 10000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (smp sonicMessageParser) ParseMessage(message []byte) (Envelope, error) {
|
||||||
|
sv := &sonicVisitor{smp: &smp}
|
||||||
|
sv.whereWeAre = inEnvelope
|
||||||
|
|
||||||
|
err := ast.Preorder(string(message), sv, nil)
|
||||||
|
|
||||||
|
return sv.mainEnvelope, err
|
||||||
}
|
}
|
||||||
|
@@ -147,9 +147,11 @@ func TestParseMessage(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("sonic", func(t *testing.T) {
|
t.Run("sonic", func(t *testing.T) {
|
||||||
|
smp := NewSonicMessageParser()
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.Name, func(t *testing.T) {
|
t.Run(testCase.Name, func(t *testing.T) {
|
||||||
envelope, err := ParseMessageSonic(testCase.Message)
|
envelope, err := smp.ParseMessage(testCase.Message)
|
||||||
|
|
||||||
if testCase.ExpectedEnvelope == nil && envelope == nil {
|
if testCase.ExpectedEnvelope == nil && envelope == nil {
|
||||||
return
|
return
|
||||||
|
Reference in New Issue
Block a user