From fae56fd9afb0fe9ab90ea041fcc7b277ae377bad Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Tue, 29 Oct 2013 22:22:40 +0100 Subject: [PATCH] Generates combinations at runtime. --- Makefile.am | 7 +- check.c | 99 ++++++++--------- combo.h | 90 ++++++++++++++++ combos.h | 111 ------------------- mkcombos.c | 98 ----------------- selftest.c | 298 +++++++++++++--------------------------------------- 6 files changed, 217 insertions(+), 486 deletions(-) create mode 100644 combo.h delete mode 100644 combos.h delete mode 100644 mkcombos.c diff --git a/Makefile.am b/Makefile.am index 4e1b66f4..4c7c9151 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ bin_PROGRAMS = snapraid check_PROGRAMS = mktest -EXTRA_PROGRAMS = mktables mkcombos +EXTRA_PROGRAMS = mktables snapraid_SOURCES = \ snapraid.c \ @@ -31,9 +31,6 @@ snapraid_SOURCES = \ mktables_SOURCES = \ mktables.c -mkcombos_SOURCES = \ - mkcombos.c - noinst_HEADERS = \ portable.h \ snapraid.h \ @@ -63,8 +60,8 @@ noinst_HEADERS = \ fnmatch.h \ cpu.h \ raid.h \ + combo.h \ tables.h \ - combos.h \ import.h \ mingw.h \ unix.h diff --git a/check.c b/check.c index 865099e3..d2d4ba58 100644 --- a/check.c +++ b/check.c @@ -24,7 +24,7 @@ #include "parity.h" #include "handle.h" #include "raid.h" -#include "combos.h" +#include "combo.h" /****************************************************************************/ /* check */ @@ -144,10 +144,11 @@ static int is_parity_matching(struct snapraid_state* state, unsigned diskmax, un */ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, unsigned char** buffer, unsigned char** buffer_recov, unsigned char* buffer_zero) { - unsigned i, j; + unsigned i, n; int error; int has_hash; int d[LEV_MAX]; + int p[LEV_MAX]; /* no fix required */ if (failed_count == 0) { @@ -155,9 +156,10 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u raid_gen(state->level, buffer, diskmax, state->block_size); return 0; } - + /* if too many error, we don't have any strategy */ - if (failed_count > state->level) { + n = state->level; + if (failed_count > n) { return -1; } @@ -170,71 +172,74 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u /* check if there is at least a failed block that can be checked for correctness using the hash */ /* if there isn't, we have to sacrifice a parity block to check that the result is correct */ has_hash = 0; - for(j=0;j=0;++i) { - int* c = combo[l-1][i]; + /* if we don't have a hash, but we have an extra parity */ + /* (strictly-less failures than number of parities) */ + if (!has_hash && failed_count < n) { + /* number of parity to use, one more to check the recovering */ + unsigned r = failed_count + 1; - /* if parity is missing, do nothing */ - for(j=0;jblock_size); - } + /* copy the parities to use, one less because the last is used for checking */ + for(i=0;iblock_size); - /* recover using one less parity */ - raid_recov(l-1, d, c, buffer, diskmax, buffer_zero, state->block_size); + /* recover using one less parity, the p[r-1] one */ + raid_recov(r-1, d, p, buffer, diskmax, buffer_zero, state->block_size); - /* use the remaining parity to check the result */ - if (is_parity_matching(state, diskmax, c[l-1], buffer, buffer_recov)) + /* use the remaining p[r-1] parity to check the result */ + if (is_parity_matching(state, diskmax, p[r-1], buffer, buffer_recov)) return 0; /* log */ fprintf(stdlog, "parity_error:%u:", pos); - for(j=0;j=0;++i) { - int* c = combo[l-1][i]; - - /* if parity is missing, do nothing */ - for(j=0;jblock_size); - } + /* copy the parities to use */ + for(i=0;iblock_size); /* recover */ - raid_recov(l, d, c, buffer, diskmax, buffer_zero, state->block_size); + raid_recov(r, d, p, buffer, diskmax, buffer_zero, state->block_size); /* use the hash to check the result */ if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero)) @@ -242,14 +247,14 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u /* log */ fprintf(stdlog, "parity_error:%u:", pos); - for(j=0;j. + */ + +#ifndef __COMBO_H +#define __COMBO_H + +#include + +/** + * Get the first combination of r of n elements. + * + * Typical use is with combination_next() in the form : + * + * int i[R]; + * combination_first(R, N, i); + * do { + * code using i[0], i[1], ..., i[R-1] + * } while (combination_next(R, N, i)); + * + * It's equivalent at the code : + * + * for(i[0]=0;i[0]= h) { + + /* if we are at the first level, we have finished */ + if (i == 0) + return 0; + + /* increase the previous position */ + --i; + --h; + goto recurse; + } + + ++i; + + /* initialize all the next positions, if any */ + while (i < r) { + c[i] = c[i-1] + 1; /* each position start at the next value of the previous one */ + ++i; + } + + return 1; +} +#endif diff --git a/combos.h b/combos.h deleted file mode 100644 index 88853915..00000000 --- a/combos.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2013 Andrea Mazzoleni - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** - * Combinations of 6 elements. - * Ends is marked with first element as -1. - */ -static int combo[][32][8] = { - /* combinations of 1 of 6 elements */ - { - { 0 }, - { 1 }, - { 2 }, - { 3 }, - { 4 }, - { 5 }, - { -1 } - }, - /* combinations of 2 of 6 elements */ - { - { 0, 1 }, - { 0, 2 }, - { 0, 3 }, - { 0, 4 }, - { 0, 5 }, - { 1, 2 }, - { 1, 3 }, - { 1, 4 }, - { 1, 5 }, - { 2, 3 }, - { 2, 4 }, - { 2, 5 }, - { 3, 4 }, - { 3, 5 }, - { 4, 5 }, - { -1 } - }, - /* combinations of 3 of 6 elements */ - { - { 0, 1, 2 }, - { 0, 1, 3 }, - { 0, 1, 4 }, - { 0, 1, 5 }, - { 0, 2, 3 }, - { 0, 2, 4 }, - { 0, 2, 5 }, - { 0, 3, 4 }, - { 0, 3, 5 }, - { 0, 4, 5 }, - { 1, 2, 3 }, - { 1, 2, 4 }, - { 1, 2, 5 }, - { 1, 3, 4 }, - { 1, 3, 5 }, - { 1, 4, 5 }, - { 2, 3, 4 }, - { 2, 3, 5 }, - { 2, 4, 5 }, - { 3, 4, 5 }, - { -1 } - }, - /* combinations of 4 of 6 elements */ - { - { 0, 1, 2, 3 }, - { 0, 1, 2, 4 }, - { 0, 1, 2, 5 }, - { 0, 1, 3, 4 }, - { 0, 1, 3, 5 }, - { 0, 1, 4, 5 }, - { 0, 2, 3, 4 }, - { 0, 2, 3, 5 }, - { 0, 2, 4, 5 }, - { 0, 3, 4, 5 }, - { 1, 2, 3, 4 }, - { 1, 2, 3, 5 }, - { 1, 2, 4, 5 }, - { 1, 3, 4, 5 }, - { 2, 3, 4, 5 }, - { -1 } - }, - /* combinations of 5 of 6 elements */ - { - { 0, 1, 2, 3, 4 }, - { 0, 1, 2, 3, 5 }, - { 0, 1, 2, 4, 5 }, - { 0, 1, 3, 4, 5 }, - { 0, 2, 3, 4, 5 }, - { 1, 2, 3, 4, 5 }, - { -1 } - }, - /* combinations of 6 of 6 elements */ - { - { 0, 1, 2, 3, 4, 5 }, - { -1 } - }, -}; - diff --git a/mkcombos.c b/mkcombos.c deleted file mode 100644 index d1d2239f..00000000 --- a/mkcombos.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2013 Andrea Mazzoleni - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include - -unsigned combo(unsigned mask, unsigned pick, unsigned from, unsigned to, unsigned* map) -{ - unsigned i; - unsigned count; - - if (pick == 0) { - map[0] = mask; - return 1; - } - - count = 0; - for(i=from;i<=to;++i) - count += combo(mask | (1 << i), pick - 1, i + 1, to, map + count); - - return count; -} - -void print_combo(unsigned n) -{ - unsigned map[10000]; - unsigned count; - unsigned i, j, k; - - printf("/**\n"); - printf(" * Combinations of %u elements.\n", n); - printf(" * Ends is marked with first element as -1.\n"); - printf(" */\n"); - printf("static int combo[][32][8] = {\n"); /* fixed value 32/8, change them if changing maximum n */ - - for(i=1;i<=n;++i) { - count = combo(0, i, 0, n - 1, map); - - printf("\t/* combinations of %u of %u elements */\n", i, n); - printf("\t{\n"); - for(j=0;j>= 1; - if (mask) - printf(", "); - } else { - mask >>= 1; - } - } - printf(" },\n"); - } - printf("\t\t{ -1 }\n"); - printf("\t},\n"); - } - printf("};\n\n"); -} - -int main(void) -{ - printf("/*\n"); - printf(" * Copyright (C) 2013 Andrea Mazzoleni\n"); - printf(" *\n"); - printf(" * This program is free software: you can redistribute it and/or modify\n"); - printf(" * it under the terms of the GNU General Public License as published by\n"); - printf(" * the Free Software Foundation, either version 2 of the License, or\n"); - printf(" * (at your option) any later version.\n"); - printf(" *\n"); - printf(" * This program is distributed in the hope that it will be useful,\n"); - printf(" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); - printf(" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); - printf(" * GNU General Public License for more details.\n"); - printf(" *\n"); - printf(" * You should have received a copy of the GNU General Public License\n"); - printf(" * along with this program. If not, see .\n"); - printf(" */\n"); - printf("\n"); - - print_combo(6); - return 0; -} - diff --git a/selftest.c b/selftest.c index 19b19acd..30d14b0f 100644 --- a/selftest.c +++ b/selftest.c @@ -23,7 +23,7 @@ #include "cpu.h" #include "elem.h" #include "state.h" -#include "combos.h" +#include "combo.h" struct hash_test_vector { const char* data; @@ -47,7 +47,40 @@ static struct hash_test_vector TEST_SPOOKY2[] = { { 0, 0, { 0 } } }; -static void recovtest(int diskmax, unsigned block_size) +/** + * Binomial coefficient of n over r. + */ +static unsigned bc(unsigned n, unsigned r) +{ + if (r == 0 || n == r) + return 1; + else + return bc(n - 1, r - 1) + bc(n - 1, r); +} + +static void combotest(void) +{ + unsigned r; + unsigned count; + int p[LEV_MAX]; + + /* all parities */ + for(r=1;r<=LEV_MAX;++r) { + /* count combination (r of LEV_MAX) parities */ + count = 0; + combination_first(r, LEV_MAX, p); + do { + ++count; + } while (combination_next(r, LEV_MAX, p)); + + if (count != bc(LEV_MAX, r)) { + fprintf(stderr, "Failed COMBO test\n"); + exit(EXIT_FAILURE); + } + } +} + +static void recovtest(unsigned diskmax, unsigned block_size) { void* buffer_alloc; unsigned char** buffer; @@ -60,8 +93,10 @@ static void recovtest(int diskmax, unsigned block_size) unsigned char* waste; unsigned buffermax; int d[LEV_MAX]; - int i; + int p[LEV_MAX]; + unsigned i; unsigned j; + unsigned r; buffermax = diskmax + LEV_MAX * 2 + 2; @@ -93,232 +128,45 @@ static void recovtest(int diskmax, unsigned block_size) for(i=0;i=0;++i) { - /* set */ - for(j=0;j<6;++j) { - /* remove the missing data */ - data_save[j] = data[d[j]]; - data[d[j]] = test[j]; - /* set the parity to use */ - parity[combo[5][i][j]] = parity_save[combo[5][i][j]]; - } + /* all parity levels */ + for(r=1;r<=LEV_MAX;++r) { - /* recover */ - raid_recov(6, d, combo[5][i], buffer, diskmax, zero, block_size); + /* all combinations (r of diskmax) disks */ + combination_first(r, diskmax, d); + do { - /* check */ - for(j=0;j<6;++j) { - if (memcmp(test[j], data_save[j], block_size) != 0) { - fprintf(stderr, "Failed RAID test\n"); - exit(EXIT_FAILURE); + /* all combinations (r of LEV_MAX) parities */ + combination_first(r, LEV_MAX, p); + do { + /* set */ + for(i=0;i=0;++i) { - /* set */ - for(j=0;j<5;++j) { - /* remove the missing data */ - data_save[j] = data[d[j]]; - data[d[j]] = test[j]; - /* set the parity to use */ - parity[combo[4][i][j]] = parity_save[combo[4][i][j]]; - } - - /* recover */ - raid_recov(5, d, combo[4][i], buffer, diskmax, zero, block_size); - - /* check */ - for(j=0;j<5;++j) { - if (memcmp(test[j], data_save[j], block_size) != 0) { - fprintf(stderr, "Failed RAID test\n"); - exit(EXIT_FAILURE); + /* check */ + for(i=0;i=0;++i) { - /* set */ - for(j=0;j<4;++j) { - /* remove the missing data */ - data_save[j] = data[d[j]]; - data[d[j]] = test[j]; - /* set the parity to use */ - parity[combo[3][i][j]] = parity_save[combo[3][i][j]]; - } - - /* recover */ - raid_recov(4, d, combo[3][i], buffer, diskmax, zero, block_size); - - /* check */ - for(j=0;j<4;++j) { - if (memcmp(test[j], data_save[j], block_size) != 0) { - fprintf(stderr, "Failed RAID test\n"); - exit(EXIT_FAILURE); + /* restore */ + for(i=0;i=0;++i) { - /* set */ - for(j=0;j<3;++j) { - /* remove the missing data */ - data_save[j] = data[d[j]]; - data[d[j]] = test[j]; - /* set the parity to use */ - parity[combo[2][i][j]] = parity_save[combo[2][i][j]]; - } - - /* recover */ - raid_recov(3, d, combo[2][i], buffer, diskmax, zero, block_size); - - /* check */ - for(j=0;j<3;++j) { - if (memcmp(test[j], data_save[j], block_size) != 0) { - fprintf(stderr, "Failed RAID test\n"); - exit(EXIT_FAILURE); - } - } - - /* restore */ - for(j=0;j<3;++j) { - /* restore the data */ - data[d[j]] = data_save[j]; - /* restore the parity */ - parity[combo[2][i][j]] = waste; - } - } - } - } - } - - /* check 2data */ - for(d[0]=0;d[0]=0;++i) { - /* set */ - for(j=0;j<2;++j) { - /* remove the missing data */ - data_save[j] = data[d[j]]; - data[d[j]] = test[j]; - /* set the parity to use */ - parity[combo[1][i][j]] = parity_save[combo[1][i][j]]; - } - - /* recover */ - raid_recov(2, d, combo[1][i], buffer, diskmax, zero, block_size); - - /* check */ - for(j=0;j<2;++j) { - if (memcmp(test[j], data_save[j], block_size) != 0) { - fprintf(stderr, "Failed RAID test\n"); - exit(EXIT_FAILURE); - } - } - - /* restore */ - for(j=0;j<2;++j) { - /* restore the data */ - data[d[j]] = data_save[j]; - /* restore the parity */ - parity[combo[1][i][j]] = waste; - } - } - } - } - - /* check 1data */ - for(d[0]=0;d[0]=0;++i) { - /* set */ - for(j=0;j<1;++j) { - /* remove the missing data */ - data_save[j] = data[d[j]]; - data[d[j]] = test[j]; - /* set the parity to use */ - parity[combo[0][i][j]] = parity_save[combo[0][i][j]]; - } - - /* recover */ - raid_recov(1, d, combo[0][i], buffer, diskmax, zero, block_size); - - /* check */ - for(j=0;j<1;++j) { - if (memcmp(test[j], data_save[j], block_size) != 0) { - fprintf(stderr, "Failed RAID test\n"); - exit(EXIT_FAILURE); - } - } - - /* restore */ - for(j=0;j<1;++j) { - /* restore the data */ - data[d[j]] = data_save[j]; - /* restore the parity */ - parity[combo[0][i][j]] = waste; - } - } + } while (combination_next(r, LEV_MAX, p)); + } while (combination_next(r, diskmax, d)); } free(buffer_alloc); @@ -396,7 +244,7 @@ static void gentest(unsigned diskmax, unsigned block_size) /* check it */ for(i=0;i