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
|
/* JSON numbers are read as strtod reads them, bit for bit: in the forms a
* client writes (a program's shortest digits, fixed decimals, exponents), at
* the edges of the exact path (2^53, 10^22) and past them, and refused when
* not finite or not JSON. A bool is true or false, nothing else; an i64 an
* integer's digits, exact in its range. */
#include "message-json.h"
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHECK(x) do { if (!(x)) { fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #x); exit(1); } } while (0)
static uint64_t next_random(uint64_t *state) {
uint64_t x = *state;
x ^= x << 13, x ^= x >> 7, x ^= x << 17;
return *state = x;
}
/* The text read as a number field: true with its value, or false. */
static bool read_number(const char *text, double *value) {
static const PureField number = {.type = PURE_FIELD_NUMBER};
unsigned char prefix[8];
if (!pure_json_fixed(&number, (const unsigned char *)text, strlen(text), prefix)) return false;
memcpy(value, prefix, sizeof(*value));
return true;
}
static void same_as_strtod(const char *text) {
double got, want = strtod(text, NULL);
CHECK(read_number(text, &got) && !memcmp(&got, &want, sizeof(got)));
}
int main(void) {
static const char *const edges[] = {
"0", "-0", "0.0", "-0.0", "1", "0.1", "0.2", "0.30000000000000004", "123456.789", "1e22", "1e23", "1E-22",
"1e-23", "9007199254740991", "9007199254740992", "9007199254740993", "900719925474099.3", "12345678901234567890",
"0.000000000000000000000001", "1.7976931348623157e308", "4.9406564584124654e-324", "2.2250738585072014e-308",
"-123.456e-7", "5e+0", "0.5e1", "1234567890.0987654321"};
for (size_t i = 0; i < sizeof(edges) / sizeof(*edges); ++i) same_as_strtod(edges[i]);
static const char *const refused[] = {"1e309", "-1e400", "01", "1.", ".5", "+1", "1e", "--1", "0x10", "1.5e+", "NaN"};
for (size_t i = 0; i < sizeof(refused) / sizeof(*refused); ++i) {
double value;
CHECK(!read_number(refused[i], &value));
}
static const PureField truth = {.type = PURE_FIELD_BOOL, .offset = 1};
unsigned char prefix[2] = {7, 7};
CHECK(pure_json_fixed(&truth, (const unsigned char *)"true", 4, prefix) && prefix[1] == 1 && prefix[0] == 7);
CHECK(pure_json_fixed(&truth, (const unsigned char *)"false", 5, prefix) && prefix[1] == 0);
static const char *const not_bools[] = {"", "1", "0", "True", "\"true\"", "tru", "truex", "false ", "null", "t"};
for (size_t i = 0; i < sizeof(not_bools) / sizeof(*not_bools); ++i)
CHECK(!pure_json_fixed(&truth, (const unsigned char *)not_bools[i], strlen(not_bools[i]), prefix) && prefix[1] == 0);
double number;
CHECK(!read_number("true", &number) && !read_number("false", &number));
/* An i64 is an integer's digits, exactly, in its range. */
static const PureField whole = {.type = PURE_FIELD_I64, .offset = 0};
static const struct { const char *text; int64_t value; } integers[] = {
{"0", 0}, {"-0", 0}, {"9007199254740993", INT64_C(9007199254740993)}, {"9223372036854775807", INT64_MAX},
{"-9223372036854775808", INT64_MIN}, {"-1", -1}};
for (size_t i = 0; i < sizeof(integers) / sizeof(*integers); ++i) {
unsigned char bytes[8];
int64_t value;
CHECK(pure_json_fixed(&whole, (const unsigned char *)integers[i].text, strlen(integers[i].text), bytes));
memcpy(&value, bytes, 8);
CHECK(value == integers[i].value);
}
static const char *const not_integers[] = {"9223372036854775808", "-9223372036854775809", "1.0", "1e3", "+1", "01", "\"1\"",
"", "-", "1.5", "true", "99999999999999999999"};
for (size_t i = 0; i < sizeof(not_integers) / sizeof(*not_integers); ++i) {
unsigned char bytes[8];
CHECK(!pure_json_fixed(&whole, (const unsigned char *)not_integers[i], strlen(not_integers[i]), bytes));
}
uint64_t seed = 0x6a736f6eu;
size_t read = 0;
for (unsigned i = 0; i < 400000; ++i) {
uint64_t bits = next_random(&seed);
double value;
switch (i % 4) {
case 0: value = (double)(int64_t)(bits % 2000001 - 1000000) / 100.0; break;
case 1: value = (double)(bits % 100000000) / pow(10, (double)(bits >> 50 & 15)); break;
case 2: memcpy(&value, &bits, sizeof(value)); if (!isfinite(value)) value = -2.5; break;
default: value = ldexp((double)(bits >> 11), (int)(bits % 120) - 90); break;
}
char text[64];
snprintf(text, sizeof(text), "%.17g", value);
same_as_strtod(text);
snprintf(text, sizeof(text), "%.*f", (int)(bits >> 56) % 12, value);
if (strlen(text) < 40) same_as_strtod(text);
snprintf(text, sizeof(text), "%.*e", (int)(bits >> 58) % 17, value);
same_as_strtod(text);
read += 3;
}
printf("{\"json_numbers\":%zu}\n", read);
return 0;
}
|