From 74498fda136743c49806c4c577e2ba315300430e Mon Sep 17 00:00:00 2001 From: Chris Busbey <cbusbey@connamara.com> Date: Mon, 23 Nov 2015 16:18:31 -0800 Subject: [PATCH] groups -> field maps --- field_map.go | 7 +- repeating_group.go | 75 ++++++++----- repeating_group_test.go | 231 ++++++++++++++++++++++++---------------- 3 files changed, 193 insertions(+), 120 deletions(-) diff --git a/field_map.go b/field_map.go index a2024b78..6a253cd4 100644 --- a/field_map.go +++ b/field_map.go @@ -103,17 +103,18 @@ func (m FieldMap) GetField(tag Tag, parser FieldValueReader) MessageRejectError return nil } -func (m FieldMap) SetField(tag Tag, field FieldValueWriter) { +func (m FieldMap) SetField(tag Tag, field FieldValueWriter) FieldMap { tValues := make([]TagValue, 1) tValues[0].init(tag, field.Write()) m.tagLookup[tag] = tValues + return m } -func (m FieldMap) Set(field FieldWriter) { +func (m FieldMap) Set(field FieldWriter) FieldMap { tValues := make([]TagValue, 1) tValues[0].init(field.Tag(), field.Write()) - m.tagLookup[field.Tag()] = tValues + return m } func (m FieldMap) sortedTags() []Tag { diff --git a/repeating_group.go b/repeating_group.go index 53894c16..d84b2fed 100644 --- a/repeating_group.go +++ b/repeating_group.go @@ -2,6 +2,7 @@ package quickfix import ( "bytes" + "math" "strconv" ) @@ -10,30 +11,33 @@ type RepeatingGroupField struct { FieldValue } -type Group []RepeatingGroupField +type GroupTemplate []RepeatingGroupField +type Group struct{ FieldMap } type RepeatingGroup struct { - GroupTemplate Group - Groups []Group + GroupTemplate + Groups []Group } -func (f *RepeatingGroup) AddGroup(group Group) { - f.Groups = append(f.Groups, group) +func (f *RepeatingGroup) Add() Group { + var g Group + g.init(f.groupTagOrder()) + + f.Groups = append(f.Groups, g) + return g } func (f RepeatingGroup) Write() []byte { buf := bytes.NewBufferString(strconv.Itoa(len(f.Groups))) + buf.WriteString("") for _, group := range f.Groups { - for _, field := range group { - buf.WriteString("") - buf.WriteString(strconv.Itoa(int(field.Tag))) - buf.WriteString("=") - buf.Write(field.Write()) - } + group.write(buf) } - return buf.Bytes() + //remove the last soh char + bytes := buf.Bytes() + return bytes[:len(bytes)-1] } func (f RepeatingGroup) findFieldInGroupTemplate(t Tag) (field RepeatingGroupField, ok bool) { @@ -49,6 +53,28 @@ func (f RepeatingGroup) findFieldInGroupTemplate(t Tag) (field RepeatingGroupFie return } +func (f RepeatingGroup) groupTagOrder() tagOrder { + tagMap := make(map[Tag]int) + for i, f := range f.GroupTemplate { + tagMap[f.Tag] = i + } + + return func(i, j Tag) bool { + orderi := math.MaxInt64 + orderj := math.MaxInt64 + + if iIndex, ok := tagMap[i]; ok { + orderi = iIndex + } + + if jIndex, ok := tagMap[j]; ok { + orderj = jIndex + } + + return orderi < orderj + } +} + func (f RepeatingGroup) isDelimiter(t Tag) bool { return t == f.GroupTemplate[0].Tag } @@ -65,7 +91,9 @@ func (f *RepeatingGroup) Read(tv TagValues) (TagValues, error) { } tv = tv[1:] + tagOrdering := f.groupTagOrder() var group Group + group.init(tagOrdering) for len(tv) > 0 { field, ok := f.findFieldInGroupTemplate(tv[0].Tag) if !ok { @@ -77,17 +105,13 @@ func (f *RepeatingGroup) Read(tv TagValues) (TagValues, error) { } if f.isDelimiter(field.Tag) { - group = Group{field} + group = Group{} + group.init(tagOrdering) + f.Groups = append(f.Groups, group) - } else { - if len(group) == 0 { - // didn't get initial delimiter - break - } - - group = append(group, field) - f.Groups[len(f.Groups)-1] = group } + + group.SetField(field.Tag, field) } return tv, err @@ -95,8 +119,7 @@ func (f *RepeatingGroup) Read(tv TagValues) (TagValues, error) { func (f RepeatingGroup) Clone() FieldValue { var clone RepeatingGroup - - clone.GroupTemplate = make(Group, len(f.GroupTemplate)) + clone.GroupTemplate = make(GroupTemplate, len(f.GroupTemplate)) clone.Groups = make([]Group, len(f.Groups)) for i, field := range f.GroupTemplate { @@ -104,11 +127,7 @@ func (f RepeatingGroup) Clone() FieldValue { } for i, group := range f.Groups { - clone.Groups[i] = make(Group, len(group)) - - for j, field := range group { - clone.Groups[i][j] = RepeatingGroupField{field.Tag, field.FieldValue.Clone()} - } + clone.Groups[i].init(group.tagOrder) } return &clone diff --git a/repeating_group_test.go b/repeating_group_test.go index 857886ed..8d94ff45 100644 --- a/repeating_group_test.go +++ b/repeating_group_test.go @@ -5,28 +5,77 @@ import ( "testing" ) -func TestRepeatingGroup_Write(t *testing.T) { +func TestRepeatingGroup_Add(t *testing.T) { + f := RepeatingGroup{GroupTemplate: GroupTemplate{RepeatingGroupField{1, new(FIXString)}}} + var tests = []struct { - groups []Group - expected []byte + expectedLen int }{ - {[]Group{{RepeatingGroupField{Tag(1), NewFIXString("hello")}}}, - []byte("11=hello")}, - {[]Group{{RepeatingGroupField{Tag(1), NewFIXString("hello")}, RepeatingGroupField{Tag(2), NewFIXString("world")}}}, - []byte("11=hello2=world")}, - {[]Group{{RepeatingGroupField{Tag(1), NewFIXString("hello")}}, {RepeatingGroupField{Tag(1), NewFIXString("world")}}}, - []byte("21=hello1=world")}, - {[]Group{{RepeatingGroupField{Tag(1), NewFIXString("hello")}, RepeatingGroupField{Tag(2), NewFIXString("world")}}, {RepeatingGroupField{Tag(1), NewFIXString("goodbye")}}}, - []byte("21=hello2=world1=goodbye")}, + {1}, + {2}, } for _, test := range tests { - var f RepeatingGroup - for _, group := range test.groups { - f.AddGroup(group) + g := f.Add() + if len(f.Groups) != test.expectedLen { + t.Errorf("Expected %v groups, got %v", test.expectedLen, len(f.Groups)) + } + + g.SetField(Tag(1), FIXString("hello")) + + if !f.Groups[test.expectedLen-1].Has(Tag(1)) { + t.Errorf("expected tag in group %v", test.expectedLen) } - fieldBytes := f.Write() + var v FIXString + f.Groups[test.expectedLen-1].GetField(Tag(1), &v) + + if string(v) != "hello" { + t.Errorf("expected hello in group %v", test.expectedLen) + } + } +} + +func TestRepeatingGroup_Write(t *testing.T) { + f1 := RepeatingGroup{GroupTemplate: GroupTemplate{ + RepeatingGroupField{1, new(FIXString)}, + RepeatingGroupField{2, new(FIXString)}, + }} + + f1.Add().SetField(Tag(1), FIXString("hello")) + + f2 := RepeatingGroup{GroupTemplate: GroupTemplate{ + RepeatingGroupField{1, new(FIXString)}, + RepeatingGroupField{2, new(FIXString)}, + }} + f2.Add().SetField(Tag(1), FIXString("hello")).SetField(Tag(2), FIXString("world")) + + f3 := RepeatingGroup{GroupTemplate: GroupTemplate{ + RepeatingGroupField{1, new(FIXString)}, + RepeatingGroupField{2, new(FIXString)}, + }} + f3.Add().SetField(Tag(1), FIXString("hello")) + f3.Add().SetField(Tag(1), FIXString("world")) + + f4 := RepeatingGroup{GroupTemplate: GroupTemplate{ + RepeatingGroupField{1, new(FIXString)}, + RepeatingGroupField{2, new(FIXString)}, + }} + f4.Add().SetField(Tag(1), FIXString("hello")).SetField(Tag(2), FIXString("world")) + f4.Add().SetField(Tag(1), FIXString("goodbye")) + + var tests = []struct { + f RepeatingGroup + expected []byte + }{ + {f1, []byte("11=hello")}, + {f2, []byte("11=hello2=world")}, + {f3, []byte("21=hello1=world")}, + {f4, []byte("21=hello2=world1=goodbye")}, + } + + for _, test := range tests { + fieldBytes := test.f.Write() if !bytes.Equal(test.expected, fieldBytes) { t.Errorf("expected %s got %s", test.expected, fieldBytes) } @@ -35,78 +84,71 @@ func TestRepeatingGroup_Write(t *testing.T) { func TestRepeatingGroup_Read(t *testing.T) { - singleFieldTemplate := Group{RepeatingGroupField{Tag(1), new(FIXString)}} - multiFieldTemplate := Group{RepeatingGroupField{Tag(1), new(FIXString)}, RepeatingGroupField{Tag(2), new(FIXString)}, RepeatingGroupField{Tag(3), new(FIXString)}} + singleFieldTemplate := GroupTemplate{RepeatingGroupField{Tag(1), new(FIXString)}} + multiFieldTemplate := GroupTemplate{RepeatingGroupField{Tag(1), new(FIXString)}, RepeatingGroupField{Tag(2), new(FIXString)}, RepeatingGroupField{Tag(3), new(FIXString)}} tests := []struct { - groupTemplate Group - tv TagValues - expectedGroupNum int - expectedGroupBytes [][][]byte + groupTemplate GroupTemplate + tv TagValues + expectedGroupTvs []TagValues }{ - {singleFieldTemplate, TagValues{TagValue{Value: []byte("0")}}, 0, [][][]byte{}}, - {singleFieldTemplate, TagValues{TagValue{Value: []byte("1")}, TagValue{Tag: Tag(1), Value: []byte("hello")}}, 1, - [][][]byte{ - [][]byte{ - []byte("hello")}}}, - {singleFieldTemplate, TagValues{TagValue{Value: []byte("1")}, TagValue{Tag: Tag(1), Value: []byte("hello")}, TagValue{Tag: Tag(2), Value: []byte("not in group")}}, 1, - [][][]byte{ - [][]byte{ - []byte("hello")}}}, - {singleFieldTemplate, TagValues{TagValue{Value: []byte("2")}, TagValue{Tag: Tag(1), Value: []byte("hello")}, TagValue{Tag: Tag(1), Value: []byte("world")}}, 2, - [][][]byte{ - [][]byte{[]byte("hello")}, - [][]byte{[]byte("world")}}}, - {multiFieldTemplate, TagValues{ - TagValue{Value: []byte("2")}, - TagValue{Tag: Tag(1), Value: []byte("hello")}, - TagValue{Tag: Tag(1), Value: []byte("goodbye")}, - TagValue{Tag: Tag(2), Value: []byte("cruel")}, - TagValue{Tag: Tag(3), Value: []byte("world")}, - }, 2, - [][][]byte{ - [][]byte{[]byte("hello")}, - [][]byte{[]byte("goodbye"), []byte("cruel"), []byte("world")}}}, - {multiFieldTemplate, TagValues{ - TagValue{Value: []byte("3")}, - TagValue{Tag: Tag(1), Value: []byte("hello")}, - TagValue{Tag: Tag(1), Value: []byte("goodbye")}, - TagValue{Tag: Tag(2), Value: []byte("cruel")}, - TagValue{Tag: Tag(3), Value: []byte("world")}, - TagValue{Tag: Tag(1), Value: []byte("another")}, - }, 3, - [][][]byte{ - [][]byte{[]byte("hello")}, - [][]byte{[]byte("goodbye"), []byte("cruel"), []byte("world")}, - [][]byte{[]byte("another")}}}, + {singleFieldTemplate, TagValues{TagValue{Value: []byte("0")}}, + []TagValues{}}, + {singleFieldTemplate, TagValues{TagValue{Value: []byte("1")}, TagValue{Tag: Tag(1), Value: []byte("hello")}}, + []TagValues{TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}}}, + {singleFieldTemplate, + TagValues{TagValue{Value: []byte("1")}, + TagValue{Tag: Tag(1), Value: []byte("hello")}, + TagValue{Tag: Tag(2), Value: []byte("not in group")}}, + []TagValues{ + TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}}}, + {singleFieldTemplate, + TagValues{TagValue{Value: []byte("2")}, + TagValue{Tag: Tag(1), Value: []byte("hello")}, + TagValue{Tag: Tag(1), Value: []byte("world")}}, + []TagValues{ + TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}, + TagValues{TagValue{Tag: Tag(1), Value: []byte("world")}}, + }}, + {multiFieldTemplate, + TagValues{ + TagValue{Value: []byte("2")}, + TagValue{Tag: Tag(1), Value: []byte("hello")}, + TagValue{Tag: Tag(1), Value: []byte("goodbye")}, TagValue{Tag: Tag(2), Value: []byte("cruel")}, TagValue{Tag: Tag(3), Value: []byte("world")}, + }, + []TagValues{ + TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}, + TagValues{TagValue{Tag: Tag(1), Value: []byte("goodbye")}, TagValue{Tag: Tag(2), Value: []byte("cruel")}, TagValue{Tag: Tag(3), Value: []byte("world")}}, + }}, + {multiFieldTemplate, + TagValues{ + TagValue{Value: []byte("3")}, + TagValue{Tag: Tag(1), Value: []byte("hello")}, + TagValue{Tag: Tag(1), Value: []byte("goodbye")}, TagValue{Tag: Tag(2), Value: []byte("cruel")}, TagValue{Tag: Tag(3), Value: []byte("world")}, + TagValue{Tag: Tag(1), Value: []byte("another")}, + }, + []TagValues{ + TagValues{TagValue{Tag: Tag(1), Value: []byte("hello")}}, + TagValues{TagValue{Tag: Tag(1), Value: []byte("goodbye")}, TagValue{Tag: Tag(2), Value: []byte("cruel")}, TagValue{Tag: Tag(3), Value: []byte("world")}}, + TagValues{TagValue{Tag: Tag(1), Value: []byte("another")}}, + }}, } for _, test := range tests { f := RepeatingGroup{GroupTemplate: test.groupTemplate} f.Read(test.tv) - if len(f.Groups) != test.expectedGroupNum { - t.Errorf("expected %v groups, got %v", test.expectedGroupNum, len(f.Groups)) - } - - var allGroupBytes [][][]byte - for _, group := range f.Groups { - var groupBytes [][]byte - for _, field := range group { - groupBytes = append(groupBytes, field.Write()) - } - - allGroupBytes = append(allGroupBytes, groupBytes) + if len(f.Groups) != len(test.expectedGroupTvs) { + t.Errorf("expected %v groups, got %v", len(test.expectedGroupTvs), len(f.Groups)) } - for i, groupBytes := range allGroupBytes { - if len(groupBytes) != len(test.expectedGroupBytes[i]) { - t.Errorf("Expected %v fields for group %v (%s) got %v (%s)", len(test.expectedGroupBytes[i]), i, test.expectedGroupBytes[i], len(groupBytes), groupBytes) - } + for g, group := range f.Groups { + for _, expected := range test.expectedGroupTvs[g] { + var actual FIXString + group.GetField(expected.Tag, &actual) - for j, fieldBytes := range groupBytes { - if !bytes.Equal(fieldBytes, test.expectedGroupBytes[i][j]) { - t.Errorf("Expected '%s' for field %v of group %v, got '%s'", test.expectedGroupBytes[i][j], j, i, fieldBytes) + if !bytes.Equal(expected.Value, []byte(actual)) { + t.Errorf("%v, %v: expected %s, got %s", g, expected.Tag, expected.Value, actual) } } } @@ -123,7 +165,14 @@ func TestRepeatingGroup_ReadComplete(t *testing.T) { t.Error("Unexpected error, ", err) } - template := Group{RepeatingGroupField{Tag(269), new(FIXString)}, RepeatingGroupField{Tag(270), new(FIXString)}, RepeatingGroupField{Tag(271), new(FIXString)}, RepeatingGroupField{Tag(272), new(FIXString)}, RepeatingGroupField{Tag(273), new(FIXString)}} + template := GroupTemplate{ + RepeatingGroupField{Tag(269), new(FIXString)}, + RepeatingGroupField{Tag(270), new(FIXString)}, + RepeatingGroupField{Tag(271), new(FIXString)}, + RepeatingGroupField{Tag(272), new(FIXString)}, + RepeatingGroupField{Tag(273), new(FIXString)}, + } + f := RepeatingGroup{GroupTemplate: template} err = msg.Body.GetField(Tag(268), &f) if err != nil { @@ -140,27 +189,31 @@ func TestRepeatingGroup_ReadComplete(t *testing.T) { []Tag{Tag(269), Tag(270), Tag(272), Tag(273)}, []Tag{Tag(269), Tag(271), Tag(272), Tag(273)}, } - expectedGroupBytes := [][][]byte{ - [][]byte{[]byte("4"), []byte("0.07499"), []byte("20151027"), []byte("18:41:52.698")}, - [][]byte{[]byte("7"), []byte("0.07501"), []byte("20151027"), []byte("18:41:52.698")}, - [][]byte{[]byte("8"), []byte("0.07494"), []byte("20151027"), []byte("18:41:52.698")}, - [][]byte{[]byte("B"), []byte("60"), []byte("20151027"), []byte("18:41:52.698")}, + + expectedGroupValues := [][]FIXString{ + []FIXString{FIXString("4"), FIXString("0.07499"), FIXString("20151027"), FIXString("18:41:52.698")}, + []FIXString{FIXString("7"), FIXString("0.07501"), FIXString("20151027"), FIXString("18:41:52.698")}, + []FIXString{FIXString("8"), FIXString("0.07494"), FIXString("20151027"), FIXString("18:41:52.698")}, + []FIXString{FIXString("B"), FIXString("60"), FIXString("20151027"), FIXString("18:41:52.698")}, } for i, group := range f.Groups { - if len(group) != len(expectedGroupTags[i]) { - t.Errorf("expected %v tags in group %v got %v", len(expectedGroupTags[i]), i, len(group)) - } - for j, field := range group { - if field.Tag != expectedGroupTags[i][j] { - t.Errorf("expected %v in group %v, field %v got %v", expectedGroupTags[i][j], i, j, field.Tag) + for j, tag := range expectedGroupTags[i] { + if !group.Has(tag) { + t.Errorf("expected %v in group %v", expectedGroupTags[i][j], i) + continue } - if !bytes.Equal(field.Write(), expectedGroupBytes[i][j]) { - t.Errorf("Expected '%s' for field %v of group %v, got '%s'", expectedGroupBytes[i][j], j, i, field.Write()) + var actual FIXString + if err := group.GetField(tag, &actual); err != nil { + t.Errorf("error getting field %v from group %v", tag, i) + continue } + if expectedGroupValues[i][j] != actual { + t.Errorf("Expected %v got %v", expectedGroupTags[i][j], actual) + } } } } -- GitLab