diff --git a/.gitmodules b/.gitmodules index d8dff9d9e..4622e8782 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "dependencies/benchmark"] path = dependencies/benchmark url = https://github.com/google/benchmark.git +[submodule "dependencies/cxxopts"] + path = dependencies/cxxopts + url = https://github.com/jarro2783/cxxopts diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 5e0ab489d..4abd4fb6b 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -68,3 +68,7 @@ if (SIMDJSON_COMPETITION) add_library(competition-all INTERFACE) target_link_libraries(competition-all INTERFACE competition-core competition-jsoncppdist competition-json11 competition-fastjson competition-gason competition-ujson4c) endif() + +initialize_submodule(cxxopts) +add_library(cxxopts INTERFACE) +target_include_directories(cxxopts INTERFACE cxxopts/include) diff --git a/dependencies/cxxopts b/dependencies/cxxopts new file mode 160000 index 000000000..794c97528 --- /dev/null +++ b/dependencies/cxxopts @@ -0,0 +1 @@ +Subproject commit 794c975287355de48158d9a80ed502d26b20a472 diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 0e79364c0..81d8aae6c 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,4 +1,4 @@ -link_libraries(simdjson simdjson-internal-flags simdjson-windows-headers) +link_libraries(simdjson simdjson-internal-flags simdjson-windows-headers cxxopts) add_executable(json2json json2json.cpp) add_executable(jsonstats jsonstats.cpp) diff --git a/tools/json2json.cpp b/tools/json2json.cpp index 9ad25d2d8..d02ba74c5 100644 --- a/tools/json2json.cpp +++ b/tools/json2json.cpp @@ -1,40 +1,53 @@ #include #include #include "simdjson.h" +#ifndef __cpp_exceptions +#define CXXOPTS_NO_EXCEPTIONS +#endif +#include "cxxopts.hpp" -void usage(const char *exe) { - std::cerr << exe << " v" << STRINGIFY(SIMDJSON_VERSION) << " (" << simdjson::active_implementation->name() << ")" << std::endl; - std::cerr << std::endl; - std::cerr << "Reads json in, out the result of the parsing. " << std::endl; - std::cerr << "Usage: " << exe << " " << std::endl; - std::cerr << "The -d flag dumps the raw content of the tape." << std::endl; -} int main(int argc, char *argv[]) { - bool rawdump = false; +#ifdef __cpp_exceptions + try { +#endif + std::string progName = "json2json"; - int c; + std::string progUsage = "json2json version "; + progUsage += STRINGIFY(SIMDJSON_VERSION); + progUsage += " ("; + progUsage += simdjson::active_implementation->name(); + progUsage += ")\n"; + progUsage += "Reads json in, out the result of the parsing.\n"; + progUsage += argv[0]; + progUsage += " \n"; - while ((c = getopt(argc, argv, "dh")) != -1) { - switch (c) { - case 'd': - rawdump = true; - break; - case 'h': - usage(argv[0]); - return EXIT_SUCCESS; - default: - abort(); - } + cxxopts::Options options(progName, progUsage); + + options.add_options() + ("d,rawdump", "Dumps the raw content of the tape.", cxxopts::value()->default_value("false")) + ("f,file", "File name.", cxxopts::value()) + ("h,help", "Print usage.") + ; + + // the first argument without an option name will be parsed into file + options.parse_positional({"file"}); + auto result = options.parse(argc, argv); + + if(result.count("help")) { + std::cerr << options.help() << std::endl; + return EXIT_SUCCESS; } - if (optind >= argc) { - usage(argv[0]); + + bool rawdump = result["rawdump"].as(); + + if(!result.count("file")) { + std::cerr << "No filename specified." << std::endl; + std::cerr << options.help() << std::endl; return EXIT_FAILURE; } - const char *filename = argv[optind]; - if (optind + 1 < argc) { - std::cerr << "warning: ignoring everything after " << argv[optind + 1] - << std::endl; - } + + const char *filename = result["file"].as().c_str(); + simdjson::dom::parser parser; auto [doc, error] = parser.load(filename); // do the parsing, return false on error if (error != simdjson::SUCCESS) { @@ -48,4 +61,10 @@ int main(int argc, char *argv[]) { std::cout << doc; } return EXIT_SUCCESS; +#ifdef __cpp_exceptions + } catch (const cxxopts::OptionException& e) { + std::cout << "error parsing options: " << e.what() << std::endl; + return EXIT_FAILURE; + } +#endif } diff --git a/tools/jsonstats.cpp b/tools/jsonstats.cpp index f8a60f8e1..216ea4776 100644 --- a/tools/jsonstats.cpp +++ b/tools/jsonstats.cpp @@ -2,6 +2,10 @@ #include #include "simdjson.h" +#ifndef __cpp_exceptions +#define CXXOPTS_NO_EXCEPTIONS +#endif +#include "cxxopts.hpp" size_t count_nonasciibytes(const uint8_t *input, size_t length) { size_t count = 0; @@ -180,18 +184,37 @@ stat_t simdjson_compute_stats(const simdjson::padded_string &p) { } int main(int argc, char *argv[]) { - int myoptind = 1; - if (myoptind >= argc) { - std::cerr << "Reads json, prints stats. " << std::endl; - std::cerr << "Usage: " << argv[0] << " " << std::endl; - exit(1); +#ifdef __cpp_exceptions + try { +#endif + std::string progName = "jsonstat"; + std::string progUsage = "Reads json, prints stats.\n"; + progUsage += argv[0]; + progUsage += " "; + + cxxopts::Options options(progName, progUsage); + + options.add_options() + ("h,help", "Print usage.") + ("f,file", "File name.", cxxopts::value()) + ; + + options.parse_positional({"file"}); + auto result = options.parse(argc, argv); + + if(result.count("help")) { + std::cerr << options.help() << std::endl; + return EXIT_SUCCESS; } - const char *filename = argv[myoptind]; - if (myoptind + 1 < argc) { - std::cerr << "warning: ignoring everything after " << argv[myoptind + 1] - << std::endl; + + if(!result.count("file")) { + std::cerr << "No filename specified." << std::endl; + std::cerr << options.help() << std::endl; + return EXIT_FAILURE; } + const char *filename = result["file"].as().c_str(); + auto [p, error] = simdjson::padded_string::load(filename); if (error) { std::cerr << "Could not load the file " << filename << std::endl; @@ -206,33 +229,33 @@ int main(int argc, char *argv[]) { // a JSON object and then to serialize it. printf(R"({ - "integer_count" = %10zu, - "integer32_count" = %10zu, - "unsigned_integer32_count" = %10zu, - "unsigned_integer_count" = %10zu, - "float_count" = %10zu, - "string_count" = %10zu, - "string_byte_count" = %10zu, - "ascii_string_count" = %10zu, - "string_maximum_length" = %10zu, - "backslash_count" = %10zu, - "non_ascii_byte_count" = %10zu, - "object_count" = %10zu, - "maximum_object_size" = %10zu, - "array_count" = %10zu, - "maximum_array_size" = %10zu, - "null_count" = %10zu, - "true_count" = %10zu, - "false_count" = %10zu, - "byte_count" = %10zu, - "structural_indexes_count" = %10zu, - "key_count" = %10zu, - "ascii_key_count" = %10zu, - "key_maximum_length" = %10zu, - "key_distinct_count" = %10zu, + "integer_count" = %10zu, + "integer32_count" = %10zu, + "unsigned_integer32_count" = %10zu, + "unsigned_integer_count" = %10zu, + "float_count" = %10zu, + "string_count" = %10zu, + "string_byte_count" = %10zu, + "ascii_string_count" = %10zu, + "string_maximum_length" = %10zu, + "backslash_count" = %10zu, + "non_ascii_byte_count" = %10zu, + "object_count" = %10zu, + "maximum_object_size" = %10zu, + "array_count" = %10zu, + "maximum_array_size" = %10zu, + "null_count" = %10zu, + "true_count" = %10zu, + "false_count" = %10zu, + "byte_count" = %10zu, + "structural_indexes_count" = %10zu, + "key_count" = %10zu, + "ascii_key_count" = %10zu, + "key_maximum_length" = %10zu, + "key_distinct_count" = %10zu, "repeated_key_distinct_count"= %10zu, - "repeated_key_byte_count" = %10zu; - "maximum_depth" = %10zu + "repeated_key_byte_count" = %10zu; + "maximum_depth" = %10zu } )", s.integer_count,s.integer32_count,s.unsigned_integer32_count,s.unsigned_integer_count, @@ -244,4 +267,10 @@ int main(int argc, char *argv[]) { s.ascii_key_count, s.key_maximum_length, s.all_keys.size(), s.repeated_keys.size(), s.repeated_key_byte_count, s.maximum_depth); return EXIT_SUCCESS; -} \ No newline at end of file +#ifdef __cpp_exceptions + } catch (const cxxopts::OptionException& e) { + std::cout << "error parsing options: " << e.what() << std::endl; + return EXIT_FAILURE; + } +#endif +} diff --git a/tools/minify.cpp b/tools/minify.cpp index c9c0415af..f9f264c42 100644 --- a/tools/minify.cpp +++ b/tools/minify.cpp @@ -5,74 +5,57 @@ #include #include "simdjson.h" +#ifndef __cpp_exceptions +#define CXXOPTS_NO_EXCEPTIONS +#endif +#include "cxxopts.hpp" -// Stash the exe_name in main() for functions to use -char* exe_name; +cxxopts::Options options("minify", "Runs the parser against the given json files in a loop, measuring speed and other statistics."); -void print_usage(std::ostream& out) { - out << "Usage: " << exe_name << " [-a ARCH] " << std::endl; - out << std::endl; - out << "Runs the parser against the given json files in a loop, measuring speed and other statistics." << std::endl; - out << std::endl; - out << "Options:" << std::endl; - out << std::endl; - out << "-a IMPL - Use the given parser implementation. By default, detects the most advanced" << std::endl; - out << " implementation supported on the host machine." << std::endl; - for (auto impl : simdjson::available_implementations) { - out << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << std::endl; - } -} - -void exit_usage(std::string message) { +void usage(std::string message) { std::cerr << message << std::endl; - std::cerr << std::endl; - print_usage(std::cerr); - exit(EXIT_FAILURE); + std::cerr << options.help() << std::endl; } - -struct option_struct { - char* filename{}; - - option_struct(int argc, char **argv) { - int c; - - while ((c = getopt(argc, argv, "a:")) != -1) { - switch (c) { - case 'a': { - const simdjson::implementation *impl = simdjson::available_implementations[optarg]; - if (!impl) { - std::string exit_message = std::string("Unsupported option value -a ") + optarg + ": expected -a with one of "; - for (auto imple : simdjson::available_implementations) { - exit_message += imple->name(); - exit_message += " "; - } - exit_usage(exit_message); - } - simdjson::active_implementation = impl; - break; - } - default: - // reaching here means an argument was given to getopt() which did not have a case label - exit_usage("Unexpected argument - missing case for option "+ - std::string(1,static_cast(c))+ - " (programming error)"); - } - } - - // All remaining arguments are considered to be files - if(optind + 1 == argc) { - filename = argv[optind]; - } else { - exit_usage("Please specify exactly one input file."); - } - } -}; - int main(int argc, char *argv[]) { - exe_name = argv[0]; - option_struct options(argc, argv); - std::string filename = options.filename; +#ifdef __cpp_exceptions + try { +#endif + std::stringstream ss; + ss << "Parser implementation (by default, detects the most advanced implementation supported on the host machine)." << std::endl; + ss << "Available parser implementations:" << std::endl; + for (auto impl : simdjson::available_implementations) { + ss << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << std::endl; + } + options.add_options() + ("a,arch", ss.str(), cxxopts::value()) + ("f,file", "File name.", cxxopts::value()) + ("h,help", "Print usage.") + ; + + options.parse_positional({"file"}); + auto result = options.parse(argc, argv); + + if(result.count("help")) { + usage(""); + return EXIT_SUCCESS; + } + + if(!result.count("file")) { + usage("No filename specified."); + return EXIT_FAILURE; + } + if(result.count("arch")) { + const simdjson::implementation *impl = simdjson::available_implementations[result["arch"].as().c_str()]; + if(!impl) { + usage("Unsupported implementation."); + return EXIT_FAILURE; + } + simdjson::active_implementation = impl; + } + + std::string filename = result["file"].as(); + auto [p, error] = simdjson::padded_string::load(filename); if (error) { std::cerr << "Could not load the file " << filename << std::endl; @@ -83,4 +66,10 @@ int main(int argc, char *argv[]) { error = simdjson::active_implementation->minify((const uint8_t*)p.data(), p.length(), (uint8_t*)copy.data(), copy_len); if (error) { std::cerr << error << std::endl; return 1; } printf("%s", copy.data()); +#ifdef __cpp_exceptions + } catch (const cxxopts::OptionException& e) { + std::cout << "error parsing options: " << e.what() << std::endl; + return EXIT_FAILURE; + } +#endif }