/** * Standard Constructor. a is the array to be prepared for RMQ. * n is the size of the array. * */ RMQ_succinct::RMQ_succinct(int* a, unsigned int n) { this->a = a; this->n = n; s = 1 << 3; // microblock-size sprime = 1 << 4; // block-size sprimeprime = 1 << 8; // superblock-size nb = block(n-1)+1; // number of blocks nsb = superblock(n-1)+1; // number of superblocks nmb = microblock(n-1)+1; // number of microblocks // The following is necessary because we've fixed s, s' and s'' according to the computer's // word size and NOT according to the input size. This may cause the (super-)block-size // to be too big, or, in other words, the array too small. If this code is compiled on // a 32-bit computer, this happens iff n < 113. For such small instances it isn't // advisable anyway to use this data structure, because simpler methods are faster and // less space consuming. if (nb<sprimeprime/(2*sprime)) { cerr << "Array too small...exit\n"; exit(-1); } // Type-calculation for the microblocks and pre-computation of in-microblock-queries: type = new DTsucc2[nmb]; Prec = new DTsucc*[Catalan[s][s]]; for (unsigned int i = 0; i < Catalan[s][s]; i++) { Prec[i] = new DTsucc[s]; for(unsigned int j=0; j<s;j++) Prec[i][j]=0; Prec[i][0] = 1; // init with impossible value } int* rp = new int[s+1]; // rp: rightmost path in Cart. tree unsigned int z = 0; // index in array a unsigned int start; // start of current block unsigned int end; // end of current block unsigned int q; // position in Catalan triangle unsigned int p; // --------- " ---------------- rp[0] = minus_infinity; // stopper (minus infinity) // prec[i]: the jth bit is 1 iff j is 1. pos. to the left of i where a[j] < a[i] unsigned int* gstack = new unsigned int[s]; unsigned int gstacksize; unsigned int g; // first position to the left of i where a[g[i]] < a[i] // step through microblocks for (unsigned int i = 0; i < nmb; i++) { start = z; // init start end = start + s; // end of block (not inclusive!) if (end > n) end = n;// last block could be smaller than s! // compute block type as in Fischer/Heun CPM'06: q = s; // init q p = s-1; // init p type[i] = 0; // init type (will be increased!) rp[1] = a[z]; // init rightmost path while (++z < end) { // step through current block: p--; while (rp[q-p-1] > a[z]) { // update type type[i] += Catalan[p][q]; q--; } rp[q-p] = a[z]; // add last element to rightmost path } // precompute in-block-queries for this microblock (if necessary) // as in Alstrup et al. SPAA'02: if (Prec[type[i]][0] == 1) { Prec[type[i]][0] = 0; gstacksize = 0; for (unsigned int j = start; j < end; j++) { while(gstacksize > 0 && (a[j] < a[gstack[gstacksize-1]])) { gstacksize--; } if(gstacksize > 0) { g = gstack[gstacksize-1]; Prec[type[i]][j-start] = Prec[type[i]][g-start] | (1 << (g % s)); } else Prec[type[i]][j-start] = 0; gstack[gstacksize++] = j; } } } delete[] rp; delete[] gstack; // space for out-of-block- and out-of-superblock-queries: M_depth = (unsigned int) floor(log2(((double) sprimeprime / (double) sprime))); M = new DTsucc*[M_depth]; M[0] = new DTsucc[nb]; Mprime_depth = (unsigned int) floor(log2(nsb)) + 1; Mprime = new unsigned int*[Mprime_depth]; Mprime[0] = new unsigned int[nsb]; // fill 0'th rows of M and Mprime: z = 0; // minimum in current block q = 0; // pos. of min in current superblock g = 0; // number of current superblock // step through blocks for (unsigned int i = 0; i < nb; i++) { start = z; // init start p = start; // init minimum end = start + sprime;// end of block (not inclusive!) if (end > n) end = n;// last block could be smaller than sprime! // update minimum in superblock if (a[z] < a[q]) q = z; while (++z < end) { // step through current block: // update minimum in block if (a[z] < a[p]) p = z; // update minimum in superblock if (a[z] < a[q]) q = z; } M[0][i] = p-start; // store index of block-minimum (offset!) // reached end of superblock? if (z % sprimeprime == 0 || z == n) { // store index of superblock-minimum Mprime[0][g++] = q; q = z; } } // fill M unsigned int dist = 1; // always 2^(j-1) for (unsigned int j = 1; j < M_depth; j++) { M[j] = new DTsucc[nb]; // be careful: loop may go too far for (unsigned int i = 0; i < nb - dist; i++) { M[j][i] = a[m(j-1, i)] <= a[m(j-1,i+dist)] ? // add 'skipped' elements in a M[j-1][i] : M[j-1][i+dist] + (dist*sprime); } // fill overhang for (unsigned int i = nb - dist; i < nb; i++) M[j][i] = M[j-1][i]; dist *= 2; } // fill M': dist = 1; // always 2^(j-1) for (unsigned int j = 1; j < Mprime_depth; j++) { Mprime[j] = new unsigned int[nsb]; for (unsigned int i = 0; i < nsb - dist; i++) { Mprime[j][i] = a[Mprime[j-1][i]] <= a[Mprime[j-1][i+dist]] ? Mprime[j-1][i] : Mprime[j-1][i+dist]; } // overhang for (unsigned int i = nsb - dist; i < nsb; i++) Mprime[j][i] = Mprime[j-1][i]; dist *= 2; } }
int main(int argc, char *argv[]) try { State state; auto parsers = new_subparser({"device"}); argp argp = {options, init_parsers, nullptr, doc, parsers.get(), nullptr, nullptr}; argp_parse(&argp, argc, argv, 0, nullptr, &state); Params params; try { params.load(state.device); } catch(const std::exception& e) { std::cerr << "Error: Header corrupt." << std::endl; return 1; } std::uint64_t blocks = state.device.size()/params.block_size; Pinentry pinentry; pinentry.SETDESC("Enter passphrases for a partition on this volume."); pinentry.SETPROMPT("Passphrase:"); auto passphrase = pinentry.GETPIN(); Superblock superblock(params, passphrase, blocks); try { superblock.load(state.device); } catch(...) { std::cerr << "Error: No partition found for that passphrase." << std::endl; } Hash hash(params.hash); std::string key = PBKDF2::PBKDF2(hash, passphrase, params.salt, params.iters, params.key_size); if (state.name.empty()) { hash.reset(); hash.update(key); state.name = hex(hash.digest()).substr(8); } { std::unique_ptr<dm_task, void(*)(dm_task*)> dmt( dm_task_create(DM_DEVICE_CREATE), dm_task_destroy); if (!dmt.get()) throw std::runtime_error("dm_task_create failed"); if (!dm_task_set_name(dmt.get(), state.name.c_str())) throw std::runtime_error("dm_task_set_name failed"); std::uint64_t offset = 0; for (auto block = superblock.blocks.begin()+superblock.offset; block != superblock.blocks.end(); block++, offset += params.block_size) { if (*block != 0) { std::stringstream ss; ss << params.device_cipher << " " << hex(key) << " 0 "; ss << state.device.major() << ":" << state.device.minor() << " "; ss << (*block)*params.block_size/512; if (!dm_task_add_target(dmt.get(), offset/512, params.block_size/512, "crypt", ss.str().c_str())) throw std::runtime_error("dm_task_add_target(\"crypt\") failed"); } else { if (!dm_task_add_target(dmt.get(), offset/512, params.block_size/512, "error", "")) throw std::runtime_error("dm_task_add_target(\"error\") failed"); } } if (!dm_task_run(dmt.get())) throw std::runtime_error("dm_task_run failed"); } return 0; } catch(const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; }
unsigned int RMQ_succinct::query(unsigned int i, unsigned int j) { // i's microblock unsigned int mb_i = microblock(i); // j's microblock unsigned int mb_j = microblock(j); // min: to be returned unsigned int min, min_i, min_j; // start of i's microblock unsigned int s_mi = mb_i * s; // pos. of i in its microblock unsigned int i_pos = i - s_mi; if (mb_i == mb_j) { // only one microblock-query min_i = clearbits(Prec[type[mb_i]][j-s_mi], i_pos); min = min_i == 0 ? j : s_mi + lsb(min_i); } else { // i's block unsigned int b_i = block(i); // j's block unsigned int b_j = block(j); // start of j's microblock unsigned int s_mj = mb_j * s; // position of j in its microblock unsigned int j_pos = j - s_mj; min_i = clearbits(Prec[type[mb_i]][s-1], i_pos); // left in-microblock-query min = min_i == 0 ? s_mi + s - 1 : s_mi + lsb(min_i); min_j = Prec[type[mb_j]][j_pos] == 0 ? // right in-microblock-query j : s_mj + lsb(Prec[type[mb_j]][j_pos]); if (a[min_j] < a[min]) min = min_j; // otherwise we're done! if (mb_j > mb_i + 1) { // start of block i unsigned int s_bi = b_i * sprime; // start of block j unsigned int s_bj = b_j * sprime; // do another microblock-query! if (s_bi+s > i) { mb_i++; // go one microblock to the right min_i = Prec[type[mb_i]][s-1] == 0 ? // right in-block-query s_bi + sprime - 1 : s_mi + s + lsb(Prec[type[mb_i]][s-1]); if (a[min_i] < a[min]) min = min_i; } // and yet another microblock-query! if (j >= s_bj+s) { mb_j--; // go one microblock to the left min_j = Prec[type[mb_j]][s-1] == 0 ? // right in-block-query s_mj - 1 : s_bj + lsb(Prec[type[mb_j]][s-1]); if (a[min_j] < a[min]) min = min_j; } unsigned int block_difference = b_j - b_i; // otherwise we're done! if (block_difference > 1) { // for index calculations in M and M' unsigned int k, twotothek, block_tmp; b_i++; // block where out-of-block-query starts // just one out-of-block-query if (s_bj - s_bi - sprime <= sprimeprime) { k = log2fast(block_difference - 2); // 2^k twotothek = 1 << k; i = m(k, b_i); j = m(k, b_j-twotothek); min_i = a[i] <= a[j] ? i : j; } else { // here we have to answer a superblock-query: // i's superblock unsigned int sb_i = superblock(i); // j's superblock unsigned int sb_j = superblock(j); // end of left out-of-block-query block_tmp = block((sb_i+1)*sprimeprime); k = log2fast(block_tmp - b_i); // 2^k twotothek = 1 << k; i = m(k, b_i); j = m(k, block_tmp+1-twotothek); min_i = a[i] <= a[j] ? i : j; // start of right out-of-block-query block_tmp = block(sb_j*sprimeprime); k = log2fast(b_j - block_tmp); // 2^k twotothek = 1 << k; // going one block to the left doesn't harm and saves some tests block_tmp--; i = m(k, block_tmp); j = m(k, b_j-twotothek); min_j = a[i] <= a[j] ? i : j; if (a[min_j] < a[min_i]) min_i = min_j; // finally, the superblock-query: if (sb_j > sb_i + 1) { k = log2fast(sb_j - sb_i - 2); twotothek = 1 << k; i = Mprime[k][sb_i+1]; j = Mprime[k][sb_j-twotothek]; min_j = a[i] <= a[j] ? i : j; // does NOT always return leftmost min!!! if (a[min_j] < a[min_i]) min_i = min_j; } } // does NOT always return leftmost min!!! if (a[min_i] < a[min]) min = min_i; } } } return min; }
int main(int argc, char *argv[]) try { State state; auto parsers = new_subparser({"device"}); argp argp = {options, init_parsers, nullptr, doc, parsers.get(), nullptr, nullptr}; argp_parse(&argp, argc, argv, 0, nullptr, &state); Params params; try { params.load(state.device); } catch(const std::exception& e) { std::cerr << "Error: Header corrupt." << std::endl; return 1; } std::uint64_t blocks = state.device.size()/params.block_size; if (blocks <= 1) { std::cerr << "Error: No room for any partitions." << std::endl; return 1; } std::vector<bool> allocated_blocks(blocks); allocated_blocks[0] = true; std::string passphrase; Pinentry pinentry; pinentry.SETDESC("Enter passphrases for all partitions on this volume. " "Enter an empty passphrase after last passphrase."); pinentry.SETPROMPT("Passphrase:"); while ((passphrase = pinentry.GETPIN()) != "") { Superblock superblock(params, passphrase, blocks); try { superblock.load(state.device); for (auto block : superblock.blocks) allocated_blocks[block] = true; } catch(...) { pinentry.SETERROR("No partition found for that passphrase."); } } std::size_t free_blocks = 0; for (bool allocated : allocated_blocks) if (!allocated) free_blocks++; std::cout << free_blocks << " blocks free." << std::endl; if (free_blocks == 0) { std::cout << "Error: not enough free space." << std::endl; return 1; } if (state.blocks == 0) state.blocks = free_blocks; if (state.partition_size == 0) { decltype(state.partition_size) last; state.partition_size = state.blocks; do { last = state.partition_size; state.partition_size = state.blocks-Superblock::size_in_blocks(params, state.partition_size); } while (last != state.partition_size); } std::uint64_t blocks_required = state.partition_size+ Superblock::size_in_blocks(params, state.partition_size); state.blocks = std::min(state.blocks, blocks_required); if (state.blocks > free_blocks) { std::cerr << "Error: not enough free space." << std::endl; return 1; } pinentry.SETDESC("Enter passphrase for the new partition."); Superblock new_partition(params, pinentry.GETPIN(), blocks); if (allocated_blocks[new_partition.blocks.front()]) { std::cerr << "Error: superblock location already in use." << std::endl; return 1; } allocated_blocks[new_partition.blocks.front()] = true; state.blocks--; std::vector<std::uint64_t> pool; pool.reserve(free_blocks-1); for (std::size_t i = 0; i < blocks; i++) if (!allocated_blocks[i]) pool.push_back(i); std::shuffle(pool.begin(), pool.end(), std::random_device()); for (; state.blocks > 0; state.blocks--, state.partition_size--) { new_partition.blocks.push_back(pool.back()); pool.pop_back(); } for (; state.partition_size > 0; state.partition_size--) new_partition.blocks.push_back(0); new_partition.store(state.device); return 0; } catch(const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; }