added bit-unpacking algorithm
This commit is contained in:
parent
1909b52c32
commit
f8b493de2b
125
src/bitpack.cc
125
src/bitpack.cc
@ -23,17 +23,37 @@ constexpr int ipow(int base, int exp) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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<vuint16::const_iterator>(it), t_input.end(), 9);
|
||||||
|
ret.insert(ret.end(), next_vec.begin(), next_vec.end());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret.push_back(static_cast<unsigned char>(*it));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Packs \p t_input into unsigned char, assuming the max value of t_input only
|
* Packs \p t_input into unsigned char, assuming the max value of t_input
|
||||||
* takes \p t_n bits
|
* only takes \p t_n bits
|
||||||
*
|
*
|
||||||
* \param t_input_begin pointer to the beginning of the vector of values to be
|
* \param t_input_begin pointer to the beginning of the vector of values to
|
||||||
* packed \param t_input_end pointer to the end of the input vector \param t_n
|
* be packed \param t_input_end pointer to the end of the input vector
|
||||||
* maximum size of an input value in bits \return Returns a vector of unsigned
|
* \param t_n maximum size of an input value in bits \return Returns a
|
||||||
* char containing the packed values from t_input
|
* vector of unsigned char containing the packed values from t_input
|
||||||
*/
|
*/
|
||||||
vuchar pack_n(const vuint16::const_iterator t_input_begin,
|
[[nodiscard]] vuchar
|
||||||
const vuint16::const_iterator t_input_end, int t_n) {
|
pack_n(const vuint16::const_iterator t_input_begin,
|
||||||
|
const vuint16::const_iterator t_input_end, const int t_n) {
|
||||||
if (t_n == 16) {
|
if (t_n == 16) {
|
||||||
return pack_16(t_input_begin, t_input_end);
|
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;
|
int right_shift = 0;
|
||||||
|
|
||||||
for (auto it = t_input_begin; it != t_input_end; ++it) {
|
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;
|
left_shift += step;
|
||||||
if (left_shift >= t_n) {
|
if (left_shift >= t_n) {
|
||||||
left_shift = (left_shift - t_n) + step;
|
left_shift = (left_shift - t_n) + step;
|
||||||
@ -79,6 +91,16 @@ vuchar pack_n(const vuint16::const_iterator t_input_begin,
|
|||||||
} else {
|
} else {
|
||||||
current_char = (*it << right_shift) & 0xFF;
|
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) {
|
if (current_char != 0) {
|
||||||
ret.push_back(current_char);
|
ret.push_back(current_char);
|
||||||
@ -86,7 +108,7 @@ vuchar pack_n(const vuint16::const_iterator t_input_begin,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
vuchar pack_16(const vuint16::const_iterator t_input_begin,
|
[[nodiscard]] vuchar pack_16(const vuint16::const_iterator t_input_begin,
|
||||||
const vuint16::const_iterator t_input_end) {
|
const vuint16::const_iterator t_input_end) {
|
||||||
vuchar ret{};
|
vuchar ret{};
|
||||||
std::for_each(t_input_begin, t_input_end, [&](const auto value) {
|
std::for_each(t_input_begin, t_input_end, [&](const auto value) {
|
||||||
@ -96,17 +118,74 @@ vuchar pack_16(const vuint16::const_iterator t_input_begin,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
vuchar pack(const vuint16 t_input) {
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
vuchar ret{};
|
// unpacking //
|
||||||
constexpr int max_value = ipow(2, 8);
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
[[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) {
|
for (auto it = t_input.begin(); it != t_input.end(); ++it) {
|
||||||
|
ret.push_back(static_cast<uint16_t>(*it));
|
||||||
if (*it >= max_value) {
|
if (*it >= max_value) {
|
||||||
const auto next_vec =
|
auto next_vec{unpack_n(it, t_input.end(), 9)};
|
||||||
pack_n(static_cast<vuint16::const_iterator>(it), t_input.end(), 9);
|
|
||||||
ret.insert(ret.end(), next_vec.begin(), next_vec.end());
|
ret.insert(ret.end(), next_vec.begin(), next_vec.end());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret.push_back(static_cast<unsigned char>(*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<uint16_t>((*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<uint16_t>((*it << 8) | *(++it)));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -9,17 +9,29 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
/// \brief Bat-packs the input dynamically
|
||||||
|
[[nodiscard]] std::vector<unsigned char>
|
||||||
|
pack(const std::vector<std::uint16_t> &);
|
||||||
|
|
||||||
/// \brief Packs std::uint16_t of n bits into unsigned char
|
/// \brief Packs std::uint16_t of n bits into unsigned char
|
||||||
std::vector<unsigned char>
|
[[nodiscard]] std::vector<unsigned char>
|
||||||
pack_n(const std::vector<std::uint16_t>::const_iterator,
|
pack_n(const std::vector<std::uint16_t>::const_iterator,
|
||||||
const std::vector<std::uint16_t>::const_iterator, int);
|
const std::vector<std::uint16_t>::const_iterator, const int);
|
||||||
|
|
||||||
/// \brief Specialization of \ref pack_n for 16bits
|
/// \brief Specialization of \ref pack_n for 16bits
|
||||||
std::vector<unsigned char>
|
[[nodiscard]] std::vector<unsigned char>
|
||||||
pack_16(const std::vector<std::uint16_t>::const_iterator,
|
pack_16(const std::vector<std::uint16_t>::const_iterator,
|
||||||
const std::vector<std::uint16_t>::const_iterator);
|
const std::vector<std::uint16_t>::const_iterator);
|
||||||
|
|
||||||
/// \brief Bat-packs the input dynamically
|
[[nodiscard]] std::vector<std::uint16_t>
|
||||||
std::vector<unsigned char> pack(const std::vector<std::uint16_t>);
|
unpack(const std::vector<unsigned char> &);
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<std::uint16_t>
|
||||||
|
unpack_n(const std::vector<unsigned char>::const_iterator,
|
||||||
|
const std::vector<unsigned char>::const_iterator, const int t_n);
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<std::uint16_t>
|
||||||
|
unpack_16(const std::vector<unsigned char>::const_iterator,
|
||||||
|
const std::vector<unsigned char>::const_iterator);
|
||||||
|
|
||||||
#endif /* LZW_SRC_BITPACK_H_ */
|
#endif /* LZW_SRC_BITPACK_H_ */
|
||||||
|
28
src/io.cc
28
src/io.cc
@ -20,35 +20,19 @@ using std::uint16_t;
|
|||||||
using vuint16 = vector<uint16_t>;
|
using vuint16 = vector<uint16_t>;
|
||||||
using vvuint16 = vector<vuint16>;
|
using vvuint16 = vector<vuint16>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constexpr unsigned char char_size = 12;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Écrit dans le fichier \p t_out les chunks passés en paramètre. Le fichier de
|
* É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
|
* 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
|
* - Sur deux octets sont écrit un `uint16_t` déterminant le nombre de chunk
|
||||||
* composant le fichier\n
|
* composant le fichier\n
|
||||||
* - Sont ensuite écrits les chunks sur un nombre variable d’octets suivant la
|
* - Sont ensuite écrits les chunks sur un nombre variable d’octets suivant la
|
||||||
* taille des chunks\n
|
* taille des chunks\n
|
||||||
* \n
|
* \n
|
||||||
* Un chunk est composé de la manière qui suit :\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
|
* - Sur deux octets sont écrit un `uint16_t` déterminant le nombre d’octets
|
||||||
* caractères (uint16_t) composant le chunk\n
|
* composant le chunk\n
|
||||||
* - Sur n*2 octets la chaîne de caractères compressés (n représentant le
|
* - Sur le nombre d’octets précisés par le header du chunk se trouvent les
|
||||||
* nombre de caractères dans le chunk).\n
|
* données compressées par l’algorithme lzw puis via bit-packing.\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.
|
|
||||||
*
|
*
|
||||||
* \param[out] t_out Fichier de sortie
|
* \param[out] t_out Fichier de sortie
|
||||||
* \param[in] t_text Collection ordonnée des chunks à écrire dans \p t_out
|
* \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) {
|
if constexpr (debug_mode) {
|
||||||
std::printf("Number of chunks: %u\n", size);
|
std::printf("Number of chunks: %u\n", size);
|
||||||
}
|
}
|
||||||
fwrite(&char_size, sizeof(char_size), 1, t_out);
|
|
||||||
fwrite(&size, sizeof(size), 1, t_out);
|
fwrite(&size, sizeof(size), 1, t_out);
|
||||||
for (const auto &chunk : t_text) {
|
for (const auto &chunk : t_text) {
|
||||||
write_chunk(t_out, chunk);
|
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 output = pack(t_chunk);
|
||||||
const auto chunk_size = static_cast<uint16_t>(output.size());
|
const auto chunk_size = static_cast<uint16_t>(output.size());
|
||||||
fwrite(&chunk_size, sizeof(chunk_size), 1, t_out);
|
fwrite(&chunk_size, sizeof(chunk_size), 1, t_out);
|
||||||
fwrite((const char *)(output.data()), sizeof(output[0]), output.size(),
|
fwrite(output.data(), sizeof(output[0]), output.size(), t_out);
|
||||||
t_out);
|
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,7 @@ std::tuple<string, string, bool> process_args(int t_argc, char *t_argv[]) {
|
|||||||
std::get<2>(ret) = false;
|
std::get<2>(ret) = false;
|
||||||
break;
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
|
[[fallthrough]];
|
||||||
default:
|
default:
|
||||||
puts("Error: unknown parameter.");
|
puts("Error: unknown parameter.");
|
||||||
help();
|
help();
|
||||||
|
Loading…
Reference in New Issue
Block a user