blob: d4ac7f3da199306be4c6e097f06a4551a8db530c [file] [log] [blame]
Nigel Tao12103402020-09-07 23:11:07 +10001// Copyright 2020 The Wuffs Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// ----------------
16
17// manual-test-parse-number-f64.c tests Wuffs' parse_number_f64 function. The
Nigel Tao93f95502020-10-23 18:46:12 +110018// https://github.com/nigeltao/parse-number-fxx-test-data repository contains
Nigel Tao12103402020-09-07 23:11:07 +100019// the data files, containing one test case per line, like:
20//
Nigel Tao93f95502020-10-23 18:46:12 +110021// 3C00 3F800000 3FF0000000000000 1
22// 3D00 3FA00000 3FF4000000000000 1.25
23// 3D9A 3FB33333 3FF6666666666666 1.4
24// 57B7 42F6E979 405EDD2F1A9FBE77 123.456
25// 622A 44454000 4088A80000000000 789
26// 7C00 7F800000 7FF0000000000000 123.456e789
Nigel Tao12103402020-09-07 23:11:07 +100027
28#include <inttypes.h>
29#include <stdio.h>
30#include <stdlib.h>
31
32// Wuffs ships as a "single file C library" or "header file library" as per
33// https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
34//
35// To use that single file as a "foo.c"-like implementation, instead of a
36// "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or
37// compiling it.
38#define WUFFS_IMPLEMENTATION
39
Nigel Tao6fb76972021-10-07 14:36:35 +110040// Defining the WUFFS_CONFIG__STATIC_FUNCTIONS macro is optional, but when
41// combined with WUFFS_IMPLEMENTATION, it demonstrates making all of Wuffs'
42// functions have static storage.
43//
44// This can help the compiler ignore or discard unused code, which can produce
45// faster compiles and smaller binaries. Other motivations are discussed in the
46// "ALLOW STATIC IMPLEMENTATION" section of
47// https://raw.githubusercontent.com/nothings/stb/master/docs/stb_howto.txt
48#define WUFFS_CONFIG__STATIC_FUNCTIONS
49
Nigel Tao12103402020-09-07 23:11:07 +100050// Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
Nigel Tao2f788042021-01-23 19:29:19 +110051// release/c/etc.c choose which parts of Wuffs to build. That file contains the
52// entire Wuffs standard library, implementing a variety of codecs and file
Nigel Tao12103402020-09-07 23:11:07 +100053// formats. Without this macro definition, an optimizing compiler or linker may
54// very well discard Wuffs code for unused codecs, but listing the Wuffs
55// modules we use makes that process explicit. Preprocessing means that such
56// code simply isn't compiled.
57#define WUFFS_CONFIG__MODULES
58#define WUFFS_CONFIG__MODULE__BASE
59
60// If building this program in an environment that doesn't easily accommodate
61// relative includes, you can use the script/inline-c-relative-includes.go
62// program to generate a stand-alone C++ file.
63#include "../release/c/wuffs-unsupported-snapshot.c"
64
Nigel Tao6775c052020-10-24 17:44:06 +110065// Uncomment one or all of these to use the
66// - github.com/lemire/fast_double_parser
67// - github.com/lemire/fast_float
68// libraries. These header-only libraries are C++, not C.
69//
Nigel Tao12103402020-09-07 23:11:07 +100070// #define USE_LEMIRE_FAST_DOUBLE_PARSER
Nigel Tao6775c052020-10-24 17:44:06 +110071// #define USE_LEMIRE_FAST_FLOAT
Nigel Tao12103402020-09-07 23:11:07 +100072
73#ifdef USE_LEMIRE_FAST_DOUBLE_PARSER
74#include "/the/path/to/fast_double_parser/include/fast_double_parser.h"
75#endif
Nigel Tao6775c052020-10-24 17:44:06 +110076#ifdef USE_LEMIRE_FAST_FLOAT
77#include "/the/path/to/fast_float/include/fast_float/fast_float.h"
78#endif
Nigel Tao12103402020-09-07 23:11:07 +100079
80const uint8_t hex[256] = {
81 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x00-0x07
82 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x08-0x0F
83 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x10-0x17
84 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x18-0x1F
85 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x20-0x27
86 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x28-0x2F
87 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, // 0x30-0x37 0-7
88 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x38-0x3F 8-9
89
90 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, // 0x40-0x47 A-F
91 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x48-0x4F
92 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x50-0x57
93 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x58-0x5F
Nigel Tao93f95502020-10-23 18:46:12 +110094 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, // 0x60-0x67 a-f
Nigel Tao12103402020-09-07 23:11:07 +100095 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x68-0x6F
96 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x70-0x77
97 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x78-0x7F
98
99 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x80-0x87
100 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x88-0x8F
101 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x90-0x97
102 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x98-0x9F
103 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xA0-0xA7
104 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xA8-0xAF
105 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xB0-0xB7
106 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xB8-0xBF
107
108 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xC0-0xC7
109 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xC8-0xCF
110 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xD0-0xD7
111 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xD8-0xDF
112 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xE0-0xE7
113 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xE8-0xEF
114 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xF0-0xF7
115 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xF8-0xFF
116};
117
118#ifndef SRC_BUFFER_ARRAY_SIZE
119#define SRC_BUFFER_ARRAY_SIZE (64 * 1024 * 1024)
120#endif
121
122uint8_t g_src_buffer_array[SRC_BUFFER_ARRAY_SIZE];
123
124wuffs_base__io_buffer g_src;
125
126const char* g_filename;
127FILE* g_file;
128uint64_t g_line;
129
130const char* //
131read_src() {
132 if (g_src.meta.closed) {
133 return "main: internal error: read requested on a closed source";
134 }
135 wuffs_base__io_buffer__compact(&g_src);
136 if (g_src.meta.wi >= g_src.data.len) {
137 return "main: g_src buffer is full";
138 }
139 size_t n = fread(g_src.data.ptr + g_src.meta.wi, sizeof(uint8_t),
140 g_src.data.len - g_src.meta.wi, g_file);
141 g_src.meta.wi += n;
142 g_src.meta.closed = feof(g_file);
143 if ((n == 0) && !g_src.meta.closed) {
144 return "main: read error";
145 }
146 return NULL;
147}
148
149void //
150fail_parse(const char* impl, const char* z) {
151 fprintf(stderr, "main: %s could not parse \"%s\" at %s:%" PRIu64 "\n", impl,
152 z, g_filename, g_line);
153}
154
155void //
156fail(const char* impl, const char* z, uint64_t have, uint64_t want) {
157 fprintf(stderr, "main: %s mismatch at %s:%" PRIu64 "\n", impl, g_filename,
158 g_line);
159 fprintf(stderr, "src: %s\n", z);
Nigel Tao6775c052020-10-24 17:44:06 +1100160 fprintf(stderr, "have: 0x%016" PRIX64 " %f\n", have,
161 wuffs_base__ieee_754_bit_representation__from_u64_to_f64(have));
162 fprintf(stderr, "want: 0x%016" PRIX64 " %f\n", want,
163 wuffs_base__ieee_754_bit_representation__from_u64_to_f64(want));
Nigel Tao12103402020-09-07 23:11:07 +1000164}
165
166bool //
167process_line(wuffs_base__slice_u8 s) {
Nigel Tao93f95502020-10-23 18:46:12 +1100168 if (s.len < 32) {
Nigel Tao12103402020-09-07 23:11:07 +1000169 fprintf(stderr, "main: short input at %s:%" PRIu64 "\n", g_filename,
170 g_line);
171 return false;
172 } else if (s.len > 2048) {
173 fprintf(stderr, "main: long input at %s:%" PRIu64 "\n", g_filename, g_line);
174 return false;
175 }
176 uint64_t want = 0;
Nigel Tao93f95502020-10-23 18:46:12 +1100177 for (int i = 14; i < 30; i++) {
Nigel Tao12103402020-09-07 23:11:07 +1000178 want = (want << 4) | hex[s.ptr[i]];
179 }
Nigel Tao93f95502020-10-23 18:46:12 +1100180 s.ptr += 31;
181 s.len -= 31;
Nigel Tao12103402020-09-07 23:11:07 +1000182
183 // Convert ".123" to "0.123". Not all parsers like a leading dot.
184 if (s.ptr[0] == '.') {
185 s.ptr--;
186 s.len++;
187 s.ptr[0] = '0';
188 }
189
190 char z[2049];
191 memcpy(&z[0], s.ptr, s.len);
192 z[s.len] = 0;
193
194 // Check libc's strtod.
195 {
196 double have_f64 = strtod(&z[0], NULL);
197 uint64_t have =
198 wuffs_base__ieee_754_bit_representation__from_f64_to_u64(have_f64);
199 if (have != want) {
200 fail("strtod", &z[0], have, want);
201 return false;
202 }
203 }
204
205#ifdef USE_LEMIRE_FAST_DOUBLE_PARSER
206 // Check lemire/fast_double_parser's parse_number.
207 //
208 // https://github.com/lemire/fast_double_parser/blob/master/README.md says
209 // "the parser will reject overly large values that would not fit in
210 // binary64. It will not produce NaN or infinite values".
211 if (want < 0x7FF0000000000000ul) {
212 double have_f64;
213 if (!fast_double_parser::decimal_separator_dot::parse_number(&z[0],
214 &have_f64)) {
Nigel Tao6775c052020-10-24 17:44:06 +1100215 fail_parse("lemire/fast_double_parser", &z[0]);
Nigel Tao12103402020-09-07 23:11:07 +1000216 return false;
217 }
218 uint64_t have =
219 wuffs_base__ieee_754_bit_representation__from_f64_to_u64(have_f64);
220 if (have != want) {
Nigel Tao6775c052020-10-24 17:44:06 +1100221 fail("lemire/fast_double_parser", &z[0], have, want);
222 return false;
223 }
224 }
225#endif
226
227#ifdef USE_LEMIRE_FAST_FLOAT
228 // Check lemire/fast_float's from_chars.
229 {
230 double have_f64;
231 fast_float::from_chars_result result = fast_float::from_chars(
232 static_cast<char*>(static_cast<void*>(s.ptr)),
233 static_cast<char*>(static_cast<void*>(s.ptr + s.len)), have_f64,
234 fast_float::chars_format::general);
235 if (result.ec != std::errc()) {
236 fail_parse("lemire/fast_float", &z[0]);
237 return false;
238 }
239 uint64_t have =
240 wuffs_base__ieee_754_bit_representation__from_f64_to_u64(have_f64);
241 if (have != want) {
242 fail("lemire/fast_float", &z[0], have, want);
Nigel Tao12103402020-09-07 23:11:07 +1000243 return false;
244 }
245 }
246#endif
247
248 // Check Wuffs' wuffs_base__parse_number_f64.
249 {
250 wuffs_base__result_f64 res = wuffs_base__parse_number_f64(
251 s, WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
252 if (res.status.repr) {
Nigel Tao6775c052020-10-24 17:44:06 +1100253 fail_parse("wuffs", &z[0]);
Nigel Tao12103402020-09-07 23:11:07 +1000254 return false;
255 }
256 uint64_t have =
257 wuffs_base__ieee_754_bit_representation__from_f64_to_u64(res.value);
258 if (have != want) {
259 fail("wuffs", &z[0], have, want);
260 return false;
261 }
262 }
263
264 return true;
265}
266
267bool //
268process_file(char* filename) {
269 if (g_file) {
270 fclose(g_file);
271 g_file = NULL;
272 }
273 g_filename = filename;
Nigel Tao816475b2021-07-07 20:28:35 +1000274 g_file = fopen(g_filename, "rb");
Nigel Tao12103402020-09-07 23:11:07 +1000275 if (!g_file) {
276 fprintf(stderr, "main: could not open %s\n", g_filename);
277 return false;
278 }
279 g_line = 0;
280 g_src = wuffs_base__slice_u8__writer(
281 wuffs_base__make_slice_u8(&g_src_buffer_array[0], SRC_BUFFER_ARRAY_SIZE));
282
283 while (true) {
284 for (size_t i = g_src.meta.ri; i < g_src.meta.wi; i++) {
285 if (g_src.data.ptr[i] == '\n') {
286 g_line++;
287 if (!process_line(wuffs_base__make_slice_u8(
288 &g_src.data.ptr[g_src.meta.ri], i - g_src.meta.ri))) {
289 return false;
290 }
291 g_src.meta.ri = i + 1;
292 continue;
293 }
294 }
295
296 if (g_src.meta.closed) {
297 if (g_src.meta.ri != g_src.meta.wi) {
298 fprintf(stderr, "main: unexpected end-of-file\n");
299 return false;
300 }
301 break;
302 }
303
304 const char* error_msg = read_src();
305 if (error_msg) {
306 fprintf(stderr, "%s\n", error_msg);
307 return false;
308 }
309 }
310
311 printf("%8" PRIu64 " OK in %s\n", g_line, g_filename);
312 return true;
313}
314
315int //
316main(int argc, char** argv) {
317 g_file = NULL;
318 for (int argi = 1; argi < argc; argi++) {
319 if (!process_file(argv[argi])) {
320 return 1;
321 }
322 }
323 return 0;
324}