/
niceSOFT
/
subversion
Обзор
Документация
Войти
/
niceSOFT
/
subversion
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
trunk
tools/dev/gen-test-data/gen_diff_test_data.c
539 строк
12 KB
Nathan Hartman
* tools/dev/gen-test-data/gen_diff_test_data.c:
02 июл 2021, 18:44
02 июл 2021, 18:44
7a68fd4
Код
Авторство
О чём код?
/* gen_diff_test_data.c -- Generate sample data to test svn's diff * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 * * http://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. * ==================================================================== * * * This is a program to generate some pathological sample data for * testing and improving diff implementations. * * The output is deterministic but varies based on a seed value, like * that provided to a pseudo random number generator. The output * length is controlled as well. Both parameters are given at the * command line. The output is written to stdout. * * Presumably if two large outputs are generated by two runs with * different seed values, it will take a diff algorithm a long time to * calculate their longest common subsequence. * * * Usage: * * $ gen_diff_test_data <seed> <length> * * * Implementation notes: * * Rather than use the system-provided pseudo random number generator, * this program implements the hailstone sequence (see [1]) to assure * that users on different systems can produce same outputs when using * same seed and length values. That way people don't have to send * each other huge >100M files of useless junk. :-) * * * References: * * [1] Hailstone sequence: See Collatz Conjecture * https://en.wikipedia.org/wiki/Collatz_conjecture */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdarg.h> #include <string.h> #include <limits.h> #define PROGRAM_VERSION "0.01" /* starting number for a hailstone sequence */ static uint64_t g_seed; /* desired length of the output (approximately; actual may be longer) */ static uint64_t g_length; /* current number in the hailstone sequence */ static uint64_t g_curr; /* current word in words[] array */ static uint64_t g_word_index; /* number of bytes written to stdout */ static uint64_t g_written; /* how much to indent lines */ static int g_indents; /* a bunch of random words to print in the output */ static const char * words[] = { "list", "exe", "MODULE", "EXE", "BIT", "database", "POINT", "link", "node", "parent", "BYTE", "enumerated", "OPTION", "managed", "deprecated", "point", "inheritance", "OUT", "VARIABLE", "PERL", "core", "else", "provider", "IMPLEMENTATION", "ENDIANNESS", "platform", "TYPE", "SCANNER", "libc", "lisp", "PROCESSOR", "path", "optimisation", "NANO", "subversion", "FORTRAN", "support", "EMPTY", "parser", "EXTENSION", "LOOP", "COLUMN", "resource", "end", "SUBCLASS", "optimal", "silicon", "row", "EXTENSIONS", "config", "EXCEPTION", "INHERITANCE", "BEGIN", "emacs", "VALLEY", "PROJECT", "EXTERNAL", "version", "subclass", "array", "ABI", "OPTIMISATION", "CLEAN", "ENVIRONMENT", "COL", "string", "RESOURCE", "VECTOR", "true", "STANDALONE", "VAR", "cobol", "DATA", "main", "TOOL", "ERROR", "IF", "drive", "errno", "artifact", "NO", "no", "DEVICE", "namespace", "name", "while", "dependencies", "IOCTL", "FLOAT", "SUBVERSION", "variable", "fortran", "external", "COBOL", "SILICON", "table", "API", "DATABASE", "ioctl", "BUILTIN", "polymorphism", "empty", "extensions", "OPTIMAL", "target", "optimization", "superclass", "INTERFACE", "interface", "PREFERENCES", "FOR", "asm", "var", "diagnostic", "PARALLELIZATION", "type", "xml", "linker", "PROVIDER", "leaf", "valley", "LINK", "TOOLCHAIN", "false", "DIAGNOSTIC", "RUNTIME", "CONFIGURATION", "CORE", "CONST", "MANAGED", "LEAF", "encoding", "switch", "CASE", "ERRNO", "DEBUG", "LIST", "double", "STATE", "builtin", "TARGET", "PYTHON", "SCRIPT", "definitions", "file", "if", "TABLE", "SETTINGS", "compiler", "ENUMERATED", "FALSE", "EXECUTABLE", "technical", "POLYMORPHISM", "vector", "STUDIO", "NAME", "float", "VERSION", "exception", "TRUE", "bit", "STORAGE", "INCANTATION", "endianness", "NODE", "id", "XML", "DONE", "INVOCATION", "environment", "PARENT", "SUPPORT", "tool", "ARRAY", "state", "project", "configuration", "const", "module", "builder", "BUILDER", "parallelization", "perl", "standalone", "ARTIFACT", "OPTIMIZATION", "COMPILER", "executable", "DEPENDENCIES", "nil", "column", "debug", "FILE", "option", "DEPRECATED", "COMMAND", "abi", "processor", "ENCODING", "command", "WHILE", "LISP", "vim", "DOUBLE", "folder", "script", "EMACS", "col", "DRIVE", "build", "case", "PARSER", "device", "clean", "NIL", "storage", "preferences", "VIM", "END", "NAMESPACE", "data", "toolchain", "STRING", "error", "description", "RELEASE", "incantation", "nano", "do", "TECHNICAL", "ROW", "scanner", "binary", "SUPERCLASS", "DESCRIPTION", "DO", "CONFIG", "invocation", "DIRECTORY", "done", "SWITCH", "NULL", "FOLDER", "LIBC", "BUILD", "ASM", "directory", "LINKER", "MAIN", "ID", "THEN", "implementation", "ELSE", "PLATFORM", "PATH", "then", "connection", "studio", "DEFINITIONS", "out", "null", "CONNECTION", "loop", "python", "runtime", "api", "BINARY" }; /* temporary space for constructing strings */ static char scratchpad1[1024]; static char scratchpad2[1024]; /* something bad happened; print message and terminate execution */ static void die(const char * s) { if (s) { fprintf(stderr, "gen_diff_test_data: %s\n", s); } exit(1); } /* given a value, calculate next value in hailstone sequence * * f(n) = 3n+1 if n odd, n/2 if n even */ static uint64_t hailstone(uint64_t n) { return (n & 1) ? (n * 3) + 1 : n >> 1; } /* advance global variable to next value in hailstone sequence * if reached end of sequence, reseed and restart */ static void advance(void) { if (g_curr == 1) { g_seed++; g_curr = g_seed; } else { g_curr = hailstone(g_curr); } } /* get another "pseudo-random" word from words[] and advance in * hailstone sequence */ static const char * word(void) { const char * ret; g_word_index += g_curr; ret = words[g_word_index % (sizeof(words) / sizeof(words[0]))]; advance(); return ret; } /* get another "pseudo-random" number and advance in hailstone * sequence */ static int number(void) { int ret = (int) g_curr; advance(); return ret; } /* print a hopefully helpful message and then quit */ static void usage(void) { fprintf(stderr, "gen_diff_test_data version %s\n\n", PROGRAM_VERSION); fprintf(stderr, "Usage: gen_diff_test_data <seed> <length>\n" "Where:\n" " seed - controls the content of the output\n" " length - in bytes controls amount written\n" " approximately; actual output could be\n" " longer; can use k, m, or g suffix\n\n"); exit(1); } /* parse command line arguments and validate them successfully or quit */ static void parse_args(int argc, const char * argv[]) { char * endptr; long int val; if (argc != 3) { usage(); } /* parse the seed value */ val = strtol(argv[1], &endptr, 0); if ((val < 2) || (val == LONG_MAX)) { die("seed must be in 1 < seed < LONG_MAX"); } if ((endptr) && (*endptr)) { die("unexpected stuff after seed"); } g_seed = (uint64_t) val; /* parse the length value */ val = strtol(argv[2], &endptr, 0); if ((val < 1) || (val == LONG_MAX)) { die("length must be in 0 < length < LONG_MAX"); } g_length = (uint64_t) val; if (endptr) { switch (*endptr) { case 0: break; case 'g': case 'G': g_length <<= 10; case 'm': case 'M': g_length <<= 10; case 'k': case 'K': g_length <<= 10; endptr++; if (*endptr) { die("unexpected stuff after length"); } break; default: die("unknown length suffix"); } } } /* print a string to stdout or else! */ static void print_or_die(const char * s, ...) { va_list args; int ret; va_start(args, s); ret = vfprintf(stdout, s, args); va_end(args); if (ret < 0) { die("sorry, vfprintf() failed!"); } g_written += (uint64_t) ret; } /* print a string to a buffer or else! */ static void snprintf_or_die(char * s, size_t n, const char * fmt, ...) { va_list args; int ret; va_start(args, fmt); ret = vsnprintf(s, n, fmt, args); va_end(args); if ((ret < 0) || ((size_t) ret >= n)) { die("sorry, vsnprintf() failed!"); } } /* really lame function to "indent" by the current indent level by * repeatedly printing spaces */ static void indent_or_die(void) { int indents = g_indents; while (indents > 0) { print_or_die(" "); indents--; } } /* print an opening XML-looking tag and increase indent level */ static void open_tag(const char * s) { indent_or_die(); print_or_die("<%s>\n", s); g_indents++; } /* safely decrease indent level and print a closing XML-looking tag */ static void close_tag(const char * s) { if (g_indents > 0) { g_indents--; } indent_or_die(); print_or_die("</%s>\n", s); } /* on one line, print an opening XML-looking tag, possibly with * params, then print some contents, then print a closing tag; does * not change indent level */ static void one_line_tag(const char * tag, const char * params, const char * contents) { indent_or_die(); if ((params) && (*params)) { print_or_die("<%s %s>%s</%s>\n", tag, params, contents, tag); } else { print_or_die("<%s>%s</%s>\n", tag, contents, tag); } } static void print_thing_1(void) { snprintf_or_die(scratchpad1, sizeof(scratchpad1), "%s=\"%s\"", word(), word()); snprintf_or_die(scratchpad2, sizeof(scratchpad2), "%d", number()); one_line_tag(word(), scratchpad1, scratchpad2); } static void print_thing_2(void) { snprintf_or_die(scratchpad2, sizeof(scratchpad2), "%d", number()); one_line_tag(word(), NULL, scratchpad2); } static void print_thing_3(void) { snprintf_or_die(scratchpad2, sizeof(scratchpad2), "%s", word()); one_line_tag(word(), NULL, scratchpad2); } static void print_thing_4(void) { snprintf_or_die(scratchpad1, sizeof(scratchpad1), "%s=\"%s\" %s=\"%s\" %s=\"%s\"", word(), word(), word(), word(), word(), word()); snprintf_or_die(scratchpad2, sizeof(scratchpad2), "%d", number()); one_line_tag(word(), scratchpad1, scratchpad2); } static void print_thing_x(int x) { if ((x + 30) >= (sizeof(words) / sizeof(words[0]))) { x = 0; } snprintf_or_die(scratchpad1, sizeof(scratchpad1), "%s=\"%s\" %s=\"%s\" %s=\"%s\"", words[x + 5], words[x + 10], words[x + 15], words[x + 20], words[x + 25], words[x + 30]); snprintf_or_die(scratchpad2, sizeof(scratchpad2), "%d", x); one_line_tag(word(), scratchpad1, scratchpad2); } static void print_sequence_1(void) { open_tag("level1"); print_thing_1(); print_thing_2(); open_tag("level2"); print_thing_3(); print_thing_x(10); print_thing_4(); open_tag("level3"); print_thing_1(); print_thing_2(); print_thing_x(35); print_thing_3(); open_tag("level4"); print_thing_3(); print_thing_2(); print_thing_4(); close_tag("level4"); close_tag("level3"); close_tag("level2"); print_thing_3(); close_tag("level1"); } /* generate a whole bunch of output that looks like XML with pseudo * random contents, but lots of similar lines; in other words, stuff * to keep a diff algorithm busy for a while */ static void generate_output(void) { open_tag("level0"); while (g_written < g_length) { print_sequence_1(); } close_tag("level0"); } int main(int argc, const char * argv[]) { parse_args(argc, argv); g_curr = g_seed; g_word_index = 0; g_written = 0; g_indents = 0; generate_output(); return 0; }