Skip to content

Commit 30184f7

Browse files
committed
Small code optimisations.
1 parent 2ba3365 commit 30184f7

File tree

4 files changed

+131
-93
lines changed

4 files changed

+131
-93
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ _OS:_ Linux Mint 17.2 64bit
2626

2727
FIX message type | FIX specification | Validation | Average time to parse one message
2828
----------------------------------|------------------------------------------|------------|----------------------------------
29-
NewOrderSingle('D') | Hand-coded spec. for this message only | No | 0.326 µs/msg
30-
NewOrderSingle('D') | Hand-coded spec. for this message only | Yes | 0.547 µs/msg
31-
NewOrderSingle('D') | Compiled full spec. for FIX.4.4 | Yes | 0.739 µs/msg
32-
MarketDataIncrementalRefresh('X') | Hand-coded spec. for this message only | Yes | 1.263 µs/msg
33-
MarketDataIncrementalRefresh('X') | Compiled full spec. for FIX.4.4 | Yes | 1.443 µs/msg
29+
NewOrderSingle('D') | Hand-coded spec. for this message only | No | 0.317 µs/msg
30+
NewOrderSingle('D') | Hand-coded spec. for this message only | Yes | 0.554 µs/msg
31+
NewOrderSingle('D') | Compiled full spec. for FIX.4.4 | Yes | 0.757 µs/msg
32+
MarketDataIncrementalRefresh('X') | Hand-coded spec. for this message only | Yes | 1.223 µs/msg
33+
MarketDataIncrementalRefresh('X') | Compiled full spec. for FIX.4.4 | Yes | 1.386 µs/msg
3434

3535
For more details see `doc/` directory of the project.

src/converters.c

Lines changed: 108 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,47 @@ fix_error copy_fix_tag_as_string(const fix_group* const group, unsigned tag, cha
5656
return FE_OK;
5757
}
5858

59+
// ascii digits to long converters
60+
static
61+
const char* convert_significant_digits(const char* s, long* const result)
62+
{
63+
long res = 0;
64+
unsigned c = CHAR_TO_INT(*s) - '0';
65+
66+
if(c == 0)
67+
return NULL;
68+
69+
if(c <= 9)
70+
{
71+
res = c;
72+
73+
for(c = CHAR_TO_INT(*++s) - '0'; c <= 9; c = CHAR_TO_INT(*++s) - '0')
74+
{
75+
const long t = res * 10 + c;
76+
77+
if(t < res) // overflow
78+
return NULL;
79+
80+
res = t;
81+
}
82+
}
83+
84+
// done
85+
*result = res;
86+
return s;
87+
}
88+
89+
static
90+
const char* convert_digits(const char* s, long* const result)
91+
{
92+
// skip leading zeroes
93+
while(*s == '0')
94+
++s;
95+
96+
// convert digits
97+
return convert_significant_digits(s, result);
98+
}
99+
59100
// tag as long integer
60101
fix_error get_fix_tag_as_long(const fix_group* const group, unsigned tag, long* const result)
61102
{
@@ -70,31 +111,33 @@ fix_error get_fix_tag_as_long(const fix_group* const group, unsigned tag, long*
70111
if(err != FE_OK)
71112
return err;
72113

73-
// conversion
74-
long val = 0;
75-
const char* s = value.begin;
76-
const int negative = (*s == '-') ? (++s, 1) : 0;
114+
if(fix_string_length(value) > 20) // ???
115+
RETURN( FE_INVALID_VALUE );
77116

78-
// quickly skip leading zeroes
79-
while(*s == '0')
80-
++s;
117+
// sign
118+
bool neg = false;
81119

82-
// convert all significant digits
83-
while(*s >= '0' && *s <= '9')
120+
if(*value.begin == '-')
84121
{
85-
const long new_val = val * 10L + *s++ - '0';
122+
++value.begin;
123+
neg = true;
124+
}
86125

87-
if(new_val < val) // overflow
88-
RETURN( FE_INVALID_VALUE );
126+
// conversion
127+
long val;
89128

90-
val = new_val;
91-
}
129+
value.begin = convert_digits(value.begin, &val);
92130

93-
if(*s != SOH || s == value.begin + negative) // no SOH terminator or the sign only
131+
// validation
132+
if(!value.begin || (neg && val == 0)) // overflow or '-0'
133+
RETURN( FE_INVALID_VALUE );
134+
135+
if(value.begin < value.end) // unprocessed bytes
94136
RETURN( FE_INCORRECT_VALUE_FORMAT );
95137

138+
// all clear
96139
if(result)
97-
*result = negative ? -val : val;
140+
*result = neg ? -val : val;
98141

99142
return FE_OK;
100143
}
@@ -118,62 +161,70 @@ fix_error get_fix_tag_as_double(const fix_group* const group, unsigned tag, doub
118161
if(err != FE_OK)
119162
return err;
120163

121-
// conversion
122-
long val = 0, frac = 0;
123-
unsigned ndig = 0; // number of significant digits
124-
const char* s = value.begin;
125-
const double sign = (*s == '-') ? (++s, -1.) : 1.;
126-
const char* mark = s;
164+
// sign
165+
bool neg = false;
166+
167+
if(*value.begin == '-')
168+
{
169+
++value.begin;
170+
neg = true;
171+
}
127172

128173
// skip leading zeroes
129-
while(*s == '0')
130-
++s;
174+
while(*value.begin == '0')
175+
++value.begin;
131176

132177
// integer part
133-
while(*s >= '0' && *s <= '9')
134-
{
135-
if(++ndig > 15)
136-
RETURN( FE_INVALID_VALUE );
178+
long int_part;
179+
const char* s = convert_significant_digits(value.begin, &int_part);
137180

138-
val = val * 10L + *s++ - '0';
139-
}
181+
if(!s)
182+
RETURN( FE_INVALID_VALUE );
140183

141-
if(s == mark) // cannot have empty integer part
142-
RETURN( FE_INCORRECT_VALUE_FORMAT );
184+
unsigned nsig = s - value.begin; // significant digits counter
143185

144-
// fractional part
186+
if(nsig > 15)
187+
RETURN( FE_INVALID_VALUE );
188+
189+
long frac_part = 0;
145190
unsigned nfrac = 0;
146191

147-
if(*s == '.')
192+
if(*s == '.' && *++s != SOH)
148193
{
149-
mark = ++s;
194+
// fractional part
195+
value.begin = s;
196+
s = convert_digits(s, &frac_part);
150197

151-
if(ndig == 0)
152-
while(*s == '0') // skip leading zeroes
153-
++s;
198+
if(!s)
199+
RETURN( FE_INCORRECT_VALUE_FORMAT );
154200

155-
while(*s >= '0' && *s <= '9')
156-
{
157-
if(++ndig > 15)
158-
RETURN( FE_INVALID_VALUE );
201+
nfrac = s - value.begin;
159202

160-
frac = frac * 10L + *s++ - '0';
161-
}
162-
163-
nfrac = s - mark;
203+
if(nsig + nfrac > 15) // counting trailing zeros as significant, contrary to the definition
204+
RETURN( FE_INCORRECT_VALUE_FORMAT );
164205
}
165206

166-
if(*s != SOH)
207+
// final checks
208+
if(s < value.end) // unprocessed bytes
167209
RETURN( FE_INCORRECT_VALUE_FORMAT );
168210

169-
static const double mult[] = { 0., 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15 };
211+
if(neg && int_part == 0 && frac_part == 0) // -0.0
212+
RETURN( FE_INVALID_VALUE );
213+
214+
// compose result
215+
static const double factor[] = { 0., 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15 };
216+
217+
double res = (double)int_part;
218+
219+
if(frac_part != 0)
220+
res += (double)frac_part * factor[nfrac];
221+
222+
if(neg)
223+
res = -res;
170224

225+
// all done
171226
if(result)
172-
#ifdef FP_FAST_FMA
173-
*result = copysign(fma((double)frac, mult[nfrac], (double)val), sign);
174-
#else
175-
*result = copysign(frac * mult[nfrac] + val, sign);
176-
#endif
227+
*result = res;
177228

178229
return FE_OK;
179230
}
@@ -228,14 +279,14 @@ fix_error get_fix_tag_as_boolean(const fix_group* const group, unsigned tag, boo
228279

229280
// matchers (unsafe macros!)
230281
#define READ_FIRST_DIGIT(s, r) \
231-
switch(CHAR_TO_INT(*(s))) { \
282+
switch(*(s)) { \
232283
case '0' ... '9': (r) = *(s) - '0'; break; \
233284
default: RETURN( FE_INCORRECT_VALUE_FORMAT ); \
234285
} \
235286
++(s)
236287

237288
#define READ_DIGIT(s, r) \
238-
switch(CHAR_TO_INT(*(s))) { \
289+
switch(*(s)) { \
239290
case '0' ... '9': (r) = (r) * 10 + *(s) - '0'; break; \
240291
default: RETURN( FE_INCORRECT_VALUE_FORMAT ); \
241292
} \
@@ -309,7 +360,7 @@ fix_error read_time_part(const fix_group* const group, fix_string* const ps, utc
309360
static
310361
fix_error read_time_ms_part(const fix_group* const group, fix_string* const ps, utc_timestamp* const ts)
311362
{
312-
fix_error err = read_time_part(group, ps, ts);
363+
const fix_error err = read_time_part(group, ps, ts);
313364

314365
if(err != FE_OK)
315366
return err;
@@ -470,7 +521,7 @@ fix_error get_fix_tag_as_LocalMktDate(const fix_group* const group, unsigned tag
470521
// FUNCTION EXPECTS A STRING IN THE "YYYY-MM-DD" FORMAT.
471522

472523
fix_string value;
473-
fix_error err = get_fix_tag_as_string(group, tag, &value);
524+
const fix_error err = get_fix_tag_as_string(group, tag, &value);
474525

475526
if(err != FE_OK)
476527
return err;

src/parser.c

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ unsigned read_uint(fix_parser* const parser, const char delim)
116116

117117
// assuming (s < parser->frame.end) and *(parser->frame.end - 1) == SOH
118118

119-
unsigned tag = CHAR_TO_INT(*s++) - '0';
119+
unsigned res = CHAR_TO_INT(*s++) - '0';
120120

121-
if(tag == 0 || tag > 9) // leading zeroes are not allowed
121+
if(res == 0 || res > 9) // leading zeroes are not allowed
122122
goto ERROR_EXIT;
123123

124124
for(unsigned c = CHAR_TO_INT(*s++); c != CHAR_TO_INT(delim); c = CHAR_TO_INT(*s++))
@@ -128,16 +128,16 @@ unsigned read_uint(fix_parser* const parser, const char delim)
128128
if(c > 9) // not a digit
129129
goto ERROR_EXIT;
130130

131-
c = tag * 10 + c;
131+
c = res * 10 + c;
132132

133-
if(c < tag) // overflow
133+
if(c < res) // overflow
134134
goto ERROR_EXIT;
135135

136-
tag = c;
136+
res = c;
137137
}
138138

139139
parser->result.error.context.end = parser->frame.begin = s;
140-
return tag;
140+
return res;
141141

142142
ERROR_EXIT:
143143
parser->result.error.context.end = s;
@@ -157,17 +157,14 @@ unsigned next_tag(fix_parser* const parser)
157157
}
158158

159159
// read tag
160-
unsigned tag = read_uint(parser, '=');
160+
const unsigned tag = parser->result.error.tag = read_uint(parser, '=');
161161

162162
if(tag == 0) // invalid tag
163163
{
164164
parser->result.error.code = FE_INVALID_TAG;
165-
parser->result.error.tag = 0;
166165
return 0;
167166
}
168167

169-
parser->result.error.tag = tag;
170-
171168
if(parser->frame.begin == parser->frame.end) // empty tag value
172169
{
173170
parser->result.error.code = FE_EMPTY_VALUE;
@@ -206,14 +203,8 @@ unsigned read_uint_value(fix_parser* const parser)
206203
{
207204
const unsigned val = read_uint(parser, SOH);
208205

209-
if(val != 0)
210-
{
211-
parser->result.error.code = FE_OK;
212-
return val;
213-
}
214-
215-
parser->result.error.code = FE_INCORRECT_VALUE_FORMAT;
216-
return 0;
206+
parser->result.error.code = (val != 0) ? FE_OK : FE_INCORRECT_VALUE_FORMAT;
207+
return val;
217208
}
218209

219210
// read bytes to the first SOH, i.e., a FIX string
@@ -623,7 +614,7 @@ const fix_error_details* get_fix_group_error_details(const fix_group* const grou
623614
}
624615

625616
// tag accessors ------------------------------------------------------------------------------------------
626-
static
617+
static inline
627618
fix_error set_group_error(const fix_group* const group, unsigned tag, fix_error err)
628619
{
629620
set_error_ctx(group->error, err, tag, EMPTY_STR);

src/scanner.c

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,24 +102,21 @@ static
102102
unsigned char copy_cs(char* restrict p, const char* restrict s, unsigned n)
103103
{
104104
unsigned char cs = 0;
105+
const char* const end = s + n;
105106

106107
#ifdef USE_SSE
107-
if(n >= 16)
108+
if(end - s >= 16)
108109
{
109110
__m128i cs128 = _mm_loadu_si128((const __m128i*)s);
110111

111-
s += 16;
112112
_mm_storeu_si128((__m128i*)p, cs128);
113-
p += 16;
114113

115-
while((n -= 16) >= 16)
114+
for(s += 16, p += 16; end - s >= 16; s += 16, p += 16)
116115
{
117116
const __m128i tmp = _mm_loadu_si128((const __m128i*)s);
118117

119-
s += 16;
120-
_mm_storeu_si128((__m128i*)p, tmp);
121-
p += 16;
122118
cs128 = _mm_add_epi8(cs128, tmp);
119+
_mm_storeu_si128((__m128i*)p, tmp);
123120
}
124121

125122
cs128 = _mm_add_epi8(cs128, _mm_srli_si128(cs128, 8));
@@ -129,22 +126,21 @@ unsigned char copy_cs(char* restrict p, const char* restrict s, unsigned n)
129126
cs += _mm_extract_epi16(cs128, 0); // SSE4: _mm_extract_epi8 ?
130127
}
131128

132-
if(n >= 8)
129+
if(end - s >= 8)
133130
{
134131
__m128i cs64 = _mm_loadl_epi64((const __m128i*)s);
135132

136133
_mm_storel_epi64((__m128i*)p, cs64);
134+
s += 8;
135+
p += 8;
137136
cs64 = _mm_add_epi8(cs64, _mm_srli_si128(cs64, 4));
138137
cs64 = _mm_add_epi8(cs64, _mm_srli_si128(cs64, 2));
139138
cs64 = _mm_add_epi8(cs64, _mm_srli_si128(cs64, 1));
140139
cs += _mm_extract_epi16(cs64, 0); // SSE4: _mm_extract_epi8 ?
141-
p += 8;
142-
s += 8;
143-
n -= 8;
144140
}
145141
#endif // #ifdef USE_SSE
146142

147-
while(n-- > 0)
143+
while(s < end)
148144
cs += (*p++ = *s++);
149145

150146
return cs;

0 commit comments

Comments
 (0)