From 08a1bc5b3561bbeaef680e2fff2acd886052423e Mon Sep 17 00:00:00 2001 From: Matej Spiller Muys Date: Sun, 22 Dec 2024 21:40:10 +0100 Subject: [PATCH] Replace locking fieldmap with concurrent safe haxmap --- .gitignore | 1 + field_map.go | 185 +++++++++------------------------------- go.mod | 20 +++-- go.sum | 55 ++++++------ message.go | 43 ++++------ repeating_group.go | 13 +-- repeating_group_test.go | 2 - 7 files changed, 95 insertions(+), 224 deletions(-) diff --git a/.gitignore b/.gitignore index fb92ea1b5..87aece5f2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.swp *.swo .idea +*.iml vendor _test/test _test/echo_server diff --git a/field_map.go b/field_map.go index 4aac64b1d..a8f8453af 100644 --- a/field_map.go +++ b/field_map.go @@ -17,9 +17,10 @@ package quickfix import ( "bytes" - "sort" - "sync" + "slices" "time" + + "github.com/alphadose/haxmap" ) // field stores a slice of TagValues. @@ -40,46 +41,33 @@ func writeField(f field, buffer *bytes.Buffer) { } // tagOrder true if tag i should occur before tag j. -type tagOrder func(i, j Tag) bool - -type tagSort struct { - tags []Tag - compare tagOrder -} - -func (t tagSort) Len() int { return len(t.tags) } -func (t tagSort) Swap(i, j int) { t.tags[i], t.tags[j] = t.tags[j], t.tags[i] } -func (t tagSort) Less(i, j int) bool { return t.compare(t.tags[i], t.tags[j]) } +type tagOrder func(i, j Tag) int // FieldMap is a collection of fix fields that make up a fix message. type FieldMap struct { - tagLookup map[Tag]field - tagSort - rwLock *sync.RWMutex + tagLookup *haxmap.Map[Tag, field] + compare tagOrder } // ascending tags. -func normalFieldOrder(i, j Tag) bool { return i < j } +func normalFieldOrder(i, j Tag) int { return int(i - j) } func (m *FieldMap) init() { m.initWithOrdering(normalFieldOrder) } func (m *FieldMap) initWithOrdering(ordering tagOrder) { - m.rwLock = &sync.RWMutex{} - m.tagLookup = make(map[Tag]field) + m.tagLookup = haxmap.New[Tag, field](10) m.compare = ordering } // Tags returns all of the Field Tags in this FieldMap. func (m FieldMap) Tags() []Tag { - m.rwLock.RLock() - defer m.rwLock.RUnlock() - - tags := make([]Tag, 0, len(m.tagLookup)) - for t := range m.tagLookup { - tags = append(tags, t) - } + tags := make([]Tag, 0, m.tagLookup.Len()) + m.tagLookup.ForEach(func(tag Tag, f field) bool { + tags = append(tags, tag) + return true + }) return tags } @@ -91,33 +79,13 @@ func (m FieldMap) Get(parser Field) MessageRejectError { // Has returns true if the Tag is present in this FieldMap. func (m FieldMap) Has(tag Tag) bool { - m.rwLock.RLock() - defer m.rwLock.RUnlock() - - _, ok := m.tagLookup[tag] + _, ok := m.tagLookup.Get(tag) return ok } // GetField parses of a field with Tag tag. Returned reject may indicate the field is not present, or the field value is invalid. func (m FieldMap) GetField(tag Tag, parser FieldValueReader) MessageRejectError { - m.rwLock.RLock() - defer m.rwLock.RUnlock() - - f, ok := m.tagLookup[tag] - if !ok { - return ConditionallyRequiredFieldMissing(tag) - } - - if err := parser.Read(f[0].value); err != nil { - return IncorrectDataFormatForValue(tag) - } - - return nil -} - -// GetField parses of a field with Tag tag. Returned reject may indicate the field is not present, or the field value is invalid. -func (m FieldMap) getFieldNoLock(tag Tag, parser FieldValueReader) MessageRejectError { - f, ok := m.tagLookup[tag] + f, ok := m.tagLookup.Get(tag) if !ok { return ConditionallyRequiredFieldMissing(tag) } @@ -131,10 +99,7 @@ func (m FieldMap) getFieldNoLock(tag Tag, parser FieldValueReader) MessageReject // GetBytes is a zero-copy GetField wrapper for []bytes fields. func (m FieldMap) GetBytes(tag Tag) ([]byte, MessageRejectError) { - m.rwLock.RLock() - defer m.rwLock.RUnlock() - - f, ok := m.tagLookup[tag] + f, ok := m.tagLookup.Get(tag) if !ok { return nil, ConditionallyRequiredFieldMissing(tag) } @@ -144,7 +109,7 @@ func (m FieldMap) GetBytes(tag Tag) ([]byte, MessageRejectError) { // getBytesNoLock is a lock free zero-copy GetField wrapper for []bytes fields. func (m FieldMap) getBytesNoLock(tag Tag) ([]byte, MessageRejectError) { - f, ok := m.tagLookup[tag] + f, ok := m.tagLookup.Get(tag) if !ok { return nil, ConditionallyRequiredFieldMissing(tag) } @@ -176,26 +141,8 @@ func (m FieldMap) GetInt(tag Tag) (int, MessageRejectError) { return int(val), err } -// GetInt is a lock free GetField wrapper for int fields. -func (m FieldMap) getIntNoLock(tag Tag) (int, MessageRejectError) { - bytes, err := m.getBytesNoLock(tag) - if err != nil { - return 0, err - } - - var val FIXInt - if val.Read(bytes) != nil { - err = IncorrectDataFormatForValue(tag) - } - - return int(val), err -} - // GetTime is a GetField wrapper for utc timestamp fields. func (m FieldMap) GetTime(tag Tag) (t time.Time, err MessageRejectError) { - m.rwLock.RLock() - defer m.rwLock.RUnlock() - bytes, err := m.GetBytes(tag) if err != nil { return @@ -218,21 +165,9 @@ func (m FieldMap) GetString(tag Tag) (string, MessageRejectError) { return string(val), nil } -// GetString is a GetField wrapper for string fields. -func (m FieldMap) getStringNoLock(tag Tag) (string, MessageRejectError) { - var val FIXString - if err := m.getFieldNoLock(tag, &val); err != nil { - return "", err - } - return string(val), nil -} - // GetGroup is a Get function specific to Group Fields. func (m FieldMap) GetGroup(parser FieldGroupReader) MessageRejectError { - m.rwLock.RLock() - defer m.rwLock.RUnlock() - - f, ok := m.tagLookup[parser.Tag()] + f, ok := m.tagLookup.Get(parser.Tag()) if !ok { return ConditionallyRequiredFieldMissing(parser.Tag()) } @@ -277,67 +212,38 @@ func (m *FieldMap) SetString(tag Tag, value string) *FieldMap { // Remove removes a tag from field map. func (m *FieldMap) Remove(tag Tag) { - m.rwLock.Lock() - defer m.rwLock.Unlock() - - delete(m.tagLookup, tag) + m.tagLookup.Del(tag) } // Clear purges all fields from field map. func (m *FieldMap) Clear() { - m.rwLock.Lock() - defer m.rwLock.Unlock() - - m.tags = m.tags[0:0] - for k := range m.tagLookup { - delete(m.tagLookup, k) - } -} - -func (m *FieldMap) clearNoLock() { - m.tags = m.tags[0:0] - for k := range m.tagLookup { - delete(m.tagLookup, k) - } + m.tagLookup.Clear() } // CopyInto overwrites the given FieldMap with this one. func (m *FieldMap) CopyInto(to *FieldMap) { - m.rwLock.RLock() - defer m.rwLock.RUnlock() - - to.tagLookup = make(map[Tag]field) - for tag, f := range m.tagLookup { + to.tagLookup = haxmap.New[Tag, field](m.tagLookup.Len()) + m.tagLookup.ForEach(func(tag Tag, f field) bool { clone := make(field, 1) clone[0] = f[0] - to.tagLookup[tag] = clone - } - to.tags = make([]Tag, len(m.tags)) - copy(to.tags, m.tags) + to.tagLookup.Set(tag, clone) + return true + }) to.compare = m.compare } func (m *FieldMap) add(f field) { - t := fieldTag(f) - if _, ok := m.tagLookup[t]; !ok { - m.tags = append(m.tags, t) - } - - m.tagLookup[t] = f + m.tagLookup.Set(fieldTag(f), f) } func (m *FieldMap) getOrCreate(tag Tag) field { - m.rwLock.Lock() - defer m.rwLock.Unlock() - - if f, ok := m.tagLookup[tag]; ok { + if f, ok := m.tagLookup.Get(tag); ok { f = f[:1] return f } f := make(field, 1) - m.tagLookup[tag] = f - m.tags = append(m.tags, tag) + m.tagLookup.Set(tag, f) return f } @@ -350,39 +256,27 @@ func (m *FieldMap) Set(field FieldWriter) *FieldMap { // SetGroup is a setter specific to group fields. func (m *FieldMap) SetGroup(field FieldGroupWriter) *FieldMap { - m.rwLock.Lock() - defer m.rwLock.Unlock() - - _, ok := m.tagLookup[field.Tag()] - if !ok { - m.tags = append(m.tags, field.Tag()) - } - m.tagLookup[field.Tag()] = field.Write() + m.tagLookup.Set(field.Tag(), field.Write()) return m } func (m *FieldMap) sortedTags() []Tag { - sort.Sort(m) - return m.tags + tags := m.Tags() + slices.SortFunc(tags, m.compare) + return tags } func (m FieldMap) write(buffer *bytes.Buffer) { - m.rwLock.Lock() - defer m.rwLock.Unlock() - for _, tag := range m.sortedTags() { - if f, ok := m.tagLookup[tag]; ok { + if f, ok := m.tagLookup.Get(tag); ok { writeField(f, buffer) } } } func (m FieldMap) total() int { - m.rwLock.RLock() - defer m.rwLock.RUnlock() - total := 0 - for _, fields := range m.tagLookup { + m.tagLookup.ForEach(func(tag Tag, fields field) bool { for _, tv := range fields { switch tv.tag { case tagCheckSum: // Tag does not contribute to total. @@ -390,17 +284,15 @@ func (m FieldMap) total() int { total += tv.total() } } - } + return true + }) return total } func (m FieldMap) length() int { - m.rwLock.RLock() - defer m.rwLock.RUnlock() - length := 0 - for _, fields := range m.tagLookup { + m.tagLookup.ForEach(func(tag Tag, fields field) bool { for _, tv := range fields { switch tv.tag { case tagBeginString, tagBodyLength, tagCheckSum: // Tags do not contribute to length. @@ -408,7 +300,8 @@ func (m FieldMap) length() int { length += tv.length() } } - } + return true + }) return length } diff --git a/go.mod b/go.mod index 8b36cf024..bfebe1791 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,14 @@ module github.com/quickfixgo/quickfix go 1.21 require ( + github.com/alphadose/haxmap v1.4.1 github.com/mattn/go-sqlite3 v1.14.22 github.com/pires/go-proxyproto v0.7.0 github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.4.0 - github.com/stretchr/testify v1.8.4 - go.mongodb.org/mongo-driver v1.15.0 - golang.org/x/net v0.24.0 + github.com/stretchr/testify v1.10.0 + go.mongodb.org/mongo-driver v1.17.1 + golang.org/x/net v0.33.0 ) require ( @@ -17,16 +18,17 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/klauspost/compress v1.15.12 // indirect github.com/kr/text v0.2.0 // indirect - github.com/montanaflynn/stats v0.6.6 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect - github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/text v0.14.0 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index a7e47992c..997c6d271 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,12 @@ +github.com/alphadose/haxmap v1.4.1 h1:VtD6VCxUkjNIfJk/aWdYFfOzrRddDFjmvmRmILg7x8Q= +github.com/alphadose/haxmap v1.4.1/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= @@ -16,8 +17,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/montanaflynn/stats v0.6.6 h1:Duep6KMIDpY4Yo11iFsvyqJDyfzLF9+sndUKT+v64GQ= -github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -26,43 +27,38 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= -go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= +go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE= +golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -73,17 +69,14 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/message.go b/message.go index 3e1e7202b..7705e5763 100644 --- a/message.go +++ b/message.go @@ -41,7 +41,7 @@ type msgParser struct { } // in the message header, the first 3 tags in the message header must be 8,9,35. -func headerFieldOrdering(i, j Tag) bool { +func headerFieldOrdering(i, j Tag) int { var ordering = func(t Tag) uint32 { switch t { case tagBeginString: @@ -60,12 +60,12 @@ func headerFieldOrdering(i, j Tag) bool { switch { case orderi < orderj: - return true + return -1 case orderi > orderj: - return false + return 1 } - return i < j + return int(i - j) } // Init initializes the Header instance. @@ -85,15 +85,15 @@ func (b *Body) Init() { type Trailer struct{ FieldMap } // In the trailer, CheckSum (tag 10) must be last. -func trailerFieldOrdering(i, j Tag) bool { +func trailerFieldOrdering(i, j Tag) int { switch { case i == tagCheckSum: - return false + return 1 case j == tagCheckSum: - return true + return -1 } - return i < j + return int(i - j) } // Init initializes the FIX message. @@ -181,17 +181,10 @@ func ParseMessageWithDataDictionary( // doParsing executes the message parsing process. func doParsing(mp *msgParser) (err error) { - mp.msg.Header.rwLock.Lock() - defer mp.msg.Header.rwLock.Unlock() - mp.msg.Body.rwLock.Lock() - defer mp.msg.Body.rwLock.Unlock() - mp.msg.Trailer.rwLock.Lock() - defer mp.msg.Trailer.rwLock.Unlock() - // Initialize for parsing. - mp.msg.Header.clearNoLock() - mp.msg.Body.clearNoLock() - mp.msg.Trailer.clearNoLock() + mp.msg.Header.Clear() + mp.msg.Body.Clear() + mp.msg.Trailer.Clear() // Allocate expected message fields in one chunk. fieldCount := bytes.Count(mp.rawBytes, []byte{'\001'}) @@ -269,7 +262,7 @@ func doParsing(mp *msgParser) (err error) { } if mp.parsedFieldBytes.tag == tagXMLDataLen { - xmlDataLen, _ = mp.msg.Header.getIntNoLock(tagXMLDataLen) + xmlDataLen, _ = mp.msg.Header.GetInt(tagXMLDataLen) } mp.fieldIndex++ } @@ -294,7 +287,7 @@ func doParsing(mp *msgParser) (err error) { } } - bodyLength, err := mp.msg.Header.getIntNoLock(tagBodyLength) + bodyLength, err := mp.msg.Header.GetInt(tagBodyLength) if err != nil { err = parseError{OrigError: err.Error()} } else if length != bodyLength && !xmlDataMsg { @@ -328,7 +321,7 @@ func parseGroup(mp *msgParser, tags []Tag) { // Add the field member to the group. dm = append(dm, *mp.parsedFieldBytes) } else if isHeaderField(mp.parsedFieldBytes.tag, mp.transportDataDictionary) { - // Found a header tag for some reason.. + // Found a header tag for some reason. mp.msg.Body.add(dm) mp.msg.Header.add(mp.msg.fields[mp.fieldIndex : mp.fieldIndex+1]) break @@ -375,7 +368,7 @@ func parseGroup(mp *msgParser, tags []Tag) { // tags slice will contain multiple tags if the tag in question is found while processing a group already. func isNumInGroupField(msg *Message, tags []Tag, appDataDictionary *datadictionary.DataDictionary) bool { if appDataDictionary != nil { - msgt, err := msg.msgTypeNoLock() + msgt, err := msg.MsgType() if err != nil { return false } @@ -408,7 +401,7 @@ func isNumInGroupField(msg *Message, tags []Tag, appDataDictionary *datadictiona // tags slice will contain multiple tags if the tag in question is found while processing a group already. func getGroupFields(msg *Message, tags []Tag, appDataDictionary *datadictionary.DataDictionary) (fields []*datadictionary.FieldDef) { if appDataDictionary != nil { - msgt, err := msg.msgTypeNoLock() + msgt, err := msg.MsgType() if err != nil { return } @@ -478,10 +471,6 @@ func (m *Message) MsgType() (string, MessageRejectError) { return m.Header.GetString(tagMsgType) } -func (m *Message) msgTypeNoLock() (string, MessageRejectError) { - return m.Header.getStringNoLock(tagMsgType) -} - // IsMsgTypeOf returns true if the Header contains MsgType (tag 35) field and its value is the specified one. func (m *Message) IsMsgTypeOf(msgType string) bool { if v, err := m.MsgType(); err == nil { diff --git a/repeating_group.go b/repeating_group.go index 853cc7d35..b494e6e04 100644 --- a/repeating_group.go +++ b/repeating_group.go @@ -129,13 +129,11 @@ func (f RepeatingGroup) Write() []TagValue { for _, group := range f.groups { tags := group.sortedTags() - group.rwLock.RLock() for _, tag := range tags { - if fields, ok := group.tagLookup[tag]; ok { + if fields, ok := group.tagLookup.Get(tag); ok { tvs = append(tvs, fields...) } } - group.rwLock.RUnlock() } return tvs @@ -159,7 +157,7 @@ func (f RepeatingGroup) groupTagOrder() tagOrder { tagMap[f.Tag()] = i } - return func(i, j Tag) bool { + return func(i, j Tag) int { orderi := math.MaxInt32 orderj := math.MaxInt32 @@ -171,7 +169,7 @@ func (f RepeatingGroup) groupTagOrder() tagOrder { orderj = jIndex } - return orderi < orderj + return orderi - orderj } } @@ -215,10 +213,7 @@ func (f *RepeatingGroup) Read(tv []TagValue) ([]TagValue, error) { f.groups = append(f.groups, group) } - group.rwLock.Lock() - group.tagLookup[tvRange[0].tag] = tvRange - group.tags = append(group.tags, gi.Tag()) - group.rwLock.Unlock() + group.tagLookup.Set(tvRange[0].tag, tvRange) } if len(f.groups) != expectedGroupSize { diff --git a/repeating_group_test.go b/repeating_group_test.go index abd316d78..437e41790 100644 --- a/repeating_group_test.go +++ b/repeating_group_test.go @@ -200,8 +200,6 @@ func TestRepeatingGroup_Read(t *testing.T) { for _, expected := range test.expectedGroupTvs[g] { var actual FIXString require.Nil(t, group.GetField(expected.tag, &actual)) - require.NotNil(t, group.tags) - require.Equal(t, len(group.tags), len(group.tagLookup)) if !bytes.Equal(expected.value, []byte(actual)) { t.Errorf("%v, %v: expected %s, got %s", g, expected.tag, expected.value, actual)