fast

package module
v0.0.0-...-4263f2f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 22, 2020 License: Apache-2.0 Imports: 12 Imported by: 0

README

goFAST

GoDoc

goFAST is a Go implementation of the FAST Protocol (FIX Adapted for STreaming). Work-in-progress, expect bugs and missing features.

Installation

Install the FAST library using the "go get" command:

go get github.com/co11ter/goFAST

Usage

See documentation examples.

Benchmark

Run go test -bench=.. Only Decoder Benchmark is implemented.

$ go test -bench=.
goos: linux
goarch: amd64
pkg: github.com/co11ter/goFAST
BenchmarkDecoder_DecodeReflection-4   	  200000	     10403 ns/op	     795 B/op	      68 allocs/op
BenchmarkDecoder_DecodeReceiver-4     	  300000	      5453 ns/op	     321 B/op	      32 allocs/op
PASS
ok  	github.com/co11ter/goFAST	4.977s

TODO

  • apply errors
  • add benchmark of encoder
  • optimize encoder
  • implement delta and tail operators for string type

Documentation

Overview

Package fast implements FAST (FIX Adapted for STreaming) encoder/decoder.

Example (ReceiverDecode)
package main

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/co11ter/goFAST"
)

var xmlDecodeTemplate = `
<?xml version="1.0" encoding="UTF-8"?>
<templates xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
	<template name="Done" id="1" xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
		<string name="Type" id="15">
			<constant value="99"/>
		</string>
		<string name="Test" id="131" presence="optional"/>
		<uInt64 name="Time" id="20" presence="optional"/>
		<int32 name="Equal" id="271"/>
		<sequence name="Sequence">
			<length name="SeqLength" id="146"/>
			<uInt64 name="SomeField" id="38"/>
		</sequence>
	</template>
</templates>`

type ReceiverSeq struct {
	SomeField uint64
}

type ReceiverMsg struct {
	TemplateID uint
	Type       string
	Test       string
	Time       uint64
	Equal      int32
	Sequence   []ReceiverSeq

	seqLocked bool
	seqIndex  int
}

func (br *ReceiverMsg) SetTemplateID(tid uint) {
	br.TemplateID = tid
}

func (br *ReceiverMsg) SetLength(field *fast.Field) {
	if field.Name == "Sequence" && len(br.Sequence) < field.Value.(int) {
		br.Sequence = make([]ReceiverSeq, field.Value.(int))
	}
}

func (br *ReceiverMsg) Lock(field *fast.Field) bool {
	br.seqLocked = field.Name == "Sequence"
	if br.seqLocked {
		br.seqIndex = field.Value.(int)
	}
	return br.seqLocked
}

func (br *ReceiverMsg) Unlock() {
	br.seqLocked = false
	br.seqIndex = 0
}

func (br *ReceiverMsg) SetValue(field *fast.Field) {
	switch field.ID {
	case 15:
		br.Type = field.Value.(string)
	case 131:
		br.Test = field.Value.(string)
	case 20:
		br.Time = field.Value.(uint64)
	case 271:
		br.Equal = field.Value.(int32)
	case 38:
		br.Sequence[br.seqIndex].SomeField = field.Value.(uint64)
	}
}

func main() {
	var msg ReceiverMsg
	reader := bytes.NewReader(
		[]byte{0xc0, 0x81, 0x74, 0x65, 0x73, 0xf4, 0x80, 0x80, 0x81, 0x80, 0x82},
	)

	tpls, err := fast.ParseXMLTemplate(strings.NewReader(xmlDecodeTemplate))
	if err != nil {
		panic(err)
	}
	decoder := fast.NewDecoder(
		reader,
		tpls...,
	)

	if err := decoder.Decode(&msg); err != nil {
		panic(err)
	}
	fmt.Print(msg)

}
Output:

{1 99 test 0 0 [{0}] false 0}
Example (SenderEncode)
package main

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/co11ter/goFAST"
)

var xmlEncodeTemplate = `
<?xml version="1.0" encoding="UTF-8"?>
<templates xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
	<template name="Done" id="1" xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
		<string name="Type" id="15">
			<constant value="99"/>
		</string>
		<string name="Test" id="131" presence="optional"/>
		<uInt64 name="Time" id="20" presence="optional"/>
		<int32 name="Equal" id="271"/>
		<sequence name="Sequence">
			<length name="SeqLength" id="146"/>
			<uInt64 name="SomeField" id="38"/>
		</sequence>
	</template>

    <template name="Skip" id="999" xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
        <string name="Str" id="15">
            <constant value="99"/>
        </string>
    </template>
</templates>`

type SenderSeq struct {
	SomeField uint64
}

type SenderMsg struct {
	TemplateID uint
	Type       string
	Test       string
	Time       uint64
	Equal      int32
	Sequence   []SenderSeq

	seqLocked bool
	seqIndex  int
}

func (br *SenderMsg) GetTemplateID() uint {
	return br.TemplateID
}

func (br *SenderMsg) GetLength(field *fast.Field) {
	if field.Name == "Sequence" {
		field.Value = len(br.Sequence)
	}
}

func (br *SenderMsg) Lock(field *fast.Field) bool {
	br.seqLocked = field.Name == "Sequence"
	if br.seqLocked {
		br.seqIndex = field.Value.(int)
	}
	return br.seqLocked
}

func (br *SenderMsg) Unlock() {
	br.seqLocked = false
	br.seqIndex = 0
}

func (br *SenderMsg) GetValue(field *fast.Field) {
	switch field.ID {
	case 131:
		field.Value = br.Test
	case 38:
		field.Value = br.Sequence[br.seqIndex].SomeField
	}
}

func main() {
	var buf bytes.Buffer
	var msg = SenderMsg{
		TemplateID: 1,
		Test:       "test",
		Sequence: []SenderSeq{
			{SomeField: 2},
		},
	}

	tpls, err := fast.ParseXMLTemplate(strings.NewReader(xmlEncodeTemplate))
	if err != nil {
		panic(err)
	}
	encoder := fast.NewEncoder(&buf, tpls...)

	if err := encoder.Encode(&msg); err != nil {
		panic(err)
	}
	fmt.Printf("%x", buf.Bytes())

}
Output:

c081746573f480808182

Index

Examples

Constants

View Source
const (
	// TypeNull mark type of instruction null.
	TypeNull InstructionType = iota
	TypeUint32
	TypeInt32
	TypeUint64
	TypeInt64
	TypeLength
	TypeExponent
	TypeMantissa
	TypeDecimal
	TypeASCIIString
	TypeUnicodeString
	TypeByteVector
	TypeSequence
	TypeGroup

	OperatorNone InstructionOperator = iota
	OperatorConstant
	OperatorDelta
	OperatorDefault
	OperatorCopy
	OperatorIncrement // It's applicable to integer field types
	OperatorTail      // It's applicable to string and byte vector field types

	PresenceMandatory InstructionPresence = iota
	PresenceOptional
)

Instructions type, operator and presence

Variables

View Source
var (
	// ErrS1 is a static error if templates encoded in the concrete XML syntax are in
	// fact not well-formed, do not follow the rules of XML namespaces or are invalid
	// with respect to the schema in Appendix 1 in FAST 1.1 specification.
	// TODO have to find native lib to check xml syntax
	ErrS1 = errors.New("static error: S1")

	// ErrS2 is a static error if an operator is specified for a field of a type to
	// which the operator is not applicable.
	ErrS2 = errors.New("static error: S2")

	// ErrS3 is a static error if an initial value specified by the value attribute
	// in the concrete syntax can not be converted to a value of the type of the field.
	ErrS3 = errors.New("static error: S3")

	// ErrS4 is a static error if no initial value is specified for a constant operator.
	ErrS4 = errors.New("static error: S4")

	// ErrS5 is a static error if no initial value is specified for a default operator
	// on a mandatory field.
	ErrS5 = errors.New("static error: S5")

	// ErrD1 is a dynamic error if type of a field in a template can not be converted
	// to or from the type of the corresponding application field.
	ErrD1 = errors.New("dynamic error: D1")

	// ErrD2 is a dynamic error if an integer in the stream does not fall within the
	// bounds of the specifies integer type specified on the corresponding field.
	ErrD2 = errors.New("dynamic error: D2")

	// ErrD3 is a dynamic error if a decimal value can not be encoded due to limitation
	// introduced by using individual operators on exponent and mantissa.
	ErrD3 = errors.New("dynamic error: D3")

	// ErrD4 is a dynamic error if the type of the previous value is not the same as
	// the type of the field of the current operator.
	ErrD4 = errors.New("dynamic error: D4")

	// ErrD5 is a dynamic error if a mandatory field is not present in the stream, has
	// an undefined previous value and there is no initial value in the instruction
	// context.
	ErrD5 = errors.New("dynamic error: D5")

	// ErrD6 is a dynamic error if a mandatory field is not present in the stream and
	// has an empty previous value.
	ErrD6 = errors.New("dynamic error: D6")

	// ErrD7 is a dynamic error if the subtraction length exceeds the length of the base
	// value or if it does not fall in the value rang of an int32.
	ErrD7 = errors.New("dynamic error: D7")

	// ErrD8 is a dynamic error if the name specified on a static template reference
	// does not point to a template known be the encoder or decoder.
	ErrD8 = errors.New("dynamic error: D8")

	// ErrD9 is a dynamic error if a decoder can not find a template associated with a
	// template identifier appearing in the stream.
	ErrD9 = errors.New("dynamic error: D9")

	// ErrD10 is a dynamic error to convert byte vectors to and from other types
	// than string.
	ErrD10 = errors.New("dynamic error: D10")

	// ErrD11 is a dynamic error if the syntax of a string does not follow the rules for
	// the type converted to.
	ErrD11 = errors.New("dynamic error: D11")

	// ErrD12 is a dynamic error if a block length preamble is zero.
	ErrD12 = errors.New("dynamic error: D12")

	// ErrR1 is a reportable error if a decimal can not be represented by an exponent in
	// the range [-63 ... 63] of if the mantissa does not fit in an int64.
	ErrR1 = errors.New("reportable error: R1")

	// ErrR2 is a reportable error if the combined value after applying a tail or delta
	// operator to a Unicode string is not a valid UTF-8 sequence.
	ErrR2 = errors.New("reportable error: R2")

	// ErrR3 is a reportable error if a Unicode string that is being converted to an
	// ASCII string contains characters that are outside the ASCII character set.
	ErrR3 = errors.New("reportable error: R3")

	// ErrR4 is a reportable error if a value of an integer cannot be represented in the
	// target integer type in a conversion.
	ErrR4 = errors.New("reportable error: R4")

	// ErrR5 is a reportable error if a decimal being converted to an integer has a
	// negative exponent or if the resulting integer does not fit the target integer
	// type.
	ErrR5 = errors.New("reportable error: R5")

	// ErrR6 is a reportable error if an integer appears in an overlong encoding.
	ErrR6 = errors.New("reportable error: R6")

	// ErrR7 is a reportable error if a presence map is overlong.
	ErrR7 = errors.New("reportable error: R7")

	// ErrR8 is a reportable error if a presence map contains more bits than required.
	ErrR8 = errors.New("reportable error: R8")

	// ErrR9 is a reportable error if a string appears in an overlong encoding.
	ErrR9 = errors.New("reportable error: R9")
)

Functions

This section is empty.

Types

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

A Decoder reads and decodes FAST-encoded message from an io.Reader. You may need buffered reader since decoder reads data byte by byte.

func NewDecoder

func NewDecoder(reader io.Reader, tmps ...*Template) *Decoder

NewDecoder returns a new decoder that reads from reader.

func (*Decoder) Decode

func (d *Decoder) Decode(msg interface{}) error

Decode reads the next FAST-encoded message from reader and stores it in the value pointed to by msg. If an encountered data implements the Receiver interface and is not a nil pointer, Decode will use methods of Receiver for set decoded data.

Example (Reflection)
var xmlData = `
<?xml version="1.0" encoding="UTF-8"?>
<templates xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
	<template name="Done" id="1" xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
		<string name="Type" id="15">
			<constant value="99"/>
		</string>
		<string name="Test" id="131" presence="optional"/>
		<uInt64 name="Time" id="20" presence="optional"/>
		<int32 name="Equal" id="271"/>
		<sequence name="Sequence">
			<length name="SeqLength" id="146"/>
			<uInt64 name="SomeField" id="38"/>
		</sequence>
	</template>
</templates>`

type Seq struct {
	SomeField uint64
}

type ReflectMsg struct {
	TemplateID  uint    `fast:"*"`    // template id
	FieldByID   string  `fast:"15"`   // assign value by instruction id
	FieldByName string  `fast:"Test"` // assign value by instruction name
	Equal       int32   // name of field is default value for assign
	Nullable    *uint64 `fast:"20"` // nullable - will skip, if field data is absent
	Skip        int     `fast:"-"`  // skip
	Sequence    []Seq
}

var msg ReflectMsg
reader := bytes.NewReader(
	[]byte{0xc0, 0x81, 0x74, 0x65, 0x73, 0xf4, 0x80, 0x80, 0x81, 0x80, 0x82},
)

tpls, err := fast.ParseXMLTemplate(strings.NewReader(xmlData))
if err != nil {
	panic(err)
}
decoder := fast.NewDecoder(
	reader,
	tpls...,
)

if err := decoder.Decode(&msg); err != nil {
	panic(err)
}
fmt.Print(msg)
Output:

{1 99 test 0 <nil> 0 [{0}]}

func (*Decoder) Reset

func (d *Decoder) Reset()

Reset resets dictionary

func (*Decoder) SetLog

func (d *Decoder) SetLog(writer io.Writer)

SetLog sets writer for logging

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

A Encoder encodes and writes data to io.Writer.

func NewEncoder

func NewEncoder(writer io.Writer, tmps ...*Template) *Encoder

NewEncoder returns a new encoder that writes FAST-encoded message to writer.

func (*Encoder) Encode

func (e *Encoder) Encode(msg interface{}) error

Encode encodes msg struct to writer. If an encountered value implements the Sender interface and is not a nil pointer, Encode calls method of Sender to produce encoded message.

Example (Reflection)
var xmlData = `
<?xml version="1.0" encoding="UTF-8"?>
<templates xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
	<template name="Done" id="1" xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">
		<string name="Type" id="15">
			<constant value="99"/>
		</string>
		<string name="Test" id="131" presence="optional"/>
		<uInt64 name="Time" id="20" presence="optional"/>
		<int32 name="Equal" id="271"/>
		<sequence name="Sequence">
			<length name="SeqLength" id="146"/>
			<uInt64 name="SomeField" id="38"/>
		</sequence>
	</template>
</templates>`

type Seq struct {
	SomeField uint64
}

type ReflectMsg struct {
	TemplateID  uint    `fast:"*"`    // template id
	FieldByID   string  `fast:"15"`   // assign value by instruction id
	FieldByName string  `fast:"Test"` // assign value by instruction name
	Equal       int32   // name of field is default value for assign
	Nullable    *uint64 `fast:"20"` // nullable - will skip, if field data is absent
	Skip        int     `fast:"-"`  // skip
	Sequence    []Seq
}

var buf bytes.Buffer
var msg = ReflectMsg{
	TemplateID:  1,
	FieldByName: "test",
	Sequence: []Seq{
		{SomeField: 2},
	},
}

tpls, err := fast.ParseXMLTemplate(strings.NewReader(xmlData))
if err != nil {
	panic(err)
}
encoder := fast.NewEncoder(&buf, tpls...)

if err := encoder.Encode(&msg); err != nil {
	panic(err)
}
fmt.Printf("%x", buf.Bytes())
Output:

c081746573f4808182

func (*Encoder) Reset

func (e *Encoder) Reset()

Reset resets dictionary

func (*Encoder) SetLog

func (e *Encoder) SetLog(writer io.Writer)

SetLog sets writer for logging

type Field

type Field struct {
	ID    uint
	Name  string
	Value interface{}
	// contains filtered or unexported fields
}

Field contains value for decoding/encoding

type Instruction

type Instruction struct {
	ID           uint
	Name         string
	Presence     InstructionPresence
	Type         InstructionType
	Operator     InstructionOperator
	Instructions []*Instruction
	Value        interface{}
	// contains filtered or unexported fields
}

Instruction contains rules for encoding/decoding field.

type InstructionOperator

type InstructionOperator int

InstructionOperator specifies ways to optimize the encoding of the field.

type InstructionPresence

type InstructionPresence int

InstructionPresence specifies presence of the field.

type InstructionType

type InstructionType int

InstructionType specifies the basic encoding of the field.

type Receiver

type Receiver interface {
	// SetTemplateID indicates template id for message.
	SetTemplateID(uint)

	// SetValue indicates actual Field.Value for Field.Name or Field.ID.
	SetValue(*Field)

	// SetLength indicates length of sequence.
	SetLength(*Field)

	// Lock indicates a group or sequence. Field.Value will contain index of sequence
	Lock(*Field) bool
	Unlock()
}

Receiver is interface for setting data avoid reflection.

type Sender

type Sender interface {
	// GetTemplateID must return template id for message.
	GetTemplateID() uint

	// GetValue must set actual value to Field.Value for Field.Name or Field.ID.
	GetValue(*Field)

	// GetLength must set actual sequence length to Field.Value for Field.Name or Field.ID.
	GetLength(*Field)

	// Lock indicates a group or sequence. Field.Value will contain index of sequence
	Lock(*Field) bool
	Unlock()
}

Sender is interface for getting data avoid reflection.

type Template

type Template struct {
	ID           uint
	Name         string
	Instructions []*Instruction
}

Template collect instructions for this template

func ParseXMLTemplate

func ParseXMLTemplate(reader io.Reader) ([]*Template, error)

ParseXMLTemplate reads xml data from reader and return templates collection.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL