Skip to content

Commit 49a44ed

Browse files
committed
Add support for more conversion edge cases
1 parent 1be0fb2 commit 49a44ed

File tree

2 files changed

+150
-7
lines changed

2 files changed

+150
-7
lines changed

src/scratch/value_functions_p.h

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,14 @@ extern "C"
7272
if (ok)
7373
*ok = false;
7474

75-
// Ignore floats
75+
// Ignore dots, plus and minus signs
7676
const char *p = s;
7777

78-
while (p++ < s + n) {
79-
if (*p == '.')
78+
while (p < s + n) {
79+
if (*p == '.' || *p == '+' || *p == '-')
8080
return 0;
81+
82+
p++;
8183
}
8284

8385
double result = 0;
@@ -97,6 +99,16 @@ extern "C"
9799
if (ok)
98100
*ok = false;
99101

102+
// Ignore plus and minus signs
103+
const char *p = s;
104+
105+
while (p < s + n) {
106+
if (*p == '+' || *p == '-')
107+
return 0;
108+
109+
p++;
110+
}
111+
100112
char *err;
101113
const double ret = std::strtol(s, &err, 8);
102114

@@ -115,6 +127,16 @@ extern "C"
115127
if (ok)
116128
*ok = false;
117129

130+
// Ignore plus and minus signs
131+
const char *p = s;
132+
133+
while (p < s + n) {
134+
if (*p == '+' || *p == '-')
135+
return 0;
136+
137+
p++;
138+
}
139+
118140
char *err;
119141
const double ret = std::strtol(s, &err, 2);
120142

@@ -164,10 +186,53 @@ extern "C"
164186
p++;
165187
}
166188

167-
if (end - begin <= 0)
189+
if (end - begin <= 0 || end > strEnd) {
190+
if (ok)
191+
*ok = true;
192+
193+
return 0;
194+
}
195+
196+
char *copy = nullptr;
197+
198+
// If there's a leading plus sign, copy the string and replace the plus sign with zero (e. g. '+.15' -> '0.15')
199+
// If there's a leading minus sign with a dot, insert zero next to the minus sign (e. g. '-.15' -> '-0.15')
200+
if (begin[0] == '+' || (begin[0] == '-' && end - begin > 1 && begin[1] == '.')) {
201+
copy = new char[end - begin + 2];
202+
203+
if (begin[0] == '-') {
204+
copy[0] = '-';
205+
copy[1] = '0';
206+
memcpy(copy + 2, begin + 1, end - begin - 1);
207+
copy[end - begin + 1] = '\0';
208+
end = copy + (end - begin) + 1;
209+
} else {
210+
memcpy(copy, begin, end - begin);
211+
copy[0] = '0';
212+
copy[end - begin] = '\0';
213+
end = copy + (end - begin);
214+
}
215+
begin = copy;
216+
} else {
217+
// If there's a leading dot, copy the string and insert zero prior to the dot (e. g. '.15' -> '0.15')
218+
if (begin[0] == '.') {
219+
copy = new char[end - begin + 2];
220+
copy[0] = '0';
221+
memcpy(copy + 1, begin, end - begin);
222+
end = copy + (end - begin) + 1;
223+
begin = copy;
224+
}
225+
}
226+
227+
if (end - begin <= 0) {
228+
if (copy)
229+
delete[] copy;
230+
168231
return 0;
232+
}
169233

170-
if (end - begin > 2 && begin[0] == '0') {
234+
// Handle hex, oct and bin (currently ignored if the string was manipulated above)
235+
if (!copy && end - begin > 2 && begin[0] == '0') {
171236
const char prefix = begin[1];
172237
const char *sub = begin + 2;
173238

@@ -181,19 +246,37 @@ extern "C"
181246
}
182247

183248
// Trim leading zeros
184-
while (begin < end && (*begin == '0') && !(begin + 1 < end && begin[1] == '.'))
249+
bool trimmed = false;
250+
251+
while (begin < end && (*begin == '0') && !(begin + 1 < end && begin[1] == '.')) {
252+
trimmed = true;
185253
++begin;
254+
}
186255

187256
if (end - begin <= 0) {
188257
if (ok)
189258
*ok = true;
190259

260+
if (copy)
261+
delete[] copy;
262+
263+
return 0;
264+
}
265+
266+
// Ignore cases like '0+5' or '0-5'
267+
if (trimmed && (begin[0] == '+' || begin[0] == '-')) {
268+
if (copy)
269+
delete[] copy;
270+
191271
return 0;
192272
}
193273

194274
double ret = 0;
195275
auto [ptr, ec] = fast_float::from_chars(begin, end, ret, fast_float::chars_format::json);
196276

277+
if (copy)
278+
delete[] copy;
279+
197280
if (ec == std::errc{} && ptr == end) {
198281
if (ok)
199282
*ok = true;

test/scratch_classes/value_test.cpp

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ TEST(ValueTest, StdStringConstructor)
299299
ASSERT_FALSE(v.isNegativeInfinity());
300300
ASSERT_FALSE(v.isNaN());
301301
ASSERT_FALSE(v.isNumber());
302-
ASSERT_FALSE(v.isValidNumber());
302+
ASSERT_TRUE(v.isValidNumber());
303303
ASSERT_TRUE(v.isInt());
304304
ASSERT_FALSE(v.isBool());
305305
ASSERT_TRUE(v.isString());
@@ -1290,6 +1290,18 @@ TEST(ValueTest, ToDouble)
12901290
v = "-0.15";
12911291
ASSERT_EQ(v.toDouble(), -0.15);
12921292

1293+
v = "+.15";
1294+
ASSERT_EQ(v.toDouble(), 0.15);
1295+
v = ".15";
1296+
ASSERT_EQ(v.toDouble(), 0.15);
1297+
v = "-.15";
1298+
ASSERT_EQ(v.toDouble(), -0.15);
1299+
1300+
v = "0+5";
1301+
ASSERT_EQ(v.toDouble(), 0);
1302+
v = "0-5";
1303+
ASSERT_EQ(v.toDouble(), 0);
1304+
12931305
v = "9432.4e-12";
12941306
ASSERT_EQ(v.toDouble(), 9.4324e-9);
12951307
v = "-9432.4e-12";
@@ -1355,6 +1367,22 @@ TEST(ValueTest, ToDouble)
13551367
ASSERT_TRUE(v.isString());
13561368
ASSERT_EQ(v.toDouble(), 0);
13571369

1370+
v = "+0xa";
1371+
ASSERT_TRUE(v.isString());
1372+
ASSERT_EQ(v.toDouble(), 0);
1373+
1374+
v = "-0xa";
1375+
ASSERT_TRUE(v.isString());
1376+
ASSERT_EQ(v.toDouble(), 0);
1377+
1378+
v = "0x+a";
1379+
ASSERT_TRUE(v.isString());
1380+
ASSERT_EQ(v.toDouble(), 0);
1381+
1382+
v = "0x-a";
1383+
ASSERT_TRUE(v.isString());
1384+
ASSERT_EQ(v.toDouble(), 0);
1385+
13581386
// Octal
13591387
v = "0o506";
13601388
ASSERT_TRUE(v.isString());
@@ -1388,6 +1416,22 @@ TEST(ValueTest, ToDouble)
13881416
ASSERT_TRUE(v.isString());
13891417
ASSERT_EQ(v.toDouble(), 0);
13901418

1419+
v = "+0o2";
1420+
ASSERT_TRUE(v.isString());
1421+
ASSERT_EQ(v.toDouble(), 0);
1422+
1423+
v = "-0o2";
1424+
ASSERT_TRUE(v.isString());
1425+
ASSERT_EQ(v.toDouble(), 0);
1426+
1427+
v = "0o+2";
1428+
ASSERT_TRUE(v.isString());
1429+
ASSERT_EQ(v.toDouble(), 0);
1430+
1431+
v = "0o-2";
1432+
ASSERT_TRUE(v.isString());
1433+
ASSERT_EQ(v.toDouble(), 0);
1434+
13911435
// Binary
13921436
v = "0b101101";
13931437
ASSERT_TRUE(v.isString());
@@ -1421,6 +1465,22 @@ TEST(ValueTest, ToDouble)
14211465
ASSERT_TRUE(v.isString());
14221466
ASSERT_EQ(v.toDouble(), 0);
14231467

1468+
v = "+0b1";
1469+
ASSERT_TRUE(v.isString());
1470+
ASSERT_EQ(v.toDouble(), 0);
1471+
1472+
v = "-0b1";
1473+
ASSERT_TRUE(v.isString());
1474+
ASSERT_EQ(v.toDouble(), 0);
1475+
1476+
v = "0b+1";
1477+
ASSERT_TRUE(v.isString());
1478+
ASSERT_EQ(v.toDouble(), 0);
1479+
1480+
v = "0b-1";
1481+
ASSERT_TRUE(v.isString());
1482+
ASSERT_EQ(v.toDouble(), 0);
1483+
14241484
std::setlocale(LC_NUMERIC, oldLocale.c_str());
14251485
}
14261486

0 commit comments

Comments
 (0)