Skip to content

Commit efc54dc

Browse files
committed
rework decimal parsing
1 parent 21a27e9 commit efc54dc

2 files changed

Lines changed: 168 additions & 116 deletions

File tree

source/mir/bignum/decimal.d

Lines changed: 151 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ struct Decimal(uint maxSize64)
186186
static if (optimize || (allowSpecialValues && allowDExponent && allowStartingPlus && checkEmpty) == false)
187187
pragma(inline, true);
188188
}
189-
static if (optimize && false)
189+
static if (optimize)
190190
{
191191
import mir.utility: _expect;
192192
static if (checkEmpty)
@@ -218,161 +218,196 @@ struct Decimal(uint maxSize64)
218218
exponent = 0;
219219

220220
ulong v;
221-
bool dot;
222-
static if (allowUnderscores)
221+
222+
if (_expect(d >= 10, false))
223223
{
224-
bool recentUnderscore;
224+
static if (allowDotOnBounds)
225+
{
226+
if (d == '.' - '0')
227+
{
228+
if (str.length == 0)
229+
return false;
230+
key = DecimalExponentKey.dot;
231+
d = str[0] - '0';
232+
str = str[1 .. $];
233+
if (_expect(d < 10, true))
234+
goto FI;
235+
return false;
236+
}
237+
}
238+
static if (allowSpecialValues)
239+
goto NI;
240+
else
241+
return false;
225242
}
226-
// Was there a recent character within the set: ['.', 'e', 'E', 'd', 'D']?
227-
bool recentModifier;
243+
228244
static if (!allowLeadingZeros)
229245
{
230-
if (d == 0)
246+
if (_expect(d == 0, false))
231247
{
232248
if (str.length == 0)
233249
goto R;
234-
if (str[0] >= '0' && str[0] <= '9')
250+
d = str[0] - '0';
251+
str = str[1 .. $];
252+
if (d < 10)
235253
return false;
236-
goto S;
254+
goto DOT;
237255
}
238256
}
239257

258+
v = d;
259+
260+
S:
261+
if (str.length == 0)
262+
goto R;
263+
d = str[0] - '0';
264+
str = str[1 .. $];
265+
240266
if (d < 10)
241267
{
268+
F0:
269+
import mir.checkedint: mulu, addu;
270+
bool overflow;
271+
v = mulu(v, cast(uint)10, overflow);
272+
if (overflow)
273+
return false;
274+
v = addu(v, d, overflow);
275+
if (overflow)
276+
return false;
242277
goto S;
243278
}
244279

245-
static if (allowDotOnBounds)
280+
static if (allowUnderscores)
246281
{
247-
if (d == '.' - '0')
282+
if (d == '_' - '0')
248283
{
249284
if (str.length == 0)
250285
return false;
251-
key = DecimalExponentKey.dot;
252-
dot = true;
253-
recentModifier = true;
254-
goto F;
286+
d = str[0] - '0';
287+
str = str[1 .. $];
288+
if (_expect(d < 10, true))
289+
goto F0;
290+
return false;
255291
}
256292
}
257-
258-
static if (allowSpecialValues)
259-
{
260-
goto NI;
261-
}
262-
else
293+
DOT:
294+
if (d == DecimalExponentKey.dot)
263295
{
264-
return false;
265-
}
296+
// The dot modifier CANNOT be preceded by any modifiers.
297+
if (key != DecimalExponentKey.none)
298+
return false;
266299

267-
F: for(;;)
268-
{
269-
d = str[0] - '0';
270-
str = str[1 .. $];
300+
key = DecimalExponentKey.dot;
271301

272-
if (_expect(d <= 10, true))
302+
if (str.length == 0)
273303
{
274-
static if (allowUnderscores)
275-
{
276-
recentUnderscore = false;
277-
}
278-
recentModifier = false;
279-
{
280-
import mir.checkedint: mulu;
281-
bool overflow;
282-
v = mulu(v, cast(uint)10, overflow);
283-
if (overflow)
284-
break;
285-
}
286-
S:
287-
v += d;
288-
exponentShift -= dot;
289-
if (str.length)
290-
continue;
291-
E:
292-
exponent += exponentShift;
293-
R:
294-
coefficient.data[0] = v;
295-
coefficient.length = v != 0;
296-
static if (allowUnderscores)
297-
{
298-
return !recentUnderscore && !recentModifier;
299-
}
304+
static if (allowDotOnBounds)
305+
goto R;
300306
else
301-
{
302-
return !recentModifier;
303-
}
307+
return false;
304308
}
305309

306-
switch (d)
307-
{
308-
case DecimalExponentKey.dot:
309-
// The dot modifier CANNOT be preceded by any modifiers.
310-
if (recentModifier || key != DecimalExponentKey.none)
311-
break;
310+
IF:
312311

313-
static if (allowUnderscores)
314-
{
315-
// If we allow underscores, the dot also CANNOT be preceded by any underscores.
316-
// It must be preceded by a number.
317-
if (recentUnderscore)
318-
break;
319-
}
320-
key = DecimalExponentKey.dot;
321-
if (_expect(dot, false))
322-
break;
323-
dot = true;
324-
if (str.length)
325-
{
326-
recentModifier = true;
327-
continue;
328-
}
329-
static if (allowDotOnBounds)
330-
{
331-
goto R;
332-
}
333-
else
312+
static if (is(C == char))
313+
{
314+
import mir.bignum.internal.parse: isMadeOfEightDigits, parseEightDigits;
315+
if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
316+
{
317+
ulong multplier = 100000000;
318+
ulong value = parseEightDigits(str[0 .. 8]);
319+
str = str[8 .. $];
320+
exponentShift -= 8;
321+
if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
334322
{
335-
break;
323+
multplier = 100000000 * 100000000;
324+
value *= 100000000;
325+
value += parseEightDigits(str[0 .. 8]);
326+
str = str[8 .. $];
327+
exponentShift -= 8;
336328
}
337-
static if (allowExponent)
338-
{
339-
static if (allowDExponent)
329+
340330
{
341-
case DecimalExponentKey.d:
342-
case DecimalExponentKey.D:
343-
goto case DecimalExponentKey.e;
331+
import mir.checkedint: mulu, addu;
332+
bool overflow;
333+
v = mulu(v, multplier, overflow);
334+
if (overflow)
335+
return false;
336+
v = addu(v, value, overflow);;
337+
if (overflow)
338+
return false;
344339
}
345-
case DecimalExponentKey.e:
346-
case DecimalExponentKey.E:
347-
import mir.parse: parse;
348-
// We don't really care if the e/E/d/D modifiers are preceded by a modifier,
349-
// so as long as they are a dot or a regular number.
350-
if (key != DecimalExponentKey.dot && key != DecimalExponentKey.none)
351-
break;
352-
key = cast(DecimalExponentKey)d;
353-
if (parse(str, exponent) && str.length == 0) {
354-
recentModifier = false;
355-
goto E;
356-
}
357-
break;
358340
}
359-
static if (allowUnderscores)
341+
}
342+
343+
d = str[0] - '0';
344+
str = str[1 .. $];
345+
if (_expect(d >= 10, false))
346+
goto DOB;
347+
FI:
348+
{
349+
import mir.checkedint: mulu, addu;
350+
bool overflow;
351+
v = mulu(v, cast(uint)10, overflow);
352+
if (overflow)
353+
return false;
354+
v = addu(v, d, overflow);;
355+
if (overflow)
356+
return false;
357+
}
358+
exponentShift--;
359+
if (str.length == 0)
360+
goto E;
361+
d = str[0] - '0';
362+
str = str[1 .. $];
363+
if (d < 10)
364+
goto FI;
365+
366+
static if (allowUnderscores)
367+
{
368+
if (d == '_' - '0')
360369
{
361-
case '_' - '0':
362-
// A underscore cannot be preceded by an underscore or a modifier.
363-
if (recentUnderscore || recentModifier)
364-
break;
365-
366-
recentUnderscore = true;
367-
if (str.length)
368-
continue;
369-
break;
370+
if (str.length == 0)
371+
return false;
372+
d = str[0] - '0';
373+
str = str[1 .. $];
374+
if (_expect(d < 10, true))
375+
goto FI;
376+
return false;
370377
}
371-
default:
372378
}
373-
break;
379+
DOB:
380+
static if (!allowDotOnBounds)
381+
{
382+
if (exponentShift == 0)
383+
return false;
384+
}
385+
386+
}
387+
388+
static if (allowExponent)
389+
{
390+
if (d == DecimalExponentKey.e
391+
|| d == DecimalExponentKey.E
392+
|| d == DecimalExponentKey.d && allowDExponent
393+
|| d == DecimalExponentKey.D && allowDExponent
394+
)
395+
{
396+
import mir.parse: parse;
397+
key = cast(DecimalExponentKey)d;
398+
if (parse(str, exponent) && str.length == 0)
399+
goto E;
400+
}
374401
}
375402
return false;
403+
404+
E:
405+
exponent += exponentShift;
406+
R:
407+
coefficient.data[0] = v;
408+
coefficient.length = v != 0;
409+
return true;
410+
376411
static if (allowSpecialValues)
377412
{
378413
NI:

source/mir/bignum/internal/parse.d

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module mir.bignum.internal.parse;
2+
3+
bool isMadeOfEightDigits()(ref const char[8] chars)
4+
{
5+
pragma(inline, true);
6+
ulong val = (cast(ulong[1]) cast(ubyte[8]) chars)[0];
7+
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & 0x8080808080808080));
8+
}
9+
10+
uint parseEightDigits()(ref const char[8] chars)
11+
{
12+
pragma(inline, true);
13+
ulong val = (cast(ulong[1]) cast(ubyte[8]) chars)[0];
14+
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
15+
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
16+
return uint((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
17+
}

0 commit comments

Comments
 (0)