diff --git a/.gitignore b/.gitignore index b25c15b..792db02 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ *~ + +gmon\.out diff --git a/src/common.cc b/src/common.cc index 6def9a9..05496cb 100644 --- a/src/common.cc +++ b/src/common.cc @@ -27,7 +27,7 @@ using dic_t = std::map, uint32_t>; * \param c Caractère suivant la chaine de caractères \p nr_chaine * \return std::pair */ -std::pair +const std::pair dico(std::map, uint32_t> &t_dictionary, uint32_t t_nr_chaine, uint8_t t_c) { if (t_nr_chaine == 0xFFFFFFFF) diff --git a/src/common.hh b/src/common.hh index 4fad40c..7cfd222 100644 --- a/src/common.hh +++ b/src/common.hh @@ -10,7 +10,7 @@ #include /// \brief Recherche ou ajout de chaine dans le dictionnaire -std::pair +const std::pair dico(std::map, std::uint32_t> &t_dictionary, std::uint32_t t_nr_chaine, std::uint8_t t_c); diff --git a/src/compress.cc b/src/compress.cc index 2e00dd1..e4f2f17 100644 --- a/src/compress.cc +++ b/src/compress.cc @@ -4,41 +4,50 @@ */ #include "compress.hh" +#include "utf8.hh" #include +#include using dict_t = std::map, uint32_t>; using ustring = std::basic_string; // chaine non encodée using uvec = std::vector; // chaine encodée +using std::printf; /** * La chaine de caractère \p text est lue caractère par caractère, et est et - * selon la valeur de retour de la fonction \ref dico (permettant dans le même - * temps la création du dictionnaire), on rajoute un mot ou pas dans le vecteur - * de caractères UTF-8 représentant des mots de chars compressés. La fonction - * renvoie ledit vecteur de uint32_t. + * selon la valeur de retour de la fonction \ref dico (permettant dans le même + * temps la création du dictionnaire), on rajoute un mot ou pas dans le vecteur + * de caractères UTF-8 représentant des mots de chars compressés. La fonction + * renvoie ledit vecteur de uint32_t. * * \param t_text Chaine de caractères uint8_t représentant le fichier d'entrée * \param t_dictionary Dictionnaire de compression * \return std::vector */ -const uvec compress(const ustring &t_text, dict_t &t_dictionary) { +const uvec lzw_compress(const ustring &t_text, dict_t &t_dictionary) { std::puts("Compressing..."); uvec res{}; uint32_t w = 0xFFFFFFFF; uint32_t len = 0; + + constexpr size_t DICT_MAX = 7936; /* 12 bits */ + #ifdef Debug size_t progress = 0; #endif + for (const auto &c : t_text) { ++len; + #ifdef Debug - printf("\rprogress: %zu / %zu", progress++, t_text.size()); + printf("\rprogress: %zu / %zu", ++progress, t_text.size()); #endif - if (len > 9) { + + if (/* len > LENGTH_MAX || */ t_dictionary.size() >= DICT_MAX ) { res.push_back(static_cast(w)); w = c; len = 0; - } else if (const auto &[exists, pos] = dico(t_dictionary, w, c); + } else if (const auto &[exists, pos] = dico(t_dictionary, w, c); exists) { w = pos; } else { @@ -50,3 +59,61 @@ const uvec compress(const ustring &t_text, dict_t &t_dictionary) { printf("\n"); return res; } + +/** + * \brief function description + * + * 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 t_in_file Chemin vers le fichier d’entrée + * \param t_out_file Chemin vers le fichier de sortie + */ +void compress(const std::string &t_in_file, const char *t_out_file) { + // Fichier d’entrée + std::ifstream input_file{t_in_file}; + + // Fichier de sortie + FILE *out = nullptr; + if (t_out_file) { + out = fopen(t_out_file, "wb"); + } else { + std::string out_name{t_in_file}; + out_name.append(".lzw"); + out = fopen(out_name.c_str(), "wb"); + } + + input_file.seekg(0, std::ios::end); + // string contenant le fichier d’entrée + ustring str(static_cast(input_file.tellg()), + static_cast(0)); + input_file.seekg(0, std::ios::beg); + + // assignation du contenu du fichier à str + str.assign((std::istreambuf_iterator(input_file)), + std::istreambuf_iterator()); + + printf("Size of input file: %zu\n", str.size()); + + dict_t dictionary{}; + + const auto comp_str{lzw_compress(str, dictionary)}; + + printf("\n############################################\n"); + printf(" Compressed!\n"); + printf("############################################\n\n"); + printf("Size of compressed string: %zu\n", comp_str.size()); + printf("Size of dictionary: %zu\n", dictionary.size()); + printf("Compression ratio: %.10f\n", + static_cast(str.size() / comp_str.size())); + + for(const auto c : comp_str) + write_utf8(out, c); + + fclose(out); + input_file.close(); + + return; +} diff --git a/src/compress.hh b/src/compress.hh index e788eb2..848a634 100644 --- a/src/compress.hh +++ b/src/compress.hh @@ -11,8 +11,11 @@ /// \brief Compression d'une chaine de caractères const std::vector -compress(const std::basic_string &t_text, +lzw_compress(const std::basic_string &t_text, std::map, std::uint32_t> &t_dictionary); +/// \brief Wrapper de \ref lzw_compress +void compress(const std::string &t_in_file, const char *t_out_file); + #endif /* LZW_SRC_COMPRESS_H_ */ diff --git a/src/main.cc b/src/main.cc index db65b13..46d7c4d 100644 --- a/src/main.cc +++ b/src/main.cc @@ -6,11 +6,8 @@ * */ -#include "utf8.hh" #include "compress.hh" -#include -#include -#include +#include "getopt.h" using std::printf; using std::puts; @@ -29,55 +26,112 @@ using std::puts; */ using dic_t = std::map, uint32_t>; using ustring = std::basic_string; // chaine non encodée -using uvec = std::vector; // chaine encodée +using uvec = std::vector; // chaine encodée + +void help() { + puts("Usage:"); + puts("lzw [-options] [-i path] [-o path]"); + puts("\tThe default action is to compress the input file to a .lzw file"); + puts("\tin which the directory in which the software is executed."); + puts("\tOptions available:"); + puts("\t-i\tpath to the input file (mandatory)"); + puts("\t-o\tpath to the output file (if the file already exists, it will"); + puts("\t\tbe overwritten). Default: input path + \".lzw\""); + puts("\t-c\tcompress the input file"); + puts("\t-d\tdecompresses the input file to the output file. If no output"); + puts("\t\tpath has not been entered and if the input file ends with "); + puts("\t\t\".lzw\", the extension \".lzw\" will be removed; otherwise, the "); + puts("\t\textension \".uncompresed\" will be added"); +} int main(int argc, char *argv[]) { - if (argc < 2) { - puts("Usage: lzw "); - return 1; - } else { - for (int i = 0; i < argc; ++i) - printf("argv[%d] = %s\n", i, argv[i]); +#ifdef Debug + for (int i = 0; i < argc; ++i) + printf("argv[%d] = %s\n", i, argv[i]); +#endif + + std::string input_path{}; + std::string output_path{}; + bool compressing = true; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"help", no_argument, nullptr, 'h'}, + {"input", required_argument, nullptr, 'i'}, + {"output", required_argument, nullptr, 'o'}, + {"compress", no_argument, nullptr, 'c'}, + {"uncompress", no_argument, nullptr, 'u'}, + {0, 0, 0, 0}}; + int c = getopt_long(argc, argv, "hi:o:cu", long_options, &option_index); + if (c == -1) + break; + switch (c) { + case 0: { +#ifdef Debug + printf("\noption %s", long_options[option_index].name); + if (optarg) { + printf(" with arg %s\n", optarg); + } +#endif + break; + } + case 'h': { +#ifdef Debug + printf("From main - option --help passed\n"); +#endif + help(); + return 0; + } + case 'i': { +#ifdef Debug + printf("From main - option --input with value '%s'\n", optarg); +#endif + input_path = optarg; + break; + } + case 'o': { +#ifdef Debug + printf("From main - option --output with value '%s'\n", optarg); +#endif + output_path = optarg; + break; + } + case 'c': { +#ifdef Debug + printf("From main - option --compress\n"); +#endif + compressing = true; + break; + } + case 'u': { +#ifdef Debug + printf("From main - option --uncompress\n"); +#endif + compressing = false; + break; + } + case '?': { + puts("Error: unknown parameter."); +#ifdef Debug + printf("From main - option -?\n"); +#endif + help(); + return 1; + } + } } - // fichier d'entrée - std::ifstream t{argv[1]}; + if (input_path == "") { + puts("Error: no input file specified"); + return 2; + } - // fichier de sortie - FILE *out = fopen("output.lzw", "wb"); - - // string contenant le fichier d'entrée - ustring str{}; - - // réserver la mémoire dans le string pour le fichier à compresser - t.seekg(0, std::ios::end); - str.reserve(static_cast(t.tellg())); - t.seekg(0, std::ios::beg); - - // assigner au string les caractères du fichier - str.assign((std::istreambuf_iterator(t)), - std::istreambuf_iterator()); - - // test pour voir si tout est bien chargé - printf("Size of str: %zu\n", str.size()); - - // le dictionnaire, initialement vide. - dic_t dictionary{}; - - // compresssion du texte - const auto comp_str{compress(str, dictionary)}; - - printf("Compressed, size of compressed string: %zu\n", comp_str.size()); - printf("Compression ratio: %.10f\n", - static_cast(str.size()) / static_cast(comp_str.size())); - - printf("Number of custom words in the dictionary: %zu\n", dictionary.size()); - - for(const auto c : comp_str) - write_utf8(out, c); - - fclose(out); - t.close(); + if (compressing) { + compress(input_path, output_path.c_str()); + } else { + puts("Not yet implemented :("); + } return 0; }