Skip to content

Commit 6861abb

Browse files
authored
Merge pull request #274 from cbusbey/outbound_allocs
reduces the number of allocations for re-used outbound messages
2 parents 2b49d12 + aaa7a0c commit 6861abb

File tree

6 files changed

+75
-61
lines changed

6 files changed

+75
-61
lines changed

field_map.go

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,30 @@ import (
66
"time"
77
)
88

9-
//tagValues stores a slice of TagValues
10-
type tagValues struct {
9+
//field stores a slice of TagValues
10+
type field struct {
1111
tvs []TagValue
1212
}
1313

14-
func (f *tagValues) Tag() Tag {
14+
func (f *field) Tag() Tag {
1515
return f.tvs[0].tag
1616
}
1717

18+
func (f *field) init(tag Tag, value []byte) {
19+
if len(f.tvs) == 0 {
20+
f.tvs = make([]TagValue, 1)
21+
} else {
22+
f.tvs = f.tvs[:1]
23+
}
24+
f.tvs[0].init(tag, value)
25+
}
26+
27+
func (f *field) write(buffer *bytes.Buffer) {
28+
for _, tv := range f.tvs {
29+
buffer.Write(tv.bytes)
30+
}
31+
}
32+
1833
// tagOrder true if tag i should occur before tag j
1934
type tagOrder func(i, j Tag) bool
2035

@@ -29,7 +44,7 @@ func (t tagSort) Less(i, j int) bool { return t.compare(t.tags[i], t.tags[j]) }
2944

3045
//FieldMap is a collection of fix fields that make up a fix message.
3146
type FieldMap struct {
32-
tagLookup map[Tag]*tagValues
47+
tagLookup map[Tag]*field
3348
tagSort
3449
}
3550

@@ -41,7 +56,7 @@ func (m *FieldMap) init() {
4156
}
4257

4358
func (m *FieldMap) initWithOrdering(ordering tagOrder) {
44-
m.tagLookup = make(map[Tag]*tagValues)
59+
m.tagLookup = make(map[Tag]*field)
4560
m.compare = ordering
4661
}
4762

@@ -68,12 +83,12 @@ func (m FieldMap) Has(tag Tag) bool {
6883

6984
//GetField parses of a field with Tag tag. Returned reject may indicate the field is not present, or the field value is invalid.
7085
func (m FieldMap) GetField(tag Tag, parser FieldValueReader) MessageRejectError {
71-
tagValues, ok := m.tagLookup[tag]
86+
f, ok := m.tagLookup[tag]
7287
if !ok {
7388
return ConditionallyRequiredFieldMissing(tag)
7489
}
7590

76-
if err := parser.Read(tagValues.tvs[0].value); err != nil {
91+
if err := parser.Read(f.tvs[0].value); err != nil {
7792
return IncorrectDataFormatForValue(tag)
7893
}
7994

@@ -82,12 +97,12 @@ func (m FieldMap) GetField(tag Tag, parser FieldValueReader) MessageRejectError
8297

8398
//GetBytes is a zero-copy GetField wrapper for []bytes fields
8499
func (m FieldMap) GetBytes(tag Tag) ([]byte, MessageRejectError) {
85-
tagValues, ok := m.tagLookup[tag]
100+
f, ok := m.tagLookup[tag]
86101
if !ok {
87102
return nil, ConditionallyRequiredFieldMissing(tag)
88103
}
89104

90-
return tagValues.tvs[0].value, nil
105+
return f.tvs[0].value, nil
91106
}
92107

93108
//GetBool is a GetField wrapper for bool fields
@@ -140,12 +155,12 @@ func (m FieldMap) GetString(tag Tag) (string, MessageRejectError) {
140155

141156
//GetGroup is a Get function specific to Group Fields.
142157
func (m FieldMap) GetGroup(parser FieldGroupReader) MessageRejectError {
143-
tagValues, ok := m.tagLookup[parser.Tag()]
158+
f, ok := m.tagLookup[parser.Tag()]
144159
if !ok {
145160
return ConditionallyRequiredFieldMissing(parser.Tag())
146161
}
147162

148-
if _, err := parser.Read(tagValues.tvs); err != nil {
163+
if _, err := parser.Read(f.tvs); err != nil {
149164
if msgRejErr, ok := err.(MessageRejectError); ok {
150165
return msgRejErr
151166
}
@@ -157,9 +172,13 @@ func (m FieldMap) GetGroup(parser FieldGroupReader) MessageRejectError {
157172

158173
//SetField sets the field with Tag tag
159174
func (m *FieldMap) SetField(tag Tag, field FieldValueWriter) *FieldMap {
160-
tValues := m.getOrCreate(tag)
161-
tValues.tvs = make([]TagValue, 1)
162-
tValues.tvs[0].init(tag, field.Write())
175+
return m.SetBytes(tag, field.Write())
176+
}
177+
178+
//SetBytes sets bytes
179+
func (m *FieldMap) SetBytes(tag Tag, value []byte) *FieldMap {
180+
f := m.getOrCreate(tag)
181+
f.init(tag, value)
163182
return m
164183
}
165184

@@ -170,12 +189,13 @@ func (m *FieldMap) SetBool(tag Tag, value bool) *FieldMap {
170189

171190
//SetInt is a SetField wrapper for int fields
172191
func (m *FieldMap) SetInt(tag Tag, value int) *FieldMap {
173-
return m.SetField(tag, FIXInt(value))
192+
v := FIXInt(value)
193+
return m.SetBytes(tag, v.Write())
174194
}
175195

176196
//SetString is a SetField wrapper for string fields
177197
func (m *FieldMap) SetString(tag Tag, value string) *FieldMap {
178-
return m.SetField(tag, FIXString(value))
198+
return m.SetBytes(tag, []byte(value))
179199
}
180200

181201
//Clear purges all fields from field map
@@ -186,31 +206,29 @@ func (m *FieldMap) Clear() {
186206
}
187207
}
188208

189-
func (m *FieldMap) add(f *tagValues) {
209+
func (m *FieldMap) add(f *field) {
190210
if _, ok := m.tagLookup[f.Tag()]; !ok {
191211
m.tags = append(m.tags, f.Tag())
192212
}
193213

194214
m.tagLookup[f.Tag()] = f
195215
}
196216

197-
func (m *FieldMap) getOrCreate(tag Tag) *tagValues {
217+
func (m *FieldMap) getOrCreate(tag Tag) *field {
198218
if f, ok := m.tagLookup[tag]; ok {
199219
return f
200220
}
201221

202-
f := new(tagValues)
222+
f := new(field)
203223
m.tagLookup[tag] = f
204224
m.tags = append(m.tags, tag)
205225
return f
206226
}
207227

208228
//Set is a setter for fields
209229
func (m *FieldMap) Set(field FieldWriter) *FieldMap {
210-
tValues := m.getOrCreate(field.Tag())
211-
tValues.tvs = make([]TagValue, 1)
212-
tValues.tvs[0].init(field.Tag(), field.Write())
213-
m.tagLookup[field.Tag()] = tValues
230+
f := m.getOrCreate(field.Tag())
231+
f.init(field.Tag(), field.Write())
214232
return m
215233
}
216234

@@ -228,10 +246,8 @@ func (m *FieldMap) sortedTags() []Tag {
228246

229247
func (m FieldMap) write(buffer *bytes.Buffer) {
230248
for _, tag := range m.sortedTags() {
231-
if fields, ok := m.tagLookup[tag]; ok {
232-
for _, tv := range fields.tvs {
233-
buffer.Write(tv.bytes)
234-
}
249+
if f, ok := m.tagLookup[tag]; ok {
250+
f.write(buffer)
235251
}
236252
}
237253
}

fix_int.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@ func (f *FIXInt) Read(bytes []byte) error {
5959
}
6060

6161
func (f FIXInt) Write() []byte {
62-
return []byte(strconv.Itoa(int(f)))
62+
return strconv.AppendInt(nil, int64(f), 10)
6363
}

message.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,23 +144,23 @@ func ParseMessage(msg *Message, rawMessage *bytes.Buffer) (err error) {
144144
return
145145
}
146146

147-
msg.Header.add(&tagValues{msg.fields[fieldIndex : fieldIndex+1]})
147+
msg.Header.add(&field{msg.fields[fieldIndex : fieldIndex+1]})
148148
fieldIndex++
149149

150150
parsedFieldBytes := &msg.fields[fieldIndex]
151151
if rawBytes, err = extractSpecificField(parsedFieldBytes, tagBodyLength, rawBytes); err != nil {
152152
return
153153
}
154154

155-
msg.Header.add(&tagValues{msg.fields[fieldIndex : fieldIndex+1]})
155+
msg.Header.add(&field{msg.fields[fieldIndex : fieldIndex+1]})
156156
fieldIndex++
157157

158158
parsedFieldBytes = &msg.fields[fieldIndex]
159159
if rawBytes, err = extractSpecificField(parsedFieldBytes, tagMsgType, rawBytes); err != nil {
160160
return
161161
}
162162

163-
msg.Header.add(&tagValues{msg.fields[fieldIndex : fieldIndex+1]})
163+
msg.Header.add(&field{msg.fields[fieldIndex : fieldIndex+1]})
164164
fieldIndex++
165165

166166
trailerBytes := []byte{}
@@ -174,13 +174,13 @@ func ParseMessage(msg *Message, rawMessage *bytes.Buffer) (err error) {
174174

175175
switch {
176176
case parsedFieldBytes.tag.IsHeader():
177-
msg.Header.add(&tagValues{msg.fields[fieldIndex : fieldIndex+1]})
177+
msg.Header.add(&field{msg.fields[fieldIndex : fieldIndex+1]})
178178
case parsedFieldBytes.tag.IsTrailer():
179-
msg.Trailer.add(&tagValues{msg.fields[fieldIndex : fieldIndex+1]})
179+
msg.Trailer.add(&field{msg.fields[fieldIndex : fieldIndex+1]})
180180
default:
181181
foundBody = true
182182
trailerBytes = rawBytes
183-
msg.Body.add(&tagValues{msg.fields[fieldIndex : fieldIndex+1]})
183+
msg.Body.add(&field{msg.fields[fieldIndex : fieldIndex+1]})
184184
}
185185
if parsedFieldBytes.tag == tagCheckSum {
186186
break
@@ -305,8 +305,8 @@ func (m *Message) String() string {
305305
return string(m.build())
306306
}
307307

308-
func newCheckSum(value int) FIXString {
309-
return FIXString(fmt.Sprintf("%03d", value))
308+
func formatCheckSum(value int) string {
309+
return fmt.Sprintf("%03d", value)
310310
}
311311

312312
//Build constructs a []byte from a Message instance
@@ -322,7 +322,7 @@ func (m *Message) build() []byte {
322322

323323
func (m *Message) cook() {
324324
bodyLength := m.Header.length() + m.Body.length() + m.Trailer.length()
325-
m.Header.SetField(tagBodyLength, FIXInt(bodyLength))
325+
m.Header.SetInt(tagBodyLength, bodyLength)
326326
checkSum := (m.Header.total() + m.Body.total() + m.Trailer.total()) % 256
327-
m.Trailer.SetField(tagCheckSum, newCheckSum(checkSum))
327+
m.Trailer.SetString(tagCheckSum, formatCheckSum(checkSum))
328328
}

repeating_group.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,24 +182,24 @@ func (f *RepeatingGroup) Read(tv []TagValue) ([]TagValue, error) {
182182
group := new(Group)
183183
group.initWithOrdering(tagOrdering)
184184
for len(tv) > 0 {
185-
field, ok := f.findItemInGroupTemplate(tv[0].tag)
185+
gi, ok := f.findItemInGroupTemplate(tv[0].tag)
186186
if !ok {
187187
break
188188
}
189189

190190
tvRange := tv
191-
if tv, err = field.Read(tv); err != nil {
191+
if tv, err = gi.Read(tv); err != nil {
192192
return tv, err
193193
}
194194

195-
if f.isDelimiter(field.Tag()) {
195+
if f.isDelimiter(gi.Tag()) {
196196
group = new(Group)
197197
group.initWithOrdering(tagOrdering)
198198

199199
f.groups = append(f.groups, group)
200200
}
201201

202-
group.tagLookup[tvRange[0].tag] = &tagValues{tvRange}
202+
group.tagLookup[tvRange[0].tag] = &field{tvRange}
203203
}
204204

205205
if len(f.groups) != expectedGroupSize {

session.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,21 @@ func (s *session) insertSendingTime(msg *Message) {
9999
}
100100
}
101101

102-
func optionallySetID(header Header, field Tag, value string) {
102+
func optionallySetID(msg *Message, field Tag, value string) {
103103
if len(value) != 0 {
104-
header.SetString(field, value)
104+
msg.Header.SetString(field, value)
105105
}
106106
}
107107

108108
func (s *session) fillDefaultHeader(msg *Message, inReplyTo *Message) {
109-
msg.Header.SetField(tagBeginString, FIXString(s.sessionID.BeginString))
110-
111-
msg.Header.SetField(tagSenderCompID, FIXString(s.sessionID.SenderCompID))
112-
optionallySetID(msg.Header, tagSenderSubID, s.sessionID.SenderSubID)
113-
optionallySetID(msg.Header, tagSenderLocationID, s.sessionID.SenderLocationID)
114-
115-
msg.Header.SetField(tagTargetCompID, FIXString(s.sessionID.TargetCompID))
116-
optionallySetID(msg.Header, tagTargetSubID, s.sessionID.TargetSubID)
117-
optionallySetID(msg.Header, tagTargetLocationID, s.sessionID.TargetLocationID)
109+
msg.Header.SetString(tagBeginString, s.sessionID.BeginString)
110+
msg.Header.SetString(tagSenderCompID, s.sessionID.SenderCompID)
111+
optionallySetID(msg, tagSenderSubID, s.sessionID.SenderSubID)
112+
optionallySetID(msg, tagSenderLocationID, s.sessionID.SenderLocationID)
113+
114+
msg.Header.SetString(tagTargetCompID, s.sessionID.TargetCompID)
115+
optionallySetID(msg, tagTargetSubID, s.sessionID.TargetSubID)
116+
optionallySetID(msg, tagTargetLocationID, s.sessionID.TargetLocationID)
118117

119118
s.insertSendingTime(msg)
120119

tag_value.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@ type TagValue struct {
1414
}
1515

1616
func (tv *TagValue) init(tag Tag, value []byte) {
17-
var buf bytes.Buffer
18-
buf.WriteString(strconv.Itoa(int(tag)))
19-
buf.WriteString("=")
20-
buf.Write(value)
21-
buf.WriteString("")
17+
tv.bytes = strconv.AppendInt(nil, int64(tag), 10)
18+
tv.bytes = append(tv.bytes, []byte("=")...)
19+
tv.bytes = append(tv.bytes, value...)
20+
tv.bytes = append(tv.bytes, []byte("")...)
2221

2322
tv.tag = tag
24-
tv.bytes = buf.Bytes()
2523
tv.value = value
2624
}
2725

@@ -41,8 +39,9 @@ func (tv *TagValue) parse(rawFieldBytes []byte) (err error) {
4139
}
4240

4341
tv.tag = Tag(parsedTag)
44-
tv.value = rawFieldBytes[(sepIndex + 1):(len(rawFieldBytes) - 1)]
45-
tv.bytes = rawFieldBytes
42+
n := len(rawFieldBytes)
43+
tv.value = rawFieldBytes[(sepIndex + 1):(n - 1):(n - 1)]
44+
tv.bytes = rawFieldBytes[:n:n]
4645

4746
return
4847
}

0 commit comments

Comments
 (0)