From 284f69d2e9bee6ef839e67078263264b2533786f Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Mon, 14 Feb 2022 10:57:40 +0800 Subject: [PATCH] Rewrite with circular doubly-linked list In original CMU assignment, a singly-linked list was used. However, toward to Linux kernel development, we would like to stick to its linked list implementation. That is, we take the circular doubly-linked list to re-implement the queue oriented operations. In addition, we introduce some LeetCode exercises based on Linux-like lists. Now, queue is replaced with linux kernel linked list. Also, implementation of l_new, l_free, l_insert_head, l_insert_tail, l_remove_head and l_size are added. Implementation of l_sort and l_reverse are added. Add command "mid" which would return the middle element in list. Relative test also be added in this commit Add command "swap" which would swap every two adjacent nodes in list. Relative test also be added in this commit. Reference: https://leetcode.com/problems/swap-nodes-in-pairs/ Replace "get_mid" with "remove_mid", which is LeetCode problem. Relative test bench also be updated in this commit. Reference: https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/ Add command remove_tail. Suppress the null pointer errors Newer versions of cppcheck find the potential NULL pointer bug in list_entry. The function is difficult to refactor without extensive work, so suppress the potential NULL pointer error which cannot occur for now. remove_tail and remove_head was re-desinged. We extract "free" from remove_.* because "free" is not stable (lead not constant time). Relative testbench also update in this commit. Add function "is_circular" which check if the queue is doubly circular. Suppress simulation of list removal on Arm64 We suffered from some unknown issues that simulation of list removal can not pass on Arm64 based platforms such as ThunderX2/Linux and Apple M1 (macOS). At the moment, we just skip the simulations for Arm64. We support new command: dedup. Relative test bench also be updated. Co-authored-by: eecheng87 --- .ci/check-format.sh | 12 + .ci/check-sanity.sh | 12 + .clang-format | 11 + .github/workflows/main.yml | 30 +++ README.md | 4 +- console.c | 74 +++--- console.h | 1 + dudect/constant.c | 86 ++++-- dudect/constant.h | 10 +- dudect/fixture.c | 99 ++++--- dudect/fixture.h | 4 +- dudect/ttest.c | 4 +- linenoise.h | 2 + list.h | 435 ++++++++++++++++++++++++++++++ qtest.c | 470 ++++++++++++++++++++++----------- queue.c | 129 +++++---- queue.h | 89 +++++-- random.c | 7 - random.h | 9 +- report.c | 6 +- scripts/aspell-pws | 6 + scripts/checksums | 2 + scripts/driver.py | 12 +- scripts/pre-commit.hook | 16 +- scripts/pre-push.hook | 2 +- traces/trace-02-ops.cmd | 10 +- traces/trace-04-ops.cmd | 5 +- traces/trace-05-ops.cmd | 10 +- traces/trace-06-ops.cmd | 8 + traces/trace-13-perf.cmd | 8 - traces/trace-14-perf.cmd | 6 +- traces/trace-15-perf.cmd | 25 +- traces/trace-16-perf.cmd | 24 +- traces/trace-17-complexity.cmd | 6 +- 34 files changed, 1228 insertions(+), 406 deletions(-) create mode 100755 .ci/check-format.sh create mode 100755 .ci/check-sanity.sh create mode 100644 .github/workflows/main.yml create mode 100644 list.h create mode 100644 scripts/checksums create mode 100644 traces/trace-06-ops.cmd delete mode 100644 traces/trace-13-perf.cmd diff --git a/.ci/check-format.sh b/.ci/check-format.sh new file mode 100755 index 000000000..a90113e60 --- /dev/null +++ b/.ci/check-format.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +SOURCES=$(find $(git rev-parse --show-toplevel) | egrep "\.(cpp|h)\$") + +set -x + +for file in ${SOURCES}; +do + clang-format-12 ${file} > expected-format + diff -u -p --label="${file}" --label="expected coding style" ${file} expected-format +done +exit $(clang-format-12 --output-replacements-xml ${SOURCES} | egrep -c "") diff --git a/.ci/check-sanity.sh b/.ci/check-sanity.sh new file mode 100755 index 000000000..6fd38faa3 --- /dev/null +++ b/.ci/check-sanity.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +SHA1SUM=$(which sha1sum) +if [ $? -ne 0 ]; then + SHA1SUM=shasum +fi + +$SHA1SUM -c scripts/checksums +if [ $? -ne 0 ]; then + echo "[!] You are not allowed to change the header file queue.h or list.h" >&2 + exit 1 +fi diff --git a/.clang-format b/.clang-format index 9705d27f7..30c5a3f49 100644 --- a/.clang-format +++ b/.clang-format @@ -13,3 +13,14 @@ UseTab: Never IndentWidth: 4 BreakBeforeBraces: Linux AccessModifierOffset: -4 +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH + - list_for_each + - list_for_each_safe + - list_for_each_entry + - list_for_each_entry_safe + - hlist_for_each_entry + - rb_list_foreach + - rb_list_foreach_safe diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..c48bd9c7e --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,30 @@ +name: CI + +on: [push, pull_request] + +jobs: + lab0-c: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2.4.0 + - name: install-dependencies + run: | + .ci/check-sanity.sh + sudo apt-get update + sudo apt-get -q -y install build-essential cppcheck + - name: make + run: make + - name: make check + run: make check + - name: make test + run: make test + + coding-style: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2.4.0 + - name: coding convention + run: | + sudo apt-get install -q -y clang-format-12 + sh .ci/check-format.sh + shell: bash diff --git a/README.md b/README.md index 8a4d557ba..5de1b2b2b 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ of the skills tested are: * Implementing robust code that operates correctly with invalid arguments, including NULL pointers. The lab involves implementing a queue, supporting both last-in, first-out (LIFO) and first-in-first-out (FIFO) -queueing disciplines. The underlying data structure is a singly-linked list, enhanced to make some of the -operations more efficient. +queueing disciplines. The underlying data structure is a circular doubly-linked list, enhanced to make some of +the operations more efficient. ## Prerequisites diff --git a/console.c b/console.c index 57a8d07d3..6ad338ac9 100644 --- a/console.c +++ b/console.c @@ -69,14 +69,6 @@ static bool has_infile = false; static cmd_function quit_helpers[MAXQUIT]; static int quit_helper_cnt = 0; -static bool do_quit_cmd(int argc, char *argv[]); -static bool do_help_cmd(int argc, char *argv[]); -static bool do_option_cmd(int argc, char *argv[]); -static bool do_source_cmd(int argc, char *argv[]); -static bool do_log_cmd(int argc, char *argv[]); -static bool do_time_cmd(int argc, char *argv[]); -static bool do_comment_cmd(int argc, char *argv[]); - static void init_in(); static bool push_file(char *fname); @@ -84,33 +76,6 @@ static void pop_file(); static bool interpret_cmda(int argc, char *argv[]); -/* Initialize interpreter */ -void init_cmd() -{ - cmd_list = NULL; - param_list = NULL; - err_cnt = 0; - quit_flag = false; - - add_cmd("help", do_help_cmd, " | Show documentation"); - add_cmd("option", do_option_cmd, - " [name val] | Display or set options"); - add_cmd("quit", do_quit_cmd, " | Exit program"); - add_cmd("source", do_source_cmd, - " file | Read commands from source file"); - add_cmd("log", do_log_cmd, " file | Copy output to file"); - add_cmd("time", do_time_cmd, " cmd arg ... | Time command execution"); - add_cmd("#", do_comment_cmd, " ... | Display comment"); - add_param("simulation", &simulation, "Start/Stop simulation mode", NULL); - add_param("verbose", &verblevel, "Verbosity level", NULL); - add_param("error", &err_limit, "Number of errors until exit", NULL); - add_param("echo", &echo, "Do/don't echo commands", NULL); - - init_in(); - init_time(&last_time); - first_time = last_time; -} - /* Add a new command */ void add_cmd(char *name, cmd_function operation, char *documentation) { @@ -264,7 +229,7 @@ void set_echo(bool on) } /* Built-in commands */ -static bool do_quit_cmd(int argc, char *argv[]) +static bool do_quit(int argc, char *argv[]) { cmd_ptr c = cmd_list; bool ok = true; @@ -292,7 +257,7 @@ static bool do_quit_cmd(int argc, char *argv[]) return ok; } -static bool do_help_cmd(int argc, char *argv[]) +static bool do_help(int argc, char *argv[]) { cmd_ptr clist = cmd_list; report(1, "Commands:", argv[0]); @@ -336,7 +301,7 @@ bool get_int(char *vname, int *loc) return true; } -static bool do_option_cmd(int argc, char *argv[]) +static bool do_option(int argc, char *argv[]) { if (argc == 1) { param_ptr plist = param_list; @@ -383,7 +348,7 @@ static bool do_option_cmd(int argc, char *argv[]) return true; } -static bool do_source_cmd(int argc, char *argv[]) +static bool do_source(int argc, char *argv[]) { if (argc < 2) { report(1, "No source file given"); @@ -398,7 +363,7 @@ static bool do_source_cmd(int argc, char *argv[]) return true; } -static bool do_log_cmd(int argc, char *argv[]) +static bool do_log(int argc, char *argv[]) { if (argc < 2) { report(1, "No log file given"); @@ -412,7 +377,7 @@ static bool do_log_cmd(int argc, char *argv[]) return result; } -static bool do_time_cmd(int argc, char *argv[]) +static bool do_time(int argc, char *argv[]) { double delta = delta_time(&last_time); bool ok = true; @@ -432,6 +397,31 @@ static bool do_time_cmd(int argc, char *argv[]) return ok; } +/* Initialize interpreter */ +void init_cmd() +{ + cmd_list = NULL; + param_list = NULL; + err_cnt = 0; + quit_flag = false; + + ADD_COMMAND(help, " | Show documentation"); + ADD_COMMAND(option, " [name val] | Display or set options"); + ADD_COMMAND(quit, " | Exit program"); + ADD_COMMAND(source, " file | Read commands from source file"); + ADD_COMMAND(log, " file | Copy output to file"); + ADD_COMMAND(time, " cmd arg ... | Time command execution"); + add_cmd("#", do_comment_cmd, " ... | Display comment"); + add_param("simulation", &simulation, "Start/Stop simulation mode", NULL); + add_param("verbose", &verblevel, "Verbosity level", NULL); + add_param("error", &err_limit, "Number of errors until exit", NULL); + add_param("echo", &echo, "Do/don't echo commands", NULL); + + init_in(); + init_time(&last_time); + first_time = last_time; +} + /* Create new buffer for named file. * Name == NULL for stdin. * Return true if successful. @@ -600,7 +590,7 @@ bool finish_cmd() { bool ok = true; if (!quit_flag) - ok = ok && do_quit_cmd(0, NULL); + ok = ok && do_quit(0, NULL); has_infile = false; return ok && err_cnt == 0; } diff --git a/console.h b/console.h index 51e42f97b..9a1b61199 100644 --- a/console.h +++ b/console.h @@ -43,6 +43,7 @@ void init_cmd(); /* Add a new command */ void add_cmd(char *name, cmd_function operation, char *documentation); +#define ADD_COMMAND(cmd, msg) add_cmd(#cmd, do_##cmd, msg) /* Add a new parameter */ void add_param(char *name, diff --git a/dudect/constant.c b/dudect/constant.c index f511e41b6..13c607ce3 100644 --- a/dudect/constant.c +++ b/dudect/constant.c @@ -10,42 +10,53 @@ #include "queue.h" #include "random.h" -#define NR_MEASURE 150 +#define N_MEASURE 150 + /* Allow random number range from 0 to 65535 */ const size_t chunk_size = 16; + /* Number of measurements per test */ -const size_t number_measurements = NR_MEASURE; +const size_t n_measure = N_MEASURE; + const int drop_size = 20; + /* Maintain a queue independent from the qtest since * we do not want the test to affect the original functionality */ -static queue_t *q = NULL; -static char random_string[NR_MEASURE][8]; +static struct list_head *l = NULL; + +static char random_string[N_MEASURE][8]; static int random_string_iter = 0; -enum { test_insert_tail, test_size }; + +enum { + test_insert_head, + test_insert_tail, + test_remove_head, + test_remove_tail, +}; /* Implement the necessary queue interface to simulation */ void init_dut(void) { - q = NULL; + l = NULL; } char *get_random_string(void) { - random_string_iter = (random_string_iter + 1) % NR_MEASURE; + random_string_iter = (random_string_iter + 1) % N_MEASURE; return random_string[random_string_iter]; } void prepare_inputs(uint8_t *input_data, uint8_t *classes) { - randombytes(input_data, number_measurements * chunk_size); - for (size_t i = 0; i < number_measurements; i++) { + randombytes(input_data, n_measure * chunk_size); + for (size_t i = 0; i < n_measure; i++) { classes[i] = randombit(); if (classes[i] == 0) memset(input_data + (size_t) i * chunk_size, 0, chunk_size); } - for (size_t i = 0; i < NR_MEASURE; ++i) { + for (size_t i = 0; i < N_MEASURE; ++i) { /* Generate random string */ randombytes((uint8_t *) random_string[i], 7); random_string[i][7] = 0; @@ -57,9 +68,25 @@ void measure(int64_t *before_ticks, uint8_t *input_data, int mode) { - assert(mode == test_insert_tail || mode == test_size); - if (mode == test_insert_tail) { - for (size_t i = drop_size; i < number_measurements - drop_size; i++) { + assert(mode == test_insert_head || mode == test_insert_tail || + mode == test_remove_head || mode == test_remove_tail); + + switch (mode) { + case test_insert_head: + for (size_t i = drop_size; i < n_measure - drop_size; i++) { + char *s = get_random_string(); + dut_new(); + dut_insert_head( + get_random_string(), + *(uint16_t *) (input_data + i * chunk_size) % 10000); + before_ticks[i] = cpucycles(); + dut_insert_head(s, 1); + after_ticks[i] = cpucycles(); + dut_free(); + } + break; + case test_insert_tail: + for (size_t i = drop_size; i < n_measure - drop_size; i++) { char *s = get_random_string(); dut_new(); dut_insert_head( @@ -70,8 +97,37 @@ void measure(int64_t *before_ticks, after_ticks[i] = cpucycles(); dut_free(); } - } else { - for (size_t i = drop_size; i < number_measurements - drop_size; i++) { + break; + case test_remove_head: + for (size_t i = drop_size; i < n_measure - drop_size; i++) { + dut_new(); + dut_insert_head( + get_random_string(), + *(uint16_t *) (input_data + i * chunk_size) % 10000); + before_ticks[i] = cpucycles(); + element_t *e = q_remove_head(l, NULL, 0); + after_ticks[i] = cpucycles(); + if (e) + q_release_element(e); + dut_free(); + } + break; + case test_remove_tail: + for (size_t i = drop_size; i < n_measure - drop_size; i++) { + dut_new(); + dut_insert_head( + get_random_string(), + *(uint16_t *) (input_data + i * chunk_size) % 10000); + before_ticks[i] = cpucycles(); + element_t *e = q_remove_tail(l, NULL, 0); + after_ticks[i] = cpucycles(); + if (e) + q_release_element(e); + dut_free(); + } + break; + default: + for (size_t i = drop_size; i < n_measure - drop_size; i++) { dut_new(); dut_insert_head( get_random_string(), diff --git a/dudect/constant.h b/dudect/constant.h index 81ed0928e..a5b820846 100644 --- a/dudect/constant.h +++ b/dudect/constant.h @@ -2,29 +2,29 @@ #define DUDECT_CONSTANT_H #include -#define dut_new() ((void) (q = q_new())) +#define dut_new() ((void) (l = q_new())) #define dut_size(n) \ do { \ for (int __iter = 0; __iter < n; ++__iter) \ - q_size(q); \ + q_size(l); \ } while (0) #define dut_insert_head(s, n) \ do { \ int j = n; \ while (j--) \ - q_insert_head(q, s); \ + q_insert_head(l, s); \ } while (0) #define dut_insert_tail(s, n) \ do { \ int j = n; \ while (j--) \ - q_insert_tail(q, s); \ + q_insert_tail(l, s); \ } while (0) -#define dut_free() ((void) (q_free(q))) +#define dut_free() ((void) (q_free(l))) void init_dut(); void prepare_inputs(uint8_t *input_data, uint8_t *classes); diff --git a/dudect/fixture.c b/dudect/fixture.c index cfc7f3605..5c8082169 100644 --- a/dudect/fixture.c +++ b/dudect/fixture.c @@ -24,7 +24,6 @@ * * - as long as any of the different test fails, the code will be deemed * variable time. - * */ #include "fixture.h" @@ -39,19 +38,19 @@ #include "constant.h" #include "ttest.h" -#define enough_measurements 10000 +#define enough_measure 10000 #define test_tries 10 extern const int drop_size; extern const size_t chunk_size; -extern const size_t number_measurements; +extern const size_t n_measure; static t_ctx *t; /* threshold values for Welch's t-test */ -#define t_threshold_bananas \ - 500 /* Test failed with overwhelming probability \ - */ -#define t_threshold_moderate 10 /* Test failed */ +enum { + t_threshold_bananas = 500, /* Test failed with overwhelming probability */ + t_threshold_moderate = 10, /* Test failed */ +}; static void __attribute__((noreturn)) die(void) { @@ -62,19 +61,18 @@ static void differentiate(int64_t *exec_times, const int64_t *before_ticks, const int64_t *after_ticks) { - for (size_t i = 0; i < number_measurements; i++) { + for (size_t i = 0; i < n_measure; i++) exec_times[i] = after_ticks[i] - before_ticks[i]; - } } static void update_statistics(const int64_t *exec_times, uint8_t *classes) { - for (size_t i = 0; i < number_measurements; i++) { + for (size_t i = 0; i < n_measure; i++) { int64_t difference = exec_times[i]; - /* Cpu cycle counter overflowed or dropped measurement */ - if (difference <= 0) { + /* CPU cycle counter overflowed or dropped measurement */ + if (difference <= 0) continue; - } + /* do a t-test on the execution time */ t_push(t, difference, classes[i]); } @@ -88,14 +86,13 @@ static bool report(void) printf("\033[A\033[2K"); printf("meas: %7.2lf M, ", (number_traces_max_t / 1e6)); - if (number_traces_max_t < enough_measurements) { + if (number_traces_max_t < enough_measure) { printf("not enough measurements (%.0f still to go).\n", - enough_measurements - number_traces_max_t); + enough_measure - number_traces_max_t); return false; } - /* - * max_t: the t statistic value + /* max_t: the t statistic value * max_tau: a t value normalized by sqrt(number of measurements). * this way we can compare max_tau taken with different * number of measurements. This is sort of "distance @@ -108,23 +105,25 @@ static bool report(void) printf("max t: %+7.2f, max tau: %.2e, (5/tau)^2: %.2e.\n", max_t, max_tau, (double) (5 * 5) / (double) (max_tau * max_tau)); - if (max_t > t_threshold_bananas) { + /* Definitely not constant time */ + if (max_t > t_threshold_bananas) return false; - } else if (max_t > t_threshold_moderate) { + + /* Probably not constant time. */ + if (max_t > t_threshold_moderate) return false; - } else { /* max_t < t_threshold_moderate */ - return true; - } + + /* For the moment, maybe constant time. */ + return true; } static bool doit(int mode) { - int64_t *before_ticks = calloc(number_measurements + 1, sizeof(int64_t)); - int64_t *after_ticks = calloc(number_measurements + 1, sizeof(int64_t)); - int64_t *exec_times = calloc(number_measurements, sizeof(int64_t)); - uint8_t *classes = calloc(number_measurements, sizeof(uint8_t)); - uint8_t *input_data = - calloc(number_measurements * chunk_size, sizeof(uint8_t)); + int64_t *before_ticks = calloc(n_measure + 1, sizeof(int64_t)); + int64_t *after_ticks = calloc(n_measure + 1, sizeof(int64_t)); + int64_t *exec_times = calloc(n_measure, sizeof(int64_t)); + uint8_t *classes = calloc(n_measure, sizeof(uint8_t)); + uint8_t *input_data = calloc(n_measure * chunk_size, sizeof(uint8_t)); if (!before_ticks || !after_ticks || !exec_times || !classes || !input_data) { @@ -153,19 +152,17 @@ static void init_once(void) t_init(t); } -bool is_insert_tail_const(void) +static bool TEST_CONST(char *text, int mode) { bool result = false; t = malloc(sizeof(t_ctx)); for (int cnt = 0; cnt < test_tries; ++cnt) { - printf("Testing insert_tail...(%d/%d)\n\n", cnt, test_tries); + printf("Testing %s...(%d/%d)\n\n", text, cnt, test_tries); init_once(); - for (int i = 0; - i < - enough_measurements / (number_measurements - drop_size * 2) + 1; + for (int i = 0; i < enough_measure / (n_measure - drop_size * 2) + 1; ++i) - result = doit(0); + result = doit(mode); printf("\033[A\033[2K\033[A\033[2K"); if (result == true) break; @@ -174,22 +171,22 @@ bool is_insert_tail_const(void) return result; } -bool is_size_const(void) +bool is_insert_head_const(void) { - bool result = false; - t = malloc(sizeof(t_ctx)); - for (int cnt = 0; cnt < test_tries; ++cnt) { - printf("Testing size...(%d/%d)\n\n", cnt, test_tries); - init_once(); - for (int i = 0; - i < - enough_measurements / (number_measurements - drop_size * 2) + 1; - ++i) - result = doit(1); - printf("\033[A\033[2K\033[A\033[2K"); - if (result == true) - break; - } - free(t); - return result; + return TEST_CONST("insert_head", 0); +} + +bool is_insert_tail_const(void) +{ + return TEST_CONST("insert_tail", 1); +} + +bool is_remove_head_const(void) +{ + return TEST_CONST("remove_head", 2); +} + +bool is_remove_tail_const(void) +{ + return TEST_CONST("remove_tail", 3); } diff --git a/dudect/fixture.h b/dudect/fixture.h index 296a6897c..2234efca4 100644 --- a/dudect/fixture.h +++ b/dudect/fixture.h @@ -5,7 +5,9 @@ #include "constant.h" /* Interface to test if function is constant */ +bool is_insert_head_const(void); bool is_insert_tail_const(void); -bool is_size_const(void); +bool is_remove_head_const(void); +bool is_remove_tail_const(void); #endif diff --git a/dudect/ttest.c b/dudect/ttest.c index fdb877f1b..d8cb73ce6 100644 --- a/dudect/ttest.c +++ b/dudect/ttest.c @@ -5,8 +5,7 @@ * This is basically Student's t-test for unequal * variances and unequal sample sizes. * - * see https://en.wikipedia.org/wiki/Welch%27s_t-test - * + * See https://en.wikipedia.org/wiki/Welch%27s_t-test */ #include "ttest.h" @@ -20,6 +19,7 @@ void t_push(t_ctx *ctx, double x, uint8_t class) { assert(class == 0 || class == 1); ctx->n[class]++; + /* Welford method for computing online variance * in a numerically stable way. */ diff --git a/linenoise.h b/linenoise.h index 382de1dcc..174887333 100644 --- a/linenoise.h +++ b/linenoise.h @@ -49,6 +49,7 @@ typedef struct linenoiseCompletions { char **cvec; } linenoiseCompletions; +/* clang-format off */ typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); typedef char *(linenoiseHintsCallback)(const char *, int *color, int *bold); typedef void(linenoiseFreeHintsCallback)(void *); @@ -56,6 +57,7 @@ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); void linenoiseSetHintsCallback(linenoiseHintsCallback *); void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); void linenoiseAddCompletion(linenoiseCompletions *, const char *); +/* clang-format on */ char *linenoise(const char *prompt); void linenoiseFree(void *ptr); diff --git a/list.h b/list.h new file mode 100644 index 000000000..5c8400ac3 --- /dev/null +++ b/list.h @@ -0,0 +1,435 @@ +/* Linux-like doubly-linked list implementation */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* "typeof" is a GNU extension. + * Reference: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html + */ +#if defined(__GNUC__) +#define __LIST_HAVE_TYPEOF 1 +#endif + +/** + * struct list_head - Head and node of a doubly-linked list + * @prev: pointer to the previous node in the list + * @next: pointer to the next node in the list + * + * The simple doubly-linked list consists of a head and nodes attached to + * this head. Both node and head share the same struct type. The list_* + * functions and macros can be used to access and modify this data structure. + * + * The @prev pointer of the list head points to the last list node of the + * list and @next points to the first list node of the list. For an empty list, + * both member variables point to the head. + * + * The list nodes are usually embedded in a container structure which holds the + * actual data. Such an container object is called entry. The helper list_entry + * can be used to calculate the object address from the address of the node. + */ +struct list_head { + struct list_head *prev; + struct list_head *next; +}; + +/** + * container_of() - Calculate address of object that contains address ptr + * @ptr: pointer to member variable + * @type: type of the structure containing ptr + * @member: name of the member variable in struct @type + * + * Return: @type pointer of object containing ptr + */ +#ifndef container_of +#ifdef __LIST_HAVE_TYPEOF +#define container_of(ptr, type, member) \ + __extension__({ \ + const __typeof__(((type *) 0)->member) *__pmember = (ptr); \ + (type *) ((char *) __pmember - offsetof(type, member)); \ + }) +#else +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) -offsetof(type, member))) +#endif +#endif + +/** + * LIST_HEAD - Declare list head and initialize it + * @head: name of the new object + */ +#define LIST_HEAD(head) struct list_head head = {&(head), &(head)} + +/** + * INIT_LIST_HEAD() - Initialize empty list head + * @head: pointer to list head + * + * This can also be used to initialize a unlinked list node. + * + * A node is usually linked inside a list, will be added to a list in + * the near future or the entry containing the node will be free'd soon. + * + * But an unlinked node may be given to a function which uses list_del(_init) + * before it ends up in a previously mentioned state. The list_del(_init) on an + * initialized node is well defined and safe. But the result of a + * list_del(_init) on an uninitialized node is undefined (unrelated memory is + * modified, crashes, ...). + */ +static inline void INIT_LIST_HEAD(struct list_head *head) +{ + head->next = head; + head->prev = head; +} + +/** + * list_add() - Add a list node to the beginning of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ +static inline void list_add(struct list_head *node, struct list_head *head) +{ + struct list_head *next = head->next; + + next->prev = node; + node->next = next; + node->prev = head; + head->next = node; +} + +/** + * list_add_tail() - Add a list node to the end of the list + * @node: pointer to the new node + * @head: pointer to the head of the list + */ +static inline void list_add_tail(struct list_head *node, struct list_head *head) +{ + struct list_head *prev = head->prev; + + prev->next = node; + node->next = head; + node->prev = prev; + head->prev = node; +} + +/** + * list_del() - Remove a list node from the list + * @node: pointer to the node + * + * The node is only removed from the list. Neither the memory of the removed + * node nor the memory of the entry containing the node is free'd. The node + * has to be handled like an uninitialized node. Accessing the next or prev + * pointer of the node is not safe. + * + * Unlinked, initialized nodes are also uninitialized after list_del. + * + * LIST_POISONING can be enabled during build-time to provoke an invalid memory + * access when the memory behind the next/prev pointer is used after a list_del. + * This only works on systems which prohibit access to the predefined memory + * addresses. + */ +static inline void list_del(struct list_head *node) +{ + struct list_head *next = node->next; + struct list_head *prev = node->prev; + + next->prev = prev; + prev->next = next; + +#ifdef LIST_POISONING + node->prev = (struct list_head *) (0x00100100); + node->next = (struct list_head *) (0x00200200); +#endif +} + +/** + * list_del_init() - Remove a list node from the list and reinitialize it + * @node: pointer to the node + * + * The removed node will not end up in an uninitialized state like when using + * list_del. Instead the node is initialized again to the unlinked state. + */ +static inline void list_del_init(struct list_head *node) +{ + list_del(node); + INIT_LIST_HEAD(node); +} + +/** + * list_empty() - Check if list head has no nodes attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not empty !0 - list is empty + */ +static inline int list_empty(const struct list_head *head) +{ + return (head->next == head); +} + +/** + * list_is_singular() - Check if list head has exactly one node attached + * @head: pointer to the head of the list + * + * Return: 0 - list is not singular !0 -list has exactly one entry + */ +static inline int list_is_singular(const struct list_head *head) +{ + return (!list_empty(head) && head->prev == head->next); +} + +/** + * list_splice() - Add list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *head_first = head->next; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->next = list_first; + list_first->prev = head; + + list_last->next = head_first; + head_first->prev = list_last; +} + +/** + * list_splice_tail() - Add list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. The @list head is not + * modified and has to be initialized to be used as a valid list head/node + * again. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + struct list_head *head_last = head->prev; + struct list_head *list_first = list->next; + struct list_head *list_last = list->prev; + + if (list_empty(list)) + return; + + head->prev = list_last; + list_last->next = head; + + list_first->prev = head_last; + head_last->next = list_first; +} + +/** + * list_splice_init() - Move list nodes from a list to beginning of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the beginning of the list of @head. + * It is similar to list_add but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + list_splice(list, head); + INIT_LIST_HEAD(list); +} + +/** + * list_splice_tail_init() - Move list nodes from a list to end of another list + * @list: pointer to the head of the list with the node entries + * @head: pointer to the head of the list + * + * All nodes from @list are added to to the end of the list of @head. + * It is similar to list_add_tail but for multiple nodes. + * + * The @list head will not end up in an uninitialized state like when using + * list_splice. Instead the @list is initialized again to the an empty + * list/unlinked state. + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + list_splice_tail(list, head); + INIT_LIST_HEAD(list); +} + +/** + * list_cut_position() - Move beginning of a list to another list + * @head_to: pointer to the head of the list which receives nodes + * @head_from: pointer to the head of the list + * @node: pointer to the node in which defines the cutting point + * + * All entries from the beginning of the list @head_from to (including) the + * @node is moved to @head_to. + * + * @head_to is replaced when @head_from is not empty. @node must be a real + * list node from @head_from or the behavior is undefined. + */ +static inline void list_cut_position(struct list_head *head_to, + struct list_head *head_from, + struct list_head *node) +{ + struct list_head *head_from_first = head_from->next; + + if (list_empty(head_from)) + return; + + if (head_from == node) { + INIT_LIST_HEAD(head_to); + return; + } + + head_from->next = node->next; + head_from->next->prev = head_from; + + head_to->prev = node; + node->next = head_to; + head_to->next = head_from_first; + head_to->next->prev = head_to; +} + +/** + * list_move() - Move a list node to the beginning of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the beginning of + * @head + */ +static inline void list_move(struct list_head *node, struct list_head *head) +{ + list_del(node); + list_add(node, head); +} + +/** + * list_move_tail() - Move a list node to the end of the list + * @node: pointer to the node + * @head: pointer to the head of the list + * + * The @node is removed from its old position/node and add to the end of @head + */ +static inline void list_move_tail(struct list_head *node, + struct list_head *head) +{ + list_del(node); + list_add_tail(node, head); +} + +/** + * list_entry() - Calculate address of entry that contains list node + * @node: pointer to list node + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of entry containing node + */ +#define list_entry(node, type, member) container_of(node, type, member) + +/** + * list_first_entry() - get first entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of first entry in list + */ +#define list_first_entry(head, type, member) \ + list_entry((head)->next, type, member) + +/** + * list_last_entry() - get last entry of the list + * @head: pointer to the head of the list + * @type: type of the entry containing the list node + * @member: name of the list_head member variable in struct @type + * + * Return: @type pointer of last entry in list + */ +#define list_last_entry(head, type, member) \ + list_entry((head)->prev, type, member) + +/** + * list_for_each - iterate over list nodes + * @node: list_head pointer used as iterator + * @head: pointer to the head of the list + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + */ +#define list_for_each(node, head) \ + for (node = (head)->next; node != (head); node = node->next) + +/** + * list_for_each_entry - iterate over list entries + * @entry: pointer used as iterator + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The nodes and the head of the list must must be kept unmodified while + * iterating through it. Any modifications to the the list will cause undefined + * behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#ifdef __LIST_HAVE_TYPEOF +#define list_for_each_entry(entry, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member); \ + &entry->member != (head); \ + entry = list_entry(entry->member.next, __typeof__(*entry), member)) +#endif + +/** + * list_for_each_safe - iterate over list nodes and allow deletes + * @node: list_head pointer used as iterator + * @safe: list_head pointer used to store info for next entry in list + * @head: pointer to the head of the list + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + */ +#define list_for_each_safe(node, safe, head) \ + for (node = (head)->next, safe = node->next; node != (head); \ + node = safe, safe = node->next) + +/** + * list_for_each_entry_safe - iterate over list entries and allow deletes + * @entry: pointer used as iterator + * @safe: @type pointer used to store info for next entry in list + * @head: pointer to the head of the list + * @member: name of the list_head member variable in struct type of @entry + * + * The current node (iterator) is allowed to be removed from the list. Any + * other modifications to the the list will cause undefined behavior. + * + * FIXME: remove dependency of __typeof__ extension + */ +#define list_for_each_entry_safe(entry, safe, head, member) \ + for (entry = list_entry((head)->next, __typeof__(*entry), member), \ + safe = list_entry(entry->member.next, __typeof__(*entry), member); \ + &entry->member != (head); entry = safe, \ + safe = list_entry(safe->member.next, __typeof__(*entry), member)) + +#undef __LIST_HAVE_TYPEOF + +#ifdef __cplusplus +} +#endif diff --git a/qtest.c b/qtest.c index e92565a94..dd4097281 100644 --- a/qtest.c +++ b/qtest.c @@ -13,6 +13,7 @@ #include #include #include "dudect/fixture.h" +#include "list.h" /* Our program needs to use regular malloc/free */ #define INTERNAL 1 @@ -42,21 +43,28 @@ /* * How large is a queue before it's considered big. * This affects how it gets printed - * and whether cautious mode is used when freeing the list + * and whether cautious mode is used when freeing the queue */ -#define BIG_QUEUE 30 -static int big_queue_size = BIG_QUEUE; +#define BIG_LIST 30 +static int big_list_size = BIG_LIST; + /* Global variables */ -/* Queue being tested */ -static queue_t *q = NULL; +/* List being tested */ +typedef struct { + struct list_head *l; + /* meta data of list */ + int size; +} list_head_meta_t; + +static list_head_meta_t l_meta; /* Number of elements in queue */ -static size_t qcnt = 0; +static size_t lcnt = 0; /* How many times can queue operations fail */ -static int fail_limit = BIG_QUEUE; +static int fail_limit = BIG_LIST; static int fail_count = 0; static int string_length = MAXSTRING; @@ -67,49 +75,8 @@ static const char charset[] = "abcdefghijklmnopqrstuvwxyz"; /* Forward declarations */ static bool show_queue(int vlevel); -static bool do_new(int argc, char *argv[]); -static bool do_free(int argc, char *argv[]); -static bool do_insert_head(int argc, char *argv[]); -static bool do_insert_tail(int argc, char *argv[]); -static bool do_remove_head(int argc, char *argv[]); -static bool do_remove_head_quiet(int argc, char *argv[]); -static bool do_reverse(int argc, char *argv[]); -static bool do_size(int argc, char *argv[]); -static bool do_sort(int argc, char *argv[]); -static bool do_show(int argc, char *argv[]); - -static void queue_init(); - -static void console_init() -{ - add_cmd("new", do_new, " | Create new queue"); - add_cmd("free", do_free, " | Delete queue"); - add_cmd("ih", do_insert_head, - " str [n] | Insert string str at head of queue n times. " - "Generate random string(s) if str equals RAND. (default: n == 1)"); - add_cmd("it", do_insert_tail, - " str [n] | Insert string str at tail of queue n times. " - "Generate random string(s) if str equals RAND. (default: n == 1)"); - add_cmd("rh", do_remove_head, - " [str] | Remove from head of queue. Optionally compare " - "to expected value str"); - add_cmd( - "rhq", do_remove_head_quiet, - " | Remove from head of queue without reporting value."); - add_cmd("reverse", do_reverse, " | Reverse queue"); - add_cmd("sort", do_sort, " | Sort queue in ascending order"); - add_cmd("size", do_size, - " [n] | Compute queue size n times (default: n == 1)"); - add_cmd("show", do_show, " | Show queue contents"); - add_param("length", &string_length, "Maximum length of displayed string", - NULL); - add_param("malloc", &fail_probability, "Malloc failure probability percent", - NULL); - add_param("fail", &fail_limit, - "Number of times allow queue operations to return false", NULL); -} -static bool do_new(int argc, char *argv[]) +static bool do_free(int argc, char *argv[]) { if (argc != 1) { report(1, "%s takes no arguments", argv[0]); @@ -117,22 +84,33 @@ static bool do_new(int argc, char *argv[]) } bool ok = true; - if (q) { - report(3, "Freeing old queue"); - ok = do_free(argc, argv); - } + if (!l_meta.l) + report(3, "Warning: Calling free on null queue"); error_check(); + if (lcnt > big_list_size) + set_cautious_mode(false); if (exception_setup(true)) - q = q_new(); + q_free(l_meta.l); exception_cancel(); - qcnt = 0; + set_cautious_mode(true); + + l_meta.size = 0; + l_meta.l = NULL; + lcnt = 0; show_queue(3); + size_t bcnt = allocation_check(); + if (bcnt > 0) { + report(1, "ERROR: Freed queue, but %lu blocks are still allocated", + bcnt); + ok = false; + } + return ok && !error_check(); } -static bool do_free(int argc, char *argv[]) +static bool do_new(int argc, char *argv[]) { if (argc != 1) { report(1, "%s takes no arguments", argv[0]); @@ -140,30 +118,23 @@ static bool do_free(int argc, char *argv[]) } bool ok = true; - if (!q) - report(3, "Warning: Calling free on null queue"); + if (l_meta.l) { + report(3, "Freeing old queue"); + ok = do_free(argc, argv); + } error_check(); - if (qcnt > big_queue_size) - set_cautious_mode(false); - if (exception_setup(true)) - q_free(q); + if (exception_setup(true)) { + l_meta.l = q_new(); + l_meta.size = 0; + } exception_cancel(); - set_cautious_mode(true); - - q = NULL; - qcnt = 0; + lcnt = 0; show_queue(3); - size_t bcnt = allocation_check(); - if (bcnt > 0) { - report(1, "ERROR: Freed queue, but %lu blocks are still allocated", - bcnt); - ok = false; - } - return ok && !error_check(); } + /* * TODO: Add a buf_size check of if the buf_size may be less * than MIN_RANDSTR_LEN. @@ -180,8 +151,23 @@ static void fill_rand_string(char *buf, size_t buf_size) buf[len] = '\0'; } -static bool do_insert_head(int argc, char *argv[]) +/* insert head */ +static bool do_ih(int argc, char *argv[]) { + if (simulation) { + if (argc != 1) { + report(1, "%s does not need arguments in simulation mode", argv[0]); + return false; + } + bool ok = is_insert_head_const(); + if (!ok) { + report(1, "ERROR: Probably not constant time"); + return false; + } + report(1, "Probably constant time"); + return ok; + } + char *lasts = NULL; char randstr_buf[MAX_RANDSTR_LEN]; int reps = 1; @@ -204,7 +190,7 @@ static bool do_insert_head(int argc, char *argv[]) inserts = randstr_buf; } - if (!q) + if (!l_meta.l) report(3, "Warning: Calling insert head on null queue"); error_check(); @@ -212,26 +198,29 @@ static bool do_insert_head(int argc, char *argv[]) for (int r = 0; ok && r < reps; r++) { if (need_rand) fill_rand_string(randstr_buf, sizeof(randstr_buf)); - bool rval = q_insert_head(q, inserts); + bool rval = q_insert_head(l_meta.l, inserts); if (rval) { - qcnt++; - if (!q->head->value) { - report(1, "ERROR: Failed to save copy of string in list"); + lcnt++; + l_meta.size++; + char *cur_inserts = + list_entry(l_meta.l->next, element_t, list)->value; + if (!cur_inserts) { + report(1, "ERROR: Failed to save copy of string in queue"); ok = false; - } else if (r == 0 && inserts == q->head->value) { + } else if (r == 0 && inserts == cur_inserts) { report(1, "ERROR: Need to allocate and copy string for new " - "list element"); + "queue element"); ok = false; break; - } else if (r == 1 && lasts == q->head->value) { + } else if (r == 1 && lasts == cur_inserts) { report(1, "ERROR: Need to allocate separate string for each " - "list element"); + "queue element"); ok = false; break; } - lasts = q->head->value; + lasts = cur_inserts; } else { fail_count++; if (fail_count < fail_limit) @@ -252,7 +241,8 @@ static bool do_insert_head(int argc, char *argv[]) return ok; } -static bool do_insert_tail(int argc, char *argv[]) +/* insert tail */ +static bool do_it(int argc, char *argv[]) { if (simulation) { if (argc != 1) { @@ -289,7 +279,7 @@ static bool do_insert_tail(int argc, char *argv[]) inserts = randstr_buf; } - if (!q) + if (!l_meta.l) report(3, "Warning: Calling insert tail on null queue"); error_check(); @@ -297,11 +287,14 @@ static bool do_insert_tail(int argc, char *argv[]) for (int r = 0; ok && r < reps; r++) { if (need_rand) fill_rand_string(randstr_buf, sizeof(randstr_buf)); - bool rval = q_insert_tail(q, inserts); + bool rval = q_insert_tail(l_meta.l, inserts); if (rval) { - qcnt++; - if (!q->head->value) { - report(1, "ERROR: Failed to save copy of string in list"); + lcnt++; + l_meta.size++; + char *cur_inserts = + list_entry(l_meta.l->prev, element_t, list)->value; + if (!cur_inserts) { + report(1, "ERROR: Failed to save copy of string in queue"); ok = false; } } else { @@ -323,8 +316,30 @@ static bool do_insert_tail(int argc, char *argv[]) return ok; } -static bool do_remove_head(int argc, char *argv[]) +static bool do_remove(int option, int argc, char *argv[]) { + // option 0 is for remove head; option 1 is for remove tail + + /* FIXME: It is known that both functions is_remove_tail_const() and + * is_remove_head_const() can not pass dudect on Arm64. We shall figure + * out the exact reasons and resolve later. + */ +#if !defined(__aarch64__) + if (simulation) { + if (argc != 1) { + report(1, "%s does not need arguments in simulation mode", argv[0]); + return false; + } + bool ok = option ? is_remove_tail_const() : is_remove_head_const(); + if (!ok) { + report(1, "ERROR: Probably not constant time"); + return false; + } + report(1, "Probably constant time"); + return ok; + } +#endif + if (argc != 1 && argc != 2) { report(1, "%s needs 0-1 arguments", argv[0]); return false; @@ -356,18 +371,23 @@ static bool do_remove_head(int argc, char *argv[]) memset(removes + 1, 'X', string_length + STRINGPAD - 1); removes[string_length + STRINGPAD] = '\0'; - if (!q) - report(3, "Warning: Calling remove head on null queue"); - else if (!q->head) + if (!l_meta.size) report(3, "Warning: Calling remove head on empty queue"); error_check(); - bool rval = false; + element_t *re = NULL; if (exception_setup(true)) - rval = q_remove_head(q, removes, string_length + 1); + re = option ? q_remove_tail(l_meta.l, removes, string_length + 1) + : q_remove_head(l_meta.l, removes, string_length + 1); exception_cancel(); - if (rval) { + bool is_null = re ? false : true; + + if (!is_null) { + // q_remove_head and q_remove_tail are not responsible for releasing + // node + q_release_element(re); + removes[string_length + STRINGPAD] = '\0'; if (removes[0] == '\0') { report(1, "ERROR: Failed to store removed value"); @@ -389,7 +409,8 @@ static bool do_remove_head(int argc, char *argv[]) } else { report(2, "Removed %s from queue", removes); } - qcnt--; + lcnt--; + l_meta.size--; } else { fail_count++; if (!check && fail_count < fail_limit) { @@ -414,7 +435,18 @@ static bool do_remove_head(int argc, char *argv[]) return ok && !error_check(); } -static bool do_remove_head_quiet(int argc, char *argv[]) +static inline bool do_rh(int argc, char *argv[]) +{ + return do_remove(0, argc, argv); +} + +static inline bool do_rt(int argc, char *argv[]) +{ + return do_remove(1, argc, argv); +} + +/* remove head quietly */ +static bool do_rhq(int argc, char *argv[]) { if (argc != 1) { report(1, "%s takes no arguments", argv[0]); @@ -422,20 +454,24 @@ static bool do_remove_head_quiet(int argc, char *argv[]) } bool ok = true; - if (!q) - report(3, "Warning: Calling remove head on null queue"); - else if (!q->head) + if (!l_meta.size) report(3, "Warning: Calling remove head on empty queue"); error_check(); - bool rval = false; + element_t *re = NULL; + if (exception_setup(true)) - rval = q_remove_head(q, NULL, 0); + re = q_remove_head(l_meta.l, NULL, 0); exception_cancel(); - if (rval) { + if (re) { + // q_remove_head and q_remove_tail are not responsible for releasing + // node + q_release_element(re); + report(2, "Removed element from queue"); - qcnt--; + lcnt--; + l_meta.size--; } else { fail_count++; if (fail_count < fail_limit) @@ -450,6 +486,47 @@ static bool do_remove_head_quiet(int argc, char *argv[]) return ok && !error_check(); } +static bool do_dedup(int argc, char *argv[]) +{ + if (argc != 1) { + report(1, "%s takes no arguments", argv[0]); + return false; + } + + bool ok = true; + // set_noallocate_mode(true); + if (exception_setup(true)) + ok = q_delete_dup(l_meta.l); + exception_cancel(); + + // set_noallocate_mode(false); + + if (!ok) { + report(1, "ERROR: Calling delete duplicate on null queue"); + return false; + } + + element_t *item = NULL; + if (l_meta.size) { + list_for_each_entry (item, l_meta.l, list) { + element_t *next_item; + if (item->list.next == l_meta.l) + break; + next_item = list_entry(item->list.next, element_t, list); + + // assume queue has been sorted + if (strcmp(item->value, next_item->value) == 0) { + report(1, "ERROR: Contain duplicate string on queue"); + ok = false; + break; + } + } + } + show_queue(3); + + return ok && !error_check(); +} + static bool do_reverse(int argc, char *argv[]) { if (argc != 1) { @@ -457,13 +534,13 @@ static bool do_reverse(int argc, char *argv[]) return false; } - if (!q) + if (!l_meta.l) report(3, "Warning: Calling reverse on null queue"); error_check(); set_noallocate_mode(true); if (exception_setup(true)) - q_reverse(q); + q_reverse(l_meta.l); exception_cancel(); set_noallocate_mode(false); @@ -473,20 +550,6 @@ static bool do_reverse(int argc, char *argv[]) static bool do_size(int argc, char *argv[]) { - if (simulation) { - if (argc != 1) { - report(1, "%s does not need arguments in simulation mode", argv[0]); - return false; - } - bool ok = is_size_const(); - if (!ok) { - report(1, "ERROR: Probably not constant time"); - return false; - } - report(1, "Probably constant time"); - return ok; - } - if (argc != 1 && argc != 2) { report(1, "%s takes 0-1 arguments", argv[0]); return false; @@ -506,25 +569,25 @@ static bool do_size(int argc, char *argv[]) } int cnt = 0; - if (!q) + if (!l_meta.l) report(3, "Warning: Calling size on null queue"); error_check(); if (exception_setup(true)) { for (int r = 0; ok && r < reps; r++) { - cnt = q_size(q); + cnt = q_size(l_meta.l); ok = ok && !error_check(); } } exception_cancel(); if (ok) { - if (qcnt == cnt) { + if (lcnt == cnt) { report(2, "Queue size = %d", cnt); } else { report(1, "ERROR: Computed queue size as %d, but correct value is %d", - cnt, (int) qcnt); + cnt, (int) lcnt); ok = false; } } @@ -541,27 +604,31 @@ bool do_sort(int argc, char *argv[]) return false; } - if (!q) + if (!l_meta.l) report(3, "Warning: Calling sort on null queue"); error_check(); - int cnt = q_size(q); + int cnt = q_size(l_meta.l); if (cnt < 2) report(3, "Warning: Calling sort on single node"); error_check(); set_noallocate_mode(true); if (exception_setup(true)) - q_sort(q); + q_sort(l_meta.l); exception_cancel(); set_noallocate_mode(false); bool ok = true; - if (q) { - for (list_ele_t *e = q->head; e && --cnt; e = e->next) { + if (l_meta.size) { + for (struct list_head *cur_l = l_meta.l->next; + cur_l != l_meta.l && --cnt; cur_l = cur_l->next) { /* Ensure each element in ascending order */ /* FIXME: add an option to specify sorting order */ - if (strcasecmp(e->value, e->next->value) > 0) { + element_t *item, *next_item; + item = list_entry(cur_l, element_t, list); + next_item = list_entry(cur_l->next, element_t, list); + if (strcasecmp(item->value, next_item->value) > 0) { report(1, "ERROR: Not sorted in ascending order"); ok = false; break; @@ -573,6 +640,66 @@ bool do_sort(int argc, char *argv[]) return ok && !error_check(); } +static bool do_dm(int argc, char *argv[]) +{ + if (argc != 1) { + report(1, "%s takes no arguments", argv[0]); + return false; + } + + if (!l_meta.l) + report(3, "Warning: Try to access null queue"); + error_check(); + + bool ok = true; + if (exception_setup(true)) + ok = q_delete_mid(l_meta.l); + exception_cancel(); + + show_queue(3); + return ok && !error_check(); +} + +static bool do_swap(int argc, char *argv[]) +{ + if (argc != 1) { + report(1, "%s takes no arguments", argv[0]); + return false; + } + + if (!l_meta.l) + report(3, "Warning: Try to access null queue"); + error_check(); + + set_noallocate_mode(true); + if (exception_setup(true)) + q_swap(l_meta.l); + exception_cancel(); + + set_noallocate_mode(false); + + show_queue(3); + return !error_check(); +} + +static bool is_circular() +{ + struct list_head *cur = l_meta.l->next; + while (cur != l_meta.l) { + if (!cur) + return false; + cur = cur->next; + } + + cur = l_meta.l->prev; + while (cur != l_meta.l) { + if (!cur) + return false; + cur = cur->prev; + } + return true; +} + static bool show_queue(int vlevel) { bool ok = true; @@ -580,19 +707,28 @@ static bool show_queue(int vlevel) return true; int cnt = 0; - if (!q) { - report(vlevel, "q = NULL"); + if (!l_meta.l) { + report(vlevel, "l = NULL"); return true; } - report_noreturn(vlevel, "q = ["); - list_ele_t *e = q->head; + if (!is_circular()) { + report(vlevel, "ERROR: Queue is not doubly circular"); + return false; + } + + report_noreturn(vlevel, "l = ["); + + struct list_head *ori = l_meta.l; + struct list_head *cur = l_meta.l->next; + if (exception_setup(true)) { - while (ok && e && cnt < qcnt) { - if (cnt < big_queue_size) + while (ok && ori != cur && cnt < lcnt) { + element_t *e = list_entry(cur, element_t, list); + if (cnt < big_list_size) report_noreturn(vlevel, cnt == 0 ? "%s" : " %s", e->value); - e = e->next; cnt++; + cur = cur->next; ok = ok && !error_check(); } } @@ -603,17 +739,14 @@ static bool show_queue(int vlevel) return false; } - if (!e) { - if (cnt <= big_queue_size) + if (cur == ori) { + if (cnt <= big_list_size) report(vlevel, "]"); else report(vlevel, " ... ]"); } else { report(vlevel, " ... ]"); - report( - vlevel, - "ERROR: Either list has cycle, or queue has more than %d elements", - qcnt); + report(vlevel, "ERROR: Queue has more than %d elements", lcnt); ok = false; } @@ -629,6 +762,47 @@ static bool do_show(int argc, char *argv[]) return show_queue(0); } +static void console_init() +{ + ADD_COMMAND(new, " | Create new queue"); + ADD_COMMAND(free, " | Delete queue"); + ADD_COMMAND( + ih, + " str [n] | Insert string str at head of queue n times. " + "Generate random string(s) if str equals RAND. (default: n == 1)"); + ADD_COMMAND( + it, + " str [n] | Insert string str at tail of queue n times. " + "Generate random string(s) if str equals RAND. (default: n == 1)"); + ADD_COMMAND( + rh, + " [str] | Remove from head of queue. Optionally compare " + "to expected value str"); + ADD_COMMAND( + rt, + " [str] | Remove from tail of queue. Optionally compare " + "to expected value str"); + ADD_COMMAND( + rhq, + " | Remove from head of queue without reporting value."); + ADD_COMMAND(reverse, " | Reverse queue"); + ADD_COMMAND(sort, " | Sort queue in ascending order"); + ADD_COMMAND( + size, " [n] | Compute queue size n times (default: n == 1)"); + ADD_COMMAND(show, " | Show queue contents"); + ADD_COMMAND(dm, " | Delete middle node in queue"); + ADD_COMMAND( + dedup, " | Delete all nodes that have duplicate string"); + ADD_COMMAND(swap, + " | Swap every two adjacent nodes in queue"); + add_param("length", &string_length, "Maximum length of displayed string", + NULL); + add_param("malloc", &fail_probability, "Malloc failure probability percent", + NULL); + add_param("fail", &fail_limit, + "Number of times allow queue operations to return false", NULL); +} + /* Signal handlers */ static void sigsegvhandler(int sig) { @@ -649,7 +823,7 @@ static void sigalrmhandler(int sig) static void queue_init() { fail_count = 0; - q = NULL; + l_meta.l = NULL; signal(SIGSEGV, sigsegvhandler); signal(SIGALRM, sigalrmhandler); } @@ -657,11 +831,11 @@ static void queue_init() static bool queue_quit(int argc, char *argv[]) { report(3, "Freeing queue"); - if (qcnt > big_queue_size) + if (lcnt > big_list_size) set_cautious_mode(false); if (exception_setup(true)) - q_free(q); + q_free(l_meta.l); exception_cancel(); set_cautious_mode(true); diff --git a/queue.c b/queue.c index a77dbf715..a1489bd4d 100644 --- a/queue.c +++ b/queue.c @@ -5,25 +5,23 @@ #include "harness.h" #include "queue.h" +/* Notice: sometimes, Cppcheck would find the potential NULL pointer bugs, + * but some of them cannot occur. You can suppress them by adding the + * following line. + * cppcheck-suppress nullPointer + */ + /* * Create empty queue. * Return NULL if could not allocate space. */ -queue_t *q_new() +struct list_head *q_new() { - queue_t *q = malloc(sizeof(queue_t)); - /* TODO: What if malloc returned NULL? */ - q->head = NULL; - return q; + return NULL; } /* Free all storage used by queue */ -void q_free(queue_t *q) -{ - /* TODO: How about freeing the list elements and the strings? */ - /* Free queue structure */ - free(q); -} +void q_free(struct list_head *l) {} /* * Attempt to insert element at head of queue. @@ -32,15 +30,8 @@ void q_free(queue_t *q) * Argument s points to the string to be stored. * The function must explicitly allocate space and copy the string into it. */ -bool q_insert_head(queue_t *q, char *s) +bool q_insert_head(struct list_head *head, char *s) { - list_ele_t *newh; - /* TODO: What should you do if the q is NULL? */ - newh = malloc(sizeof(list_ele_t)); - /* Don't forget to allocate space for the string and copy it */ - /* What if either call to malloc returns NULL? */ - newh->next = q->head; - q->head = newh; return true; } @@ -51,40 +42,92 @@ bool q_insert_head(queue_t *q, char *s) * Argument s points to the string to be stored. * The function must explicitly allocate space and copy the string into it. */ -bool q_insert_tail(queue_t *q, char *s) +bool q_insert_tail(struct list_head *head, char *s) { - /* TODO: You need to write the complete code for this function */ - /* Remember: It should operate in O(1) time */ - /* TODO: Remove the above comment when you are about to implement. */ - return false; + return true; } /* * Attempt to remove element from head of queue. - * Return true if successful. - * Return false if queue is NULL or empty. + * Return target element. + * Return NULL if queue is NULL or empty. * If sp is non-NULL and an element is removed, copy the removed string to *sp * (up to a maximum of bufsize-1 characters, plus a null terminator.) - * The space used by the list element and the string should be freed. + * + * NOTE: "remove" is different from "delete" + * The space used by the list element and the string should not be freed. + * The only thing "remove" need to do is unlink it. + * + * REF: + * https://english.stackexchange.com/questions/52508/difference-between-delete-and-remove */ -bool q_remove_head(queue_t *q, char *sp, size_t bufsize) +element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize) { - /* TODO: You need to fix up this code. */ - /* TODO: Remove the above comment when you are about to implement. */ - q->head = q->head->next; - return true; + return NULL; +} + +/* + * Attempt to remove element from tail of queue. + * Other attribute is as same as q_remove_head. + */ +element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize) +{ + return NULL; +} + +/* + * WARN: This is for external usage, don't modify it + * Attempt to release element. + */ +void q_release_element(element_t *e) +{ + free(e->value); + free(e); } /* * Return number of elements in queue. * Return 0 if q is NULL or empty */ -int q_size(queue_t *q) +int q_size(struct list_head *head) { - /* TODO: You need to write the code for this function */ - /* Remember: It should operate in O(1) time */ - /* TODO: Remove the above comment when you are about to implement. */ - return 0; + return -1; +} + +/* + * Delete the middle node in list. + * The middle node of a linked list of size n is the + * ⌊n / 2⌋th node from the start using 0-based indexing. + * If there're six element, the third member should be return. + * Return NULL if list is NULL or empty. + */ +bool q_delete_mid(struct list_head *head) +{ + // https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/ + return true; +} + +/* + * Delete all nodes that have duplicate string, + * leaving only distinct strings from the original list. + * Return true if successful. + * Return false if list is NULL. + * + * Note: this function always be called after sorting, in other words, + * list is guaranteed to be sorted in ascending order. + */ +bool q_delete_dup(struct list_head *head) +{ + // https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/ + return true; +} + +/* + * Attempt to swap every two adjacent nodes. + */ +void q_swap(struct list_head *head) +{ + // https://leetcode.com/problems/swap-nodes-in-pairs/ } /* @@ -94,19 +137,11 @@ int q_size(queue_t *q) * (e.g., by calling q_insert_head, q_insert_tail, or q_remove_head). * It should rearrange the existing ones. */ -void q_reverse(queue_t *q) -{ - /* TODO: You need to write the code for this function */ - /* TODO: Remove the above comment when you are about to implement. */ -} +void q_reverse(struct list_head *head) {} /* * Sort elements of queue in ascending order * No effect if q is NULL or empty. In addition, if q has only one * element, do nothing. */ -void q_sort(queue_t *q) -{ - /* TODO: You need to write the code for this function */ - /* TODO: Remove the above comment when you are about to implement. */ -} +void q_sort(struct list_head *head) {} diff --git a/queue.h b/queue.h index 3b48875af..5a578792b 100644 --- a/queue.h +++ b/queue.h @@ -5,31 +5,21 @@ * This program implements a queue supporting both FIFO and LIFO * operations. * - * It uses a singly-linked list to represent the set of queue elements + * It uses a circular doubly-linked list to represent the set of queue elements */ #include #include +#include "list.h" -/* Data structure declarations */ - -/* Linked list element (You shouldn't need to change this) */ -typedef struct ELE { +/* Linked list element */ +typedef struct { /* Pointer to array holding string. * This array needs to be explicitly allocated and freed */ char *value; - struct ELE *next; -} list_ele_t; - -/* Queue structure */ -typedef struct { - list_ele_t *head; /* Linked list of elements */ - /* TODO: You will need to add more fields to this structure - * to efficiently implement q_size and q_insert_tail. - */ - /* TODO: Remove the above comment when you are about to implement. */ -} queue_t; + struct list_head list; +} element_t; /* Operations on queue */ @@ -37,13 +27,13 @@ typedef struct { * Create empty queue. * Return NULL if could not allocate space. */ -queue_t *q_new(); +struct list_head *q_new(); /* * Free ALL storage used by queue. * No effect if q is NULL */ -void q_free(queue_t *q); +void q_free(struct list_head *head); /* * Attempt to insert element at head of queue. @@ -52,7 +42,7 @@ void q_free(queue_t *q); * Argument s points to the string to be stored. * The function must explicitly allocate space and copy the string into it. */ -bool q_insert_head(queue_t *q, char *s); +bool q_insert_head(struct list_head *head, char *s); /* * Attempt to insert element at tail of queue. @@ -61,23 +51,68 @@ bool q_insert_head(queue_t *q, char *s); * Argument s points to the string to be stored. * The function must explicitly allocate space and copy the string into it. */ -bool q_insert_tail(queue_t *q, char *s); +bool q_insert_tail(struct list_head *head, char *s); /* * Attempt to remove element from head of queue. - * Return true if successful. - * Return false if queue is NULL or empty. + * Return target element. + * Return NULL if queue is NULL or empty. * If sp is non-NULL and an element is removed, copy the removed string to *sp * (up to a maximum of bufsize-1 characters, plus a null terminator.) - * The space used by the list element and the string should be freed. + * + * NOTE: "remove" is different from "delete" + * The space used by the list element and the string should not be freed. + * The only thing "remove" need to do is unlink it. + * + * REF: + * https://english.stackexchange.com/questions/52508/difference-between-delete-and-remove + */ +element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize); + +/* + * Attempt to remove element from tail of queue. + * Other attribute is as same as q_remove_head. + */ +element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize); + +/* + * Attempt to release element. */ -bool q_remove_head(queue_t *q, char *sp, size_t bufsize); +void q_release_element(element_t *e); /* * Return number of elements in queue. * Return 0 if q is NULL or empty */ -int q_size(queue_t *q); +int q_size(struct list_head *head); + +/* + * Delete the middle node in list. + * The middle node of a linked list of size n is the + * ⌊n / 2⌋th node from the start using 0-based indexing. + * If there're six element, the third member should be return. + * Return NULL if lm is NULL or empty. + * + * Ref: https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/ + */ +bool q_delete_mid(struct list_head *head); + +/* + * Delete all nodes that have duplicate string, + * leaving only distinct strings from the original list. + * Return true if successful. + * Return false if list is NULL. + * + * Ref: https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/ + */ +bool q_delete_dup(struct list_head *head); + +/* + * Attempt to swap every two adjacent nodes. + * + * Ref: https://leetcode.com/problems/swap-nodes-in-pairs/ + */ +void q_swap(struct list_head *head); /* * Reverse elements in queue @@ -86,13 +121,13 @@ int q_size(queue_t *q); * (e.g., by calling q_insert_head, q_insert_tail, or q_remove_head). * It should rearrange the existing ones. */ -void q_reverse(queue_t *q); +void q_reverse(struct list_head *head); /* * Sort elements of queue in ascending order * No effect if q is NULL or empty. In addition, if q has only one * element, do nothing. */ -void q_sort(queue_t *q); +void q_sort(struct list_head *head); #endif /* LAB0_QUEUE_H */ diff --git a/random.c b/random.c index cd57066d5..9cb027a26 100644 --- a/random.c +++ b/random.c @@ -37,10 +37,3 @@ void randombytes(uint8_t *x, size_t how_much) xlen -= i; } } - -uint8_t randombit(void) -{ - uint8_t ret = 0; - randombytes(&ret, 1); - return (ret & 1); -} diff --git a/random.h b/random.h index d1aae5189..6bac953cd 100644 --- a/random.h +++ b/random.h @@ -3,7 +3,14 @@ #include #include + void randombytes(uint8_t *x, size_t xlen); -uint8_t randombit(void); + +static inline uint8_t randombit(void) +{ + uint8_t ret = 0; + randombytes(&ret, 1); + return ret & 1; +} #endif diff --git a/report.c b/report.c index 91c547d4f..b169e3ded 100644 --- a/report.c +++ b/report.c @@ -54,9 +54,9 @@ void report_event(message_t msg, char *fmt, ...) { va_list ap; bool fatal = msg == MSG_FATAL; - char *msg_name = msg == MSG_WARN - ? "WARNING" - : msg == MSG_ERROR ? "ERROR" : "FATAL ERROR"; + char *msg_name = msg == MSG_WARN ? "WARNING" + : msg == MSG_ERROR ? "ERROR" + : "FATAL ERROR"; int level = msg == MSG_WARN ? 2 : msg == MSG_ERROR ? 1 : 0; if (verblevel < level) return; diff --git a/scripts/aspell-pws b/scripts/aspell-pws index 394c236e2..fe4bc7ec7 100644 --- a/scripts/aspell-pws +++ b/scripts/aspell-pws @@ -135,3 +135,9 @@ vim lldb utf unicode +checksum +sha +shasum +verifier +CI +foreach diff --git a/scripts/checksums b/scripts/checksums new file mode 100644 index 000000000..b8ae17c10 --- /dev/null +++ b/scripts/checksums @@ -0,0 +1,2 @@ +dba62ae6590328e0f22912593ed6c1e3264ec901 queue.h +0ab0e0224f33940770e9e6297da833073004aebf list.h diff --git a/scripts/driver.py b/scripts/driver.py index f0abc2c06..8e3979f87 100755 --- a/scripts/driver.py +++ b/scripts/driver.py @@ -24,14 +24,14 @@ class Tracer: 3: "trace-03-ops", 4: "trace-04-ops", 5: "trace-05-ops", - 6: "trace-06-string", - 7: "trace-07-robust", + 6: "trace-06-ops", + 7: "trace-07-string", 8: "trace-08-robust", 9: "trace-09-robust", - 10: "trace-10-malloc", + 10: "trace-10-robust", 11: "trace-11-malloc", 12: "trace-12-malloc", - 13: "trace-13-perf", + 13: "trace-13-malloc", 14: "trace-14-perf", 15: "trace-15-perf", 16: "trace-16-perf", @@ -58,7 +58,7 @@ class Tracer: 17: "Trace-17" } - maxScores = [0, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5] + maxScores = [0, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5] RED = '\033[91m' GREEN = '\033[92m' @@ -89,7 +89,7 @@ def runTrace(self, tid): fname = "%s/%s.cmd" % (self.traceDirectory, self.traceDict[tid]) vname = "%d" % self.verbLevel clist = self.command + ["-v", vname, "-f", fname] - + try: retcode = subprocess.call(clist) except Exception as e: diff --git a/scripts/pre-commit.hook b/scripts/pre-commit.hook index 4b2ba1d56..20a08f4af 100755 --- a/scripts/pre-commit.hook +++ b/scripts/pre-commit.hook @@ -1,6 +1,9 @@ #!/usr/bin/env bash -CPPCHECK_suppresses="--inline-suppr harness.c --suppress=unmatchedSuppression:harness.c --suppress=missingIncludeSystem --suppress=unusedFunction:linenoise.c --suppress=variableScope:linenoise.c --suppress=nullPointerRedundantCheck:report.c" +CPPCHECK_suppresses="--inline-suppr harness.c --suppress=unmatchedSuppression:harness.c --suppress=missingIncludeSystem \ +--suppress=unusedFunction:linenoise.c --suppress=variableScope:linenoise.c \ +--suppress=nullPointerRedundantCheck:report.c \ +--suppress=nullPointer:qtest.c" CPPCHECK_OPTS="-I. --enable=all --error-exitcode=1 --force $CPPCHECK_suppresses ." RETURN=0 @@ -40,6 +43,11 @@ if [ $? -ne 0 ]; then DIFF=diff fi +SHA1SUM=$(which sha1sum) +if [ $? -ne 0 ]; then + SHA1SUM=shasum +fi + FILES=`git diff --cached --name-only --diff-filter=ACMR | grep -E "\.(c|cpp|h)$"` for FILE in $FILES; do nf=`git checkout-index --temp $FILE | cut -f 1` @@ -72,6 +80,12 @@ if [ ! -z "${FILES[*]}" ]; then echo "${FILES[*]}" fi +$SHA1SUM -c scripts/checksums +if [ $? -ne 0 ]; then + echo "[!] You are not allowed to change the header file queue.h or list.h" >&2 + exit 1 +fi + # Prevent unsafe functions root=$(git rev-parse --show-toplevel) banned="([^f]gets\()|(sprintf\()|(strcpy\()" diff --git a/scripts/pre-push.hook b/scripts/pre-push.hook index dd3f12549..c6d3d4941 100755 --- a/scripts/pre-push.hook +++ b/scripts/pre-push.hook @@ -23,7 +23,7 @@ fi # Show hints echo -e "${YELLOW}Hint${NC}: You might want to know why Git is always ${GREEN}asking for my password${NC}." -echo -e " https://help.github.com/en/github/using-git/why-is-git-always-asking-for-my-password" +echo -e " https://docs.github.com/en/get-started/getting-started-with-git/why-is-git-always-asking-for-my-password" echo "" # only run this if you are pushing to master diff --git a/traces/trace-02-ops.cmd b/traces/trace-02-ops.cmd index 41ec44e7b..5ef39385c 100644 --- a/traces/trace-02-ops.cmd +++ b/traces/trace-02-ops.cmd @@ -1,4 +1,4 @@ -# Test of insert_head, insert_tail, and remove_head +# Test of insert_head, insert_tail, remove_head, remove_tail, and delete_mid option fail 0 option malloc 0 new @@ -8,9 +8,13 @@ ih dolphin it meerkat it bear it gerbil +it tiger +rt tiger +dm +dm +it meerkat rh dolphin rh bear -rh gerbil -rh meerkat rh bear rh gerbil +rh meerkat diff --git a/traces/trace-04-ops.cmd b/traces/trace-04-ops.cmd index b6911b33b..aec444420 100644 --- a/traces/trace-04-ops.cmd +++ b/traces/trace-04-ops.cmd @@ -1,10 +1,13 @@ -# Test of insert_head, insert_tail, size, and sort +# Test of insert_head, insert_tail, size, swap, and sort option fail 0 option malloc 0 new ih gerbil ih bear ih dolphin +swap +rh bear +ih bear size it meerkat it bear diff --git a/traces/trace-05-ops.cmd b/traces/trace-05-ops.cmd index 19085e920..63f56ef19 100644 --- a/traces/trace-05-ops.cmd +++ b/traces/trace-05-ops.cmd @@ -1,4 +1,4 @@ -# Test of insert_head, insert_tail, remove_head, reverse, size, and sort +# Test of insert_head, insert_tail, remove_head, reverse, size, swap, and sort option fail 0 option malloc 0 new @@ -16,13 +16,13 @@ reverse size sort it fish +swap reverse -rh fish rh meerkat -reverse -rh bear -rh bear +rh fish rh gerbil rh gerbil +rh bear +rh bear size free diff --git a/traces/trace-06-ops.cmd b/traces/trace-06-ops.cmd new file mode 100644 index 000000000..b7f9f4f88 --- /dev/null +++ b/traces/trace-06-ops.cmd @@ -0,0 +1,8 @@ +# Test of insert_head, insert_tail, delete duplicate, and sort +new +ih RAND 4 +it gerbil 3 +it lion 2 +it zebra 2 +sort +dedup \ No newline at end of file diff --git a/traces/trace-13-perf.cmd b/traces/trace-13-perf.cmd deleted file mode 100644 index abd926d37..000000000 --- a/traces/trace-13-perf.cmd +++ /dev/null @@ -1,8 +0,0 @@ -# Test performance of insert_tail -option fail 0 -option malloc 0 -new -ih dolphin 1000000 -it gerbil 1000 -reverse -it jaguar 1000 diff --git a/traces/trace-14-perf.cmd b/traces/trace-14-perf.cmd index ce0fe458c..32dcbee56 100644 --- a/traces/trace-14-perf.cmd +++ b/traces/trace-14-perf.cmd @@ -1,7 +1,9 @@ -# Test performance of size +# Test performance of insert_tail, reverse, and sort option fail 0 option malloc 0 new ih dolphin 1000000 -size 1000 +it gerbil 1000000 +reverse +sort diff --git a/traces/trace-15-perf.cmd b/traces/trace-15-perf.cmd index 56985c58d..88492b32f 100644 --- a/traces/trace-15-perf.cmd +++ b/traces/trace-15-perf.cmd @@ -1,11 +1,24 @@ -# Test performance of insert_tail, size, reverse, and sort +# Test performance of sort with random and descending orders +# 10000: all correct sorting algorithms are expected pass +# 50000: sorting algorithms with O(n^2) time complexity are expected failed +# 100000: sorting algorithms with O(nlogn) time complexity are expected pass option fail 0 option malloc 0 new -ih dolphin 1000000 -it gerbil 1000000 -size 1000 +ih RAND 10000 +sort +reverse +sort +free +new +ih RAND 50000 +sort +reverse +sort +free +new +ih RAND 100000 +sort reverse sort -size 1000 - +free diff --git a/traces/trace-16-perf.cmd b/traces/trace-16-perf.cmd index 88492b32f..abd926d37 100644 --- a/traces/trace-16-perf.cmd +++ b/traces/trace-16-perf.cmd @@ -1,24 +1,8 @@ -# Test performance of sort with random and descending orders -# 10000: all correct sorting algorithms are expected pass -# 50000: sorting algorithms with O(n^2) time complexity are expected failed -# 100000: sorting algorithms with O(nlogn) time complexity are expected pass +# Test performance of insert_tail option fail 0 option malloc 0 new -ih RAND 10000 -sort +ih dolphin 1000000 +it gerbil 1000 reverse -sort -free -new -ih RAND 50000 -sort -reverse -sort -free -new -ih RAND 100000 -sort -reverse -sort -free +it jaguar 1000 diff --git a/traces/trace-17-complexity.cmd b/traces/trace-17-complexity.cmd index ee02b5cca..6a68c9241 100644 --- a/traces/trace-17-complexity.cmd +++ b/traces/trace-17-complexity.cmd @@ -1,5 +1,7 @@ -# Test if q_insert_tail and q_size is constant time complexity +# Test if time complexity of q_insert_tail, q_insert_head, q_remove_tail, and q_remove_head is constant option simulation 1 it -size +ih +rh +rt option simulation 0