From 454a261c95bed283e33b30a2b80dd01f7cd6bd7e Mon Sep 17 00:00:00 2001 From: yrutschle Date: Wed, 30 Mar 2022 22:56:41 +0200 Subject: [PATCH] initial hash object with testing framework --- hash.c | 259 +++++++++++++++++++++++ hash.h | 16 ++ hashtest/Makefile | 6 + hashtest/delete.tst | 8 + hashtest/delete.tst.ref | 34 +++ hashtest/delete_at_end.tst | 9 + hashtest/delete_at_end.tst.ref | 34 +++ hashtest/delete_below_floor.tst | 9 + hashtest/delete_below_floor.tst.ref | 34 +++ hashtest/delete_discont.tst | 10 + hashtest/delete_discont.tst.ref | 34 +++ hashtest/delete_empty.tst | 11 + hashtest/delete_empty.tst.ref | 34 +++ hashtest/delete_full.tst | 39 ++++ hashtest/delete_full.tst.ref | 34 +++ hashtest/delete_middle.tst | 10 + hashtest/delete_middle.tst.ref | 34 +++ hashtest/delete_wrap.tst | 8 + hashtest/delete_wrap.tst.ref | 34 +++ hashtest/delete_wrap_at_end.tst | 10 + hashtest/delete_wrap_at_end.tst.ref | 34 +++ hashtest/delete_wrap_below_floor.tst | 8 + hashtest/delete_wrap_below_floor.tst.ref | 34 +++ hashtest/delete_wrap_discont.tst | 11 + hashtest/delete_wrap_discont.tst.ref | 34 +++ hashtest/htest | Bin 0 -> 25216 bytes hashtest/htest.c | 105 +++++++++ hashtest/insert.tst | 6 + hashtest/insert.tst.ref | 34 +++ hashtest/insert_discont.tst | 8 + hashtest/insert_discont.tst.ref | 34 +++ hashtest/insert_full.tst | 40 ++++ hashtest/insert_full.tst.ref | 34 +++ hashtest/insert_full_floor.tst | 7 + hashtest/insert_full_floor.tst.ref | 34 +++ hashtest/insert_wrap.tst | 7 + hashtest/insert_wrap.tst.ref | 34 +++ hashtest/mkrand.pl | 41 ++++ hashtest/run | 30 +++ 39 files changed, 1202 insertions(+) create mode 100644 hash.c create mode 100644 hash.h create mode 100644 hashtest/Makefile create mode 100644 hashtest/delete.tst create mode 100644 hashtest/delete.tst.ref create mode 100644 hashtest/delete_at_end.tst create mode 100644 hashtest/delete_at_end.tst.ref create mode 100644 hashtest/delete_below_floor.tst create mode 100644 hashtest/delete_below_floor.tst.ref create mode 100644 hashtest/delete_discont.tst create mode 100644 hashtest/delete_discont.tst.ref create mode 100644 hashtest/delete_empty.tst create mode 100644 hashtest/delete_empty.tst.ref create mode 100644 hashtest/delete_full.tst create mode 100644 hashtest/delete_full.tst.ref create mode 100644 hashtest/delete_middle.tst create mode 100644 hashtest/delete_middle.tst.ref create mode 100644 hashtest/delete_wrap.tst create mode 100644 hashtest/delete_wrap.tst.ref create mode 100644 hashtest/delete_wrap_at_end.tst create mode 100644 hashtest/delete_wrap_at_end.tst.ref create mode 100644 hashtest/delete_wrap_below_floor.tst create mode 100644 hashtest/delete_wrap_below_floor.tst.ref create mode 100644 hashtest/delete_wrap_discont.tst create mode 100644 hashtest/delete_wrap_discont.tst.ref create mode 100755 hashtest/htest create mode 100644 hashtest/htest.c create mode 100644 hashtest/insert.tst create mode 100644 hashtest/insert.tst.ref create mode 100644 hashtest/insert_discont.tst create mode 100644 hashtest/insert_discont.tst.ref create mode 100644 hashtest/insert_full.tst create mode 100644 hashtest/insert_full.tst.ref create mode 100644 hashtest/insert_full_floor.tst create mode 100644 hashtest/insert_full_floor.tst.ref create mode 100644 hashtest/insert_wrap.tst create mode 100644 hashtest/insert_wrap.tst.ref create mode 100755 hashtest/mkrand.pl create mode 100755 hashtest/run diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..0b5db47 --- /dev/null +++ b/hash.c @@ -0,0 +1,259 @@ +/* + * a fixed-sized hash + * +# Copyright (C) 2022 Yves Rutschle +# +# This program is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more +# details. +# +# The full text for the General Public License is here: +# http://www.gnu.org/licenses/gpl.html +# +# */ + + +/* * The hash is open-addressing, linear search, robin-hood insertion, with + * backward shift deletion and moving floor. + * https://codecapsule.com/2013/11/11/robin-hood-hashing/ + * https://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ + * This means items are reordered upon insertion and deletion, and the hash + * is well-ordered at all times with no tombstones. + * + * Items that 'wrap' around push the 'floor' up. Searching for low items will + * therefore start from the floor up. + * + * Each pointer is either: + * - to a connection struct + * - FREE (NULL) if not allocated + * + * */ + +#include +#include + +#include "gap.h" +#include "hash.h" + +static const int h_keylen = 5; /* How many bits in the hash key? (hash is 2^h_keylen big) */ +static const int hash_size = (1 << h_keylen); /* 8 => 256 */ +static void* const FREE = NULL; + +struct hash { + int item_cnt; /* Number of items in the hash */ + int floor; /* Where is the highest key. Or the lowest insert point */ + gap_array* data; + + hash_make_key_fn hash_make_key; + hash_cmp_item_fn cmp_item; +}; + +typedef struct hash hash; + + +hash* hash_init(hash_make_key_fn make_key, hash_cmp_item_fn cmp_item) +{ + hash* h = malloc(sizeof(*h)); + if (!h) return NULL; + + h->item_cnt = 0; + h->floor = 0; + h->data = gap_init(hash_size); + h->hash_make_key = make_key; + h->cmp_item = cmp_item; + + return h; +} + +/* Return the index following i in h */ +static int hash_next_index(hash* h, int i) +{ + return (i + 1) % hash_size; +} + +/* Returns the index in h of specified address, -1 if not found + * item is an item object that must return the target wanted index and for + * which comparison with the searched object will succeed. + * */ +int hash_find_index(hash* h, hash_item item) +{ + hash_item cnx; + int index = h->hash_make_key(item); + int cnt = 0; + + if (index < h->floor) index = h->floor; + + cnx = gap_get(h->data, index); +#ifdef DEBUG + fprintf(stderr, "searching %d\n", index); +#endif + while (cnx != FREE) { + if (cnt++ > hash_size) return -1; + + if (!h->cmp_item(cnx, item)) + break; + + index = hash_next_index(h, index); + cnx = gap_get(h->data, index); +#ifdef DEBUG + fprintf(stderr, "searching %d\n", index); +#endif + } + if (cnx == FREE) return -1; + return index; +} + +/* says if we should swap the bubble (element that's going up) and the current + * indexed element. + * index: current insertion index + * wanted_index: index wanted by the element at the current index + * bubble: bubble wanted index + * floor: index of lowest wanted index (or index of the highest wrapped wanted + * index) + * wrapped: whether we wrapped or not (if we reach the floor from below after + * wrapping, we should swap as it's a 'highest' element pushing the floor up. + * it we're inserting a higher-than bottom element at the floor, it should + * bubble up). + */ +static int i_should_swap(int index, int wanted_index, int bubble_index, int floor, int wrapped) +{ + int res; + + if ((index == floor)) + res = wrapped; + else + res = bubble_index < wanted_index; + +#if DEBUG + fprintf(stderr, "i_should_swap(%d, %d, %d, %d) = %d\n", index, wanted_index, bubble_index, floor, res); +#endif + + return res; +} + +int hash_insert(hash* h, hash_item new) +{ + int bubble_wanted_index = h->hash_make_key(new); + int index = bubble_wanted_index; + gap_array* hash = h->data; + + if (h->item_cnt == hash_size) + return -1; + + if (index < h->floor) index = h->floor; + + hash_item curr_item = gap_get(hash, index); + int orig_floor = h->floor; + int wrapped = 0; + while (1) { + if (curr_item == NULL) { +#if DEBUG + fprintf(stderr, "final insert at %d\n", index); +#endif + gap_set(hash, index, new); + h->item_cnt++; + return 0; + } + + int curr_wanted_index = h->hash_make_key(curr_item); + + if (i_should_swap(index, curr_wanted_index, bubble_wanted_index, orig_floor, wrapped)) { + gap_set(h->data, index, new); +#if DEBUG + fprintf(stderr, "intermediate insert [%s] at %d\n", &new->client_addr, index); +#endif + new = curr_item; + bubble_wanted_index = curr_wanted_index; + } + + index = hash_next_index(h, index); + curr_item = gap_get(hash, index); + + if (index == 0) h->floor++; + if (index == 0) wrapped = 1; + } + + return 0; +} + + + + +static int next_in_right_place(hash* h, hash_item item, int index) +{ + if (!item) return 0; + int wanted_index = h->hash_make_key(item); + return (wanted_index == index); +} + +/* Remove cnx from the hash */ +int hash_remove(hash* h, hash_item item) +{ + gap_array* hash = h->data; + + int index = hash_find_index(h, item); + if (index == -1) return -1; /* Tried to remove something that isn't there */ + + int lower_floor = 0; /* If we remove something below the floor, we'll need to lower it */ + while (1) { + if (index < h->floor) lower_floor = 1; + int next_index = hash_next_index(h, index); + hash_item next = gap_get(h->data, next_index); + if ((next == FREE) || next_in_right_place(h, next, next_index)) { + h->item_cnt--; + if (lower_floor) h->floor--; + gap_set(hash, index, FREE); + return 0; + } + + gap_set(hash, index, next); + + index = hash_next_index(h, index);; +#if DEBUG + fprintf(stderr, "index %d floor %d\n", index, h->floor); +#endif + } + return 0; +} + + +#include +#include +#define STR_LENGTH 16 +struct hash_item { + int wanted_index; + char str[STR_LENGTH]; +}; +void hash_dump(hash* h, char* filename) +{ + char str[STR_LENGTH]; + FILE* out = fopen(filename, "w"); + + if (!out) { + perror(filename); + exit(1); + } + + fprintf(out, "\n", h->floor, h->item_cnt); + for (int i = 0; i < hash_size; i++) { + hash_item item = gap_get(h->data, i); + int idx = 0; + + memset(str, 0, STR_LENGTH); + if (item) { + idx = h->hash_make_key(item); + memcpy(str, item->str, STR_LENGTH); + } + fprintf(out, "\t%d:%d:%s\n", i, idx, str); + } + fprintf(out, "\n"); + fclose(out); +} diff --git a/hash.h b/hash.h new file mode 100644 index 0000000..49a747b --- /dev/null +++ b/hash.h @@ -0,0 +1,16 @@ + + +typedef struct hash hash; +typedef struct hash_item* hash_item; +typedef int (*hash_make_key_fn)(hash_item item); + +/* Function that compares two items: returns 0 if they are the same */ +typedef int (*hash_cmp_item_fn)(hash_item item1, hash_item item2); + +hash* hash_init(hash_make_key_fn make_key, hash_cmp_item_fn cmp_item); +int hash_find_index(hash* h, hash_item item); +int hash_insert(hash* h, hash_item new); +int hash_remove(hash* h, hash_item item); + + +void hash_dump(hash* h, char* filename); /* For development only */ diff --git a/hashtest/Makefile b/hashtest/Makefile new file mode 100644 index 0000000..49e961e --- /dev/null +++ b/hashtest/Makefile @@ -0,0 +1,6 @@ + +OBJ=../hash.o ../gap.o htest.o + +htest: $(OBJ) + $(CC) -o htest $(OBJ) + diff --git a/hashtest/delete.tst b/hashtest/delete.tst new file mode 100644 index 0000000..5cee269 --- /dev/null +++ b/hashtest/delete.tst @@ -0,0 +1,8 @@ +# Basic delete +a 10 aa +a 10 ab +a 10 ac +a 20 ba +a 21 bb + +d 21 bb diff --git a/hashtest/delete.tst.ref b/hashtest/delete.tst.ref new file mode 100644 index 0000000..a95a3dc --- /dev/null +++ b/hashtest/delete.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:10:aa + 11:10:ab + 12:10:ac + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:20:ba + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_at_end.tst b/hashtest/delete_at_end.tst new file mode 100644 index 0000000..e89ec12 --- /dev/null +++ b/hashtest/delete_at_end.tst @@ -0,0 +1,9 @@ +# Delete inside a block with nothing after + +a 10 aa +a 10 ab +a 12 ac +a 13 ad +a 14 ae + +d 14 ae diff --git a/hashtest/delete_at_end.tst.ref b/hashtest/delete_at_end.tst.ref new file mode 100644 index 0000000..b883c40 --- /dev/null +++ b/hashtest/delete_at_end.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:10:aa + 11:10:ab + 12:12:ac + 13:13:ad + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_below_floor.tst b/hashtest/delete_below_floor.tst new file mode 100644 index 0000000..432b5b9 --- /dev/null +++ b/hashtest/delete_below_floor.tst @@ -0,0 +1,9 @@ +# wrap-around and delete below floor +a 2 ba +a 30 aa +a 30 ab +a 30 ac +a 30 ad +a 2 bb + +d 30 ab diff --git a/hashtest/delete_below_floor.tst.ref b/hashtest/delete_below_floor.tst.ref new file mode 100644 index 0000000..e20cf3a --- /dev/null +++ b/hashtest/delete_below_floor.tst.ref @@ -0,0 +1,34 @@ + + 0:30:ad + 1:0: + 2:2:ba + 3:2:bb + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ac + diff --git a/hashtest/delete_discont.tst b/hashtest/delete_discont.tst new file mode 100644 index 0000000..f27d8dc --- /dev/null +++ b/hashtest/delete_discont.tst @@ -0,0 +1,10 @@ +# delete in a discontinuous block + +a 10 aa +a 11 ab +a 12 ac +a 14 ad +a 10 bc + +d 11 ab + diff --git a/hashtest/delete_discont.tst.ref b/hashtest/delete_discont.tst.ref new file mode 100644 index 0000000..07230ec --- /dev/null +++ b/hashtest/delete_discont.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:10:aa + 11:10:bc + 12:12:ac + 13:0: + 14:14:ad + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_empty.tst b/hashtest/delete_empty.tst new file mode 100644 index 0000000..ce29d4d --- /dev/null +++ b/hashtest/delete_empty.tst @@ -0,0 +1,11 @@ +# Delete an unexisting element. And on an empty hash + +a 10 aa + +d 10 ab +d 12 bc + +# Empty for real +d 10 aa + +d 10 aa diff --git a/hashtest/delete_empty.tst.ref b/hashtest/delete_empty.tst.ref new file mode 100644 index 0000000..e51b8b1 --- /dev/null +++ b/hashtest/delete_empty.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_full.tst b/hashtest/delete_full.tst new file mode 100644 index 0000000..f950d38 --- /dev/null +++ b/hashtest/delete_full.tst @@ -0,0 +1,39 @@ +# delete on a full hash + +# First, fill the hash :-) + +a 0 aa +a 1 ab +a 2 ac +a 3 ad +a 4 ae +a 5 af +a 6 ag +a 7 ah +a 8 ai +a 9 af +a 10 ba +a 11 bb +a 12 bc +a 13 bd +a 14 be +a 15 bf +a 16 bg +a 17 bh +a 18 bi +a 19 bj +a 20 ca +a 21 cb +a 22 cd +a 23 ce +a 24 cf +a 25 cg +a 26 ch +a 27 ci +a 28 cj +a 29 ck +a 30 da +a 31 db + + +d 21 cb diff --git a/hashtest/delete_full.tst.ref b/hashtest/delete_full.tst.ref new file mode 100644 index 0000000..7dd0dea --- /dev/null +++ b/hashtest/delete_full.tst.ref @@ -0,0 +1,34 @@ + + 0:0:aa + 1:1:ab + 2:2:ac + 3:3:ad + 4:4:ae + 5:5:af + 6:6:ag + 7:7:ah + 8:8:ai + 9:9:af + 10:10:ba + 11:11:bb + 12:12:bc + 13:13:bd + 14:14:be + 15:15:bf + 16:16:bg + 17:17:bh + 18:18:bi + 19:19:bj + 20:20:ca + 21:0: + 22:22:cd + 23:23:ce + 24:24:cf + 25:25:cg + 26:26:ch + 27:27:ci + 28:28:cj + 29:29:ck + 30:30:da + 31:31:db + diff --git a/hashtest/delete_middle.tst b/hashtest/delete_middle.tst new file mode 100644 index 0000000..29b0771 --- /dev/null +++ b/hashtest/delete_middle.tst @@ -0,0 +1,10 @@ +# Delete inside a block with something discontinuous + +a 10 aa +a 10 ab +a 12 ac +a 13 ad +a 14 ae + +# ab shifts, ac and next doesn't +d 10 aa diff --git a/hashtest/delete_middle.tst.ref b/hashtest/delete_middle.tst.ref new file mode 100644 index 0000000..9484187 --- /dev/null +++ b/hashtest/delete_middle.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:10:ab + 11:0: + 12:12:ac + 13:13:ad + 14:14:ae + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_wrap.tst b/hashtest/delete_wrap.tst new file mode 100644 index 0000000..cea8800 --- /dev/null +++ b/hashtest/delete_wrap.tst @@ -0,0 +1,8 @@ +# Basic delete when wrapping, between wrap and floor +a 30 aa +a 30 ab +a 30 ac +a 30 ba +a 30 bb + +d 30 ac diff --git a/hashtest/delete_wrap.tst.ref b/hashtest/delete_wrap.tst.ref new file mode 100644 index 0000000..8cddd01 --- /dev/null +++ b/hashtest/delete_wrap.tst.ref @@ -0,0 +1,34 @@ + + 0:30:ba + 1:30:bb + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ab + diff --git a/hashtest/delete_wrap_at_end.tst b/hashtest/delete_wrap_at_end.tst new file mode 100644 index 0000000..e2b1ce1 --- /dev/null +++ b/hashtest/delete_wrap_at_end.tst @@ -0,0 +1,10 @@ +# Delete inside a block with wrapping, with something after + +a 30 aa +a 30 ab +a 30 ac +a 1 ad +a 3 ae + +# shift ad but not ae +d 14 ae diff --git a/hashtest/delete_wrap_at_end.tst.ref b/hashtest/delete_wrap_at_end.tst.ref new file mode 100644 index 0000000..73ea879 --- /dev/null +++ b/hashtest/delete_wrap_at_end.tst.ref @@ -0,0 +1,34 @@ + + 0:30:ac + 1:1:ad + 2:0: + 3:3:ae + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ab + diff --git a/hashtest/delete_wrap_below_floor.tst b/hashtest/delete_wrap_below_floor.tst new file mode 100644 index 0000000..7fa5038 --- /dev/null +++ b/hashtest/delete_wrap_below_floor.tst @@ -0,0 +1,8 @@ +# delete before wrap +a 30 aa +a 30 ab +a 30 ac +a 30 ad + +# shift ac and ad +d 30 ab diff --git a/hashtest/delete_wrap_below_floor.tst.ref b/hashtest/delete_wrap_below_floor.tst.ref new file mode 100644 index 0000000..e94683e --- /dev/null +++ b/hashtest/delete_wrap_below_floor.tst.ref @@ -0,0 +1,34 @@ + + 0:30:ad + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ac + diff --git a/hashtest/delete_wrap_discont.tst b/hashtest/delete_wrap_discont.tst new file mode 100644 index 0000000..1c49746 --- /dev/null +++ b/hashtest/delete_wrap_discont.tst @@ -0,0 +1,11 @@ +# Delete with wrapping in discontinuous group + +a 30 aa +a 30 ab +a 30 ac +a 31 ad +a 2 ba +a 3 bb + +# shift ac and ad but not ba and bb +d 30 ab diff --git a/hashtest/delete_wrap_discont.tst.ref b/hashtest/delete_wrap_discont.tst.ref new file mode 100644 index 0000000..83873da --- /dev/null +++ b/hashtest/delete_wrap_discont.tst.ref @@ -0,0 +1,34 @@ + + 0:31:ad + 1:0: + 2:2:ba + 3:3:bb + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ac + diff --git a/hashtest/htest b/hashtest/htest new file mode 100755 index 0000000000000000000000000000000000000000..55dc1c826c084febd1827f72a982837d2aa404a2 GIT binary patch literal 25216 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 z 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 literal 0 HcmV?d00001 diff --git a/hashtest/htest.c b/hashtest/htest.c new file mode 100644 index 0000000..a61da13 --- /dev/null +++ b/hashtest/htest.c @@ -0,0 +1,105 @@ +/* Wee testing program from the hash code: + * htest