@@ -17,9 +17,10 @@ package quickfix
1717
1818import (
1919 "bytes"
20- "sort"
21- "sync"
20+ "slices"
2221 "time"
22+
23+ "github.com/alphadose/haxmap"
2324)
2425
2526// field stores a slice of TagValues.
@@ -40,46 +41,33 @@ func writeField(f field, buffer *bytes.Buffer) {
4041}
4142
4243// tagOrder true if tag i should occur before tag j.
43- type tagOrder func (i , j Tag ) bool
44-
45- type tagSort struct {
46- tags []Tag
47- compare tagOrder
48- }
49-
50- func (t tagSort ) Len () int { return len (t .tags ) }
51- func (t tagSort ) Swap (i , j int ) { t .tags [i ], t .tags [j ] = t .tags [j ], t .tags [i ] }
52- func (t tagSort ) Less (i , j int ) bool { return t .compare (t .tags [i ], t .tags [j ]) }
44+ type tagOrder func (i , j Tag ) int
5345
5446// FieldMap is a collection of fix fields that make up a fix message.
5547type FieldMap struct {
56- tagLookup map [Tag ]field
57- tagSort
58- rwLock * sync.RWMutex
48+ tagLookup * haxmap.Map [Tag , field ]
49+ compare tagOrder
5950}
6051
6152// ascending tags.
62- func normalFieldOrder (i , j Tag ) bool { return i < j }
53+ func normalFieldOrder (i , j Tag ) int { return int ( i - j ) }
6354
6455func (m * FieldMap ) init () {
6556 m .initWithOrdering (normalFieldOrder )
6657}
6758
6859func (m * FieldMap ) initWithOrdering (ordering tagOrder ) {
69- m .rwLock = & sync.RWMutex {}
70- m .tagLookup = make (map [Tag ]field )
60+ m .tagLookup = haxmap .New [Tag , field ]()
7161 m .compare = ordering
7262}
7363
7464// Tags returns all of the Field Tags in this FieldMap.
7565func (m FieldMap ) Tags () []Tag {
76- m .rwLock .RLock ()
77- defer m .rwLock .RUnlock ()
78-
79- tags := make ([]Tag , 0 , len (m .tagLookup ))
80- for t := range m .tagLookup {
81- tags = append (tags , t )
82- }
66+ var tags []Tag
67+ m .tagLookup .ForEach (func (tag Tag , _ field ) bool {
68+ tags = append (tags , tag )
69+ return true
70+ })
8371
8472 return tags
8573}
@@ -91,33 +79,13 @@ func (m FieldMap) Get(parser Field) MessageRejectError {
9179
9280// Has returns true if the Tag is present in this FieldMap.
9381func (m FieldMap ) Has (tag Tag ) bool {
94- m .rwLock .RLock ()
95- defer m .rwLock .RUnlock ()
96-
97- _ , ok := m .tagLookup [tag ]
82+ _ , ok := m .tagLookup .Get (tag )
9883 return ok
9984}
10085
10186// GetField parses of a field with Tag tag. Returned reject may indicate the field is not present, or the field value is invalid.
10287func (m FieldMap ) GetField (tag Tag , parser FieldValueReader ) MessageRejectError {
103- m .rwLock .RLock ()
104- defer m .rwLock .RUnlock ()
105-
106- f , ok := m .tagLookup [tag ]
107- if ! ok {
108- return ConditionallyRequiredFieldMissing (tag )
109- }
110-
111- if err := parser .Read (f [0 ].value ); err != nil {
112- return IncorrectDataFormatForValue (tag )
113- }
114-
115- return nil
116- }
117-
118- // GetField parses of a field with Tag tag. Returned reject may indicate the field is not present, or the field value is invalid.
119- func (m FieldMap ) getFieldNoLock (tag Tag , parser FieldValueReader ) MessageRejectError {
120- f , ok := m .tagLookup [tag ]
88+ f , ok := m .tagLookup .Get (tag )
12189 if ! ok {
12290 return ConditionallyRequiredFieldMissing (tag )
12391 }
@@ -131,20 +99,7 @@ func (m FieldMap) getFieldNoLock(tag Tag, parser FieldValueReader) MessageReject
13199
132100// GetBytes is a zero-copy GetField wrapper for []bytes fields.
133101func (m FieldMap ) GetBytes (tag Tag ) ([]byte , MessageRejectError ) {
134- m .rwLock .RLock ()
135- defer m .rwLock .RUnlock ()
136-
137- f , ok := m .tagLookup [tag ]
138- if ! ok {
139- return nil , ConditionallyRequiredFieldMissing (tag )
140- }
141-
142- return f [0 ].value , nil
143- }
144-
145- // getBytesNoLock is a lock free zero-copy GetField wrapper for []bytes fields.
146- func (m FieldMap ) getBytesNoLock (tag Tag ) ([]byte , MessageRejectError ) {
147- f , ok := m .tagLookup [tag ]
102+ f , ok := m .tagLookup .Get (tag )
148103 if ! ok {
149104 return nil , ConditionallyRequiredFieldMissing (tag )
150105 }
@@ -176,26 +131,8 @@ func (m FieldMap) GetInt(tag Tag) (int, MessageRejectError) {
176131 return int (val ), err
177132}
178133
179- // GetInt is a lock free GetField wrapper for int fields.
180- func (m FieldMap ) getIntNoLock (tag Tag ) (int , MessageRejectError ) {
181- bytes , err := m .getBytesNoLock (tag )
182- if err != nil {
183- return 0 , err
184- }
185-
186- var val FIXInt
187- if val .Read (bytes ) != nil {
188- err = IncorrectDataFormatForValue (tag )
189- }
190-
191- return int (val ), err
192- }
193-
194134// GetTime is a GetField wrapper for utc timestamp fields.
195135func (m FieldMap ) GetTime (tag Tag ) (t time.Time , err MessageRejectError ) {
196- m .rwLock .RLock ()
197- defer m .rwLock .RUnlock ()
198-
199136 bytes , err := m .GetBytes (tag )
200137 if err != nil {
201138 return
@@ -218,21 +155,9 @@ func (m FieldMap) GetString(tag Tag) (string, MessageRejectError) {
218155 return string (val ), nil
219156}
220157
221- // GetString is a GetField wrapper for string fields.
222- func (m FieldMap ) getStringNoLock (tag Tag ) (string , MessageRejectError ) {
223- var val FIXString
224- if err := m .getFieldNoLock (tag , & val ); err != nil {
225- return "" , err
226- }
227- return string (val ), nil
228- }
229-
230158// GetGroup is a Get function specific to Group Fields.
231159func (m FieldMap ) GetGroup (parser FieldGroupReader ) MessageRejectError {
232- m .rwLock .RLock ()
233- defer m .rwLock .RUnlock ()
234-
235- f , ok := m .tagLookup [parser .Tag ()]
160+ f , ok := m .tagLookup .Get (parser .Tag ())
236161 if ! ok {
237162 return ConditionallyRequiredFieldMissing (parser .Tag ())
238163 }
@@ -277,67 +202,38 @@ func (m *FieldMap) SetString(tag Tag, value string) *FieldMap {
277202
278203// Remove removes a tag from field map.
279204func (m * FieldMap ) Remove (tag Tag ) {
280- m .rwLock .Lock ()
281- defer m .rwLock .Unlock ()
282-
283- delete (m .tagLookup , tag )
205+ m .tagLookup .Del (tag )
284206}
285207
286208// Clear purges all fields from field map.
287209func (m * FieldMap ) Clear () {
288- m .rwLock .Lock ()
289- defer m .rwLock .Unlock ()
290-
291- m .tags = m .tags [0 :0 ]
292- for k := range m .tagLookup {
293- delete (m .tagLookup , k )
294- }
295- }
296-
297- func (m * FieldMap ) clearNoLock () {
298- m .tags = m .tags [0 :0 ]
299- for k := range m .tagLookup {
300- delete (m .tagLookup , k )
301- }
210+ m .tagLookup .Clear ()
302211}
303212
304213// CopyInto overwrites the given FieldMap with this one.
305214func (m * FieldMap ) CopyInto (to * FieldMap ) {
306- m .rwLock .RLock ()
307- defer m .rwLock .RUnlock ()
308-
309- to .tagLookup = make (map [Tag ]field )
310- for tag , f := range m .tagLookup {
215+ to .tagLookup = haxmap .New [Tag , field ]()
216+ m .tagLookup .ForEach (func (tag Tag , f field ) bool {
311217 clone := make (field , 1 )
312218 clone [0 ] = f [0 ]
313- to .tagLookup [tag ] = clone
314- }
315- to .tags = make ([]Tag , len (m .tags ))
316- copy (to .tags , m .tags )
219+ to .tagLookup .Set (tag , clone )
220+ return true
221+ })
317222 to .compare = m .compare
318223}
319224
320225func (m * FieldMap ) add (f field ) {
321- t := fieldTag (f )
322- if _ , ok := m .tagLookup [t ]; ! ok {
323- m .tags = append (m .tags , t )
324- }
325-
326- m .tagLookup [t ] = f
226+ m .tagLookup .Set (fieldTag (f ), f )
327227}
328228
329229func (m * FieldMap ) getOrCreate (tag Tag ) field {
330- m .rwLock .Lock ()
331- defer m .rwLock .Unlock ()
332-
333- if f , ok := m .tagLookup [tag ]; ok {
230+ if f , ok := m .tagLookup .Get (tag ); ok {
334231 f = f [:1 ]
335232 return f
336233 }
337234
338235 f := make (field , 1 )
339- m .tagLookup [tag ] = f
340- m .tags = append (m .tags , tag )
236+ m .tagLookup .Set (tag , f )
341237 return f
342238}
343239
@@ -350,65 +246,52 @@ func (m *FieldMap) Set(field FieldWriter) *FieldMap {
350246
351247// SetGroup is a setter specific to group fields.
352248func (m * FieldMap ) SetGroup (field FieldGroupWriter ) * FieldMap {
353- m .rwLock .Lock ()
354- defer m .rwLock .Unlock ()
355-
356- _ , ok := m .tagLookup [field .Tag ()]
357- if ! ok {
358- m .tags = append (m .tags , field .Tag ())
359- }
360- m .tagLookup [field .Tag ()] = field .Write ()
249+ m .tagLookup .Set (field .Tag (), field .Write ())
361250 return m
362251}
363252
364253func (m * FieldMap ) sortedTags () []Tag {
365- sort .Sort (m )
366- return m .tags
254+ tags := m .Tags ()
255+ slices .SortFunc (tags , m .compare )
256+ return tags
367257}
368258
369259func (m FieldMap ) write (buffer * bytes.Buffer ) {
370- m .rwLock .Lock ()
371- defer m .rwLock .Unlock ()
372-
373260 for _ , tag := range m .sortedTags () {
374- if f , ok := m .tagLookup [ tag ] ; ok {
261+ if f , ok := m .tagLookup . Get ( tag ) ; ok {
375262 writeField (f , buffer )
376263 }
377264 }
378265}
379266
380267func (m FieldMap ) total () int {
381- m .rwLock .RLock ()
382- defer m .rwLock .RUnlock ()
383-
384268 total := 0
385- for _ , fields := range m . tagLookup {
269+ m . tagLookup . ForEach ( func ( _ Tag , fields field ) bool {
386270 for _ , tv := range fields {
387271 switch tv .tag {
388272 case tagCheckSum : // Tag does not contribute to total.
389273 default :
390274 total += tv .total ()
391275 }
392276 }
393- }
277+ return true
278+ })
394279
395280 return total
396281}
397282
398283func (m FieldMap ) length () int {
399- m .rwLock .RLock ()
400- defer m .rwLock .RUnlock ()
401-
402284 length := 0
403- for _ , fields := range m . tagLookup {
285+ m . tagLookup . ForEach ( func ( _ Tag , fields field ) bool {
404286 for _ , tv := range fields {
405287 switch tv .tag {
406288 case tagBeginString , tagBodyLength , tagCheckSum : // Tags do not contribute to length.
407289 default :
408290 length += tv .length ()
409291 }
410292 }
411- }
293+ return true
294+ })
412295
413296 return length
414297}
0 commit comments