@@ -76,17 +76,46 @@ type Group struct{ FieldMap }
7676
7777// RepeatingGroup is a FIX Repeating Group type.
7878type RepeatingGroup struct {
79- tag Tag
80- template GroupTemplate
81- groups []* Group
79+ tag Tag
80+ template GroupTemplate
81+ groups []* Group
82+ tagOrder tagOrder // cached to avoid closure allocation per Read/Add call
83+ delimiter Tag // cached to avoid template[0].Tag() calls
8284}
8385
8486// NewRepeatingGroup returns an initilized RepeatingGroup instance.
8587func NewRepeatingGroup (tag Tag , template GroupTemplate ) * RepeatingGroup {
86- return & RepeatingGroup {
88+ rg := & RepeatingGroup {
8789 tag : tag ,
8890 template : template ,
8991 }
92+ rg .initCachedValues ()
93+ return rg
94+ }
95+
96+ // initCachedValues pre-computes tag ordering and delimiter once per RepeatingGroup.
97+ // Previously, groupTagOrder() created a new closure with a new map on every call,
98+ // causing significant allocations during message parsing with repeating groups.
99+ func (f * RepeatingGroup ) initCachedValues () {
100+ if len (f .template ) > 0 {
101+ f .delimiter = f .template [0 ].Tag ()
102+ }
103+
104+ tagMap := make (map [Tag ]int , len (f .template ))
105+ for i , tmpl := range f .template {
106+ tagMap [tmpl .Tag ()] = i
107+ }
108+ f .tagOrder = func (i , j Tag ) bool {
109+ orderi := math .MaxInt32
110+ orderj := math .MaxInt32
111+ if iIndex , ok := tagMap [i ]; ok {
112+ orderi = iIndex
113+ }
114+ if jIndex , ok := tagMap [j ]; ok {
115+ orderj = jIndex
116+ }
117+ return orderi < orderj
118+ }
90119}
91120
92121// Tag returns the Tag for this repeating Group.
@@ -96,10 +125,12 @@ func (f RepeatingGroup) Tag() Tag {
96125
97126// Clone makes a copy of this RepeatingGroup (tag, template).
98127func (f RepeatingGroup ) Clone () GroupItem {
99- return & RepeatingGroup {
128+ rg := & RepeatingGroup {
100129 tag : f .tag ,
101130 template : f .template .Clone (),
102131 }
132+ rg .initCachedValues ()
133+ return rg
103134}
104135
105136// Len returns the number of Groups in this RepeatingGroup.
@@ -115,7 +146,10 @@ func (f RepeatingGroup) Get(i int) *Group {
115146// Add appends a new group to the RepeatingGroup and returns the new Group.
116147func (f * RepeatingGroup ) Add () * Group {
117148 g := new (Group )
118- g .initWithOrdering (f .groupTagOrder ())
149+ if f .tagOrder == nil {
150+ f .initCachedValues ()
151+ }
152+ g .initWithOrdering (f .tagOrder )
119153
120154 f .groups = append (f .groups , g )
121155 return g
@@ -153,34 +187,15 @@ func (f RepeatingGroup) findItemInGroupTemplate(t Tag) (item GroupItem, ok bool)
153187 return
154188}
155189
156- func (f RepeatingGroup ) groupTagOrder () tagOrder {
157- tagMap := make (map [Tag ]int )
158- for i , f := range f .template {
159- tagMap [f .Tag ()] = i
190+ func (f RepeatingGroup ) getDelimiter () Tag {
191+ if f .delimiter != 0 {
192+ return f .delimiter
160193 }
161-
162- return func (i , j Tag ) bool {
163- orderi := math .MaxInt32
164- orderj := math .MaxInt32
165-
166- if iIndex , ok := tagMap [i ]; ok {
167- orderi = iIndex
168- }
169-
170- if jIndex , ok := tagMap [j ]; ok {
171- orderj = jIndex
172- }
173-
174- return orderi < orderj
175- }
176- }
177-
178- func (f RepeatingGroup ) delimiter () Tag {
179194 return f .template [0 ].Tag ()
180195}
181196
182197func (f RepeatingGroup ) isDelimiter (t Tag ) bool {
183- return t == f .delimiter ()
198+ return t == f .getDelimiter ()
184199}
185200
186201func (f * RepeatingGroup ) Read (tv []TagValue ) ([]TagValue , error ) {
@@ -194,7 +209,11 @@ func (f *RepeatingGroup) Read(tv []TagValue) ([]TagValue, error) {
194209 }
195210
196211 tv = tv [1 :cap (tv )]
197- tagOrdering := f .groupTagOrder ()
212+ // Use cached tag ordering, initialize if needed.
213+ if f .tagOrder == nil {
214+ f .initCachedValues ()
215+ }
216+ tagOrdering := f .tagOrder
198217 group := new (Group )
199218 group .initWithOrdering (tagOrdering )
200219 for len (tv ) > 0 {
@@ -222,7 +241,7 @@ func (f *RepeatingGroup) Read(tv []TagValue) ([]TagValue, error) {
222241 }
223242
224243 if len (f .groups ) != expectedGroupSize {
225- return tv , repeatingGroupFieldsOutOfOrder (f .tag , fmt .Sprintf ("group %v: template is wrong or delimiter %v not found: expected %v groups, but found %v" , f .tag , f .delimiter (), expectedGroupSize , len (f .groups )))
244+ return tv , repeatingGroupFieldsOutOfOrder (f .tag , fmt .Sprintf ("group %v: template is wrong or delimiter %v not found: expected %v groups, but found %v" , f .tag , f .getDelimiter (), expectedGroupSize , len (f .groups )))
226245 }
227246
228247 return tv , err
0 commit comments