| // Copyright 2020 The Wuffs Authors. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //    https://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | // ---------------- | 
 |  | 
 | // manual-test-parse-number-f64.c tests Wuffs' parse_number_f64 function. The | 
 | // https://github.com/nigeltao/parse-number-fxx-test-data repository contains | 
 | // the data files, containing one test case per line, like: | 
 | // | 
 | // 3C00 3F800000 3FF0000000000000 1 | 
 | // 3D00 3FA00000 3FF4000000000000 1.25 | 
 | // 3D9A 3FB33333 3FF6666666666666 1.4 | 
 | // 57B7 42F6E979 405EDD2F1A9FBE77 123.456 | 
 | // 622A 44454000 4088A80000000000 789 | 
 | // 7C00 7F800000 7FF0000000000000 123.456e789 | 
 |  | 
 | #include <inttypes.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 |  | 
 | // Wuffs ships as a "single file C library" or "header file library" as per | 
 | // https://github.com/nothings/stb/blob/master/docs/stb_howto.txt | 
 | // | 
 | // To use that single file as a "foo.c"-like implementation, instead of a | 
 | // "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or | 
 | // compiling it. | 
 | #define WUFFS_IMPLEMENTATION | 
 |  | 
 | // Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of | 
 | // release/c/etc.c whitelist which parts of Wuffs to build. That file contains | 
 | // the entire Wuffs standard library, implementing a variety of codecs and file | 
 | // formats. Without this macro definition, an optimizing compiler or linker may | 
 | // very well discard Wuffs code for unused codecs, but listing the Wuffs | 
 | // modules we use makes that process explicit. Preprocessing means that such | 
 | // code simply isn't compiled. | 
 | #define WUFFS_CONFIG__MODULES | 
 | #define WUFFS_CONFIG__MODULE__BASE | 
 |  | 
 | // If building this program in an environment that doesn't easily accommodate | 
 | // relative includes, you can use the script/inline-c-relative-includes.go | 
 | // program to generate a stand-alone C++ file. | 
 | #include "../release/c/wuffs-unsupported-snapshot.c" | 
 |  | 
 | // Uncomment one or all of these to use the | 
 | //  - github.com/lemire/fast_double_parser | 
 | //  - github.com/lemire/fast_float | 
 | // libraries. These header-only libraries are C++, not C. | 
 | // | 
 | // #define USE_LEMIRE_FAST_DOUBLE_PARSER | 
 | // #define USE_LEMIRE_FAST_FLOAT | 
 |  | 
 | #ifdef USE_LEMIRE_FAST_DOUBLE_PARSER | 
 | #include "/the/path/to/fast_double_parser/include/fast_double_parser.h" | 
 | #endif | 
 | #ifdef USE_LEMIRE_FAST_FLOAT | 
 | #include "/the/path/to/fast_float/include/fast_float/fast_float.h" | 
 | #endif | 
 |  | 
 | const uint8_t hex[256] = { | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x00-0x07 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x08-0x0F | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x10-0x17 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x18-0x1F | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x20-0x27 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x28-0x2F | 
 |     0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,  // 0x30-0x37 0-7 | 
 |     0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x38-0x3F 8-9 | 
 |  | 
 |     0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,  // 0x40-0x47 A-F | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x48-0x4F | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x50-0x57 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x58-0x5F | 
 |     0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0,  // 0x60-0x67 a-f | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x68-0x6F | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x70-0x77 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x78-0x7F | 
 |  | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x80-0x87 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x88-0x8F | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x90-0x97 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0x98-0x9F | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xA0-0xA7 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xA8-0xAF | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xB0-0xB7 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xB8-0xBF | 
 |  | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xC0-0xC7 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xC8-0xCF | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xD0-0xD7 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xD8-0xDF | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xE0-0xE7 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xE8-0xEF | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xF0-0xF7 | 
 |     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  // 0xF8-0xFF | 
 | }; | 
 |  | 
 | #ifndef SRC_BUFFER_ARRAY_SIZE | 
 | #define SRC_BUFFER_ARRAY_SIZE (64 * 1024 * 1024) | 
 | #endif | 
 |  | 
 | uint8_t g_src_buffer_array[SRC_BUFFER_ARRAY_SIZE]; | 
 |  | 
 | wuffs_base__io_buffer g_src; | 
 |  | 
 | const char* g_filename; | 
 | FILE* g_file; | 
 | uint64_t g_line; | 
 |  | 
 | const char*  // | 
 | read_src() { | 
 |   if (g_src.meta.closed) { | 
 |     return "main: internal error: read requested on a closed source"; | 
 |   } | 
 |   wuffs_base__io_buffer__compact(&g_src); | 
 |   if (g_src.meta.wi >= g_src.data.len) { | 
 |     return "main: g_src buffer is full"; | 
 |   } | 
 |   size_t n = fread(g_src.data.ptr + g_src.meta.wi, sizeof(uint8_t), | 
 |                    g_src.data.len - g_src.meta.wi, g_file); | 
 |   g_src.meta.wi += n; | 
 |   g_src.meta.closed = feof(g_file); | 
 |   if ((n == 0) && !g_src.meta.closed) { | 
 |     return "main: read error"; | 
 |   } | 
 |   return NULL; | 
 | } | 
 |  | 
 | void  // | 
 | fail_parse(const char* impl, const char* z) { | 
 |   fprintf(stderr, "main: %s could not parse \"%s\" at %s:%" PRIu64 "\n", impl, | 
 |           z, g_filename, g_line); | 
 | } | 
 |  | 
 | void  // | 
 | fail(const char* impl, const char* z, uint64_t have, uint64_t want) { | 
 |   fprintf(stderr, "main: %s mismatch at %s:%" PRIu64 "\n", impl, g_filename, | 
 |           g_line); | 
 |   fprintf(stderr, "src:  %s\n", z); | 
 |   fprintf(stderr, "have: 0x%016" PRIX64 " %f\n", have, | 
 |           wuffs_base__ieee_754_bit_representation__from_u64_to_f64(have)); | 
 |   fprintf(stderr, "want: 0x%016" PRIX64 " %f\n", want, | 
 |           wuffs_base__ieee_754_bit_representation__from_u64_to_f64(want)); | 
 | } | 
 |  | 
 | bool  // | 
 | process_line(wuffs_base__slice_u8 s) { | 
 |   if (s.len < 32) { | 
 |     fprintf(stderr, "main: short input at %s:%" PRIu64 "\n", g_filename, | 
 |             g_line); | 
 |     return false; | 
 |   } else if (s.len > 2048) { | 
 |     fprintf(stderr, "main: long input at %s:%" PRIu64 "\n", g_filename, g_line); | 
 |     return false; | 
 |   } | 
 |   uint64_t want = 0; | 
 |   for (int i = 14; i < 30; i++) { | 
 |     want = (want << 4) | hex[s.ptr[i]]; | 
 |   } | 
 |   s.ptr += 31; | 
 |   s.len -= 31; | 
 |  | 
 |   // Convert ".123" to "0.123". Not all parsers like a leading dot. | 
 |   if (s.ptr[0] == '.') { | 
 |     s.ptr--; | 
 |     s.len++; | 
 |     s.ptr[0] = '0'; | 
 |   } | 
 |  | 
 |   char z[2049]; | 
 |   memcpy(&z[0], s.ptr, s.len); | 
 |   z[s.len] = 0; | 
 |  | 
 |   // Check libc's strtod. | 
 |   { | 
 |     double have_f64 = strtod(&z[0], NULL); | 
 |     uint64_t have = | 
 |         wuffs_base__ieee_754_bit_representation__from_f64_to_u64(have_f64); | 
 |     if (have != want) { | 
 |       fail("strtod", &z[0], have, want); | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 | #ifdef USE_LEMIRE_FAST_DOUBLE_PARSER | 
 |   // Check lemire/fast_double_parser's parse_number. | 
 |   // | 
 |   // https://github.com/lemire/fast_double_parser/blob/master/README.md says | 
 |   // "the parser will reject overly large values that would not fit in | 
 |   // binary64. It will not produce NaN or infinite values". | 
 |   if (want < 0x7FF0000000000000ul) { | 
 |     double have_f64; | 
 |     if (!fast_double_parser::decimal_separator_dot::parse_number(&z[0], | 
 |                                                                  &have_f64)) { | 
 |       fail_parse("lemire/fast_double_parser", &z[0]); | 
 |       return false; | 
 |     } | 
 |     uint64_t have = | 
 |         wuffs_base__ieee_754_bit_representation__from_f64_to_u64(have_f64); | 
 |     if (have != want) { | 
 |       fail("lemire/fast_double_parser", &z[0], have, want); | 
 |       return false; | 
 |     } | 
 |   } | 
 | #endif | 
 |  | 
 | #ifdef USE_LEMIRE_FAST_FLOAT | 
 |   // Check lemire/fast_float's from_chars. | 
 |   { | 
 |     double have_f64; | 
 |     fast_float::from_chars_result result = fast_float::from_chars( | 
 |         static_cast<char*>(static_cast<void*>(s.ptr)), | 
 |         static_cast<char*>(static_cast<void*>(s.ptr + s.len)), have_f64, | 
 |         fast_float::chars_format::general); | 
 |     if (result.ec != std::errc()) { | 
 |       fail_parse("lemire/fast_float", &z[0]); | 
 |       return false; | 
 |     } | 
 |     uint64_t have = | 
 |         wuffs_base__ieee_754_bit_representation__from_f64_to_u64(have_f64); | 
 |     if (have != want) { | 
 |       fail("lemire/fast_float", &z[0], have, want); | 
 |       return false; | 
 |     } | 
 |   } | 
 | #endif | 
 |  | 
 |   // Check Wuffs' wuffs_base__parse_number_f64. | 
 |   { | 
 |     wuffs_base__result_f64 res = wuffs_base__parse_number_f64( | 
 |         s, WUFFS_BASE__PARSE_NUMBER_XXX__DEFAULT_OPTIONS); | 
 |     if (res.status.repr) { | 
 |       fail_parse("wuffs", &z[0]); | 
 |       return false; | 
 |     } | 
 |     uint64_t have = | 
 |         wuffs_base__ieee_754_bit_representation__from_f64_to_u64(res.value); | 
 |     if (have != want) { | 
 |       fail("wuffs", &z[0], have, want); | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool  // | 
 | process_file(char* filename) { | 
 |   if (g_file) { | 
 |     fclose(g_file); | 
 |     g_file = NULL; | 
 |   } | 
 |   g_filename = filename; | 
 |   g_file = fopen(g_filename, "r"); | 
 |   if (!g_file) { | 
 |     fprintf(stderr, "main: could not open %s\n", g_filename); | 
 |     return false; | 
 |   } | 
 |   g_line = 0; | 
 |   g_src = wuffs_base__slice_u8__writer( | 
 |       wuffs_base__make_slice_u8(&g_src_buffer_array[0], SRC_BUFFER_ARRAY_SIZE)); | 
 |  | 
 |   while (true) { | 
 |     for (size_t i = g_src.meta.ri; i < g_src.meta.wi; i++) { | 
 |       if (g_src.data.ptr[i] == '\n') { | 
 |         g_line++; | 
 |         if (!process_line(wuffs_base__make_slice_u8( | 
 |                 &g_src.data.ptr[g_src.meta.ri], i - g_src.meta.ri))) { | 
 |           return false; | 
 |         } | 
 |         g_src.meta.ri = i + 1; | 
 |         continue; | 
 |       } | 
 |     } | 
 |  | 
 |     if (g_src.meta.closed) { | 
 |       if (g_src.meta.ri != g_src.meta.wi) { | 
 |         fprintf(stderr, "main: unexpected end-of-file\n"); | 
 |         return false; | 
 |       } | 
 |       break; | 
 |     } | 
 |  | 
 |     const char* error_msg = read_src(); | 
 |     if (error_msg) { | 
 |       fprintf(stderr, "%s\n", error_msg); | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   printf("%8" PRIu64 " OK in %s\n", g_line, g_filename); | 
 |   return true; | 
 | } | 
 |  | 
 | int  // | 
 | main(int argc, char** argv) { | 
 |   g_file = NULL; | 
 |   for (int argi = 1; argi < argc; argi++) { | 
 |     if (!process_file(argv[argi])) { | 
 |       return 1; | 
 |     } | 
 |   } | 
 |   return 0; | 
 | } |