From f8b493de2bc5322fa57554603c52baceea828539 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Sat, 9 Jun 2018 22:59:11 +0200 Subject: [PATCH] added bit-unpacking algorithm --- src/bitpack.cc | 133 +++++++++++++++++++++++++++++++++++++++---------- src/bitpack.hh | 22 ++++++-- src/io.cc | 28 ++--------- src/main.cc | 1 + 4 files changed, 129 insertions(+), 55 deletions(-) diff --git a/src/bitpack.cc b/src/bitpack.cc index 7931a35..8e57eb6 100644 --- a/src/bitpack.cc +++ b/src/bitpack.cc @@ -23,17 +23,37 @@ constexpr int ipow(int base, int exp) { return result; } -/** - * Packs \p t_input into unsigned char, assuming the max value of t_input only - * takes \p t_n bits - * - * \param t_input_begin pointer to the beginning of the vector of values to be - * packed \param t_input_end pointer to the end of the input vector \param t_n - * maximum size of an input value in bits \return Returns a vector of unsigned - * char containing the packed values from t_input - */ -vuchar pack_n(const vuint16::const_iterator t_input_begin, - const vuint16::const_iterator t_input_end, int t_n) { +/////////////////////////////////////////////////////////////////////////////// +// packing // +/////////////////////////////////////////////////////////////////////////////// + +[[nodiscard]] vuchar pack(const vuint16 &t_input) { + vuchar ret{}; + constexpr int max_value = ipow(2, 8); + for (auto it = t_input.begin(); it != t_input.end(); ++it) { + if (*it >= max_value) { + const auto next_vec = + pack_n(static_cast(it), t_input.end(), 9); + ret.insert(ret.end(), next_vec.begin(), next_vec.end()); + return ret; + } + ret.push_back(static_cast(*it)); + } + return ret; +} + + /** + * Packs \p t_input into unsigned char, assuming the max value of t_input + * only takes \p t_n bits + * + * \param t_input_begin pointer to the beginning of the vector of values to + * be packed \param t_input_end pointer to the end of the input vector + * \param t_n maximum size of an input value in bits \return Returns a + * vector of unsigned char containing the packed values from t_input + */ + [[nodiscard]] vuchar + pack_n(const vuint16::const_iterator t_input_begin, + const vuint16::const_iterator t_input_end, const int t_n) { if (t_n == 16) { return pack_16(t_input_begin, t_input_end); } @@ -49,14 +69,6 @@ vuchar pack_n(const vuint16::const_iterator t_input_begin, int right_shift = 0; for (auto it = t_input_begin; it != t_input_end; ++it) { - if (*t_input_end >= max_value) { - if(current_char != 0) { - ret.push_back(current_char); - } - const auto next_vec = pack_n(it, t_input_end, t_n + 1); - ret.insert(ret.end(), next_vec.begin(), next_vec.end()); - return ret; - } left_shift += step; if (left_shift >= t_n) { left_shift = (left_shift - t_n) + step; @@ -79,6 +91,16 @@ vuchar pack_n(const vuint16::const_iterator t_input_begin, } else { current_char = (*it << right_shift) & 0xFF; } + + // at the end so we can detect `max_value` while parsing for bit-unpacking + if (*t_input_end >= max_value) { + if (current_char != 0) { + ret.push_back(current_char); + } + const auto next_vec = pack_n(it, t_input_end, t_n + 1); + ret.insert(ret.end(), next_vec.begin(), next_vec.end()); + return ret; + } } if (current_char != 0) { ret.push_back(current_char); @@ -86,8 +108,8 @@ vuchar pack_n(const vuint16::const_iterator t_input_begin, return ret; } -vuchar pack_16(const vuint16::const_iterator t_input_begin, - const vuint16::const_iterator t_input_end) { +[[nodiscard]] vuchar pack_16(const vuint16::const_iterator t_input_begin, + const vuint16::const_iterator t_input_end) { vuchar ret{}; std::for_each(t_input_begin, t_input_end, [&](const auto value) { ret.push_back((value >> 8) & 0xFF); @@ -96,17 +118,74 @@ vuchar pack_16(const vuint16::const_iterator t_input_begin, return ret; } -vuchar pack(const vuint16 t_input) { - vuchar ret{}; - constexpr int max_value = ipow(2, 8); + /////////////////////////////////////////////////////////////////////////////// + // unpacking // + /////////////////////////////////////////////////////////////////////////////// + + [[nodiscard]] vuint16 unpack(const vuchar &t_input) { + vuint16 ret{}; + + constexpr int max_value = ipow(2, 8) - 1; + + // begin with 8bits for (auto it = t_input.begin(); it != t_input.end(); ++it) { + ret.push_back(static_cast(*it)); if (*it >= max_value) { - const auto next_vec = - pack_n(static_cast(it), t_input.end(), 9); + auto next_vec{unpack_n(it, t_input.end(), 9)}; ret.insert(ret.end(), next_vec.begin(), next_vec.end()); return ret; } - ret.push_back(static_cast(*it)); + } + + return ret; +} + +[[nodiscard]] vuint16 unpack_n(const vuchar::const_iterator t_begin, + const vuchar::const_iterator t_end, + const int t_n) { + int step = t_n / 8; + int left_shift = 0; + int middle_shift = 0; + int right_shift = 0; + vuint16 ret{}; + + for (auto it = t_begin; it < t_end;) { + static const int max_value = ipow(2, t_n); + uint16_t current_char = 0; + left_shift += step; + if (left_shift >= t_n) { + left_shift = (left_shift - t_n) + step; + } + current_char = static_cast((*it) << left_shift); + + bool zero_rs = right_shift; + right_shift -= step; + if (right_shift < 0) { + if (zero_rs) { + middle_shift = std::abs(right_shift); + current_char |= (*(++it)) << middle_shift; + } + + right_shift = 8 - std::abs(right_shift); + } + current_char |= *(++it) >> right_shift; + ret.push_back(current_char); + + if (current_char >= max_value) { + const auto next_vec = unpack_n(it, t_end, t_n + 1); + ret.insert(ret.end(), next_vec.begin(), next_vec.end()); + return ret; + } + } + + return ret; +} + + [[nodiscard]] vuint16 unpack_16(const vuchar::const_iterator t_begin, + const vuchar::const_iterator t_end) { + vuint16 ret{}; + for (auto it = t_begin; it < t_end; ++it) { + ret.push_back(static_cast((*it << 8) | *(++it))); } return ret; } diff --git a/src/bitpack.hh b/src/bitpack.hh index c061bc7..5d06222 100644 --- a/src/bitpack.hh +++ b/src/bitpack.hh @@ -9,17 +9,29 @@ #include #include +/// \brief Bat-packs the input dynamically +[[nodiscard]] std::vector +pack(const std::vector &); + /// \brief Packs std::uint16_t of n bits into unsigned char -std::vector +[[nodiscard]] std::vector pack_n(const std::vector::const_iterator, - const std::vector::const_iterator, int); + const std::vector::const_iterator, const int); /// \brief Specialization of \ref pack_n for 16bits -std::vector +[[nodiscard]] std::vector pack_16(const std::vector::const_iterator, const std::vector::const_iterator); -/// \brief Bat-packs the input dynamically -std::vector pack(const std::vector); +[[nodiscard]] std::vector +unpack(const std::vector &); + +[[nodiscard]] std::vector +unpack_n(const std::vector::const_iterator, + const std::vector::const_iterator, const int t_n); + +[[nodiscard]] std::vector +unpack_16(const std::vector::const_iterator, + const std::vector::const_iterator); #endif /* LZW_SRC_BITPACK_H_ */ diff --git a/src/io.cc b/src/io.cc index 0b3ec0e..caf4fbf 100644 --- a/src/io.cc +++ b/src/io.cc @@ -20,35 +20,19 @@ using std::uint16_t; using vuint16 = vector; using vvuint16 = vector; - - -constexpr unsigned char char_size = 12; - /** * Écrit dans le fichier \p t_out les chunks passés en paramètre. Le fichier de * sortie est composé des éléments suivants :\n - * - Sur deux octets sont écrit un `uint16_t` déterminant la taille d'un - * caractère\n * - Sur deux octets sont écrit un `uint16_t` déterminant le nombre de chunk * composant le fichier\n * - Sont ensuite écrits les chunks sur un nombre variable d’octets suivant la * taille des chunks\n * \n * Un chunk est composé de la manière qui suit :\n - * - Sur deux octets sont écrit un `uint16_t` déterminant le nombre de - * caractères (uint16_t) composant le chunk\n - * - Sur n*2 octets la chaîne de caractères compressés (n représentant le - * nombre de caractères dans le chunk).\n - * Les caractères uint16_t sont à nouveau compressés en 12bits via du - * bit-packing, intégrant dont en trois unsigned char deux uint16_t. Le premier - * char contient les huit bits de poids fort des douze bits significatifs. Les - * quatre bits de poids fort du second char contient les quatre bits de poids - * faible du premier uint16_t. Les quatre bits de poids faible du second char - * contiennent les quatre bits de poids fort du second uint16_t, et le - * troisième char contient les huit bits de poids faible du second uint16_t.\n - * Si le nombre de charactères dans le chunk est impair, alors les trois - * derniers chars seront remplis comme si le chunk disposait d’un caractère nul - * supplémentaire. + * - Sur deux octets sont écrit un `uint16_t` déterminant le nombre d’octets + * composant le chunk\n + * - Sur le nombre d’octets précisés par le header du chunk se trouvent les + * données compressées par l’algorithme lzw puis via bit-packing.\n * * \param[out] t_out Fichier de sortie * \param[in] t_text Collection ordonnée des chunks à écrire dans \p t_out @@ -58,7 +42,6 @@ void write_file(FILE *const t_out, const vvuint16 &t_text) { if constexpr (debug_mode) { std::printf("Number of chunks: %u\n", size); } - fwrite(&char_size, sizeof(char_size), 1, t_out); fwrite(&size, sizeof(size), 1, t_out); for (const auto &chunk : t_text) { write_chunk(t_out, chunk); @@ -76,6 +59,5 @@ void write_chunk(FILE *const t_out, const vuint16 &t_chunk) { const auto output = pack(t_chunk); const auto chunk_size = static_cast(output.size()); fwrite(&chunk_size, sizeof(chunk_size), 1, t_out); - fwrite((const char *)(output.data()), sizeof(output[0]), output.size(), - t_out); + fwrite(output.data(), sizeof(output[0]), output.size(), t_out); } diff --git a/src/main.cc b/src/main.cc index a182a42..ba43023 100644 --- a/src/main.cc +++ b/src/main.cc @@ -83,6 +83,7 @@ std::tuple process_args(int t_argc, char *t_argv[]) { std::get<2>(ret) = false; break; case '?': + [[fallthrough]]; default: puts("Error: unknown parameter."); help();