Skip to content

Commit f772bf4

Browse files
yoannlrlemire
andauthored
Port tools to cxxopts (simdjson#904)
* Port tools to cxxopts * Fix minify tool architecture argument * Fix wrong return code in json2json * Change return codes * Better handling of the errors. * Updating to latest version. * cxxopts outside of SIMDJSON_COMPETITION Co-authored-by: Daniel Lemire <lemire@gmail.com>
1 parent ac0c309 commit f772bf4

File tree

7 files changed

+168
-123
lines changed

7 files changed

+168
-123
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@
3131
[submodule "dependencies/benchmark"]
3232
path = dependencies/benchmark
3333
url = https://github.com/google/benchmark.git
34+
[submodule "dependencies/cxxopts"]
35+
path = dependencies/cxxopts
36+
url = https://github.com/jarro2783/cxxopts

dependencies/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,7 @@ if (SIMDJSON_COMPETITION)
6868
add_library(competition-all INTERFACE)
6969
target_link_libraries(competition-all INTERFACE competition-core competition-jsoncppdist competition-json11 competition-fastjson competition-gason competition-ujson4c)
7070
endif()
71+
72+
initialize_submodule(cxxopts)
73+
add_library(cxxopts INTERFACE)
74+
target_include_directories(cxxopts INTERFACE cxxopts/include)

dependencies/cxxopts

Submodule cxxopts added at 794c975

tools/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
link_libraries(simdjson simdjson-internal-flags simdjson-windows-headers)
1+
link_libraries(simdjson simdjson-internal-flags simdjson-windows-headers cxxopts)
22

33
add_executable(json2json json2json.cpp)
44
add_executable(jsonstats jsonstats.cpp)

tools/json2json.cpp

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,53 @@
11
#include <iostream>
22
#include <unistd.h>
33
#include "simdjson.h"
4+
#ifndef __cpp_exceptions
5+
#define CXXOPTS_NO_EXCEPTIONS
6+
#endif
7+
#include "cxxopts.hpp"
48

5-
void usage(const char *exe) {
6-
std::cerr << exe << " v" << STRINGIFY(SIMDJSON_VERSION) << " (" << simdjson::active_implementation->name() << ")" << std::endl;
7-
std::cerr << std::endl;
8-
std::cerr << "Reads json in, out the result of the parsing. " << std::endl;
9-
std::cerr << "Usage: " << exe << " <jsonfile>" << std::endl;
10-
std::cerr << "The -d flag dumps the raw content of the tape." << std::endl;
11-
}
129
int main(int argc, char *argv[]) {
13-
bool rawdump = false;
14-
15-
int c;
16-
17-
while ((c = getopt(argc, argv, "dh")) != -1) {
18-
switch (c) {
19-
case 'd':
20-
rawdump = true;
21-
break;
22-
case 'h':
23-
usage(argv[0]);
24-
return EXIT_SUCCESS;
25-
default:
26-
abort();
27-
}
10+
#ifdef __cpp_exceptions
11+
try {
12+
#endif
13+
std::string progName = "json2json";
14+
15+
std::string progUsage = "json2json version ";
16+
progUsage += STRINGIFY(SIMDJSON_VERSION);
17+
progUsage += " (";
18+
progUsage += simdjson::active_implementation->name();
19+
progUsage += ")\n";
20+
progUsage += "Reads json in, out the result of the parsing.\n";
21+
progUsage += argv[0];
22+
progUsage += " <jsonfile>\n";
23+
24+
cxxopts::Options options(progName, progUsage);
25+
26+
options.add_options()
27+
("d,rawdump", "Dumps the raw content of the tape.", cxxopts::value<bool>()->default_value("false"))
28+
("f,file", "File name.", cxxopts::value<std::string>())
29+
("h,help", "Print usage.")
30+
;
31+
32+
// the first argument without an option name will be parsed into file
33+
options.parse_positional({"file"});
34+
auto result = options.parse(argc, argv);
35+
36+
if(result.count("help")) {
37+
std::cerr << options.help() << std::endl;
38+
return EXIT_SUCCESS;
2839
}
29-
if (optind >= argc) {
30-
usage(argv[0]);
40+
41+
bool rawdump = result["rawdump"].as<bool>();
42+
43+
if(!result.count("file")) {
44+
std::cerr << "No filename specified." << std::endl;
45+
std::cerr << options.help() << std::endl;
3146
return EXIT_FAILURE;
3247
}
33-
const char *filename = argv[optind];
34-
if (optind + 1 < argc) {
35-
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
36-
<< std::endl;
37-
}
48+
49+
const char *filename = result["file"].as<std::string>().c_str();
50+
3851
simdjson::dom::parser parser;
3952
auto [doc, error] = parser.load(filename); // do the parsing, return false on error
4053
if (error != simdjson::SUCCESS) {
@@ -48,4 +61,10 @@ int main(int argc, char *argv[]) {
4861
std::cout << doc;
4962
}
5063
return EXIT_SUCCESS;
64+
#ifdef __cpp_exceptions
65+
} catch (const cxxopts::OptionException& e) {
66+
std::cout << "error parsing options: " << e.what() << std::endl;
67+
return EXIT_FAILURE;
68+
}
69+
#endif
5170
}

tools/jsonstats.cpp

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
#include <set>
33

44
#include "simdjson.h"
5+
#ifndef __cpp_exceptions
6+
#define CXXOPTS_NO_EXCEPTIONS
7+
#endif
8+
#include "cxxopts.hpp"
59

610
size_t count_nonasciibytes(const uint8_t *input, size_t length) {
711
size_t count = 0;
@@ -180,18 +184,37 @@ stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
180184
}
181185

182186
int main(int argc, char *argv[]) {
183-
int myoptind = 1;
184-
if (myoptind >= argc) {
185-
std::cerr << "Reads json, prints stats. " << std::endl;
186-
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
187-
exit(1);
187+
#ifdef __cpp_exceptions
188+
try {
189+
#endif
190+
std::string progName = "jsonstat";
191+
std::string progUsage = "Reads json, prints stats.\n";
192+
progUsage += argv[0];
193+
progUsage += " <jsonfile>";
194+
195+
cxxopts::Options options(progName, progUsage);
196+
197+
options.add_options()
198+
("h,help", "Print usage.")
199+
("f,file", "File name.", cxxopts::value<std::string>())
200+
;
201+
202+
options.parse_positional({"file"});
203+
auto result = options.parse(argc, argv);
204+
205+
if(result.count("help")) {
206+
std::cerr << options.help() << std::endl;
207+
return EXIT_SUCCESS;
188208
}
189-
const char *filename = argv[myoptind];
190-
if (myoptind + 1 < argc) {
191-
std::cerr << "warning: ignoring everything after " << argv[myoptind + 1]
192-
<< std::endl;
209+
210+
if(!result.count("file")) {
211+
std::cerr << "No filename specified." << std::endl;
212+
std::cerr << options.help() << std::endl;
213+
return EXIT_FAILURE;
193214
}
194215

216+
const char *filename = result["file"].as<std::string>().c_str();
217+
195218
auto [p, error] = simdjson::padded_string::load(filename);
196219
if (error) {
197220
std::cerr << "Could not load the file " << filename << std::endl;
@@ -206,33 +229,33 @@ int main(int argc, char *argv[]) {
206229
// a JSON object and then to serialize it.
207230

208231
printf(R"({
209-
"integer_count" = %10zu,
210-
"integer32_count" = %10zu,
211-
"unsigned_integer32_count" = %10zu,
212-
"unsigned_integer_count" = %10zu,
213-
"float_count" = %10zu,
214-
"string_count" = %10zu,
215-
"string_byte_count" = %10zu,
216-
"ascii_string_count" = %10zu,
217-
"string_maximum_length" = %10zu,
218-
"backslash_count" = %10zu,
219-
"non_ascii_byte_count" = %10zu,
220-
"object_count" = %10zu,
221-
"maximum_object_size" = %10zu,
222-
"array_count" = %10zu,
223-
"maximum_array_size" = %10zu,
224-
"null_count" = %10zu,
225-
"true_count" = %10zu,
226-
"false_count" = %10zu,
227-
"byte_count" = %10zu,
228-
"structural_indexes_count" = %10zu,
229-
"key_count" = %10zu,
230-
"ascii_key_count" = %10zu,
231-
"key_maximum_length" = %10zu,
232-
"key_distinct_count" = %10zu,
232+
"integer_count" = %10zu,
233+
"integer32_count" = %10zu,
234+
"unsigned_integer32_count" = %10zu,
235+
"unsigned_integer_count" = %10zu,
236+
"float_count" = %10zu,
237+
"string_count" = %10zu,
238+
"string_byte_count" = %10zu,
239+
"ascii_string_count" = %10zu,
240+
"string_maximum_length" = %10zu,
241+
"backslash_count" = %10zu,
242+
"non_ascii_byte_count" = %10zu,
243+
"object_count" = %10zu,
244+
"maximum_object_size" = %10zu,
245+
"array_count" = %10zu,
246+
"maximum_array_size" = %10zu,
247+
"null_count" = %10zu,
248+
"true_count" = %10zu,
249+
"false_count" = %10zu,
250+
"byte_count" = %10zu,
251+
"structural_indexes_count" = %10zu,
252+
"key_count" = %10zu,
253+
"ascii_key_count" = %10zu,
254+
"key_maximum_length" = %10zu,
255+
"key_distinct_count" = %10zu,
233256
"repeated_key_distinct_count"= %10zu,
234-
"repeated_key_byte_count" = %10zu;
235-
"maximum_depth" = %10zu
257+
"repeated_key_byte_count" = %10zu;
258+
"maximum_depth" = %10zu
236259
}
237260
)",
238261
s.integer_count,s.integer32_count,s.unsigned_integer32_count,s.unsigned_integer_count,
@@ -244,4 +267,10 @@ int main(int argc, char *argv[]) {
244267
s.ascii_key_count, s.key_maximum_length, s.all_keys.size(), s.repeated_keys.size(),
245268
s.repeated_key_byte_count, s.maximum_depth);
246269
return EXIT_SUCCESS;
247-
}
270+
#ifdef __cpp_exceptions
271+
} catch (const cxxopts::OptionException& e) {
272+
std::cout << "error parsing options: " << e.what() << std::endl;
273+
return EXIT_FAILURE;
274+
}
275+
#endif
276+
}

tools/minify.cpp

Lines changed: 46 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,74 +5,57 @@
55
#include <unistd.h>
66

77
#include "simdjson.h"
8+
#ifndef __cpp_exceptions
9+
#define CXXOPTS_NO_EXCEPTIONS
10+
#endif
11+
#include "cxxopts.hpp"
812

9-
// Stash the exe_name in main() for functions to use
10-
char* exe_name;
11-
12-
void print_usage(std::ostream& out) {
13-
out << "Usage: " << exe_name << " [-a ARCH] <jsonfile>" << std::endl;
14-
out << std::endl;
15-
out << "Runs the parser against the given json files in a loop, measuring speed and other statistics." << std::endl;
16-
out << std::endl;
17-
out << "Options:" << std::endl;
18-
out << std::endl;
19-
out << "-a IMPL - Use the given parser implementation. By default, detects the most advanced" << std::endl;
20-
out << " implementation supported on the host machine." << std::endl;
21-
for (auto impl : simdjson::available_implementations) {
22-
out << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << std::endl;
23-
}
24-
}
13+
cxxopts::Options options("minify", "Runs the parser against the given json files in a loop, measuring speed and other statistics.");
2514

26-
void exit_usage(std::string message) {
15+
void usage(std::string message) {
2716
std::cerr << message << std::endl;
28-
std::cerr << std::endl;
29-
print_usage(std::cerr);
30-
exit(EXIT_FAILURE);
17+
std::cerr << options.help() << std::endl;
3118
}
3219

20+
int main(int argc, char *argv[]) {
21+
#ifdef __cpp_exceptions
22+
try {
23+
#endif
24+
std::stringstream ss;
25+
ss << "Parser implementation (by default, detects the most advanced implementation supported on the host machine)." << std::endl;
26+
ss << "Available parser implementations:" << std::endl;
27+
for (auto impl : simdjson::available_implementations) {
28+
ss << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << std::endl;
29+
}
30+
options.add_options()
31+
("a,arch", ss.str(), cxxopts::value<std::string>())
32+
("f,file", "File name.", cxxopts::value<std::string>())
33+
("h,help", "Print usage.")
34+
;
3335

34-
struct option_struct {
35-
char* filename{};
36-
37-
option_struct(int argc, char **argv) {
38-
int c;
36+
options.parse_positional({"file"});
37+
auto result = options.parse(argc, argv);
3938

40-
while ((c = getopt(argc, argv, "a:")) != -1) {
41-
switch (c) {
42-
case 'a': {
43-
const simdjson::implementation *impl = simdjson::available_implementations[optarg];
44-
if (!impl) {
45-
std::string exit_message = std::string("Unsupported option value -a ") + optarg + ": expected -a with one of ";
46-
for (auto imple : simdjson::available_implementations) {
47-
exit_message += imple->name();
48-
exit_message += " ";
49-
}
50-
exit_usage(exit_message);
51-
}
52-
simdjson::active_implementation = impl;
53-
break;
54-
}
55-
default:
56-
// reaching here means an argument was given to getopt() which did not have a case label
57-
exit_usage("Unexpected argument - missing case for option "+
58-
std::string(1,static_cast<char>(c))+
59-
" (programming error)");
60-
}
61-
}
39+
if(result.count("help")) {
40+
usage("");
41+
return EXIT_SUCCESS;
42+
}
6243

63-
// All remaining arguments are considered to be files
64-
if(optind + 1 == argc) {
65-
filename = argv[optind];
66-
} else {
67-
exit_usage("Please specify exactly one input file.");
44+
if(!result.count("file")) {
45+
usage("No filename specified.");
46+
return EXIT_FAILURE;
47+
}
48+
if(result.count("arch")) {
49+
const simdjson::implementation *impl = simdjson::available_implementations[result["arch"].as<std::string>().c_str()];
50+
if(!impl) {
51+
usage("Unsupported implementation.");
52+
return EXIT_FAILURE;
6853
}
54+
simdjson::active_implementation = impl;
6955
}
70-
};
7156

72-
int main(int argc, char *argv[]) {
73-
exe_name = argv[0];
74-
option_struct options(argc, argv);
75-
std::string filename = options.filename;
57+
std::string filename = result["file"].as<std::string>();
58+
7659
auto [p, error] = simdjson::padded_string::load(filename);
7760
if (error) {
7861
std::cerr << "Could not load the file " << filename << std::endl;
@@ -83,4 +66,10 @@ int main(int argc, char *argv[]) {
8366
error = simdjson::active_implementation->minify((const uint8_t*)p.data(), p.length(), (uint8_t*)copy.data(), copy_len);
8467
if (error) { std::cerr << error << std::endl; return 1; }
8568
printf("%s", copy.data());
69+
#ifdef __cpp_exceptions
70+
} catch (const cxxopts::OptionException& e) {
71+
std::cout << "error parsing options: " << e.what() << std::endl;
72+
return EXIT_FAILURE;
73+
}
74+
#endif
8675
}

0 commit comments

Comments
 (0)