796 lines
24 KiB
C
796 lines
24 KiB
C
/*
|
|
* FSE : Finite State Entropy encoder
|
|
* Copyright (C) 2013-2015, Yann Collet.
|
|
*
|
|
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License version 2 as published by the
|
|
* Free Software Foundation. This program is dual-licensed; you may select
|
|
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
|
* ("BSD").
|
|
*
|
|
* You can contact the author at :
|
|
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
|
*/
|
|
|
|
/* **************************************************************
|
|
* Compiler specifics
|
|
****************************************************************/
|
|
#define FORCE_INLINE static __always_inline
|
|
|
|
/* **************************************************************
|
|
* Includes
|
|
****************************************************************/
|
|
#include "bitstream.h"
|
|
#include "fse.h"
|
|
#include <linux/compiler.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/math64.h>
|
|
#include <linux/string.h> /* memcpy, memset */
|
|
|
|
/* **************************************************************
|
|
* Error Management
|
|
****************************************************************/
|
|
#define FSE_STATIC_ASSERT(c) \
|
|
{ \
|
|
enum { FSE_static_assert = 1 / (int)(!!(c)) }; \
|
|
} /* use only *after* variable declarations */
|
|
|
|
/* **************************************************************
|
|
* Templates
|
|
****************************************************************/
|
|
/*
|
|
designed to be included
|
|
for type-specific functions (template emulation in C)
|
|
Objective is to write these functions only once, for improved maintenance
|
|
*/
|
|
|
|
/* safety checks */
|
|
#ifndef FSE_FUNCTION_EXTENSION
|
|
#error "FSE_FUNCTION_EXTENSION must be defined"
|
|
#endif
|
|
#ifndef FSE_FUNCTION_TYPE
|
|
#error "FSE_FUNCTION_TYPE must be defined"
|
|
#endif
|
|
|
|
/* Function names */
|
|
#define FSE_CAT(X, Y) X##Y
|
|
#define FSE_FUNCTION_NAME(X, Y) FSE_CAT(X, Y)
|
|
#define FSE_TYPE_NAME(X, Y) FSE_CAT(X, Y)
|
|
|
|
/* Function templates */
|
|
|
|
/* FSE_buildCTable_wksp() :
|
|
* Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`).
|
|
* wkspSize should be sized to handle worst case situation, which is `1<<max_tableLog * sizeof(FSE_FUNCTION_TYPE)`
|
|
* workSpace must also be properly aligned with FSE_FUNCTION_TYPE requirements
|
|
*/
|
|
size_t FSE_buildCTable_wksp(FSE_CTable *ct, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize)
|
|
{
|
|
U32 const tableSize = 1 << tableLog;
|
|
U32 const tableMask = tableSize - 1;
|
|
void *const ptr = ct;
|
|
U16 *const tableU16 = ((U16 *)ptr) + 2;
|
|
void *const FSCT = ((U32 *)ptr) + 1 /* header */ + (tableLog ? tableSize >> 1 : 1);
|
|
FSE_symbolCompressionTransform *const symbolTT = (FSE_symbolCompressionTransform *)(FSCT);
|
|
U32 const step = FSE_TABLESTEP(tableSize);
|
|
U32 highThreshold = tableSize - 1;
|
|
|
|
U32 *cumul;
|
|
FSE_FUNCTION_TYPE *tableSymbol;
|
|
size_t spaceUsed32 = 0;
|
|
|
|
cumul = (U32 *)workspace + spaceUsed32;
|
|
spaceUsed32 += FSE_MAX_SYMBOL_VALUE + 2;
|
|
tableSymbol = (FSE_FUNCTION_TYPE *)((U32 *)workspace + spaceUsed32);
|
|
spaceUsed32 += ALIGN(sizeof(FSE_FUNCTION_TYPE) * ((size_t)1 << tableLog), sizeof(U32)) >> 2;
|
|
|
|
if ((spaceUsed32 << 2) > workspaceSize)
|
|
return ERROR(tableLog_tooLarge);
|
|
workspace = (U32 *)workspace + spaceUsed32;
|
|
workspaceSize -= (spaceUsed32 << 2);
|
|
|
|
/* CTable header */
|
|
tableU16[-2] = (U16)tableLog;
|
|
tableU16[-1] = (U16)maxSymbolValue;
|
|
|
|
/* For explanations on how to distribute symbol values over the table :
|
|
* http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */
|
|
|
|
/* symbol start positions */
|
|
{
|
|
U32 u;
|
|
cumul[0] = 0;
|
|
for (u = 1; u <= maxSymbolValue + 1; u++) {
|
|
if (normalizedCounter[u - 1] == -1) { /* Low proba symbol */
|
|
cumul[u] = cumul[u - 1] + 1;
|
|
tableSymbol[highThreshold--] = (FSE_FUNCTION_TYPE)(u - 1);
|
|
} else {
|
|
cumul[u] = cumul[u - 1] + normalizedCounter[u - 1];
|
|
}
|
|
}
|
|
cumul[maxSymbolValue + 1] = tableSize + 1;
|
|
}
|
|
|
|
/* Spread symbols */
|
|
{
|
|
U32 position = 0;
|
|
U32 symbol;
|
|
for (symbol = 0; symbol <= maxSymbolValue; symbol++) {
|
|
int nbOccurences;
|
|
for (nbOccurences = 0; nbOccurences < normalizedCounter[symbol]; nbOccurences++) {
|
|
tableSymbol[position] = (FSE_FUNCTION_TYPE)symbol;
|
|
position = (position + step) & tableMask;
|
|
while (position > highThreshold)
|
|
position = (position + step) & tableMask; /* Low proba area */
|
|
}
|
|
}
|
|
|
|
if (position != 0)
|
|
return ERROR(GENERIC); /* Must have gone through all positions */
|
|
}
|
|
|
|
/* Build table */
|
|
{
|
|
U32 u;
|
|
for (u = 0; u < tableSize; u++) {
|
|
FSE_FUNCTION_TYPE s = tableSymbol[u]; /* note : static analyzer may not understand tableSymbol is properly initialized */
|
|
tableU16[cumul[s]++] = (U16)(tableSize + u); /* TableU16 : sorted by symbol order; gives next state value */
|
|
}
|
|
}
|
|
|
|
/* Build Symbol Transformation Table */
|
|
{
|
|
unsigned total = 0;
|
|
unsigned s;
|
|
for (s = 0; s <= maxSymbolValue; s++) {
|
|
switch (normalizedCounter[s]) {
|
|
case 0: break;
|
|
|
|
case -1:
|
|
case 1:
|
|
symbolTT[s].deltaNbBits = (tableLog << 16) - (1 << tableLog);
|
|
symbolTT[s].deltaFindState = total - 1;
|
|
total++;
|
|
break;
|
|
default: {
|
|
U32 const maxBitsOut = tableLog - BIT_highbit32(normalizedCounter[s] - 1);
|
|
U32 const minStatePlus = normalizedCounter[s] << maxBitsOut;
|
|
symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus;
|
|
symbolTT[s].deltaFindState = total - normalizedCounter[s];
|
|
total += normalizedCounter[s];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-**************************************************************
|
|
* FSE NCount encoding-decoding
|
|
****************************************************************/
|
|
size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog)
|
|
{
|
|
size_t const maxHeaderSize = (((maxSymbolValue + 1) * tableLog) >> 3) + 3;
|
|
return maxSymbolValue ? maxHeaderSize : FSE_NCOUNTBOUND; /* maxSymbolValue==0 ? use default */
|
|
}
|
|
|
|
static size_t FSE_writeNCount_generic(void *header, size_t headerBufferSize, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog,
|
|
unsigned writeIsSafe)
|
|
{
|
|
BYTE *const ostart = (BYTE *)header;
|
|
BYTE *out = ostart;
|
|
BYTE *const oend = ostart + headerBufferSize;
|
|
int nbBits;
|
|
const int tableSize = 1 << tableLog;
|
|
int remaining;
|
|
int threshold;
|
|
U32 bitStream;
|
|
int bitCount;
|
|
unsigned charnum = 0;
|
|
int previous0 = 0;
|
|
|
|
bitStream = 0;
|
|
bitCount = 0;
|
|
/* Table Size */
|
|
bitStream += (tableLog - FSE_MIN_TABLELOG) << bitCount;
|
|
bitCount += 4;
|
|
|
|
/* Init */
|
|
remaining = tableSize + 1; /* +1 for extra accuracy */
|
|
threshold = tableSize;
|
|
nbBits = tableLog + 1;
|
|
|
|
while (remaining > 1) { /* stops at 1 */
|
|
if (previous0) {
|
|
unsigned start = charnum;
|
|
while (!normalizedCounter[charnum])
|
|
charnum++;
|
|
while (charnum >= start + 24) {
|
|
start += 24;
|
|
bitStream += 0xFFFFU << bitCount;
|
|
if ((!writeIsSafe) && (out > oend - 2))
|
|
return ERROR(dstSize_tooSmall); /* Buffer overflow */
|
|
out[0] = (BYTE)bitStream;
|
|
out[1] = (BYTE)(bitStream >> 8);
|
|
out += 2;
|
|
bitStream >>= 16;
|
|
}
|
|
while (charnum >= start + 3) {
|
|
start += 3;
|
|
bitStream += 3 << bitCount;
|
|
bitCount += 2;
|
|
}
|
|
bitStream += (charnum - start) << bitCount;
|
|
bitCount += 2;
|
|
if (bitCount > 16) {
|
|
if ((!writeIsSafe) && (out > oend - 2))
|
|
return ERROR(dstSize_tooSmall); /* Buffer overflow */
|
|
out[0] = (BYTE)bitStream;
|
|
out[1] = (BYTE)(bitStream >> 8);
|
|
out += 2;
|
|
bitStream >>= 16;
|
|
bitCount -= 16;
|
|
}
|
|
}
|
|
{
|
|
int count = normalizedCounter[charnum++];
|
|
int const max = (2 * threshold - 1) - remaining;
|
|
remaining -= count < 0 ? -count : count;
|
|
count++; /* +1 for extra accuracy */
|
|
if (count >= threshold)
|
|
count += max; /* [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ */
|
|
bitStream += count << bitCount;
|
|
bitCount += nbBits;
|
|
bitCount -= (count < max);
|
|
previous0 = (count == 1);
|
|
if (remaining < 1)
|
|
return ERROR(GENERIC);
|
|
while (remaining < threshold)
|
|
nbBits--, threshold >>= 1;
|
|
}
|
|
if (bitCount > 16) {
|
|
if ((!writeIsSafe) && (out > oend - 2))
|
|
return ERROR(dstSize_tooSmall); /* Buffer overflow */
|
|
out[0] = (BYTE)bitStream;
|
|
out[1] = (BYTE)(bitStream >> 8);
|
|
out += 2;
|
|
bitStream >>= 16;
|
|
bitCount -= 16;
|
|
}
|
|
}
|
|
|
|
/* flush remaining bitStream */
|
|
if ((!writeIsSafe) && (out > oend - 2))
|
|
return ERROR(dstSize_tooSmall); /* Buffer overflow */
|
|
out[0] = (BYTE)bitStream;
|
|
out[1] = (BYTE)(bitStream >> 8);
|
|
out += (bitCount + 7) / 8;
|
|
|
|
if (charnum > maxSymbolValue + 1)
|
|
return ERROR(GENERIC);
|
|
|
|
return (out - ostart);
|
|
}
|
|
|
|
size_t FSE_writeNCount(void *buffer, size_t bufferSize, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
|
|
{
|
|
if (tableLog > FSE_MAX_TABLELOG)
|
|
return ERROR(tableLog_tooLarge); /* Unsupported */
|
|
if (tableLog < FSE_MIN_TABLELOG)
|
|
return ERROR(GENERIC); /* Unsupported */
|
|
|
|
if (bufferSize < FSE_NCountWriteBound(maxSymbolValue, tableLog))
|
|
return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 0);
|
|
|
|
return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 1);
|
|
}
|
|
|
|
/*-**************************************************************
|
|
* Counting histogram
|
|
****************************************************************/
|
|
/*! FSE_count_simple
|
|
This function counts byte values within `src`, and store the histogram into table `count`.
|
|
It doesn't use any additional memory.
|
|
But this function is unsafe : it doesn't check that all values within `src` can fit into `count`.
|
|
For this reason, prefer using a table `count` with 256 elements.
|
|
@return : count of most numerous element
|
|
*/
|
|
size_t FSE_count_simple(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize)
|
|
{
|
|
const BYTE *ip = (const BYTE *)src;
|
|
const BYTE *const end = ip + srcSize;
|
|
unsigned maxSymbolValue = *maxSymbolValuePtr;
|
|
unsigned max = 0;
|
|
|
|
memset(count, 0, (maxSymbolValue + 1) * sizeof(*count));
|
|
if (srcSize == 0) {
|
|
*maxSymbolValuePtr = 0;
|
|
return 0;
|
|
}
|
|
|
|
while (ip < end)
|
|
count[*ip++]++;
|
|
|
|
while (!count[maxSymbolValue])
|
|
maxSymbolValue--;
|
|
*maxSymbolValuePtr = maxSymbolValue;
|
|
|
|
{
|
|
U32 s;
|
|
for (s = 0; s <= maxSymbolValue; s++)
|
|
if (count[s] > max)
|
|
max = count[s];
|
|
}
|
|
|
|
return (size_t)max;
|
|
}
|
|
|
|
/* FSE_count_parallel_wksp() :
|
|
* Same as FSE_count_parallel(), but using an externally provided scratch buffer.
|
|
* `workSpace` size must be a minimum of `1024 * sizeof(unsigned)`` */
|
|
static size_t FSE_count_parallel_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned checkMax,
|
|
unsigned *const workSpace)
|
|
{
|
|
const BYTE *ip = (const BYTE *)source;
|
|
const BYTE *const iend = ip + sourceSize;
|
|
unsigned maxSymbolValue = *maxSymbolValuePtr;
|
|
unsigned max = 0;
|
|
U32 *const Counting1 = workSpace;
|
|
U32 *const Counting2 = Counting1 + 256;
|
|
U32 *const Counting3 = Counting2 + 256;
|
|
U32 *const Counting4 = Counting3 + 256;
|
|
|
|
memset(Counting1, 0, 4 * 256 * sizeof(unsigned));
|
|
|
|
/* safety checks */
|
|
if (!sourceSize) {
|
|
memset(count, 0, maxSymbolValue + 1);
|
|
*maxSymbolValuePtr = 0;
|
|
return 0;
|
|
}
|
|
if (!maxSymbolValue)
|
|
maxSymbolValue = 255; /* 0 == default */
|
|
|
|
/* by stripes of 16 bytes */
|
|
{
|
|
U32 cached = ZSTD_read32(ip);
|
|
ip += 4;
|
|
while (ip < iend - 15) {
|
|
U32 c = cached;
|
|
cached = ZSTD_read32(ip);
|
|
ip += 4;
|
|
Counting1[(BYTE)c]++;
|
|
Counting2[(BYTE)(c >> 8)]++;
|
|
Counting3[(BYTE)(c >> 16)]++;
|
|
Counting4[c >> 24]++;
|
|
c = cached;
|
|
cached = ZSTD_read32(ip);
|
|
ip += 4;
|
|
Counting1[(BYTE)c]++;
|
|
Counting2[(BYTE)(c >> 8)]++;
|
|
Counting3[(BYTE)(c >> 16)]++;
|
|
Counting4[c >> 24]++;
|
|
c = cached;
|
|
cached = ZSTD_read32(ip);
|
|
ip += 4;
|
|
Counting1[(BYTE)c]++;
|
|
Counting2[(BYTE)(c >> 8)]++;
|
|
Counting3[(BYTE)(c >> 16)]++;
|
|
Counting4[c >> 24]++;
|
|
c = cached;
|
|
cached = ZSTD_read32(ip);
|
|
ip += 4;
|
|
Counting1[(BYTE)c]++;
|
|
Counting2[(BYTE)(c >> 8)]++;
|
|
Counting3[(BYTE)(c >> 16)]++;
|
|
Counting4[c >> 24]++;
|
|
}
|
|
ip -= 4;
|
|
}
|
|
|
|
/* finish last symbols */
|
|
while (ip < iend)
|
|
Counting1[*ip++]++;
|
|
|
|
if (checkMax) { /* verify stats will fit into destination table */
|
|
U32 s;
|
|
for (s = 255; s > maxSymbolValue; s--) {
|
|
Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s];
|
|
if (Counting1[s])
|
|
return ERROR(maxSymbolValue_tooSmall);
|
|
}
|
|
}
|
|
|
|
{
|
|
U32 s;
|
|
for (s = 0; s <= maxSymbolValue; s++) {
|
|
count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s];
|
|
if (count[s] > max)
|
|
max = count[s];
|
|
}
|
|
}
|
|
|
|
while (!count[maxSymbolValue])
|
|
maxSymbolValue--;
|
|
*maxSymbolValuePtr = maxSymbolValue;
|
|
return (size_t)max;
|
|
}
|
|
|
|
/* FSE_countFast_wksp() :
|
|
* Same as FSE_countFast(), but using an externally provided scratch buffer.
|
|
* `workSpace` size must be table of >= `1024` unsigned */
|
|
size_t FSE_countFast_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned *workSpace)
|
|
{
|
|
if (sourceSize < 1500)
|
|
return FSE_count_simple(count, maxSymbolValuePtr, source, sourceSize);
|
|
return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 0, workSpace);
|
|
}
|
|
|
|
/* FSE_count_wksp() :
|
|
* Same as FSE_count(), but using an externally provided scratch buffer.
|
|
* `workSpace` size must be table of >= `1024` unsigned */
|
|
size_t FSE_count_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned *workSpace)
|
|
{
|
|
if (*maxSymbolValuePtr < 255)
|
|
return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 1, workSpace);
|
|
*maxSymbolValuePtr = 255;
|
|
return FSE_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, workSpace);
|
|
}
|
|
|
|
/*-**************************************************************
|
|
* FSE Compression Code
|
|
****************************************************************/
|
|
/*! FSE_sizeof_CTable() :
|
|
FSE_CTable is a variable size structure which contains :
|
|
`U16 tableLog;`
|
|
`U16 maxSymbolValue;`
|
|
`U16 nextStateNumber[1 << tableLog];` // This size is variable
|
|
`FSE_symbolCompressionTransform symbolTT[maxSymbolValue+1];` // This size is variable
|
|
Allocation is manual (C standard does not support variable-size structures).
|
|
*/
|
|
size_t FSE_sizeof_CTable(unsigned maxSymbolValue, unsigned tableLog)
|
|
{
|
|
if (tableLog > FSE_MAX_TABLELOG)
|
|
return ERROR(tableLog_tooLarge);
|
|
return FSE_CTABLE_SIZE_U32(tableLog, maxSymbolValue) * sizeof(U32);
|
|
}
|
|
|
|
/* provides the minimum logSize to safely represent a distribution */
|
|
static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue)
|
|
{
|
|
U32 minBitsSrc = BIT_highbit32((U32)(srcSize - 1)) + 1;
|
|
U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2;
|
|
U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols;
|
|
return minBits;
|
|
}
|
|
|
|
unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus)
|
|
{
|
|
U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus;
|
|
U32 tableLog = maxTableLog;
|
|
U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue);
|
|
if (tableLog == 0)
|
|
tableLog = FSE_DEFAULT_TABLELOG;
|
|
if (maxBitsSrc < tableLog)
|
|
tableLog = maxBitsSrc; /* Accuracy can be reduced */
|
|
if (minBits > tableLog)
|
|
tableLog = minBits; /* Need a minimum to safely represent all symbol values */
|
|
if (tableLog < FSE_MIN_TABLELOG)
|
|
tableLog = FSE_MIN_TABLELOG;
|
|
if (tableLog > FSE_MAX_TABLELOG)
|
|
tableLog = FSE_MAX_TABLELOG;
|
|
return tableLog;
|
|
}
|
|
|
|
unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
|
|
{
|
|
return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 2);
|
|
}
|
|
|
|
/* Secondary normalization method.
|
|
To be used when primary method fails. */
|
|
|
|
static size_t FSE_normalizeM2(short *norm, U32 tableLog, const unsigned *count, size_t total, U32 maxSymbolValue)
|
|
{
|
|
short const NOT_YET_ASSIGNED = -2;
|
|
U32 s;
|
|
U32 distributed = 0;
|
|
U32 ToDistribute;
|
|
|
|
/* Init */
|
|
U32 const lowThreshold = (U32)(total >> tableLog);
|
|
U32 lowOne = (U32)((total * 3) >> (tableLog + 1));
|
|
|
|
for (s = 0; s <= maxSymbolValue; s++) {
|
|
if (count[s] == 0) {
|
|
norm[s] = 0;
|
|
continue;
|
|
}
|
|
if (count[s] <= lowThreshold) {
|
|
norm[s] = -1;
|
|
distributed++;
|
|
total -= count[s];
|
|
continue;
|
|
}
|
|
if (count[s] <= lowOne) {
|
|
norm[s] = 1;
|
|
distributed++;
|
|
total -= count[s];
|
|
continue;
|
|
}
|
|
|
|
norm[s] = NOT_YET_ASSIGNED;
|
|
}
|
|
ToDistribute = (1 << tableLog) - distributed;
|
|
|
|
if ((total / ToDistribute) > lowOne) {
|
|
/* risk of rounding to zero */
|
|
lowOne = (U32)((total * 3) / (ToDistribute * 2));
|
|
for (s = 0; s <= maxSymbolValue; s++) {
|
|
if ((norm[s] == NOT_YET_ASSIGNED) && (count[s] <= lowOne)) {
|
|
norm[s] = 1;
|
|
distributed++;
|
|
total -= count[s];
|
|
continue;
|
|
}
|
|
}
|
|
ToDistribute = (1 << tableLog) - distributed;
|
|
}
|
|
|
|
if (distributed == maxSymbolValue + 1) {
|
|
/* all values are pretty poor;
|
|
probably incompressible data (should have already been detected);
|
|
find max, then give all remaining points to max */
|
|
U32 maxV = 0, maxC = 0;
|
|
for (s = 0; s <= maxSymbolValue; s++)
|
|
if (count[s] > maxC)
|
|
maxV = s, maxC = count[s];
|
|
norm[maxV] += (short)ToDistribute;
|
|
return 0;
|
|
}
|
|
|
|
if (total == 0) {
|
|
/* all of the symbols were low enough for the lowOne or lowThreshold */
|
|
for (s = 0; ToDistribute > 0; s = (s + 1) % (maxSymbolValue + 1))
|
|
if (norm[s] > 0)
|
|
ToDistribute--, norm[s]++;
|
|
return 0;
|
|
}
|
|
|
|
{
|
|
U64 const vStepLog = 62 - tableLog;
|
|
U64 const mid = (1ULL << (vStepLog - 1)) - 1;
|
|
U64 const rStep = div_u64((((U64)1 << vStepLog) * ToDistribute) + mid, (U32)total); /* scale on remaining */
|
|
U64 tmpTotal = mid;
|
|
for (s = 0; s <= maxSymbolValue; s++) {
|
|
if (norm[s] == NOT_YET_ASSIGNED) {
|
|
U64 const end = tmpTotal + (count[s] * rStep);
|
|
U32 const sStart = (U32)(tmpTotal >> vStepLog);
|
|
U32 const sEnd = (U32)(end >> vStepLog);
|
|
U32 const weight = sEnd - sStart;
|
|
if (weight < 1)
|
|
return ERROR(GENERIC);
|
|
norm[s] = (short)weight;
|
|
tmpTotal = end;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
size_t FSE_normalizeCount(short *normalizedCounter, unsigned tableLog, const unsigned *count, size_t total, unsigned maxSymbolValue)
|
|
{
|
|
/* Sanity checks */
|
|
if (tableLog == 0)
|
|
tableLog = FSE_DEFAULT_TABLELOG;
|
|
if (tableLog < FSE_MIN_TABLELOG)
|
|
return ERROR(GENERIC); /* Unsupported size */
|
|
if (tableLog > FSE_MAX_TABLELOG)
|
|
return ERROR(tableLog_tooLarge); /* Unsupported size */
|
|
if (tableLog < FSE_minTableLog(total, maxSymbolValue))
|
|
return ERROR(GENERIC); /* Too small tableLog, compression potentially impossible */
|
|
|
|
{
|
|
U32 const rtbTable[] = {0, 473195, 504333, 520860, 550000, 700000, 750000, 830000};
|
|
U64 const scale = 62 - tableLog;
|
|
U64 const step = div_u64((U64)1 << 62, (U32)total); /* <== here, one division ! */
|
|
U64 const vStep = 1ULL << (scale - 20);
|
|
int stillToDistribute = 1 << tableLog;
|
|
unsigned s;
|
|
unsigned largest = 0;
|
|
short largestP = 0;
|
|
U32 lowThreshold = (U32)(total >> tableLog);
|
|
|
|
for (s = 0; s <= maxSymbolValue; s++) {
|
|
if (count[s] == total)
|
|
return 0; /* rle special case */
|
|
if (count[s] == 0) {
|
|
normalizedCounter[s] = 0;
|
|
continue;
|
|
}
|
|
if (count[s] <= lowThreshold) {
|
|
normalizedCounter[s] = -1;
|
|
stillToDistribute--;
|
|
} else {
|
|
short proba = (short)((count[s] * step) >> scale);
|
|
if (proba < 8) {
|
|
U64 restToBeat = vStep * rtbTable[proba];
|
|
proba += (count[s] * step) - ((U64)proba << scale) > restToBeat;
|
|
}
|
|
if (proba > largestP)
|
|
largestP = proba, largest = s;
|
|
normalizedCounter[s] = proba;
|
|
stillToDistribute -= proba;
|
|
}
|
|
}
|
|
if (-stillToDistribute >= (normalizedCounter[largest] >> 1)) {
|
|
/* corner case, need another normalization method */
|
|
size_t const errorCode = FSE_normalizeM2(normalizedCounter, tableLog, count, total, maxSymbolValue);
|
|
if (FSE_isError(errorCode))
|
|
return errorCode;
|
|
} else
|
|
normalizedCounter[largest] += (short)stillToDistribute;
|
|
}
|
|
|
|
return tableLog;
|
|
}
|
|
|
|
/* fake FSE_CTable, for raw (uncompressed) input */
|
|
size_t FSE_buildCTable_raw(FSE_CTable *ct, unsigned nbBits)
|
|
{
|
|
const unsigned tableSize = 1 << nbBits;
|
|
const unsigned tableMask = tableSize - 1;
|
|
const unsigned maxSymbolValue = tableMask;
|
|
void *const ptr = ct;
|
|
U16 *const tableU16 = ((U16 *)ptr) + 2;
|
|
void *const FSCT = ((U32 *)ptr) + 1 /* header */ + (tableSize >> 1); /* assumption : tableLog >= 1 */
|
|
FSE_symbolCompressionTransform *const symbolTT = (FSE_symbolCompressionTransform *)(FSCT);
|
|
unsigned s;
|
|
|
|
/* Sanity checks */
|
|
if (nbBits < 1)
|
|
return ERROR(GENERIC); /* min size */
|
|
|
|
/* header */
|
|
tableU16[-2] = (U16)nbBits;
|
|
tableU16[-1] = (U16)maxSymbolValue;
|
|
|
|
/* Build table */
|
|
for (s = 0; s < tableSize; s++)
|
|
tableU16[s] = (U16)(tableSize + s);
|
|
|
|
/* Build Symbol Transformation Table */
|
|
{
|
|
const U32 deltaNbBits = (nbBits << 16) - (1 << nbBits);
|
|
for (s = 0; s <= maxSymbolValue; s++) {
|
|
symbolTT[s].deltaNbBits = deltaNbBits;
|
|
symbolTT[s].deltaFindState = s - 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* fake FSE_CTable, for rle input (always same symbol) */
|
|
size_t FSE_buildCTable_rle(FSE_CTable *ct, BYTE symbolValue)
|
|
{
|
|
void *ptr = ct;
|
|
U16 *tableU16 = ((U16 *)ptr) + 2;
|
|
void *FSCTptr = (U32 *)ptr + 2;
|
|
FSE_symbolCompressionTransform *symbolTT = (FSE_symbolCompressionTransform *)FSCTptr;
|
|
|
|
/* header */
|
|
tableU16[-2] = (U16)0;
|
|
tableU16[-1] = (U16)symbolValue;
|
|
|
|
/* Build table */
|
|
tableU16[0] = 0;
|
|
tableU16[1] = 0; /* just in case */
|
|
|
|
/* Build Symbol Transformation Table */
|
|
symbolTT[symbolValue].deltaNbBits = 0;
|
|
symbolTT[symbolValue].deltaFindState = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static size_t FSE_compress_usingCTable_generic(void *dst, size_t dstSize, const void *src, size_t srcSize, const FSE_CTable *ct, const unsigned fast)
|
|
{
|
|
const BYTE *const istart = (const BYTE *)src;
|
|
const BYTE *const iend = istart + srcSize;
|
|
const BYTE *ip = iend;
|
|
|
|
BIT_CStream_t bitC;
|
|
FSE_CState_t CState1, CState2;
|
|
|
|
/* init */
|
|
if (srcSize <= 2)
|
|
return 0;
|
|
{
|
|
size_t const initError = BIT_initCStream(&bitC, dst, dstSize);
|
|
if (FSE_isError(initError))
|
|
return 0; /* not enough space available to write a bitstream */
|
|
}
|
|
|
|
#define FSE_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s))
|
|
|
|
if (srcSize & 1) {
|
|
FSE_initCState2(&CState1, ct, *--ip);
|
|
FSE_initCState2(&CState2, ct, *--ip);
|
|
FSE_encodeSymbol(&bitC, &CState1, *--ip);
|
|
FSE_FLUSHBITS(&bitC);
|
|
} else {
|
|
FSE_initCState2(&CState2, ct, *--ip);
|
|
FSE_initCState2(&CState1, ct, *--ip);
|
|
}
|
|
|
|
/* join to mod 4 */
|
|
srcSize -= 2;
|
|
if ((sizeof(bitC.bitContainer) * 8 > FSE_MAX_TABLELOG * 4 + 7) && (srcSize & 2)) { /* test bit 2 */
|
|
FSE_encodeSymbol(&bitC, &CState2, *--ip);
|
|
FSE_encodeSymbol(&bitC, &CState1, *--ip);
|
|
FSE_FLUSHBITS(&bitC);
|
|
}
|
|
|
|
/* 2 or 4 encoding per loop */
|
|
while (ip > istart) {
|
|
|
|
FSE_encodeSymbol(&bitC, &CState2, *--ip);
|
|
|
|
if (sizeof(bitC.bitContainer) * 8 < FSE_MAX_TABLELOG * 2 + 7) /* this test must be static */
|
|
FSE_FLUSHBITS(&bitC);
|
|
|
|
FSE_encodeSymbol(&bitC, &CState1, *--ip);
|
|
|
|
if (sizeof(bitC.bitContainer) * 8 > FSE_MAX_TABLELOG * 4 + 7) { /* this test must be static */
|
|
FSE_encodeSymbol(&bitC, &CState2, *--ip);
|
|
FSE_encodeSymbol(&bitC, &CState1, *--ip);
|
|
}
|
|
|
|
FSE_FLUSHBITS(&bitC);
|
|
}
|
|
|
|
FSE_flushCState(&bitC, &CState2);
|
|
FSE_flushCState(&bitC, &CState1);
|
|
return BIT_closeCStream(&bitC);
|
|
}
|
|
|
|
size_t FSE_compress_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const FSE_CTable *ct)
|
|
{
|
|
unsigned const fast = (dstSize >= FSE_BLOCKBOUND(srcSize));
|
|
|
|
if (fast)
|
|
return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 1);
|
|
else
|
|
return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 0);
|
|
}
|
|
|
|
size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); }
|