Skip to content

Commit 764351f

Browse files
committed
Support udecimal library in code generation
1 parent 9d46c64 commit 764351f

File tree

10 files changed

+249
-24
lines changed

10 files changed

+249
-24
lines changed

.github/workflows/ci.yaml

+44-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
- name: golangci-lint
2929
uses: golangci/golangci-lint-action@v4
3030
with:
31-
version: v1.57.2
31+
version: v1.64.6
3232

3333
build:
3434
name: Build Source
@@ -101,6 +101,13 @@ jobs:
101101
STORE_TYPE: memory
102102
ACCEPTANCE_SET: server
103103
run: make generate-ci && make build && make $FIX_TEST
104+
- name: Acceptance test with udecimal
105+
env:
106+
GO111MODULE: on
107+
FIX_TEST: ${{ matrix.fix-version }}
108+
STORE_TYPE: memory
109+
ACCEPTANCE_SET: server
110+
run: make generate-ci-udecimal && make build && make $FIX_TEST
104111

105112
serverfile:
106113
name: Server FileStore Suite
@@ -135,6 +142,13 @@ jobs:
135142
STORE_TYPE: file
136143
ACCEPTANCE_SET: server
137144
run: make generate-ci && make build && make $FIX_TEST
145+
- name: Acceptance test with udecimal
146+
env:
147+
GO111MODULE: on
148+
FIX_TEST: ${{ matrix.fix-version }}
149+
STORE_TYPE: file
150+
ACCEPTANCE_SET: server
151+
run: make generate-ci-udecimal && make build && make $FIX_TEST
138152

139153
servermongo:
140154
name: Server MongoStore Suite
@@ -174,6 +188,13 @@ jobs:
174188
STORE_TYPE: mongo
175189
ACCEPTANCE_SET: server
176190
run: make generate-ci && make build && make $FIX_TEST
191+
- name: Acceptance test with udecimal
192+
env:
193+
GO111MODULE: on
194+
FIX_TEST: ${{ matrix.fix-version }}
195+
STORE_TYPE: mongo
196+
ACCEPTANCE_SET: server
197+
run: make generate-ci-udecimal && make build && make $FIX_TEST
177198

178199
resendreqchunksize:
179200
name: ResendRequestChunkSize Suite
@@ -208,6 +229,13 @@ jobs:
208229
STORE_TYPE: memory
209230
ACCEPTANCE_SET: resendreqchunksize
210231
run: make generate-ci && make build && make $FIX_TEST
232+
- name: Acceptance test with udecimal
233+
env:
234+
GO111MODULE: on
235+
FIX_TEST: ${{ matrix.fix-version }}
236+
STORE_TYPE: memory
237+
ACCEPTANCE_SET: resendreqchunksize
238+
run: make generate-ci-udecimal && make build && make $FIX_TEST
211239

212240
lastseqnumprocessed:
213241
name: LastSeqNumProcessed Suite
@@ -240,6 +268,13 @@ jobs:
240268
STORE_TYPE: memory
241269
ACCEPTANCE_SET: lastseqnumprocessed
242270
run: make generate-ci && make build && make $FIX_TEST
271+
- name: Acceptance test with udecimal
272+
env:
273+
GO111MODULE: on
274+
FIX_TEST: ${{ matrix.fix-version }}
275+
STORE_TYPE: memory
276+
ACCEPTANCE_SET: lastseqnumprocessed
277+
run: make generate-ci-udecimal && make build && make $FIX_TEST
243278

244279
nextexpectedseqnum:
245280
name: NextExpectedSeqNum Suite
@@ -269,4 +304,11 @@ jobs:
269304
FIX_TEST: ${{ matrix.fix-version }}
270305
STORE_TYPE: memory
271306
ACCEPTANCE_SET: nextexpectedseqnum
272-
run: make generate-ci && make build && make $FIX_TEST
307+
run: make generate-ci && make build && make $FIX_TEST
308+
- name: Acceptance test with udecimal
309+
env:
310+
GO111MODULE: on
311+
FIX_TEST: ${{ matrix.fix-version }}
312+
STORE_TYPE: memory
313+
ACCEPTANCE_SET: nextexpectedseqnum
314+
run: make generate-ci-udecimal && make build && make $FIX_TEST

Makefile

+9-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ clean:
77
generate: clean
88
mkdir -p gen; cd gen; go run ../cmd/generate-fix/generate-fix.go -pkg-root=github.com/quickfixgo/quickfix/gen ../spec/*.xml
99

10+
generate-udecimal: clean
11+
mkdir -p gen; cd gen; go run ../cmd/generate-fix/generate-fix.go -use-udecimal=true -pkg-root=github.com/quickfixgo/quickfix/gen ../spec/*.xml
12+
1013
fmt:
1114
gofmt -l -w -s $(shell find . -type f -name '*.go')
1215

@@ -19,14 +22,14 @@ test:
1922
linters-install:
2023
@golangci-lint --version >/dev/null 2>&1 || { \
2124
echo "installing linting tools..."; \
22-
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.57.2; \
25+
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.64.6; \
2326
}
2427

2528
lint: linters-install
2629
golangci-lint run
2730

2831
# An easy way to run the linter without going through the install process -
29-
# docker run -t --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.57.2 golangci-lint run -v
32+
# docker run -t --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.64.6 golangci-lint run -v
3033
# See https://golangci-lint.run/welcome/install/ for more details.
3134

3235
# ---------------------------------------------------------------
@@ -81,6 +84,9 @@ test-ci:
8184
go test -v -cover `go list ./... | grep -v quickfix/gen`
8285

8386
generate-ci: clean
84-
mkdir -p gen; cd gen; go run ../cmd/generate-fix/generate-fix.go -pkg-root=github.com/quickfixgo/quickfix/gen ../spec/$(shell echo $(FIX_TEST) | tr '[:lower:]' '[:upper:]').xml;
87+
mkdir -p gen; cd gen; go run ../cmd/generate-fix/generate-fix.go -pkg-root=github.com/quickfixgo/quickfix/gen ../spec/$(shell echo $(FIX_TEST) | tr '[:lower:]' '[:upper:]').xml;
88+
89+
generate-ci-udecimal: clean
90+
mkdir -p gen; cd gen; go run ../cmd/generate-fix/generate-fix.go -use-udecimal=true -pkg-root=github.com/quickfixgo/quickfix/gen ../spec/$(shell echo $(FIX_TEST) | tr '[:lower:]' '[:upper:]').xml;
8591

8692
# ---------------------------------------------------------------

cmd/generate-fix/internal/generate.go

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
var (
1515
useFloat = flag.Bool("use-float", false, "By default, FIX float fields are represented as arbitrary-precision fixed-point decimal numbers. Set to 'true' to instead generate FIX float fields as float64 values.")
16+
useUDecimal = flag.Bool("use-udecimal", false, "By default, FIX uses the shopspring/decimal library for fixed-point decimal numbers. Set to 'true' to instead use the quagmt/udecimal library.")
1617
pkgRoot = flag.String("pkg-root", "github.com/quickfixgo", "Set a string here to provide a custom import path for generated packages.")
1718
tabWidth = 8
1819
printerMode = printer.UseSpaces | printer.TabIndent

cmd/generate-fix/internal/template_helpers.go

+20-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ import (
66
"github.com/quickfixgo/quickfix/datadictionary"
77
)
88

9+
func isDecimalType(quickfixType string) bool {
10+
switch quickfixType {
11+
case "FIXDecimal", "FIXUDecimal":
12+
return true
13+
default:
14+
return false
15+
}
16+
}
17+
918
func checkIfDecimalImportRequiredForFields(fTypes []*datadictionary.FieldType) (ok bool, err error) {
1019
var t string
1120
for _, fType := range fTypes {
@@ -14,7 +23,7 @@ func checkIfDecimalImportRequiredForFields(fTypes []*datadictionary.FieldType) (
1423
return
1524
}
1625

17-
if t == "FIXDecimal" {
26+
if isDecimalType(t) {
1827
return true, nil
1928
}
2029
}
@@ -54,7 +63,7 @@ func checkFieldDecimalRequired(f *datadictionary.FieldDef) (required bool, err e
5463
return
5564
}
5665

57-
if t == "FIXDecimal" {
66+
if isDecimalType(t) {
5867
required = true
5968
return
6069
}
@@ -121,6 +130,10 @@ func collectStandardImports(m *datadictionary.MessageDef) (imports []string, err
121130

122131
func collectExtraImports(m *datadictionary.MessageDef) (imports []string, err error) {
123132
var decimalRequired bool
133+
importPath := "github.com/shopspring/decimal"
134+
if *useUDecimal {
135+
importPath = "github.com/quagmt/udecimal"
136+
}
124137
for _, f := range m.Fields {
125138
if !decimalRequired {
126139
if decimalRequired, err = checkFieldDecimalRequired(f); err != nil {
@@ -134,7 +147,7 @@ func collectExtraImports(m *datadictionary.MessageDef) (imports []string, err er
134147
}
135148

136149
if decimalRequired {
137-
imports = append(imports, "github.com/shopspring/decimal")
150+
imports = append(imports, importPath)
138151
}
139152

140153
return
@@ -191,6 +204,8 @@ func quickfixValueType(quickfixType string) (goType string, err error) {
191204
goType = "float64"
192205
case "FIXDecimal":
193206
goType = "decimal.Decimal"
207+
case "FIXUDecimal":
208+
goType = "udecimal.Decimal"
194209
default:
195210
err = fmt.Errorf("Unknown QuickFIX Type: %v", quickfixType)
196211
}
@@ -275,6 +290,8 @@ func quickfixType(field *datadictionary.FieldType) (quickfixType string, err err
275290
case "FLOAT":
276291
if *useFloat {
277292
quickfixType = "FIXFloat"
293+
} else if *useUDecimal {
294+
quickfixType = "FIXUDecimal"
278295
} else {
279296
quickfixType = "FIXDecimal"
280297
}

cmd/generate-fix/internal/templates.go

+22-3
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,14 @@ func init() {
2727
"collectStandardImports": collectStandardImports,
2828
"collectExtraImports": collectExtraImports,
2929
"checkIfDecimalImportRequiredForFields": checkIfDecimalImportRequiredForFields,
30-
"checkIfTimeImportRequiredForFields": checkIfTimeImportRequiredForFields,
31-
"checkIfEnumImportRequired": checkIfEnumImportRequired,
30+
"decimalImport": func() string {
31+
if *useUDecimal {
32+
return "github.com/quagmt/udecimal"
33+
}
34+
return "github.com/shopspring/decimal"
35+
},
36+
"checkIfTimeImportRequiredForFields": checkIfTimeImportRequiredForFields,
37+
"checkIfEnumImportRequired": checkIfEnumImportRequired,
3238
}
3339

3440
baseTemplate := template.Must(template.New("Base").Funcs(tmplFuncs).Parse(`
@@ -45,6 +51,10 @@ Set{{ .Name }}(v enum.{{ .Name }}) {
4551
Set{{ .Name }}(value decimal.Decimal, scale int32) {
4652
{{ template "receiver" }}.Set(field.New{{ .Name }}(value, scale))
4753
}
54+
{{- else if eq $qfix_type "FIXUDecimal" -}}
55+
Set{{ .Name }}(value udecimal.Decimal, scale uint8) {
56+
{{ template "receiver" }}.Set(field.New{{ .Name }}(value, scale))
57+
}
4858
{{- else -}}
4959
Set{{ .Name }}(v {{ quickfixValueType $qfix_type }}) {
5060
{{ template "receiver" }}.Set(field.New{{ .Name }}(v))
@@ -77,6 +87,8 @@ Get{{ .Name }}() (f field.{{ .Name }}Field, err quickfix.MessageRejectError) {
7787
Get{{ .Name }}() (v enum.{{ .Name }}, err quickfix.MessageRejectError) {
7888
{{- else if eq $bt "FIXDecimal" -}}
7989
Get{{ .Name }}() (v decimal.Decimal, err quickfix.MessageRejectError) {
90+
{{- else if eq $bt "FIXUDecimal" -}}
91+
Get{{ .Name }}() (v udecimal.Decimal, err quickfix.MessageRejectError) {
8092
{{- else -}}
8193
Get{{ .Name }}() (v {{ quickfixValueType $bt }}, err quickfix.MessageRejectError) {
8294
{{- end }}
@@ -345,7 +357,7 @@ package field
345357
import (
346358
{{ if checkIfTimeImportRequiredForFields . }}"time"{{ end }}
347359
348-
{{ if checkIfDecimalImportRequiredForFields . }}"github.com/shopspring/decimal"{{ end }}
360+
{{ if checkIfDecimalImportRequiredForFields . }}"{{ decimalImport }}"{{ end }}
349361
350362
"github.com/quickfixgo/quickfix"
351363
"{{ importRootPath }}/enum"
@@ -391,6 +403,11 @@ func New{{ .Name }}(val enum.{{ .Name }}) {{ .Name }}Field {
391403
func New{{ .Name }}(val decimal.Decimal, scale int32) {{ .Name }}Field {
392404
return {{ .Name }}Field{ quickfix.FIXDecimal{ Decimal: val, Scale: scale} }
393405
}
406+
{{ else if eq $base_type "FIXUDecimal" }}
407+
// New{{ .Name }} returns a new {{ .Name }}Field initialized with val and scale.
408+
func New{{ .Name }}(val udecimal.Decimal, scale uint8) {{ .Name }}Field {
409+
return {{ .Name }}Field{ quickfix.FIXUDecimal{ Decimal: val, Scale: scale} }
410+
}
394411
{{ else }}
395412
// New{{ .Name }} returns a new {{ .Name }}Field initialized with val.
396413
func New{{ .Name }}(val {{ quickfixValueType $base_type }}) {{ .Name }}Field {
@@ -402,6 +419,8 @@ func New{{ .Name }}(val {{ quickfixValueType $base_type }}) {{ .Name }}Field {
402419
func (f {{ .Name }}Field) Value() enum.{{ .Name }} { return enum.{{ .Name }}(f.String()) }
403420
{{ else if eq $base_type "FIXDecimal" }}
404421
func (f {{ .Name }}Field) Value() (val decimal.Decimal) { return f.Decimal }
422+
{{ else if eq $base_type "FIXUDecimal" }}
423+
func (f {{ .Name }}Field) Value() (val udecimal.Decimal) { return f.Decimal }
405424
{{ else }}
406425
func (f {{ .Name }}Field) Value() ({{ quickfixValueType $base_type }}) {
407426
{{- if eq $base_type "FIXString" -}}

fix_decimal_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,29 @@ import (
2323
"github.com/stretchr/testify/require"
2424
)
2525

26+
func BenchmarkFIXDecimalRead(b *testing.B) {
27+
var dec FIXDecimal
28+
byt := []byte("-124.3456")
29+
for i := 0; i < b.N; i++ {
30+
if err := dec.Read(byt); err != nil {
31+
b.FailNow()
32+
}
33+
}
34+
}
35+
36+
func BenchmarkFIXDecimalWrite(b *testing.B) {
37+
decValue, err := decimal.NewFromString("-124.3456")
38+
if err != nil {
39+
b.FailNow()
40+
}
41+
dec := FIXDecimal{Decimal: decValue, Scale: 5}
42+
for i := 0; i < b.N; i++ {
43+
if byt := dec.Write(); len(byt) == 0 {
44+
b.FailNow()
45+
}
46+
}
47+
}
48+
2649
func TestFIXDecimalWrite(t *testing.T) {
2750
var tests = []struct {
2851
decimal FIXDecimal

fix_udecimal.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) quickfixengine.org All rights reserved.
2+
//
3+
// This file may be distributed under the terms of the quickfixengine.org
4+
// license as defined by quickfixengine.org and appearing in the file
5+
// LICENSE included in the packaging of this file.
6+
//
7+
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
8+
// THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
9+
// PARTICULAR PURPOSE.
10+
//
11+
// See http://www.quickfixengine.org/LICENSE for licensing information.
12+
//
13+
// Contact [email protected] if any conditions of this licensing
14+
// are not clear to you.
15+
16+
package quickfix
17+
18+
import "github.com/quagmt/udecimal"
19+
20+
// FIXUDecimal is a FIX Float Value that implements an arbitrary precision fixed-point udecimal. Implements FieldValue.
21+
type FIXUDecimal struct {
22+
udecimal.Decimal
23+
24+
// Scale is the number of digits after the decimal point when Writing the field value as a FIX value.
25+
Scale uint8
26+
}
27+
28+
func (d FIXUDecimal) Write() []byte {
29+
return []byte(d.Decimal.Trunc(d.Scale).StringFixed(d.Scale))
30+
}
31+
32+
func (d *FIXUDecimal) Read(bytes []byte) (err error) {
33+
d.Decimal, err = udecimal.Parse(string(bytes))
34+
return
35+
}

0 commit comments

Comments
 (0)