Skip to content
GitLab
    • Explore Projects Groups Snippets
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • Q quickfix
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 48
    • Issues 48
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 25
    • Merge requests 25
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • quickfixgo
  • quickfix
  • Merge requests
  • !354
An error occurred while fetching the assigned milestone of the selected merge_request.

Fix `Message.CopyInto()` to fully deep copy and remove the error return value #338

  • Review changes

  • Download
  • Email patches
  • Plain diff
Merged Administrator requested to merge github/fork/ethanf/msg-deep-copy into master 6 years ago
  • Overview 2
  • Commits 1
  • Pipelines 0
  • Changes 4

Created by: ethanf

It's unclear if we should copy Message.rawMessage here. I chose not to in case the original buffer is changed. I think the point of Message.CopyInto() is to decouple the new instance from the old, and shouldn't be used in high-performance scenarios anyway.

I removed the error return value because I don't understand when you'd expect an error. We could add it back for API compatibility. I don't think the name CopyInto() is idiomatic Go, but I don't see any obvious examples for naming something similar.

Compare
  • master (base)

and
  • latest version
    50396c60
    1 commit, 2 years ago

4 files
+ 87
- 22

    Preferences

    File browser
    Compare changes
field_‎map.go‎ +13 -0
field_ma‎p_test.go‎ +50 -0
messa‎ge.go‎ +4 -21
message‎_test.go‎ +20 -1
field_map.go
+ 13
- 0
  • View file @ 50396c60

  • Edit in single-file editor

  • Open in Web IDE


@@ -199,6 +199,19 @@ func (m *FieldMap) Clear() {
}
}
//CopyInto overwrites the given FieldMap with this one
func (m *FieldMap) CopyInto(to *FieldMap) {
to.tagLookup = make(map[Tag]field)
for tag, f := range m.tagLookup {
clone := make(field, 1)
clone[0] = f[0]
to.tagLookup[tag] = clone
}
to.tags = make([]Tag, len(m.tags))
copy(to.tags, m.tags)
to.compare = m.compare
}
func (m *FieldMap) add(f field) {
t := fieldTag(f)
if _, ok := m.tagLookup[t]; !ok {
field_map_test.go
+ 50
- 0
  • View file @ 50396c60

  • Edit in single-file editor

  • Open in Web IDE


@@ -125,3 +125,53 @@ func TestFieldMap_BoolTypedSetAndGet(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, "N", s)
}
func TestFieldMap_CopyInto(t *testing.T) {
var fMapA FieldMap
fMapA.initWithOrdering(headerFieldOrdering)
fMapA.SetString(9, "length")
fMapA.SetString(8, "begin")
fMapA.SetString(35, "msgtype")
fMapA.SetString(1, "a")
assert.Equal(t, []Tag{8, 9, 35, 1}, fMapA.sortedTags())
var fMapB FieldMap
fMapB.init()
fMapB.SetString(1, "A")
fMapB.SetString(3, "C")
fMapB.SetString(4, "D")
assert.Equal(t, fMapB.sortedTags(), []Tag{1, 3, 4})
fMapA.CopyInto(&fMapB)
assert.Equal(t, []Tag{8, 9, 35, 1}, fMapB.sortedTags())
// new fields
s, err := fMapB.GetString(35)
assert.Nil(t, err)
assert.Equal(t, "msgtype", s)
// existing fields overwritten
s, err = fMapB.GetString(1)
assert.Nil(t, err)
assert.Equal(t, "a", s)
// old fields cleared
s, err = fMapB.GetString(3)
assert.NotNil(t, err)
// check that ordering is overwritten
fMapB.SetString(2, "B")
assert.Equal(t, []Tag{8, 9, 35, 1, 2}, fMapB.sortedTags())
// updating the existing map doesn't affect the new
fMapA.init()
fMapA.SetString(1, "AA")
s, err = fMapB.GetString(1)
assert.Nil(t, err)
assert.Equal(t, "a", s)
fMapA.Clear()
s, err = fMapB.GetString(1)
assert.Nil(t, err)
assert.Equal(t, "a", s)
}
message.go
+ 4
- 21
  • View file @ 50396c60

  • Edit in single-file editor

  • Open in Web IDE


@@ -116,16 +116,11 @@ func NewMessage() *Message {
// CopyInto erases the dest messages and copies the curreny message content
// into it.
func (m *Message) CopyInto(to *Message) error {
to.Header.Clear()
to.Body.Clear()
to.Trailer.Clear()
func (m *Message) CopyInto(to *Message) {
m.Header.CopyInto(&to.Header.FieldMap)
m.Body.CopyInto(&to.Body.FieldMap)
m.Trailer.CopyInto(&to.Trailer.FieldMap)
to.Header.FieldMap = cloneFieldMap(m.Header.FieldMap)
to.Body.FieldMap = cloneFieldMap(m.Body.FieldMap)
to.Trailer.FieldMap = cloneFieldMap(m.Trailer.FieldMap)
to.rawMessage = m.rawMessage
to.ReceiveTime = m.ReceiveTime
to.bodyBytes = make([]byte, len(m.bodyBytes))
copy(to.bodyBytes, m.bodyBytes)
@@ -133,7 +128,6 @@ func (m *Message) CopyInto(to *Message) error {
for i := range to.fields {
to.fields[i].init(m.fields[i].tag, m.fields[i].value)
}
return nil
}
//ParseMessage constructs a Message from a byte slice wrapping a FIX message.
@@ -385,14 +379,3 @@ func (m *Message) cook() {
checkSum := (m.Header.total() + m.Body.total() + m.Trailer.total()) % 256
m.Trailer.SetString(tagCheckSum, formatCheckSum(checkSum))
}
func cloneFieldMap(f FieldMap) FieldMap {
res := FieldMap{}
res.tagLookup = make(map[Tag]field)
for tag, field := range f.tagLookup {
res.tagLookup[tag] = field
}
res.tags = make([]Tag, len(f.tags))
copy(res.tags, f.tags)
return res
}
message_test.go
+ 20
- 1
  • View file @ 50396c60

  • Edit in single-file editor

  • Open in Web IDE


@@ -169,20 +169,39 @@ func (s *MessageSuite) TestReverseRouteFIX40() {
}
func (s *MessageSuite) TestCopyIntoMessage() {
s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123")))
msgString := "8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123"
msgBuf := bytes.NewBufferString(msgString)
s.Nil(ParseMessage(s.msg, msgBuf))
dest := NewMessage()
s.msg.CopyInto(dest)
checkFieldInt(s, dest.Header.FieldMap, int(tagMsgSeqNum), 2)
checkFieldInt(s, dest.Body.FieldMap, 21, 3)
checkFieldString(s, dest.Body.FieldMap, 11, "ID")
s.Equal(len(dest.bodyBytes), len(s.msg.bodyBytes))
// copying decouples the message from its input buffer, so the raw message will be re-rendered
renderedString := "8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP115=JCD116=CS128=MG129=CB142=JV143=RY144=BB145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=033"
s.Equal(dest.String(), renderedString)
s.True(reflect.DeepEqual(s.msg.bodyBytes, dest.bodyBytes))
s.True(s.msg.IsMsgTypeOf("D"))
s.Equal(s.msg.ReceiveTime, dest.ReceiveTime)
s.True(reflect.DeepEqual(s.msg.fields, dest.fields))
// update the source message to validate the copy is truly deep
newMsgString := "8=FIX.4.49=4935=A52=20140615-19:49:56553=my_user554=secret10=072"
s.Nil(ParseMessage(s.msg, bytes.NewBufferString(newMsgString)))
s.True(s.msg.IsMsgTypeOf("A"))
s.Equal(s.msg.String(), newMsgString)
// clear the source buffer also
msgBuf.Reset()
s.True(dest.IsMsgTypeOf("D"))
s.Equal(dest.String(), renderedString)
}
func checkFieldInt(s *MessageSuite, fields FieldMap, tag, expected int) {
0 Assignees
None
Assign to
0 Reviewers
None
Request review from
Labels
0
None
0
None
    Assign labels
  • Manage project labels

Milestone
No milestone
None
None
Time tracking
No estimate or time spent
Lock merge request
Unlocked
0
0 participants
Reference:
Source branch: github/fork/ethanf/msg-deep-copy

Menu

Explore Projects Groups Snippets