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:
fiatjaf
2025-03-07 15:38:20 -03:00
parent 39bde22639
commit 8d7e854779
3 changed files with 248 additions and 171 deletions

View File

@@ -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]

View File

@@ -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
} }

View File

@@ -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