text: // 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__STATIC_FUNCTIONS macro is optional, but when
  // combined with WUFFS_IMPLEMENTATION, it demonstrates making all of Wuffs'
  // functions have static storage.
  //
  // This can help the compiler ignore or discard unused code, which can produce
  // faster compiles and smaller binaries. Other motivations are discussed in the
  // "ALLOW STATIC IMPLEMENTATION" section of
  // https://raw.githubusercontent.com/nothings/stb/master/docs/stb_howto.txt
  #define WUFFS_CONFIG__STATIC_FUNCTIONS
  
  // Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
  // release/c/etc.c choose 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, "rb");
    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;
  }
  