Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "bincopydata.h"
14 :
15 : #ifdef _MSC_VER
16 : #include <io.h>
17 : #include <fcntl.h>
18 : #endif
19 :
20 : static char *exe_name = "<to_be_filled_in>";
21 :
22 : static void
23 4 : gen_tinyints(FILE *f, bool byteswap, long nrecs, char *arg)
24 : {
25 4 : (void)arg;
26 4000004 : for (long i = 0; i < nrecs; i++) {
27 4000000 : uint8_t v = (uint8_t)i;
28 4000000 : (void)byteswap;
29 4000000 : fwrite(&v, sizeof(v), 1, f);
30 : }
31 4 : }
32 :
33 : static void
34 8 : gen_smallints(FILE *f, bool byteswap, long nrecs, char *arg)
35 : {
36 8 : (void)arg;
37 4000056 : for (long i = 0; i < nrecs; i++) {
38 4000048 : uint16_t v = (uint16_t)i;
39 4000048 : if (byteswap) {
40 1000012 : copy_binary_convert16(&v);
41 : }
42 4000048 : fwrite(&v, sizeof(v), 1, f);
43 : }
44 8 : }
45 :
46 : static void
47 4 : gen_bigints(FILE *f, bool byteswap, long nrecs, char *arg)
48 : {
49 4 : (void)arg;
50 4000004 : for (long i = 0; i < nrecs; i++) {
51 4000000 : uint64_t v = (uint64_t)i;
52 4000000 : if (byteswap) {
53 1000000 : copy_binary_convert64(&v);
54 : }
55 4000000 : fwrite(&v, sizeof(v), 1, f);
56 : }
57 4 : }
58 :
59 : #ifdef HAVE_HGE
60 : static void
61 1 : gen_hugeints(FILE *f, bool byteswap, long nrecs, char *arg)
62 : {
63 1 : (void)arg;
64 1000001 : for (long i = 0; i < nrecs; i++) {
65 1000000 : uhge v = (uhge)i;
66 1000000 : if (byteswap) {
67 0 : copy_binary_convert128(&v);
68 : }
69 1000000 : fwrite(&v, sizeof(v), 1, f);
70 : }
71 1 : }
72 : #endif
73 :
74 : static void
75 4 : gen_ints(FILE *f, bool byteswap, long nrecs, char *arg)
76 : {
77 4 : (void)arg;
78 4 : assert((uintmax_t)nrecs <= (uintmax_t) UINT32_MAX);
79 4 : uint32_t n = (uint32_t) nrecs;
80 4000004 : for (uint32_t i = 0; i < n; i++) {
81 4000000 : uint32_t v = i;
82 4000000 : if (byteswap) {
83 1000000 : copy_binary_convert32(&v);
84 : }
85 4000000 : fwrite(&v, sizeof(v), 1, f);
86 : }
87 4 : }
88 :
89 : static void
90 1 : gen_more_ints(FILE *f, bool byteswap, long nrecs, char *arg)
91 : {
92 1 : (void)arg;
93 1 : assert((uintmax_t)nrecs <= (uintmax_t) UINT32_MAX);
94 1 : uint32_t n = (uint32_t) nrecs;
95 1000001 : for (uint32_t i = 0; i < n; i++) {
96 1000000 : uint32_t v = i + 1;
97 1000000 : if (byteswap) {
98 0 : copy_binary_convert32(&v);
99 : }
100 1000000 : fwrite(&v, sizeof(v), 1, f);
101 : }
102 1 : }
103 :
104 : static void
105 1 : gen_null_ints(FILE *f, bool byteswap, long nrecs, char *arg)
106 : {
107 1 : (void)arg;
108 1 : assert((uintmax_t)nrecs <= (uintmax_t) UINT32_MAX);
109 1 : uint32_t n = (uint32_t) nrecs;
110 1 : uint32_t nil = 0x80000000;
111 1000001 : for (uint32_t i = 0; i < n; i++) {
112 1000000 : uint32_t v = i % 2 == 0 ? nil : i;
113 1000000 : if (byteswap) {
114 0 : copy_binary_convert32(&v);
115 : }
116 1000000 : fwrite(&v, sizeof(v), 1, f);
117 : }
118 1 : }
119 :
120 : static void
121 1 : gen_bools(FILE *f, bool byteswap, long nrecs, char *arg)
122 : {
123 1 : (void)arg;
124 1000001 : for (long i = 0; i < nrecs; i++) {
125 1000000 : char b = i % 2;
126 1000000 : (void)byteswap;
127 1000000 : fwrite(&b, sizeof(b), 1, f);
128 : }
129 1 : }
130 :
131 : static void
132 8 : gen_floats(FILE *f, bool byteswap, long nrecs, char *arg)
133 : {
134 8 : (void)arg;
135 : // Assume for now that the raw bits are portable enough
136 :
137 4000056 : for (long i = 0; i < nrecs; i++) {
138 4000048 : float fl = (float)i;
139 4000048 : fl += 0.5;
140 4000048 : if (byteswap)
141 1000012 : copy_binary_convert32(&fl);
142 4000048 : fwrite(&fl, sizeof(fl), 1, f);
143 : }
144 8 : }
145 :
146 : static void
147 8 : gen_doubles(FILE *f, bool byteswap, long nrecs, char *arg)
148 : {
149 8 : (void)arg;
150 : // Assume for now that the raw bits are portable enough
151 :
152 4000056 : for (long i = 0; i < nrecs; i++) {
153 4000048 : double fl = (double)i;
154 4000048 : fl += 0.5;
155 4000048 : if (byteswap)
156 1000012 : copy_binary_convert64(&fl);
157 4000048 : fwrite(&fl, sizeof(fl), 1, f);
158 : }
159 8 : }
160 :
161 : static void
162 1 : gen_strings(FILE *f, bool byteswap, long nrecs, char *arg)
163 : {
164 1 : (void)arg;
165 1 : (void)byteswap;
166 1000001 : for (long i = 0; i < nrecs; i++) {
167 1000000 : fprintf(f, "int%ld", i);
168 1000000 : fputc(0, f);
169 : }
170 1 : }
171 :
172 : static void
173 1 : gen_large_strings(FILE *f, bool byteswap, long nrecs, char *arg)
174 : {
175 1 : (void)arg;
176 1 : size_t n = 280000;
177 1 : char *buf = malloc(n);
178 1 : memset(buf, 'a', n);
179 1000001 : for (long i = 0; i < nrecs; i++) {
180 1000000 : fprintf(f, "int%06ld", i);
181 1000000 : if (i % 10000 == 0)
182 100 : fwrite(buf, n, 1, f);
183 1000000 : fputc(0, f);
184 : }
185 1 : free(buf);
186 1 : (void)byteswap;
187 1 : }
188 :
189 : static void
190 1 : gen_broken_strings(FILE *f, bool byteswap, long nrecs, char *arg)
191 : {
192 1 : (void)arg;
193 : // "bröken"
194 1 : char utf8[] = {0x62, 0x72, 0xc3, 0xb6, 0x6b, 0x65, 0x6e, 0x00};
195 1 : char latin1[] = {0x62, 0x72, 0xf6, 0x6b, 0x65, 0x6e, 0x00};
196 :
197 1 : (void)byteswap;
198 1000001 : for (long i = 0; i < nrecs; i++) {
199 1000000 : if (i == 123456)
200 1 : fwrite(latin1, sizeof(latin1), 1, f);
201 : else
202 999999 : fwrite(utf8, sizeof(utf8), 1, f);
203 : }
204 1 : }
205 :
206 : static void
207 1 : gen_newline_strings(FILE *f, bool byteswap, long nrecs, char *arg)
208 : {
209 1 : (void)arg;
210 1 : (void)byteswap;
211 1000001 : for (long i = 0; i < nrecs; i++) {
212 1000000 : fprintf(f, "RN\r\nR\r%ld", i);
213 1000000 : fputc(0, f);
214 : }
215 1 : }
216 :
217 : static void
218 1 : gen_null_strings(FILE *f, bool byteswap, long nrecs, char *arg)
219 : {
220 1 : (void)arg;
221 1 : (void)byteswap;
222 1000001 : for (long i = 0; i < nrecs; i++) {
223 1000000 : if (i % 2 == 0)
224 500000 : fputc(0x80, f);
225 : else
226 500000 : fputs("banana", f);
227 1000000 : fputc(0, f);
228 : }
229 1 : }
230 :
231 : static void
232 3 : gen_null_blobs(FILE *f, bool byteswap, long nrecs, char *arg)
233 : {
234 3 : (void)arg;
235 3 : uint8_t *buffer = malloc(nrecs);
236 3000003 : for (long i = 0; i < nrecs; i++) {
237 3000000 : buffer[i] = 0xD0 + 3 - (i % 3);
238 : }
239 :
240 3000003 : for (long i = 0; i < nrecs; i++) {
241 3000000 : uint64_t header;
242 3000000 : size_t n;
243 3000000 : if (i % 3 == 2) {
244 : // null
245 999999 : n = 0;
246 999999 : header = (uint64_t)-1;
247 : } else {
248 2000001 : n = (i % 1000);
249 2000001 : header = n;
250 : }
251 3000000 : if (byteswap)
252 1000000 : copy_binary_convert64(&header);
253 3000000 : assert(sizeof(header) == 8);
254 3000000 : fwrite(&header, sizeof(header), 1, f);
255 3000000 : if (n > 0)
256 1998000 : fwrite(buffer, 1, n, f);
257 : }
258 3 : free(buffer);
259 3 : }
260 :
261 : static void
262 1 : gen_json(FILE *f, bool byteswap, long nrecs, char *arg)
263 : {
264 1 : (void)arg;
265 1 : (void)byteswap;
266 1000001 : for (long i = 0; i < nrecs; i++) {
267 1000000 : if (i % 100 == 99) {
268 10000 : fputc('\x80', f);
269 : } else {
270 990000 : fprintf(f, "{\"id\":%ld,\"msg\":\"int%ld\"}", i, i);
271 : }
272 1000000 : fputc('\0', f);
273 : }
274 1 : }
275 :
276 : #define FUNCNAME gen_decimal_tinyints
277 : #define STYP int8_t
278 : #define UTYP uint8_t
279 : // #define CONVERT
280 : #include "bincopydecimal_impl.h"
281 :
282 : #define FUNCNAME gen_decimal_smallints
283 : #define STYP int16_t
284 : #define UTYP uint16_t
285 : #define CONVERT copy_binary_convert16
286 : #include "bincopydecimal_impl.h"
287 :
288 : #define FUNCNAME gen_decimal_ints
289 : #define STYP int32_t
290 : #define UTYP uint32_t
291 : #define CONVERT copy_binary_convert32
292 : #include "bincopydecimal_impl.h"
293 :
294 :
295 : #define FUNCNAME gen_decimal_bigints
296 : #define STYP int64_t
297 : #define UTYP uint64_t
298 : #define CONVERT copy_binary_convert64
299 : #include "bincopydecimal_impl.h"
300 :
301 : #ifdef HAVE_HGE
302 : #define FUNCNAME gen_decimal_hugeints
303 : #define STYP hge
304 : #define UTYP uhge
305 : #define CONVERT copy_binary_convert128
306 : #include "bincopydecimal_impl.h"
307 : #endif
308 :
309 : typedef void (*generator_t)(FILE *f, bool byteswap, long nrecs, char *argument);
310 :
311 : static struct gen {
312 : char *name;
313 : generator_t gen;
314 : bool arg_allowed;
315 : } generators[] = {
316 : { "ints", gen_ints },
317 : { "more_ints", gen_more_ints },
318 : { "null_ints", gen_null_ints },
319 : { "bools", gen_bools },
320 : { "floats", gen_floats },
321 : { "doubles", gen_doubles },
322 : { "tinyints", gen_tinyints },
323 : { "smallints", gen_smallints },
324 : { "bigints", gen_bigints },
325 : #ifdef HAVE_HGE
326 : { "hugeints", gen_hugeints },
327 : #endif
328 : //
329 : { "dec_tinyints", gen_decimal_tinyints, .arg_allowed=true },
330 : { "dec_smallints", gen_decimal_smallints, .arg_allowed=true },
331 : { "dec_ints", gen_decimal_ints, .arg_allowed=true },
332 : { "dec_bigints", gen_decimal_bigints, .arg_allowed=true },
333 : #ifdef HAVE_HGE
334 : { "dec_hugeints", gen_decimal_hugeints, .arg_allowed=true },
335 : #endif
336 : //
337 : { "strings", gen_strings },
338 : { "large_strings", gen_large_strings },
339 : { "broken_strings", gen_broken_strings },
340 : { "newline_strings", gen_newline_strings },
341 : { "null_strings", gen_null_strings },
342 : { "null_blobs", gen_null_blobs },
343 : //
344 : { "timestamps", gen_timestamps },
345 : { "timestamp_times", gen_timestamp_times },
346 : { "timestamp_dates", gen_timestamp_dates },
347 : { "timestamp_ms", gen_timestamp_ms },
348 : { "timestamp_seconds", gen_timestamp_seconds },
349 : { "timestamp_minutes", gen_timestamp_minutes },
350 : { "timestamp_hours", gen_timestamp_hours },
351 : { "timestamp_days", gen_timestamp_days },
352 : { "timestamp_months", gen_timestamp_months },
353 : { "timestamp_years", gen_timestamp_years },
354 :
355 : { "json_objects", gen_json },
356 :
357 : { "binary_uuids", gen_bin_uuids },
358 : { "text_uuids", gen_text_uuids },
359 :
360 : { NULL, NULL },
361 : };
362 :
363 : /* Format the message and write it to stderr. Then exit with the given status.
364 : * If status is 1, include USAGE in the message.
365 : * Otherwise, if errno is set, include the error message.
366 : */
367 : void
368 0 : croak(int status, const char *ctx, ...)
369 : {
370 0 : va_list ap;
371 :
372 0 : fprintf(stderr, "Error: ");
373 0 : if (ctx != NULL) {
374 0 : fprintf(stderr, " ");
375 0 : va_start(ap, ctx);
376 0 : vfprintf(stderr, ctx, ap);
377 0 : va_end(ap);
378 : }
379 0 : fprintf(stderr, "\n");
380 0 : if (errno) {
381 0 : fprintf(stderr, "Possibly due to: %s\n", strerror(errno));
382 0 : } else if (status == 1) {
383 0 : fprintf(stderr, "USAGE: %s TYPE NRECS DESTFILE\n", exe_name);
384 0 : fprintf(stderr, "TYPE:\n");
385 0 : for (struct gen *g = generators; g->name != NULL; g++) {
386 0 : fprintf(stderr, " - %s\n", g->name);
387 : }
388 : }
389 0 : exit(status);
390 : }
391 :
392 : static generator_t
393 65 : pick_generator(const char *full_name, char **arg)
394 : {
395 65 : char *name = strdup(full_name);
396 65 : *arg = NULL;
397 :
398 65 : char *sep = strchr(name, '!');
399 65 : if (sep != NULL) {
400 4 : *arg = strdup(sep + 1);
401 4 : *sep = '\0';
402 : }
403 :
404 65 : generator_t gen = NULL;
405 806 : for (struct gen *g = generators; g->name; g++) {
406 806 : if (strcmp(g->name, name) == 0) {
407 65 : if (*arg && !g->arg_allowed)
408 0 : croak(2, "Generator '%s' does not take arguments", name);
409 65 : gen = g->gen;
410 65 : break;
411 : }
412 : }
413 :
414 65 : free(name);
415 :
416 65 : return gen;
417 : }
418 :
419 :
420 : int
421 65 : main(int argc, char *argv[])
422 : {
423 65 : exe_name = argv[0];
424 65 : generator_t gen;
425 65 : char *gen_arg = NULL;
426 65 : long nrecs;
427 65 : FILE *dest;
428 65 : bool byteswap = false;
429 65 : bool i_am_little_endian =
430 : #ifdef WORDS_BIGENDIAN
431 : false
432 : #else
433 : true
434 : #endif
435 : ;
436 65 : bool i_am_big_endian = !i_am_little_endian;
437 :
438 65 : char **args = &argv[1];
439 65 : char **args_end = &argv[argc];
440 :
441 94 : while (args < args_end && **args == '-') {
442 29 : char *arg = *args++;
443 29 : if (strcmp(arg, "--native-endian") == 0)
444 : byteswap = false;
445 20 : else if (strcmp(arg, "--big-endian") == 0)
446 : byteswap = i_am_little_endian;
447 10 : else if (strcmp(arg, "--little-endian") == 0)
448 : byteswap = i_am_big_endian;
449 : else
450 0 : croak(1, "Unexpected argument: %s", arg);
451 : }
452 :
453 65 : if (args_end - args != 3)
454 0 : croak(1, "Unexpected number of arguments");
455 :
456 65 : gen = pick_generator(args[0], &gen_arg);
457 65 : if (gen == NULL)
458 0 : croak(1, "Unknown TYPE: %s", args[0]);
459 :
460 65 : char *end;
461 65 : nrecs = strtol(args[1], &end, 10);
462 65 : if (*end != '\0')
463 0 : croak(1, "NRECS must be an integer, not '%s'", args[1]);
464 :
465 65 : char *destfilename = args[2];
466 65 : if (strcmp(destfilename, "-") == 0) {
467 : #ifdef _MSC_VER
468 : _setmode(1, O_BINARY);
469 : #endif
470 12 : dest = stdout;
471 : } else {
472 53 : dest = fopen(destfilename, "wb");
473 : }
474 :
475 65 : if (dest == NULL)
476 0 : croak(2, "Cannot open '%s' for writing", args[2]);
477 :
478 65 : gen(dest, byteswap, nrecs, gen_arg);
479 :
480 65 : fclose(dest);
481 65 : free(gen_arg);
482 :
483 65 : return 0;
484 : }
|