@@ -17,9 +17,10 @@ package quickfix
17
17
18
18
import (
19
19
"bytes"
20
- "sort"
21
- "sync"
20
+ "slices"
22
21
"time"
22
+
23
+ "github.com/alphadose/haxmap"
23
24
)
24
25
25
26
// field stores a slice of TagValues.
@@ -40,46 +41,33 @@ func writeField(f field, buffer *bytes.Buffer) {
40
41
}
41
42
42
43
// 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
53
45
54
46
// FieldMap is a collection of fix fields that make up a fix message.
55
47
type FieldMap struct {
56
- tagLookup map [Tag ]field
57
- tagSort
58
- rwLock * sync.RWMutex
48
+ tagLookup * haxmap.Map [Tag , field ]
49
+ compare tagOrder
59
50
}
60
51
61
52
// ascending tags.
62
- func normalFieldOrder (i , j Tag ) bool { return i < j }
53
+ func normalFieldOrder (i , j Tag ) int { return int ( i - j ) }
63
54
64
55
func (m * FieldMap ) init () {
65
56
m .initWithOrdering (normalFieldOrder )
66
57
}
67
58
68
59
func (m * FieldMap ) initWithOrdering (ordering tagOrder ) {
69
- m .rwLock = & sync.RWMutex {}
70
- m .tagLookup = make (map [Tag ]field )
60
+ m .tagLookup = haxmap .New [Tag , field ]()
71
61
m .compare = ordering
72
62
}
73
63
74
64
// Tags returns all of the Field Tags in this FieldMap.
75
65
func (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
+ })
83
71
84
72
return tags
85
73
}
@@ -91,33 +79,13 @@ func (m FieldMap) Get(parser Field) MessageRejectError {
91
79
92
80
// Has returns true if the Tag is present in this FieldMap.
93
81
func (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 )
98
83
return ok
99
84
}
100
85
101
86
// GetField parses of a field with Tag tag. Returned reject may indicate the field is not present, or the field value is invalid.
102
87
func (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 )
121
89
if ! ok {
122
90
return ConditionallyRequiredFieldMissing (tag )
123
91
}
@@ -131,20 +99,7 @@ func (m FieldMap) getFieldNoLock(tag Tag, parser FieldValueReader) MessageReject
131
99
132
100
// GetBytes is a zero-copy GetField wrapper for []bytes fields.
133
101
func (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 )
148
103
if ! ok {
149
104
return nil , ConditionallyRequiredFieldMissing (tag )
150
105
}
@@ -176,26 +131,8 @@ func (m FieldMap) GetInt(tag Tag) (int, MessageRejectError) {
176
131
return int (val ), err
177
132
}
178
133
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
-
194
134
// GetTime is a GetField wrapper for utc timestamp fields.
195
135
func (m FieldMap ) GetTime (tag Tag ) (t time.Time , err MessageRejectError ) {
196
- m .rwLock .RLock ()
197
- defer m .rwLock .RUnlock ()
198
-
199
136
bytes , err := m .GetBytes (tag )
200
137
if err != nil {
201
138
return
@@ -218,21 +155,9 @@ func (m FieldMap) GetString(tag Tag) (string, MessageRejectError) {
218
155
return string (val ), nil
219
156
}
220
157
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
-
230
158
// GetGroup is a Get function specific to Group Fields.
231
159
func (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 ())
236
161
if ! ok {
237
162
return ConditionallyRequiredFieldMissing (parser .Tag ())
238
163
}
@@ -277,67 +202,38 @@ func (m *FieldMap) SetString(tag Tag, value string) *FieldMap {
277
202
278
203
// Remove removes a tag from field map.
279
204
func (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 )
284
206
}
285
207
286
208
// Clear purges all fields from field map.
287
209
func (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 ()
302
211
}
303
212
304
213
// CopyInto overwrites the given FieldMap with this one.
305
214
func (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 {
311
217
clone := make (field , 1 )
312
218
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
+ })
317
222
to .compare = m .compare
318
223
}
319
224
320
225
func (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 )
327
227
}
328
228
329
229
func (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 {
334
231
f = f [:1 ]
335
232
return f
336
233
}
337
234
338
235
f := make (field , 1 )
339
- m .tagLookup [tag ] = f
340
- m .tags = append (m .tags , tag )
236
+ m .tagLookup .Set (tag , f )
341
237
return f
342
238
}
343
239
@@ -350,65 +246,52 @@ func (m *FieldMap) Set(field FieldWriter) *FieldMap {
350
246
351
247
// SetGroup is a setter specific to group fields.
352
248
func (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 ())
361
250
return m
362
251
}
363
252
364
253
func (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
367
257
}
368
258
369
259
func (m FieldMap ) write (buffer * bytes.Buffer ) {
370
- m .rwLock .Lock ()
371
- defer m .rwLock .Unlock ()
372
-
373
260
for _ , tag := range m .sortedTags () {
374
- if f , ok := m .tagLookup [ tag ] ; ok {
261
+ if f , ok := m .tagLookup . Get ( tag ) ; ok {
375
262
writeField (f , buffer )
376
263
}
377
264
}
378
265
}
379
266
380
267
func (m FieldMap ) total () int {
381
- m .rwLock .RLock ()
382
- defer m .rwLock .RUnlock ()
383
-
384
268
total := 0
385
- for _ , fields := range m . tagLookup {
269
+ m . tagLookup . ForEach ( func ( _ Tag , fields field ) bool {
386
270
for _ , tv := range fields {
387
271
switch tv .tag {
388
272
case tagCheckSum : // Tag does not contribute to total.
389
273
default :
390
274
total += tv .total ()
391
275
}
392
276
}
393
- }
277
+ return true
278
+ })
394
279
395
280
return total
396
281
}
397
282
398
283
func (m FieldMap ) length () int {
399
- m .rwLock .RLock ()
400
- defer m .rwLock .RUnlock ()
401
-
402
284
length := 0
403
- for _ , fields := range m . tagLookup {
285
+ m . tagLookup . ForEach ( func ( _ Tag , fields field ) bool {
404
286
for _ , tv := range fields {
405
287
switch tv .tag {
406
288
case tagBeginString , tagBodyLength , tagCheckSum : // Tags do not contribute to length.
407
289
default :
408
290
length += tv .length ()
409
291
}
410
292
}
411
- }
293
+ return true
294
+ })
412
295
413
296
return length
414
297
}
0 commit comments