]> code.bitgloo.com Git - clyne/consteval-huffman.git/commitdiff
Add noexcept; modify to support literal suffix
authorclyne <clyne@bitgloo.com>
Tue, 29 Dec 2020 01:54:37 +0000 (20:54 -0500)
committerGitHub <noreply@github.com>
Tue, 29 Dec 2020 01:54:37 +0000 (20:54 -0500)
consteval_huffman.hpp

index 0ca7aae96c5c50562aa01573e41d9b35e502e18e..594313e2c8763bdb456a0e986649dbc609debc53 100644 (file)
@@ -9,20 +9,35 @@
 
 #include <algorithm>
 #include <span>
-#include <string>
+
+namespace detail
+{
+    template<unsigned long int N>
+    struct huffman_string_container {
+        char data[N];
+        consteval huffman_string_container(const char (&s)[N]) noexcept {
+            std::copy(s, s + N, data);
+        }
+        consteval operator const char *() const noexcept {
+            return data;
+        }
+        consteval auto size() const noexcept {
+            return N;
+        }
+    };
+}
 
 /**
  * Compresses the given data string using Huffman coding, providing a
  * minimal run-time interface for decompressing the data.
  * @tparam data The string of data to be compressed.
- * @tparam data_length The size in bytes of the data, defaulting to strlen() behavior.
  */
-template<const char *data, auto data_length = std::char_traits<char>::length(data)>
-    requires(data_length > 0)
-class huffman_compress
+template<detail::huffman_string_container data>
+    requires(data.size() > 0)
+class huffman_compressor
 {
     using size_t = long int;
-    using usize_t = decltype(data_length);
+    using usize_t = unsigned long int;
 
     // Note: class internals need to be defined before the public interface.
 private:
@@ -42,12 +57,12 @@ private:
      * This list is sorted by increasing frequency.
      * @return Compile-time allocated array of nodes
      */
-    consteval static auto build_node_list() {
+    consteval static auto build_node_list() noexcept {
         // Build a list for counting every occuring value
         auto list = std::span(new node[256] {}, 256);
         for (int i = 0; i < 256; i++)
             list[i].value = i;
-        for (usize_t i = 0; i < data_length; i++)
+        for (usize_t i = 0; i < data.size(); i++)
             list[data[i]].freq++;
 
         std::sort(list.begin(), list.end(),
@@ -68,7 +83,7 @@ private:
     /**
      * Returns the count of how many nodes are in the node tree.
      */
-    consteval static auto tree_count() {
+    consteval static auto tree_count() noexcept {
         auto list = build_node_list();
         auto count = list.size() * 2 - 1;
         delete[] list.data();
@@ -80,7 +95,7 @@ private:
      * Huffman codes.
      * @return Compile-time allocated tree of nodes, root node at index zero.
      */
-    consteval static auto build_node_tree() {
+    consteval static auto build_node_tree() noexcept {
         auto list = build_node_list();
         auto tree = std::span(new node[tree_count()] {}, tree_count());
         
@@ -138,11 +153,11 @@ private:
      * Determines the size of the compressed data.
      * @return A pair of total bytes used, and bits used in last byte.
      */
-    consteval static auto compressed_size_info() {
+    consteval static auto compressed_size_info() noexcept {
         auto tree = build_node_tree();
         size_t bytes = 1, bits = 0;
 
-        for (usize_t i = 0; i < data_length; i++) {
+        for (usize_t i = 0; i < data.size(); i++) {
             auto leaf = std::find_if(tree.begin(), tree.end(),
                 [c = data[i]](const auto& n) { return n.value == c; });
 
@@ -160,8 +175,7 @@ private:
     /**
      * Compresses the input data, storing the result in the object instance.
      */
-    consteval void compress()
-    {
+    consteval void compress() noexcept {
         auto tree = build_node_tree();
 
         // Set up byte and bit count (note, we're compressing the data backwards)
@@ -173,7 +187,7 @@ private:
 
         // Compress data backwards, because we obtain the Huffman codes backwards
         // as we traverse towards the parent node.
-        for (auto i = data_length; i > 0; i--) {
+        for (auto i = data.size(); i > 0; i--) {
             auto leaf = std::find_if(tree.begin(), tree.end(),
                 [c = data[i - 1]](auto& n) { return n.value == c; });
 
@@ -195,7 +209,7 @@ private:
      * Format: three bytes per node.
      *     1. Node value, 2. Distance to left child, 3. Distance to right child.
      */
-    consteval void build_decode_tree() {
+    consteval void build_decode_tree() noexcept {
         auto tree = build_node_tree();
 
         for (usize_t i = 0; i < tree_count(); i++) {
@@ -221,13 +235,13 @@ private:
     }
 
 public:
-    consteval static auto compressed_size() {
+    consteval static auto compressed_size() noexcept {
         return compressed_size_info().first + 3 * tree_count();
     }
-    consteval static auto uncompressed_size() {
-        return data_length;
+    consteval static auto uncompressed_size() noexcept {
+        return data.size();
     }
-    consteval static size_t bytes_saved() {
+    consteval static size_t bytes_saved() noexcept {
         size_t diff = uncompressed_size() - compressed_size();
         return diff > 0 ? diff : 0;
     }
@@ -238,40 +252,43 @@ public:
         using difference_type = std::ptrdiff_t;
         using value_type = int;
 
-        decode_info(const huffman_compress<data, data_length>* comp_data) :
-            m_data(comp_data) { get_next(); }
+        decode_info(const huffman_compressor<data>* comp_data) noexcept
+            m_data(comp_data) { get_next(); }
         decode_info() = default;
 
-        decode_info& end() {
+        consteval static decode_info end() noexcept {
+            decode_info ender;
             if constexpr (bytes_saved() > 0) {
-                const auto [size_bytes, last_bits] = m_data->compressed_size_info();
-                m_pos = size_bytes - 1;
-                m_bit = 1 << (7 - last_bits);
+                const auto [size_bytes, last_bits] = compressed_size_info();
+                ender.m_pos = size_bytes - 1;
+                ender.m_bit = 1 << (7 - last_bits);
             } else {
-                m_pos = data_length + 1;
+                ender.m_pos = data.size() + 1;
             }
 
-            return *this;
+            return ender;
         }
 
-        bool operator==(const decode_info& other) const {
+        bool operator==(const decode_info& other) const noexcept {
             return m_bit == other.m_bit && m_pos == other.m_pos;
         }
-        auto operator*() const {
+        auto operator*() const noexcept {
             return m_current;
         }
-        decode_info& operator++() {
+        decode_info& operator++() noexcept {
             get_next();
             return *this;
         }
-        decode_info operator++(int) {
+        decode_info operator++(int) noexcept {
             auto old = *this;
             get_next();
             return old;
         }
 
     private:
-        void get_next() {
+        void get_next() noexcept {
+            if (*this == end())
+                return;
             if constexpr (bytes_saved() > 0) {
                 auto *node = m_data->decode_tree;
                 auto pos = m_pos;
@@ -291,40 +308,44 @@ public:
             }
         }
 
-        const huffman_compress<data> *m_data = nullptr;
+        const huffman_compressor<data> *m_data = nullptr;
         size_t m_pos = 0;
         unsigned char m_bit = 0x80;
         int m_current = -1;
 
-        friend class huffman_compress;
+        friend class huffman_compressor;
     };
 
-    auto begin() const {
+    auto begin() const noexcept {
         return decode_info(this);
     }
-    auto end() const {
-        return decode_info(this).end();
+    auto end() const noexcept {
+        return decode_info::end();
     }
-    auto cbegin() const { begin(); }
-    auto cend() const { end(); }
+    auto cbegin() const noexcept { begin(); }
+    auto cend() const noexcept { end(); }
 
     // Stick the requires clause here just so it's run
-    consteval huffman_compress()
+    consteval huffman_compressor() noexcept
         requires (std::forward_iterator<decode_info>)
     {
         if constexpr (bytes_saved() > 0) {
             build_decode_tree();
             compress();
-        } else {
-            std::copy(data, data + data_length, compressed_data);
         }
     }
 
 private:
     // Contains the compressed data.
-    unsigned char compressed_data[bytes_saved() > 0 ? compressed_size_info().first : data_length] = {0};
+    unsigned char compressed_data[bytes_saved() > 0 ? compressed_size_info().first : 1] = {0};
     // Contains a 'tree' that can be used to decompress the data.
-    unsigned char decode_tree[3 * tree_count()] = {0};
+    unsigned char decode_tree[bytes_saved() > 0 ? 3 * tree_count() : 1] = {0};
 };
 
+template <detail::huffman_string_container hsc>
+constexpr auto operator ""_huffman()
+{
+    return huffman_compressor<hsc>();
+}
+
 #endif // TCSULLIVAN_CONSTEVAL_HUFFMAN_HPP_