adding quickfix
This commit is contained in:
414
quickfix/field_map.go
Normal file
414
quickfix/field_map.go
Normal file
@ -0,0 +1,414 @@
|
||||
// Copyright (c) quickfixengine.org All rights reserved.
|
||||
//
|
||||
// This file may be distributed under the terms of the quickfixengine.org
|
||||
// license as defined by quickfixengine.org and appearing in the file
|
||||
// LICENSE included in the packaging of this file.
|
||||
//
|
||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
||||
// THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
|
||||
// PARTICULAR PURPOSE.
|
||||
//
|
||||
// See http://www.quickfixengine.org/LICENSE for licensing information.
|
||||
//
|
||||
// Contact ask@quickfixengine.org if any conditions of this licensing
|
||||
// are not clear to you.
|
||||
|
||||
package quickfix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// field stores a slice of TagValues.
|
||||
type field []TagValue
|
||||
|
||||
func fieldTag(f field) Tag {
|
||||
return f[0].tag
|
||||
}
|
||||
|
||||
func initField(f field, tag Tag, value []byte) {
|
||||
f[0].init(tag, value)
|
||||
}
|
||||
|
||||
func writeField(f field, buffer *bytes.Buffer) {
|
||||
for _, tv := range f {
|
||||
buffer.Write(tv.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
// tagOrder true if tag i should occur before tag j.
|
||||
type tagOrder func(i, j Tag) bool
|
||||
|
||||
type tagSort struct {
|
||||
tags []Tag
|
||||
compare tagOrder
|
||||
}
|
||||
|
||||
func (t tagSort) Len() int { return len(t.tags) }
|
||||
func (t tagSort) Swap(i, j int) { t.tags[i], t.tags[j] = t.tags[j], t.tags[i] }
|
||||
func (t tagSort) Less(i, j int) bool { return t.compare(t.tags[i], t.tags[j]) }
|
||||
|
||||
// FieldMap is a collection of fix fields that make up a fix message.
|
||||
type FieldMap struct {
|
||||
tagLookup map[Tag]field
|
||||
tagSort
|
||||
rwLock *sync.RWMutex
|
||||
}
|
||||
|
||||
// ascending tags.
|
||||
func normalFieldOrder(i, j Tag) bool { return i < j }
|
||||
|
||||
func (m *FieldMap) init() {
|
||||
m.initWithOrdering(normalFieldOrder)
|
||||
}
|
||||
|
||||
func (m *FieldMap) initWithOrdering(ordering tagOrder) {
|
||||
m.rwLock = &sync.RWMutex{}
|
||||
m.tagLookup = make(map[Tag]field)
|
||||
m.compare = ordering
|
||||
}
|
||||
|
||||
// Tags returns all of the Field Tags in this FieldMap.
|
||||
func (m FieldMap) Tags() []Tag {
|
||||
m.rwLock.RLock()
|
||||
defer m.rwLock.RUnlock()
|
||||
|
||||
tags := make([]Tag, 0, len(m.tagLookup))
|
||||
for t := range m.tagLookup {
|
||||
tags = append(tags, t)
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
// Get parses out a field in this FieldMap. Returned reject may indicate the field is not present, or the field value is invalid.
|
||||
func (m FieldMap) Get(parser Field) MessageRejectError {
|
||||
return m.GetField(parser.Tag(), parser)
|
||||
}
|
||||
|
||||
// Has returns true if the Tag is present in this FieldMap.
|
||||
func (m FieldMap) Has(tag Tag) bool {
|
||||
m.rwLock.RLock()
|
||||
defer m.rwLock.RUnlock()
|
||||
|
||||
_, ok := m.tagLookup[tag]
|
||||
return ok
|
||||
}
|
||||
|
||||
// GetField parses of a field with Tag tag. Returned reject may indicate the field is not present, or the field value is invalid.
|
||||
func (m FieldMap) GetField(tag Tag, parser FieldValueReader) MessageRejectError {
|
||||
m.rwLock.RLock()
|
||||
defer m.rwLock.RUnlock()
|
||||
|
||||
f, ok := m.tagLookup[tag]
|
||||
if !ok {
|
||||
return ConditionallyRequiredFieldMissing(tag)
|
||||
}
|
||||
|
||||
if err := parser.Read(f[0].value); err != nil {
|
||||
return IncorrectDataFormatForValue(tag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetField parses of a field with Tag tag. Returned reject may indicate the field is not present, or the field value is invalid.
|
||||
func (m FieldMap) getFieldNoLock(tag Tag, parser FieldValueReader) MessageRejectError {
|
||||
f, ok := m.tagLookup[tag]
|
||||
if !ok {
|
||||
return ConditionallyRequiredFieldMissing(tag)
|
||||
}
|
||||
|
||||
if err := parser.Read(f[0].value); err != nil {
|
||||
return IncorrectDataFormatForValue(tag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBytes is a zero-copy GetField wrapper for []bytes fields.
|
||||
func (m FieldMap) GetBytes(tag Tag) ([]byte, MessageRejectError) {
|
||||
m.rwLock.RLock()
|
||||
defer m.rwLock.RUnlock()
|
||||
|
||||
f, ok := m.tagLookup[tag]
|
||||
if !ok {
|
||||
return nil, ConditionallyRequiredFieldMissing(tag)
|
||||
}
|
||||
|
||||
return f[0].value, nil
|
||||
}
|
||||
|
||||
// getBytesNoLock is a lock free zero-copy GetField wrapper for []bytes fields.
|
||||
func (m FieldMap) getBytesNoLock(tag Tag) ([]byte, MessageRejectError) {
|
||||
f, ok := m.tagLookup[tag]
|
||||
if !ok {
|
||||
return nil, ConditionallyRequiredFieldMissing(tag)
|
||||
}
|
||||
|
||||
return f[0].value, nil
|
||||
}
|
||||
|
||||
// GetBool is a GetField wrapper for bool fields.
|
||||
func (m FieldMap) GetBool(tag Tag) (bool, MessageRejectError) {
|
||||
var val FIXBoolean
|
||||
if err := m.GetField(tag, &val); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return bool(val), nil
|
||||
}
|
||||
|
||||
// GetInt is a GetField wrapper for int fields.
|
||||
func (m FieldMap) GetInt(tag Tag) (int, MessageRejectError) {
|
||||
bytes, err := m.GetBytes(tag)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var val FIXInt
|
||||
if val.Read(bytes) != nil {
|
||||
err = IncorrectDataFormatForValue(tag)
|
||||
}
|
||||
|
||||
return int(val), err
|
||||
}
|
||||
|
||||
// GetInt is a lock free GetField wrapper for int fields.
|
||||
func (m FieldMap) getIntNoLock(tag Tag) (int, MessageRejectError) {
|
||||
bytes, err := m.getBytesNoLock(tag)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var val FIXInt
|
||||
if val.Read(bytes) != nil {
|
||||
err = IncorrectDataFormatForValue(tag)
|
||||
}
|
||||
|
||||
return int(val), err
|
||||
}
|
||||
|
||||
// GetTime is a GetField wrapper for utc timestamp fields.
|
||||
func (m FieldMap) GetTime(tag Tag) (t time.Time, err MessageRejectError) {
|
||||
m.rwLock.RLock()
|
||||
defer m.rwLock.RUnlock()
|
||||
|
||||
bytes, err := m.GetBytes(tag)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var val FIXUTCTimestamp
|
||||
if val.Read(bytes) != nil {
|
||||
err = IncorrectDataFormatForValue(tag)
|
||||
}
|
||||
|
||||
return val.Time, err
|
||||
}
|
||||
|
||||
// GetString is a GetField wrapper for string fields.
|
||||
func (m FieldMap) GetString(tag Tag) (string, MessageRejectError) {
|
||||
var val FIXString
|
||||
if err := m.GetField(tag, &val); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(val), nil
|
||||
}
|
||||
|
||||
// GetString is a GetField wrapper for string fields.
|
||||
func (m FieldMap) getStringNoLock(tag Tag) (string, MessageRejectError) {
|
||||
var val FIXString
|
||||
if err := m.getFieldNoLock(tag, &val); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(val), nil
|
||||
}
|
||||
|
||||
// GetGroup is a Get function specific to Group Fields.
|
||||
func (m FieldMap) GetGroup(parser FieldGroupReader) MessageRejectError {
|
||||
m.rwLock.RLock()
|
||||
defer m.rwLock.RUnlock()
|
||||
|
||||
f, ok := m.tagLookup[parser.Tag()]
|
||||
if !ok {
|
||||
return ConditionallyRequiredFieldMissing(parser.Tag())
|
||||
}
|
||||
|
||||
if _, err := parser.Read(f); err != nil {
|
||||
if msgRejErr, ok := err.(MessageRejectError); ok {
|
||||
return msgRejErr
|
||||
}
|
||||
return IncorrectDataFormatForValue(parser.Tag())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetField sets the field with Tag tag.
|
||||
func (m *FieldMap) SetField(tag Tag, field FieldValueWriter) *FieldMap {
|
||||
return m.SetBytes(tag, field.Write())
|
||||
}
|
||||
|
||||
// SetBytes sets bytes.
|
||||
func (m *FieldMap) SetBytes(tag Tag, value []byte) *FieldMap {
|
||||
f := m.getOrCreate(tag)
|
||||
initField(f, tag, value)
|
||||
return m
|
||||
}
|
||||
|
||||
// SetBool is a SetField wrapper for bool fields.
|
||||
func (m *FieldMap) SetBool(tag Tag, value bool) *FieldMap {
|
||||
return m.SetField(tag, FIXBoolean(value))
|
||||
}
|
||||
|
||||
// SetInt is a SetField wrapper for int fields.
|
||||
func (m *FieldMap) SetInt(tag Tag, value int) *FieldMap {
|
||||
v := FIXInt(value)
|
||||
return m.SetBytes(tag, v.Write())
|
||||
}
|
||||
|
||||
// SetString is a SetField wrapper for string fields.
|
||||
func (m *FieldMap) SetString(tag Tag, value string) *FieldMap {
|
||||
return m.SetBytes(tag, []byte(value))
|
||||
}
|
||||
|
||||
// Remove removes a tag from field map.
|
||||
func (m *FieldMap) Remove(tag Tag) {
|
||||
m.rwLock.Lock()
|
||||
defer m.rwLock.Unlock()
|
||||
|
||||
delete(m.tagLookup, tag)
|
||||
}
|
||||
|
||||
// Clear purges all fields from field map.
|
||||
func (m *FieldMap) Clear() {
|
||||
m.rwLock.Lock()
|
||||
defer m.rwLock.Unlock()
|
||||
|
||||
m.tags = m.tags[0:0]
|
||||
for k := range m.tagLookup {
|
||||
delete(m.tagLookup, k)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *FieldMap) clearNoLock() {
|
||||
m.tags = m.tags[0:0]
|
||||
for k := range m.tagLookup {
|
||||
delete(m.tagLookup, k)
|
||||
}
|
||||
}
|
||||
|
||||
// CopyInto overwrites the given FieldMap with this one.
|
||||
func (m *FieldMap) CopyInto(to *FieldMap) {
|
||||
m.rwLock.RLock()
|
||||
defer m.rwLock.RUnlock()
|
||||
|
||||
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 {
|
||||
m.tags = append(m.tags, t)
|
||||
}
|
||||
|
||||
m.tagLookup[t] = f
|
||||
}
|
||||
|
||||
func (m *FieldMap) getOrCreate(tag Tag) field {
|
||||
m.rwLock.Lock()
|
||||
defer m.rwLock.Unlock()
|
||||
|
||||
if f, ok := m.tagLookup[tag]; ok {
|
||||
f = f[:1]
|
||||
return f
|
||||
}
|
||||
|
||||
f := make(field, 1)
|
||||
m.tagLookup[tag] = f
|
||||
m.tags = append(m.tags, tag)
|
||||
return f
|
||||
}
|
||||
|
||||
// Set is a setter for fields.
|
||||
func (m *FieldMap) Set(field FieldWriter) *FieldMap {
|
||||
f := m.getOrCreate(field.Tag())
|
||||
initField(f, field.Tag(), field.Write())
|
||||
return m
|
||||
}
|
||||
|
||||
// SetGroup is a setter specific to group fields.
|
||||
func (m *FieldMap) SetGroup(field FieldGroupWriter) *FieldMap {
|
||||
m.rwLock.Lock()
|
||||
defer m.rwLock.Unlock()
|
||||
|
||||
_, ok := m.tagLookup[field.Tag()]
|
||||
if !ok {
|
||||
m.tags = append(m.tags, field.Tag())
|
||||
}
|
||||
m.tagLookup[field.Tag()] = field.Write()
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *FieldMap) sortedTags() []Tag {
|
||||
sort.Sort(m)
|
||||
return m.tags
|
||||
}
|
||||
|
||||
func (m FieldMap) write(buffer *bytes.Buffer) {
|
||||
m.rwLock.Lock()
|
||||
defer m.rwLock.Unlock()
|
||||
|
||||
for _, tag := range m.sortedTags() {
|
||||
if f, ok := m.tagLookup[tag]; ok {
|
||||
writeField(f, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m FieldMap) total() int {
|
||||
m.rwLock.RLock()
|
||||
defer m.rwLock.RUnlock()
|
||||
|
||||
total := 0
|
||||
for _, fields := range m.tagLookup {
|
||||
for _, tv := range fields {
|
||||
switch tv.tag {
|
||||
case tagCheckSum: // Tag does not contribute to total.
|
||||
default:
|
||||
total += tv.total()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
func (m FieldMap) length() int {
|
||||
m.rwLock.RLock()
|
||||
defer m.rwLock.RUnlock()
|
||||
|
||||
length := 0
|
||||
for _, fields := range m.tagLookup {
|
||||
for _, tv := range fields {
|
||||
switch tv.tag {
|
||||
case tagBeginString, tagBodyLength, tagCheckSum: // Tags do not contribute to length.
|
||||
default:
|
||||
length += tv.length()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return length
|
||||
}
|
||||
Reference in New Issue
Block a user