mirror of
https://github.com/coderforlife/ms-compress
synced 2026-06-08 13:34:55 +00:00
179 lines
6.2 KiB
C++
179 lines
6.2 KiB
C++
// ms-compress: implements Microsoft compression algorithms
|
|
// Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com
|
|
//
|
|
// This library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#ifndef MSCOMP_HUFFMAN_ENCODER
|
|
#define MSCOMP_HUFFMAN_ENCODER
|
|
|
|
#include "internal.h"
|
|
#include "Bitstream.h"
|
|
|
|
#define INVALID_SYMBOL 0xFFFF
|
|
|
|
template <byte kNumBitsMax, uint16_t NumSymbols>
|
|
class HuffmanEncoder
|
|
{
|
|
private:
|
|
uint16_t codes[NumSymbols];
|
|
byte lens[NumSymbols];
|
|
|
|
// Merge-sorts syms[l, r) using conditions[syms[x]]
|
|
// Use merge-sort so that it is stable, keeping symbols in increasing order
|
|
template<typename T> // T is either uint32_t or byte
|
|
static void msort(uint16_t* syms, uint16_t* temp, T* conditions, uint_fast16_t l, uint_fast16_t r)
|
|
{
|
|
uint_fast16_t len = r - l;
|
|
if (len <= 1) { return; }
|
|
|
|
// Not required to do these special in-place sorts, but is a bit more efficient
|
|
else if (len == 2)
|
|
{
|
|
if (conditions[syms[l+1]] < conditions[syms[ l ]]) { uint16_t t = syms[l+1]; syms[l+1] = syms[ l ]; syms[ l ] = t; }
|
|
return;
|
|
}
|
|
else if (len == 3)
|
|
{
|
|
if (conditions[syms[l+1]] < conditions[syms[ l ]]) { uint16_t t = syms[l+1]; syms[l+1] = syms[ l ]; syms[ l ] = t; }
|
|
if (conditions[syms[l+2]] < conditions[syms[l+1]]) { uint16_t t = syms[l+2]; syms[l+2] = syms[l+1]; syms[l+1] = t;
|
|
if (conditions[syms[l+1]]<conditions[syms[l]]) { uint16_t t = syms[l+1]; syms[l+1] = syms[ l ]; syms[ l ] = t; } }
|
|
return;
|
|
}
|
|
|
|
// Merge-Sort
|
|
else
|
|
{
|
|
uint_fast16_t m = l + (len >> 1), i = l, j = l, k = m;
|
|
|
|
// Divide and Conquer
|
|
msort(syms, temp, conditions, l, m);
|
|
msort(syms, temp, conditions, m, r);
|
|
memcpy(temp+l, syms+l, len*sizeof(uint16_t));
|
|
|
|
// Merge
|
|
while (j < m && k < r) syms[i++] = (conditions[temp[k]] < conditions[temp[j]]) ? temp[k++] : temp[j++]; // if == then does j which is from the lower half, keeping stable
|
|
if (j < m) memcpy(syms+i, temp+j, (m-j)*sizeof(uint16_t));
|
|
else if (k < r) memcpy(syms+i, temp+k, (r-k)*sizeof(uint16_t));
|
|
}
|
|
}
|
|
|
|
public:
|
|
INLINE const const_bytes CreateCodes(uint32_t symbol_counts[]) // 3 kb stack
|
|
{
|
|
uint16_t* syms, syms_by_count[NumSymbols], syms_by_len[NumSymbols], temp[NumSymbols]; // 3*2*512 = 3 kb
|
|
uint_fast16_t i, j, len, pos, s;
|
|
|
|
memset(this->codes, 0, NumSymbols*sizeof(uint16_t));
|
|
memset(this->lens, 0, NumSymbols*sizeof(byte));
|
|
|
|
// Fill the syms_by_count, syms_by_length, and huffman_lens with the symbols that were found
|
|
for (i = 0, len = 0; i < NumSymbols; ++i) { if (symbol_counts[i]) { syms_by_count[len] = (uint16_t)i; syms_by_len[len++] = (uint16_t)i; this->lens[i] = kNumBitsMax; } }
|
|
|
|
|
|
////////// Get the Huffman lengths //////////
|
|
msort(syms = syms_by_count, temp, symbol_counts, 0, len); // sort by the counts
|
|
if (len == 1)
|
|
{
|
|
this->lens[syms[0]] = 1; // never going to happen, but the code below would probably assign a length of 0 which is not right
|
|
}
|
|
else
|
|
{
|
|
///// Package-Merge Algorithm /////
|
|
typedef struct _collection // 516 bytes each
|
|
{
|
|
uint_fast16_t count;
|
|
byte symbols[NumSymbols];
|
|
} collection;
|
|
collection* cols = (collection*)malloc(32*sizeof(collection)), *next_cols = (collection*)malloc(32*sizeof(collection)), *temp_cols; // 32.25 kb initial allocation
|
|
uint_fast16_t cols_cap = 32, cols_len = 0, cols_pos, next_cols_len = 0;
|
|
|
|
if (!cols || !next_cols) { free(cols); free(next_cols); return NULL; }
|
|
|
|
// Start at the lowest value row, adding new collection
|
|
for (j = 0; j < kNumBitsMax; ++j)
|
|
{
|
|
cols_pos = 0;
|
|
pos = 0;
|
|
|
|
// All but the last one/none get added to collections
|
|
while ((cols_len-cols_pos + len-pos) > 1)
|
|
{
|
|
if (cols_cap == next_cols_len)
|
|
{
|
|
cols_cap <<= 1;
|
|
|
|
temp_cols = (collection*)realloc(cols, cols_cap*sizeof(collection));
|
|
if (temp_cols == NULL) { free(cols); free(next_cols); return NULL; }
|
|
cols = temp_cols;
|
|
|
|
temp_cols = (collection*)realloc(next_cols, cols_cap*sizeof(collection));
|
|
if (temp_cols == NULL) { free(cols); free(next_cols); return NULL; }
|
|
next_cols = temp_cols;
|
|
}
|
|
memset(next_cols+next_cols_len, 0, sizeof(collection));
|
|
for (i = 0; i < 2; ++i) // hopefully unrolled...
|
|
{
|
|
if (pos >= len || (cols_pos < cols_len && cols[cols_pos].count < symbol_counts[syms[pos]]))
|
|
{
|
|
// Add cols[cols_pos]
|
|
next_cols[next_cols_len].count += cols[cols_pos].count;
|
|
for (s = 0; s < NumSymbols; ++s)
|
|
next_cols[next_cols_len].symbols[s] += cols[cols_pos].symbols[s];
|
|
++cols_pos;
|
|
}
|
|
else
|
|
{
|
|
// Add syms[pos]
|
|
next_cols[next_cols_len].count += symbol_counts[syms[pos]];
|
|
++next_cols[next_cols_len].symbols[syms[pos]];
|
|
++pos;
|
|
}
|
|
}
|
|
++next_cols_len;
|
|
}
|
|
|
|
// Left over gets dropped
|
|
if (cols_pos < cols_len)
|
|
for (s = 0; s < NumSymbols; ++s)
|
|
this->lens[s] -= cols[cols_pos].symbols[s];
|
|
else if (pos < len)
|
|
--this->lens[syms[pos]];
|
|
|
|
// Move the next_collections to the current collections
|
|
temp_cols = cols; cols = next_cols; next_cols = temp_cols;
|
|
cols_len = next_cols_len;
|
|
next_cols_len = 0;
|
|
}
|
|
free(cols);
|
|
free(next_cols);
|
|
|
|
|
|
////////// Create Huffman codes from lengths //////////
|
|
msort(syms = syms_by_len, temp, this->lens, 0, len); // Sort by the code lengths
|
|
for (i = 1; i < len; ++i)
|
|
{
|
|
// Code is previous code +1 with added zeroes for increased code length
|
|
this->codes[syms[i]] = (this->codes[syms[i-1]] + 1) << (this->lens[syms[i]] - this->lens[syms[i-1]]);
|
|
}
|
|
}
|
|
|
|
|
|
return this->lens;
|
|
}
|
|
|
|
INLINE bool EncodeSymbol(uint_fast16_t sym, OutputBitstream *bits) const { return bits->WriteBits(this->codes[sym], this->lens[sym]); }
|
|
};
|
|
|
|
#endif
|