blob: be9dd1a099c50d9444edc8344338c7057071b1a6 [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
18// https://github.com/nigeltao/parse-number-f64-test-data repository contains
19// the data files, containing one test case per line, like:
20//
21// 3FF0000000000000 1
22// 3FF4000000000000 1.25
23// 3FF6666666666666 1.4
24// 405EDD2F1A9FBE77 123.456
25// 4088A80000000000 789
26// 7FF0000000000000 123.456e789
27
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
40// Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
41// release/c/etc.c whitelist which parts of Wuffs to build. That file contains
42// the entire Wuffs standard library, implementing a variety of codecs and file
43// formats. Without this macro definition, an optimizing compiler or linker may
44// very well discard Wuffs code for unused codecs, but listing the Wuffs
45// modules we use makes that process explicit. Preprocessing means that such
46// code simply isn't compiled.
47#define WUFFS_CONFIG__MODULES
48#define WUFFS_CONFIG__MODULE__BASE
49
50// If building this program in an environment that doesn't easily accommodate
51// relative includes, you can use the script/inline-c-relative-includes.go
52// program to generate a stand-alone C++ file.
53#include "../release/c/wuffs-unsupported-snapshot.c"
54
55// Uncomment this to use the github.com/lemire/fast_double_parser library. This
56// header-only library is C++, not C.
57// #define USE_LEMIRE_FAST_DOUBLE_PARSER
58
59#ifdef USE_LEMIRE_FAST_DOUBLE_PARSER
60#include "/the/path/to/fast_double_parser/include/fast_double_parser.h"
61#endif
62
63const uint8_t hex[256] = {
64 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x00-0x07
65 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x08-0x0F
66 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x10-0x17
67 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x18-0x1F
68 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x20-0x27
69 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x28-0x2F
70 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, // 0x30-0x37 0-7
71 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x38-0x3F 8-9
72
73 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, // 0x40-0x47 A-F
74 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x48-0x4F
75 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x50-0x57
76 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x58-0x5F
77 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, // 0x60-0x67 A-F
78 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x68-0x6F
79 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x70-0x77
80 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x78-0x7F
81
82 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x80-0x87
83 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x88-0x8F
84 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x90-0x97
85 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0x98-0x9F
86 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xA0-0xA7
87 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xA8-0xAF
88 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xB0-0xB7
89 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xB8-0xBF
90
91 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xC0-0xC7
92 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xC8-0xCF
93 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xD0-0xD7
94 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xD8-0xDF
95 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xE0-0xE7
96 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xE8-0xEF
97 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xF0-0xF7
98 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0xF8-0xFF
99};
100
101#ifndef SRC_BUFFER_ARRAY_SIZE
102#define SRC_BUFFER_ARRAY_SIZE (64 * 1024 * 1024)
103#endif
104
105uint8_t g_src_buffer_array[SRC_BUFFER_ARRAY_SIZE];
106
107wuffs_base__io_buffer g_src;
108
109const char* g_filename;
110FILE* g_file;
111uint64_t g_line;
112
113const char* //
114read_src() {
115 if (g_src.meta.closed) {
116 return "main: internal error: read requested on a closed source";
117 }
118 wuffs_base__io_buffer__compact(&g_src);
119 if (g_src.meta.wi >= g_src.data.len) {
120 return "main: g_src buffer is full";
121 }
122 size_t n = fread(g_src.data.ptr + g_src.meta.wi, sizeof(uint8_t),
123 g_src.data.len - g_src.meta.wi, g_file);
124 g_src.meta.wi += n;
125 g_src.meta.closed = feof(g_file);
126 if ((n == 0) && !g_src.meta.closed) {
127 return "main: read error";
128 }
129 return NULL;
130}
131
132void //
133fail_parse(const char* impl, const char* z) {
134 fprintf(stderr, "main: %s could not parse \"%s\" at %s:%" PRIu64 "\n", impl,
135 z, g_filename, g_line);
136}
137
138void //
139fail(const char* impl, const char* z, uint64_t have, uint64_t want) {
140 fprintf(stderr, "main: %s mismatch at %s:%" PRIu64 "\n", impl, g_filename,
141 g_line);
142 fprintf(stderr, "src: %s\n", z);
143 fprintf(stderr, "have: %016" PRIX64 "\n", have);
144 fprintf(stderr, "want: %016" PRIX64 "\n", want);
145}
146
147bool //
148process_line(wuffs_base__slice_u8 s) {
149 if (s.len < 18) {
150 fprintf(stderr, "main: short input at %s:%" PRIu64 "\n", g_filename,
151 g_line);
152 return false;
153 } else if (s.len > 2048) {
154 fprintf(stderr, "main: long input at %s:%" PRIu64 "\n", g_filename, g_line);
155 return false;
156 }
157 uint64_t want = 0;
158 for (int i = 0; i < 16; i++) {
159 want = (want << 4) | hex[s.ptr[i]];
160 }
161 s.ptr += 17;
162 s.len -= 17;
163
164 // Convert ".123" to "0.123". Not all parsers like a leading dot.
165 if (s.ptr[0] == '.') {
166 s.ptr--;
167 s.len++;
168 s.ptr[0] = '0';
169 }
170
171 char z[2049];
172 memcpy(&z[0], s.ptr, s.len);
173 z[s.len] = 0;
174
175 // Check libc's strtod.
176 {
177 double have_f64 = strtod(&z[0], NULL);
178 uint64_t have =
179 wuffs_base__ieee_754_bit_representation__from_f64_to_u64(have_f64);
180 if (have != want) {
181 fail("strtod", &z[0], have, want);
182 return false;
183 }
184 }
185
186#ifdef USE_LEMIRE_FAST_DOUBLE_PARSER
187 // Check lemire/fast_double_parser's parse_number.
188 //
189 // https://github.com/lemire/fast_double_parser/blob/master/README.md says
190 // "the parser will reject overly large values that would not fit in
191 // binary64. It will not produce NaN or infinite values".
192 if (want < 0x7FF0000000000000ul) {
193 double have_f64;
194 if (!fast_double_parser::decimal_separator_dot::parse_number(&z[0],
195 &have_f64)) {
196 fail_parse("lemire", z);
197 return false;
198 }
199 uint64_t have =
200 wuffs_base__ieee_754_bit_representation__from_f64_to_u64(have_f64);
201 if (have != want) {
202 fail("lemire", &z[0], have, want);
203 return false;
204 }
205 }
206#endif
207
208 // Check Wuffs' wuffs_base__parse_number_f64.
209 {
210 wuffs_base__result_f64 res = wuffs_base__parse_number_f64(
211 s, WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS);
212 if (res.status.repr) {
213 fail_parse("wuffs", z);
214 return false;
215 }
216 uint64_t have =
217 wuffs_base__ieee_754_bit_representation__from_f64_to_u64(res.value);
218 if (have != want) {
219 fail("wuffs", &z[0], have, want);
220 return false;
221 }
222 }
223
224 return true;
225}
226
227bool //
228process_file(char* filename) {
229 if (g_file) {
230 fclose(g_file);
231 g_file = NULL;
232 }
233 g_filename = filename;
234 g_file = fopen(g_filename, "r");
235 if (!g_file) {
236 fprintf(stderr, "main: could not open %s\n", g_filename);
237 return false;
238 }
239 g_line = 0;
240 g_src = wuffs_base__slice_u8__writer(
241 wuffs_base__make_slice_u8(&g_src_buffer_array[0], SRC_BUFFER_ARRAY_SIZE));
242
243 while (true) {
244 for (size_t i = g_src.meta.ri; i < g_src.meta.wi; i++) {
245 if (g_src.data.ptr[i] == '\n') {
246 g_line++;
247 if (!process_line(wuffs_base__make_slice_u8(
248 &g_src.data.ptr[g_src.meta.ri], i - g_src.meta.ri))) {
249 return false;
250 }
251 g_src.meta.ri = i + 1;
252 continue;
253 }
254 }
255
256 if (g_src.meta.closed) {
257 if (g_src.meta.ri != g_src.meta.wi) {
258 fprintf(stderr, "main: unexpected end-of-file\n");
259 return false;
260 }
261 break;
262 }
263
264 const char* error_msg = read_src();
265 if (error_msg) {
266 fprintf(stderr, "%s\n", error_msg);
267 return false;
268 }
269 }
270
271 printf("%8" PRIu64 " OK in %s\n", g_line, g_filename);
272 return true;
273}
274
275int //
276main(int argc, char** argv) {
277 g_file = NULL;
278 for (int argi = 1; argi < argc; argi++) {
279 if (!process_file(argv[argi])) {
280 return 1;
281 }
282 }
283 return 0;
284}