forked from hadrianl/ibapi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
406 lines (329 loc) · 8.52 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
package ibapi
import (
"bufio"
"bytes"
"encoding/binary"
"io"
"math"
"reflect"
"strconv"
"time"
"go.uber.org/zap"
// log "github.com/sirupsen/logrus"
)
const (
fieldSplit byte = '\x00'
// UNSETFLOAT represent unset value of float64.
UNSETFLOAT float64 = math.MaxFloat64
// UNSETINT represent unset value of int64.
UNSETINT int64 = math.MaxInt64
// NO_VALID_ID represent that the callback func of wrapper is not attached to any request.
NO_VALID_ID int64 = -1
// MAX_MSG_LEN is the max length that receiver could take.
MAX_MSG_LEN int = 0xFFFFFF
)
var log *zap.Logger
func init() {
log, _ = zap.NewProduction()
}
// APILogger sets the options of internal logger for API, such as level, encoder, output, see uber.org/zap for more information
func SetAPILogger(cfg zap.Config, opts ...zap.Option) error {
newlogger, err := cfg.Build(opts...)
log = newlogger
return err
}
// GetLogger gets a clone of the internal logger with the option, see uber.org/zap for more information
func GetLogger() *zap.Logger {
return log
}
func bytesToTime(b []byte) time.Time {
// format := "20060102 15:04:05 Mountain Standard Time"
// 214 208 185 250 177 234 215 188 202 177 188 228
format := "20060102 15:04:05 MST"
t := string(b)
localtime, err := time.ParseInLocation(format, t, time.Local)
if err != nil {
log.Error("btyes to time error", zap.Error(err))
}
return localtime
}
// readMsgBytes try to read the msg based on the message size
func readMsgBytes(reader *bufio.Reader) ([]byte, error) {
sizeBytes := make([]byte, 4) // sync.Pool?
//try to get 4bytes sizeBytes
for n, r := 0, 4; n < r; {
tempMsgBytes := make([]byte, r-n)
tn, err := reader.Read(tempMsgBytes)
if err != nil {
return nil, err
}
copy(sizeBytes[n:n+tn], tempMsgBytes)
n += tn
}
size := int(binary.BigEndian.Uint32(sizeBytes))
log.Debug("readMsgBytes", zap.Int("size", size))
msgBytes := make([]byte, size)
// XXX: maybe there is a better way to get fixed size of bytes
for n, r := 0, size; n < r; {
tempMsgBytes := make([]byte, r-n)
tn, err := reader.Read(tempMsgBytes)
if err != nil {
return nil, err
}
copy(msgBytes[n:n+tn], tempMsgBytes)
n += tn
}
log.Debug("readMsgBytes", zap.Binary("msgBytes", msgBytes))
return msgBytes, nil
}
// scanFields defines how to unpack the buf
func scanFields(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF {
return 0, nil, io.EOF
}
if len(data) < 4 {
return 0, nil, nil
}
totalSize := int(binary.BigEndian.Uint32(data[:4])) + 4
if totalSize > len(data) {
return 0, nil, nil
}
// msgBytes := make([]byte, totalSize-4, totalSize-4)
// copy(msgBytes, data[4:totalSize])
// not copy here, copied by callee more reasonable
return totalSize, data[4:totalSize], nil
}
// func field2Bytes(field interface{}) []byte {
// // var bs []byte
// bs := make([]byte, 0, 9)
// switch v := field.(type) {
// case int64:
// bs = encodeInt64(v)
// case float64:
// bs = encodeFloat64(v)
// case string:
// bs = encodeString(v)
// case bool:
// bs = encodeBool(v)
// case int:
// bs = encodeInt(v)
// case []byte:
// bs = v
// // case time.Time:
// // b = encodeTime(msg.(time.Time))
// default:
// log.Panic("failed to covert the field", zap.Reflect("field", field))
// }
// return append(bs, fieldSplit)
// }
// makeMsgBytes is a universal way to make the request ,but not an efficient way
// TODO: do some test and improve!!!
func makeMsgBytes(fields ...interface{}) []byte {
msgBytes := make([]byte, 4, 8*len(fields)+4) // pre alloc memory
for _, f := range fields {
switch v := f.(type) {
case int64:
msgBytes = strconv.AppendInt(msgBytes, v, 10)
case float64:
msgBytes = strconv.AppendFloat(msgBytes, v, 'g', 10, 64)
case string:
msgBytes = append(msgBytes, []byte(v)...)
case bool:
if v {
msgBytes = append(msgBytes, '1')
} else {
msgBytes = append(msgBytes, '0')
}
case int:
msgBytes = strconv.AppendInt(msgBytes, int64(v), 10)
case []byte:
msgBytes = append(msgBytes, v...)
default:
log.Panic("failed to covert the field", zap.Reflect("field", f)) // never reach here
}
msgBytes = append(msgBytes, fieldSplit)
}
// add the size header
binary.BigEndian.PutUint32(msgBytes, uint32(len(msgBytes)-4))
return msgBytes
}
func splitMsgBytes(data []byte) [][]byte {
fields := bytes.Split(data, []byte{fieldSplit})
return fields[:len(fields)-1]
}
func decodeInt(field []byte) int64 {
if bytes.Equal(field, []byte{}) {
return 0
}
i, err := strconv.ParseInt(string(field), 10, 64)
if err != nil {
log.Panic("failed to decode int", zap.Error(err))
}
return i
}
func decodeString(field []byte) string {
return string(field)
}
// func encodeInt64(i int64) []byte {
// return []byte(strconv.FormatInt(i, 10))
// }
// func encodeInt(i int) []byte {
// return []byte(strconv.Itoa(i))
// }
// func encodeFloat64(f float64) []byte {
// return []byte(strconv.FormatFloat(f, 'g', 10, 64))
// }
// func encodeString(str string) []byte {
// return []byte(str)
// }
// func encodeBool(b bool) []byte {
// if b {
// return []byte{'1'}
// }
// return []byte{'0'}
// }
func handleEmpty(d interface{}) string {
switch v := d.(type) {
case int64:
if v == UNSETINT {
return ""
}
return strconv.FormatInt(v, 10)
case float64:
if v == UNSETFLOAT {
return ""
}
return strconv.FormatFloat(v, 'g', 10, 64)
default:
log.Panic("no handler for such type", zap.Reflect("val", d))
return "" // never reach here
}
}
//InitDefault try to init the object with the default tag, that is a common way but not a efficent way
func InitDefault(o interface{}) {
t := reflect.TypeOf(o).Elem()
v := reflect.ValueOf(o).Elem()
fieldCount := t.NumField()
for i := 0; i < fieldCount; i++ {
field := t.Field(i)
if v.Field(i).Kind() == reflect.Struct {
InitDefault(v.Field(i).Addr().Interface())
continue
}
if defaultValue, ok := field.Tag.Lookup("default"); ok {
switch defaultValue {
case "UNSETFLOAT":
v.Field(i).SetFloat(UNSETFLOAT)
case "UNSETINT":
v.Field(i).SetInt(UNSETINT)
case "-1":
v.Field(i).SetInt(-1)
case "true":
v.Field(i).SetBool(true)
default:
log.Panic("Unknown defaultValue", zap.Reflect("default", v))
}
}
}
}
// MsgBuffer is the buffer that contains a whole msg
type MsgBuffer struct {
bytes.Buffer
bs []byte
err error
}
func (m *MsgBuffer) readInt() int64 {
var i int64
m.bs, m.err = m.ReadBytes(fieldSplit)
if m.err != nil {
log.Panic("decode int64 error", zap.Error(m.err))
}
m.bs = m.bs[:len(m.bs)-1]
if bytes.Equal(m.bs, nil) {
return 0
}
i, m.err = strconv.ParseInt(string(m.bs), 10, 64)
if m.err != nil {
log.Panic("decode int64 error", zap.Error(m.err))
}
return i
}
func (m *MsgBuffer) readIntCheckUnset() int64 {
var i int64
m.bs, m.err = m.ReadBytes(fieldSplit)
if m.err != nil {
log.Panic("decode int64 error", zap.Error(m.err))
}
m.bs = m.bs[:len(m.bs)-1]
if bytes.Equal(m.bs, nil) {
return UNSETINT
}
i, m.err = strconv.ParseInt(string(m.bs), 10, 64)
if m.err != nil {
log.Panic("decode int64 error", zap.Error(m.err))
}
return i
}
func (m *MsgBuffer) readFloat() float64 {
var f float64
m.bs, m.err = m.ReadBytes(fieldSplit)
if m.err != nil {
log.Panic("decode float64 error", zap.Error(m.err))
}
m.bs = m.bs[:len(m.bs)-1]
if bytes.Equal(m.bs, nil) {
return 0.0
}
f, m.err = strconv.ParseFloat(string(m.bs), 64)
if m.err != nil {
log.Panic("decode float64 error", zap.Error(m.err))
}
return f
}
func (m *MsgBuffer) readFloatCheckUnset() float64 {
var f float64
m.bs, m.err = m.ReadBytes(fieldSplit)
if m.err != nil {
log.Panic("decode float64 error", zap.Error(m.err))
}
m.bs = m.bs[:len(m.bs)-1]
if bytes.Equal(m.bs, nil) {
return UNSETFLOAT
}
f, m.err = strconv.ParseFloat(string(m.bs), 64)
if m.err != nil {
log.Panic("decode float64 error", zap.Error(m.err))
}
return f
}
func (m *MsgBuffer) readBool() bool {
m.bs, m.err = m.ReadBytes(fieldSplit)
if m.err != nil {
log.Panic("decode bool error", zap.Error(m.err))
}
m.bs = m.bs[:len(m.bs)-1]
if bytes.Equal(m.bs, []byte{'0'}) || bytes.Equal(m.bs, nil) {
return false
}
return true
}
func (m *MsgBuffer) readString() string {
m.bs, m.err = m.ReadBytes(fieldSplit)
if m.err != nil {
log.Panic("decode string error", zap.Error(m.err))
}
return string(m.bs[:len(m.bs)-1])
}
// NewMsgBuffer create a new MsgBuffer
func NewMsgBuffer(bs []byte) *MsgBuffer {
return &MsgBuffer{
*bytes.NewBuffer(bs),
nil,
nil}
}
// Reset reset buffer, []byte, err
func (m *MsgBuffer) Reset() {
m.Buffer.Reset()
m.bs = m.bs[:0]
m.err = nil
}