/** * \file compress.cc * \brief Implementation of compression */ #include "compress.hh" #include "common.hh" #include "io.hh" #include #include #include #include using std::ios; using std::string; using std::uint16_t; using std::uint8_t; using std::vector; using vuint16 = vector; using vvuint16 = vector; using ustring = std::basic_string; using dict_t = std::map, uint16_t>; using std::printf; ustring read_file(const string &filename) { std::ifstream file{filename, ios::binary}; assert(file); file.unsetf(ios::skipws); file.seekg(0, ios::end); const auto file_size = file.tellg(); file.seekg(0, ios::beg); ustring res{}; res.reserve(file_size); res.insert(res.begin(), std::istream_iterator(file), std::istream_iterator()); file.close(); return res; } /** * La chaîne de caractères \p t_text est lue caractère par caractère, et est * selon la valeur de retour de la fonction \ref dico (permettant dans le même * temps la création du dictionnaire), on rajoute ou non un nouveau caractère * encodé sur 12bits dans le chunk courant. Dès que le dictionnaire est plein * (2^12 caractères), le chunk est sauvegardé et vidé, et le dictionnaire est * réinitialisé. * * \param t_text Chaîne de caractères uint8_t représentant le fichier d'entrée * \return Vecteur de chunks (vecteurs de uint16_t) */ vvuint16 lzw_compress(ustring &&t_text) { std::puts("Compressing..."); const auto DICT_MAX = static_cast(ipow(2, 17) - 256); /* 16 bits */ uint16_t w = 0xFFFF; vuint16 chunk{}; vvuint16 res{}; dict_t dict{}; for (const auto c : t_text) { if (dict.size() >= DICT_MAX) { res.push_back(std::move(chunk)); chunk = vuint16{}; dict = dict_t{}; w = 0xFFFF; } if (const auto &[exists, pos] = dico(dict, w, static_cast(c)); exists) { w = pos; } else { chunk.push_back(w); w = static_cast(c); } } if (w != 0xFFFF) { chunk.push_back(w); res.push_back(std::move(chunk)); } return res; } /** * Wrapper de la fonction \ref lzw_compress gérant l'ouverture, la lecture, * l'écriture et la fermeture des fichiers d’entrée et de sortie. Si \p * t_out_file est nul (chemin non spécifié), il prendra alors la valeur de * \p t_in_file à laquelle sera annexé l’extension `.lzw`. * * \param[in] t_in_file Chemin vers le fichier d’entrée * \param[in] t_out_file Chemin vers le fichier de sortie */ void compress(const std::string &t_in_file, const char *t_out_file) { std::ofstream out{(t_out_file != nullptr) ? t_out_file : "output.lzw", std::ios::out | std::ios::binary}; if (!out.is_open()) { std::cerr << "Error at " << __FILE__ << ":" << __LINE__ - 4 << ": could not open output file. Aborting...\n"; exit(1); } const auto compressed_text(lzw_compress(read_file(t_in_file))); write_file(out, compressed_text); out.close(); }