Merge pull request #3621 from DieHertz/dataflash-read-compressed

Compression for MSP_DATAFLASH_READ
This commit is contained in:
Martin Budden 2017-07-29 13:18:29 +01:00 committed by GitHub
commit 471e01210b
10 changed files with 1073 additions and 22 deletions

View File

@ -7,6 +7,8 @@ COMMON_SRC = \
common/bitarray.c \
common/encoding.c \
common/filter.c \
common/huffman.c \
common/huffman_table.c \
common/maths.c \
common/printf.c \
common/streambuf.c \

104
src/main/common/huffman.c Normal file
View File

@ -0,0 +1,104 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight 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.
*
* Cleanflight 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdint.h>
#include "platform.h"
#ifdef USE_HUFFMAN
#include "huffman.h"
int huffmanEncodeBuf(uint8_t *outBuf, int outBufLen, const uint8_t *inBuf, int inLen, const huffmanTable_t *huffmanTable)
{
int ret = 0;
uint8_t *outByte = outBuf;
*outByte = 0;
uint8_t outBit = 0x80;
for (int ii = 0; ii < inLen; ++ii) {
const int huffCodeLen = huffmanTable[*inBuf].codeLen;
const uint16_t huffCode = huffmanTable[*inBuf].code;
++inBuf;
uint16_t testBit = 0x8000;
for (int jj = 0; jj < huffCodeLen; ++jj) {
if (huffCode & testBit) {
*outByte |= outBit;
}
testBit >>= 1;
outBit >>= 1;
if (outBit == 0) {
outBit = 0x80;
++outByte;
*outByte = 0;
++ret;
}
if (ret >= outBufLen && ii < inLen - 1 && jj < huffCodeLen - 1) {
return -1;
}
}
}
if (outBit != 0x80) {
// ensure last character in output buffer is counted
++ret;
}
return ret;
}
int huffmanEncodeBufStreaming(huffmanState_t *state, const uint8_t *inBuf, int inLen, const huffmanTable_t *huffmanTable)
{
uint8_t *savedOutBytePtr = state->outByte;
uint8_t savedOutByte = *savedOutBytePtr;
for (const uint8_t *pos = inBuf, *end = inBuf + inLen; pos < end; ++pos) {
const int huffCodeLen = huffmanTable[*pos].codeLen;
const uint16_t huffCode = huffmanTable[*pos].code;
uint16_t testBit = 0x8000;
for (int jj = 0; jj < huffCodeLen; ++jj) {
if (huffCode & testBit) {
*state->outByte |= state->outBit;
}
testBit >>= 1;
state->outBit >>= 1;
if (state->outBit == 0) {
state->outBit = 0x80;
++state->outByte;
*state->outByte = 0;
++state->bytesWritten;
}
// if buffer is filled and we haven't finished compressing
if (state->bytesWritten >= state->outBufLen && (pos < end - 1 || jj < huffCodeLen - 1)) {
// restore savedOutByte
*savedOutBytePtr = savedOutByte;
return -1;
}
}
}
return 0;
}
#endif

44
src/main/common/huffman.h Normal file
View File

@ -0,0 +1,44 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight 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.
*
* Cleanflight 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#define HUFFMAN_TABLE_SIZE 257 // 256 characters plus EOF
typedef struct huffmanTable_s {
uint8_t codeLen;
uint16_t code;
} huffmanTable_t;
typedef struct huffmanState_s {
uint16_t bytesWritten;
uint8_t *outByte;
uint16_t outBufLen;
uint8_t outBit;
} huffmanState_t;
extern const huffmanTable_t huffmanTable[HUFFMAN_TABLE_SIZE];
struct huffmanInfo_s {
uint16_t uncompressedByteCount;
};
#define HUFFMAN_INFO_SIZE sizeof(struct huffmanInfo_s)
int huffmanEncodeBuf(uint8_t *outBuf, int outBufLen, const uint8_t *inBuf, int inLen, const huffmanTable_t *huffmanTable);
int huffmanEncodeBufStreaming(huffmanState_t *state, const uint8_t *inBuf, int inLen, const huffmanTable_t *huffmanTable);

View File

@ -0,0 +1,285 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight 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.
*
* Cleanflight 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include "huffman.h"
/*
* Huffman Table, used to compress a bytestream
*/
const huffmanTable_t huffmanTable[HUFFMAN_TABLE_SIZE] = {
// Len Code Char Bitcode
{ 2, 0xC000 }, // 0x00 11
{ 3, 0xA000 }, // 0x01 101
{ 4, 0x9000 }, // 0x02 1001
{ 5, 0x8800 }, // 0x03 10001
{ 5, 0x8000 }, // 0x04 10000
{ 6, 0x7400 }, // 0x05 011101
{ 6, 0x7000 }, // 0x06 011100
{ 6, 0x6C00 }, // 0x07 011011
{ 6, 0x6800 }, // 0x08 011010
{ 7, 0x6200 }, // 0x09 0110001
{ 7, 0x6000 }, // 0x0A 0110000
{ 7, 0x5E00 }, // 0x0B 0101111
{ 7, 0x5C00 }, // 0x0C 0101110
{ 7, 0x5A00 }, // 0x0D 0101101
{ 7, 0x5800 }, // 0x0E 0101100
{ 7, 0x5600 }, // 0x0F 0101011
{ 6, 0x6400 }, // 0x10 011001
{ 7, 0x5400 }, // 0x11 0101010
{ 7, 0x5200 }, // 0x12 0101001
{ 8, 0x5100 }, // 0x13 01010001
{ 8, 0x5000 }, // 0x14 01010000
{ 8, 0x4F00 }, // 0x15 01001111
{ 8, 0x4E00 }, // 0x16 01001110
{ 8, 0x4D00 }, // 0x17 01001101
{ 8, 0x4C00 }, // 0x18 01001100
{ 8, 0x4B00 }, // 0x19 01001011
{ 8, 0x4A00 }, // 0x1A 01001010
{ 8, 0x4900 }, // 0x1B 01001001
{ 8, 0x4800 }, // 0x1C 01001000
{ 8, 0x4700 }, // 0x1D 01000111
{ 8, 0x4600 }, // 0x1E 01000110
{ 8, 0x4500 }, // 0x1F 01000101
{ 8, 0x4400 }, // 0x20 01000100
{ 8, 0x4300 }, // 0x21 01000011
{ 8, 0x4200 }, // 0x22 01000010
{ 8, 0x4100 }, // 0x23 01000001
{ 8, 0x4000 }, // 0x24 01000000
{ 9, 0x3C80 }, // 0x25 001111001
{ 9, 0x3C00 }, // 0x26 001111000
{ 9, 0x3B80 }, // 0x27 001110111
{ 9, 0x3B00 }, // 0x28 001110110
{ 9, 0x3A80 }, // 0x29 001110101
{ 9, 0x3A00 }, // 0x2A 001110100
{ 9, 0x3980 }, // 0x2B 001110011
{ 9, 0x3900 }, // 0x2C 001110010
{ 9, 0x3880 }, // 0x2D 001110001
{ 9, 0x3800 }, // 0x2E 001110000
{ 9, 0x3780 }, // 0x2F 001101111
{ 8, 0x3F00 }, // 0x30 00111111
{ 9, 0x3700 }, // 0x31 001101110
{ 9, 0x3680 }, // 0x32 001101101
{ 9, 0x3600 }, // 0x33 001101100
{ 9, 0x3580 }, // 0x34 001101011
{ 9, 0x3500 }, // 0x35 001101010
{ 9, 0x3480 }, // 0x36 001101001
{ 9, 0x3400 }, // 0x37 001101000
{ 9, 0x3380 }, // 0x38 001100111
{ 9, 0x3300 }, // 0x39 001100110
{ 9, 0x3280 }, // 0x3A 001100101
{ 9, 0x3200 }, // 0x3B 001100100
{ 9, 0x3180 }, // 0x3C 001100011
{ 9, 0x3100 }, // 0x3D 001100010
{ 9, 0x3080 }, // 0x3E 001100001
{ 9, 0x3000 }, // 0x3F 001100000
{ 8, 0x3E00 }, // 0x40 00111110
{ 9, 0x2F80 }, // 0x41 001011111
{ 9, 0x2F00 }, // 0x42 001011110
{ 9, 0x2E80 }, // 0x43 001011101
{ 9, 0x2E00 }, // 0x44 001011100
{ 9, 0x2D80 }, // 0x45 001011011
{ 9, 0x2D00 }, // 0x46 001011010
{ 9, 0x2C80 }, // 0x47 001011001
{ 9, 0x2C00 }, // 0x48 001011000
{ 9, 0x2B80 }, // 0x49 001010111
{ 10, 0x27C0 }, // 0x4A 0010011111
{ 10, 0x2780 }, // 0x4B 0010011110
{ 9, 0x2B00 }, // 0x4C 001010110
{ 10, 0x2740 }, // 0x4D 0010011101
{ 10, 0x2700 }, // 0x4E 0010011100
{ 9, 0x2A80 }, // 0x4F 001010101
{ 5, 0x7800 }, // 0x50 01111
{ 9, 0x2A00 }, // 0x51 001010100
{ 10, 0x26C0 }, // 0x52 0010011011
{ 10, 0x2680 }, // 0x53 0010011010
{ 10, 0x2640 }, // 0x54 0010011001
{ 10, 0x2600 }, // 0x55 0010011000
{ 10, 0x25C0 }, // 0x56 0010010111
{ 10, 0x2580 }, // 0x57 0010010110
{ 10, 0x2540 }, // 0x58 0010010101
{ 10, 0x2500 }, // 0x59 0010010100
{ 10, 0x24C0 }, // 0x5A 0010010011
{ 10, 0x2480 }, // 0x5B 0010010010
{ 10, 0x2440 }, // 0x5C 0010010001
{ 10, 0x2400 }, // 0x5D 0010010000
{ 10, 0x23C0 }, // 0x5E 0010001111
{ 10, 0x2380 }, // 0x5F 0010001110
{ 10, 0x2340 }, // 0x60 0010001101
{ 10, 0x2300 }, // 0x61 0010001100
{ 10, 0x22C0 }, // 0x62 0010001011
{ 10, 0x2280 }, // 0x63 0010001010
{ 10, 0x2240 }, // 0x64 0010001001
{ 10, 0x2200 }, // 0x65 0010001000
{ 10, 0x21C0 }, // 0x66 0010000111
{ 10, 0x2180 }, // 0x67 0010000110
{ 10, 0x2140 }, // 0x68 0010000101
{ 10, 0x2100 }, // 0x69 0010000100
{ 10, 0x20C0 }, // 0x6A 0010000011
{ 10, 0x2080 }, // 0x6B 0010000010
{ 10, 0x2040 }, // 0x6C 0010000001
{ 10, 0x2000 }, // 0x6D 0010000000
{ 10, 0x1FC0 }, // 0x6E 0001111111
{ 10, 0x1F80 }, // 0x6F 0001111110
{ 10, 0x1F40 }, // 0x70 0001111101
{ 10, 0x1F00 }, // 0x71 0001111100
{ 10, 0x1EC0 }, // 0x72 0001111011
{ 10, 0x1E80 }, // 0x73 0001111010
{ 10, 0x1E40 }, // 0x74 0001111001
{ 10, 0x1E00 }, // 0x75 0001111000
{ 10, 0x1DC0 }, // 0x76 0001110111
{ 10, 0x1D80 }, // 0x77 0001110110
{ 10, 0x1D40 }, // 0x78 0001110101
{ 10, 0x1D00 }, // 0x79 0001110100
{ 10, 0x1CC0 }, // 0x7A 0001110011
{ 10, 0x1C80 }, // 0x7B 0001110010
{ 10, 0x1C40 }, // 0x7C 0001110001
{ 10, 0x1C00 }, // 0x7D 0001110000
{ 10, 0x1BC0 }, // 0x7E 0001101111
{ 10, 0x1B80 }, // 0x7F 0001101110
{ 9, 0x2980 }, // 0x80 001010011
{ 10, 0x1B40 }, // 0x81 0001101101
{ 10, 0x1B00 }, // 0x82 0001101100
{ 10, 0x1AC0 }, // 0x83 0001101011
{ 10, 0x1A80 }, // 0x84 0001101010
{ 10, 0x1A40 }, // 0x85 0001101001
{ 10, 0x1A00 }, // 0x86 0001101000
{ 10, 0x19C0 }, // 0x87 0001100111
{ 10, 0x1980 }, // 0x88 0001100110
{ 10, 0x1940 }, // 0x89 0001100101
{ 10, 0x1900 }, // 0x8A 0001100100
{ 10, 0x18C0 }, // 0x8B 0001100011
{ 10, 0x1880 }, // 0x8C 0001100010
{ 10, 0x1840 }, // 0x8D 0001100001
{ 10, 0x1800 }, // 0x8E 0001100000
{ 10, 0x17C0 }, // 0x8F 0001011111
{ 10, 0x1780 }, // 0x90 0001011110
{ 10, 0x1740 }, // 0x91 0001011101
{ 10, 0x1700 }, // 0x92 0001011100
{ 10, 0x16C0 }, // 0x93 0001011011
{ 10, 0x1680 }, // 0x94 0001011010
{ 10, 0x1640 }, // 0x95 0001011001
{ 10, 0x1600 }, // 0x96 0001011000
{ 10, 0x15C0 }, // 0x97 0001010111
{ 10, 0x1580 }, // 0x98 0001010110
{ 10, 0x1540 }, // 0x99 0001010101
{ 10, 0x1500 }, // 0x9A 0001010100
{ 10, 0x14C0 }, // 0x9B 0001010011
{ 10, 0x1480 }, // 0x9C 0001010010
{ 10, 0x1440 }, // 0x9D 0001010001
{ 10, 0x1400 }, // 0x9E 0001010000
{ 10, 0x13C0 }, // 0x9F 0001001111
{ 10, 0x1380 }, // 0xA0 0001001110
{ 10, 0x1340 }, // 0xA1 0001001101
{ 10, 0x1300 }, // 0xA2 0001001100
{ 10, 0x12C0 }, // 0xA3 0001001011
{ 10, 0x1280 }, // 0xA4 0001001010
{ 10, 0x1240 }, // 0xA5 0001001001
{ 10, 0x1200 }, // 0xA6 0001001000
{ 10, 0x11C0 }, // 0xA7 0001000111
{ 10, 0x1180 }, // 0xA8 0001000110
{ 10, 0x1140 }, // 0xA9 0001000101
{ 10, 0x1100 }, // 0xAA 0001000100
{ 10, 0x10C0 }, // 0xAB 0001000011
{ 10, 0x1080 }, // 0xAC 0001000010
{ 10, 0x1040 }, // 0xAD 0001000001
{ 10, 0x1000 }, // 0xAE 0001000000
{ 10, 0x0FC0 }, // 0xAF 0000111111
{ 10, 0x0F80 }, // 0xB0 0000111110
{ 10, 0x0F40 }, // 0xB1 0000111101
{ 10, 0x0F00 }, // 0xB2 0000111100
{ 10, 0x0EC0 }, // 0xB3 0000111011
{ 10, 0x0E80 }, // 0xB4 0000111010
{ 10, 0x0E40 }, // 0xB5 0000111001
{ 10, 0x0E00 }, // 0xB6 0000111000
{ 10, 0x0DC0 }, // 0xB7 0000110111
{ 10, 0x0D80 }, // 0xB8 0000110110
{ 10, 0x0D40 }, // 0xB9 0000110101
{ 10, 0x0D00 }, // 0xBA 0000110100
{ 10, 0x0CC0 }, // 0xBB 0000110011
{ 10, 0x0C80 }, // 0xBC 0000110010
{ 10, 0x0C40 }, // 0xBD 0000110001
{ 10, 0x0C00 }, // 0xBE 0000110000
{ 10, 0x0BC0 }, // 0xBF 0000101111
{ 10, 0x0B80 }, // 0xC0 0000101110
{ 10, 0x0B40 }, // 0xC1 0000101101
{ 10, 0x0B00 }, // 0xC2 0000101100
{ 10, 0x0AC0 }, // 0xC3 0000101011
{ 10, 0x0A80 }, // 0xC4 0000101010
{ 10, 0x0A40 }, // 0xC5 0000101001
{ 10, 0x0A00 }, // 0xC6 0000101000
{ 10, 0x09C0 }, // 0xC7 0000100111
{ 10, 0x0980 }, // 0xC8 0000100110
{ 10, 0x0940 }, // 0xC9 0000100101
{ 10, 0x0900 }, // 0xCA 0000100100
{ 10, 0x08C0 }, // 0xCB 0000100011
{ 10, 0x0880 }, // 0xCC 0000100010
{ 10, 0x0840 }, // 0xCD 0000100001
{ 10, 0x0800 }, // 0xCE 0000100000
{ 10, 0x07C0 }, // 0xCF 0000011111
{ 10, 0x0780 }, // 0xD0 0000011110
{ 10, 0x0740 }, // 0xD1 0000011101
{ 10, 0x0700 }, // 0xD2 0000011100
{ 10, 0x06C0 }, // 0xD3 0000011011
{ 10, 0x0680 }, // 0xD4 0000011010
{ 11, 0x0320 }, // 0xD5 00000011001
{ 10, 0x0640 }, // 0xD6 0000011001
{ 10, 0x0600 }, // 0xD7 0000011000
{ 10, 0x05C0 }, // 0xD8 0000010111
{ 10, 0x0580 }, // 0xD9 0000010110
{ 10, 0x0540 }, // 0xDA 0000010101
{ 10, 0x0500 }, // 0xDB 0000010100
{ 10, 0x04C0 }, // 0xDC 0000010011
{ 11, 0x0300 }, // 0xDD 00000011000
{ 10, 0x0480 }, // 0xDE 0000010010
{ 10, 0x0440 }, // 0xDF 0000010001
{ 9, 0x2900 }, // 0xE0 001010010
{ 10, 0x0400 }, // 0xE1 0000010000
{ 10, 0x03C0 }, // 0xE2 0000001111
{ 11, 0x02E0 }, // 0xE3 00000010111
{ 10, 0x0380 }, // 0xE4 0000001110
{ 11, 0x02C0 }, // 0xE5 00000010110
{ 11, 0x02A0 }, // 0xE6 00000010101
{ 11, 0x0280 }, // 0xE7 00000010100
{ 11, 0x0260 }, // 0xE8 00000010011
{ 11, 0x0240 }, // 0xE9 00000010010
{ 11, 0x0220 }, // 0xEA 00000010001
{ 11, 0x0200 }, // 0xEB 00000010000
{ 11, 0x01E0 }, // 0xEC 00000001111
{ 11, 0x01C0 }, // 0xED 00000001110
{ 11, 0x01A0 }, // 0xEE 00000001101
{ 10, 0x0340 }, // 0xEF 0000001101
{ 8, 0x3D00 }, // 0xF0 00111101
{ 9, 0x2880 }, // 0xF1 001010001
{ 11, 0x0180 }, // 0xF2 00000001100
{ 11, 0x0160 }, // 0xF3 00000001011
{ 11, 0x0140 }, // 0xF4 00000001010
{ 11, 0x0120 }, // 0xF5 00000001001
{ 11, 0x0100 }, // 0xF6 00000001000
{ 11, 0x00E0 }, // 0xF7 00000000111
{ 11, 0x00C0 }, // 0xF8 00000000110
{ 12, 0x0010 }, // 0xF9 000000000001
{ 11, 0x00A0 }, // 0xFA 00000000101
{ 11, 0x0080 }, // 0xFB 00000000100
{ 11, 0x0060 }, // 0xFC 00000000011
{ 11, 0x0040 }, // 0xFD 00000000010
{ 11, 0x0020 }, // 0xFE 00000000001
{ 9, 0x2800 }, // 0xFF 001010000
{ 12, 0x0000 }, // EOF 000000000000
};

View File

@ -33,6 +33,7 @@
#include "common/color.h"
#include "common/maths.h"
#include "common/streambuf.h"
#include "common/huffman.h"
#include "config/config_eeprom.h"
#include "config/feature.h"
@ -286,7 +287,12 @@ static void serializeDataflashSummaryReply(sbuf_t *dst)
}
#ifdef USE_FLASHFS
static void serializeDataflashReadReply(sbuf_t *dst, uint32_t address, const uint16_t size, bool useLegacyFormat)
enum compressionType_e {
NO_COMPRESSION,
HUFFMAN
};
static void serializeDataflashReadReply(sbuf_t *dst, uint32_t address, const uint16_t size, bool useLegacyFormat, bool allowCompression)
{
BUILD_BUG_ON(MSP_PORT_DATAFLASH_INFO_SIZE < 16);
@ -296,26 +302,73 @@ static void serializeDataflashReadReply(sbuf_t *dst, uint32_t address, const uin
readLen = bytesRemainingInBuf;
}
// size will be lower than that requested if we reach end of volume
if (readLen > flashfsGetSize() - address) {
const uint32_t flashfsSize = flashfsGetSize();
if (readLen > flashfsSize - address) {
// truncate the request
readLen = flashfsGetSize() - address;
readLen = flashfsSize - address;
}
sbufWriteU32(dst, address);
if (!useLegacyFormat) {
// new format supports variable read lengths
sbufWriteU16(dst, readLen);
sbufWriteU8(dst, 0); // placeholder for compression format
}
// bytesRead will equal readLen
const int bytesRead = flashfsReadAbs(address, sbufPtr(dst), readLen);
sbufAdvance(dst, bytesRead);
// legacy format does not support compression
const uint8_t compressionMethod = (!allowCompression || useLegacyFormat) ? NO_COMPRESSION : HUFFMAN;
if (useLegacyFormat) {
// pad the buffer with zeros
for (int i = bytesRead; i < size; i++) {
sbufWriteU8(dst, 0);
if (compressionMethod == NO_COMPRESSION) {
if (!useLegacyFormat) {
// new format supports variable read lengths
sbufWriteU16(dst, readLen);
sbufWriteU8(dst, 0); // placeholder for compression format
}
const int bytesRead = flashfsReadAbs(address, sbufPtr(dst), readLen);
sbufAdvance(dst, bytesRead);
if (useLegacyFormat) {
// pad the buffer with zeros
for (int i = bytesRead; i < size; i++) {
sbufWriteU8(dst, 0);
}
}
} else {
#ifdef USE_HUFFMAN
// compress in 256-byte chunks
const uint16_t READ_BUFFER_SIZE = 256;
uint8_t readBuffer[READ_BUFFER_SIZE];
huffmanState_t state = {
.bytesWritten = 0,
.outByte = sbufPtr(dst) + MSP_PORT_DATAFLASH_INFO_SIZE + HUFFMAN_INFO_SIZE,
.outBufLen = readLen - HUFFMAN_INFO_SIZE,
.outBit = 0x80,
};
*state.outByte = 0;
uint16_t bytesReadTotal = 0;
// read until output buffer overflows or flash is exhausted
while (state.bytesWritten < state.outBufLen && address + bytesReadTotal < flashfsSize) {
const int bytesRead = flashfsReadAbs(address + bytesReadTotal, readBuffer,
MIN(sizeof(readBuffer), flashfsSize - address - bytesReadTotal));
const int status = huffmanEncodeBufStreaming(&state, readBuffer, bytesRead, huffmanTable);
if (status == -1) {
// overflow
break;
}
bytesReadTotal += bytesRead;
}
if (state.outBit != 0x80) {
++state.bytesWritten;
}
// header
sbufWriteU16(dst, sizeof(uint16_t) + state.bytesWritten);
sbufWriteU8(dst, compressionMethod);
// payload
sbufWriteU16(dst, bytesReadTotal);
sbufAdvance(dst, state.bytesWritten);
#endif
}
}
#endif // USE_FLASHFS
@ -1185,16 +1238,20 @@ static void mspFcDataFlashReadCommand(sbuf_t *dst, sbuf_t *src)
const unsigned int dataSize = sbufBytesRemaining(src);
const uint32_t readAddress = sbufReadU32(src);
uint16_t readLength;
bool allowCompression = false;
bool useLegacyFormat;
if (dataSize >= sizeof(uint32_t) + sizeof(uint16_t)) {
readLength = sbufReadU16(src);
if (sbufBytesRemaining(src)) {
allowCompression = sbufReadU8(src);
}
useLegacyFormat = false;
} else {
readLength = 128;
useLegacyFormat = true;
}
serializeDataflashReadReply(dst, readAddress, readLength, useLegacyFormat);
serializeDataflashReadReply(dst, readAddress, readLength, useLegacyFormat, allowCompression);
}
#endif

View File

@ -56,12 +56,11 @@
//#define SD_CS_PIN PB12
//#define SD_SPI_INSTANCE SPI2
//#define USE_FLASHFS
//#define USE_FLASH_M25P16
#define USE_FLASHFS
#define USE_FLASH_M25P16
//#define M25P16_CS_GPIO GPIOB
//#define M25P16_CS_PIN GPIO_Pin_12
//#define M25P16_SPI_INSTANCE SPI2
#define M25P16_CS_PIN PB12
#define M25P16_SPI_INSTANCE SPI2
// SPI1
// PB5 SPI1_MOSI
// PB4 SPI1_MISO

View File

@ -1,5 +1,5 @@
F3_TARGETS += $(TARGET)
FEATURES = VCP SDCARD
FEATURES = VCP SDCARD ONBOARDFLASH
TARGET_SRC = \
drivers/accgyro/accgyro_adxl345.c \

View File

@ -125,6 +125,7 @@
#define VTX_SMARTAUDIO
#define VTX_TRAMP
#define USE_CAMERA_CONTROL
#define USE_HUFFMAN
#ifdef USE_SERIALRX_SPEKTRUM
#define USE_SPEKTRUM_BIND

View File

@ -235,6 +235,13 @@ rcsplit_unitest_DEFINES := \
USE_UART3 \
USE_RCSPLIT \
huffman_unittest_SRC := \
$(USER_DIR)/common/huffman.c \
$(USER_DIR)/common/huffman_table.c
huffman_unittest_DEFINES := \
USE_HUFFMAN
# Please tweak the following variable definitions as needed by your
# project, except GTEST_HEADERS, which you can use in your own targets
# but shouldn't modify.

View File

@ -0,0 +1,552 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight 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.
*
* Cleanflight 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
extern "C" {
#include "common/huffman.h"
}
#include "unittest_macros.h"
#include "gtest/gtest.h"
#define OUTBUF_LEN 128
static uint8_t outBuf[OUTBUF_LEN];
/*
* Huffman Tree, used to decompress a bytestream.
*
* The leaf nodes of the Huffman tree are stored in an array.
*/
#define HUFFMAN_EOF (-1)
#define HUFFMAN_TREE_SIZE 257 // 256 characters plus EOF
typedef struct huffmanTree_s {
int16_t value;
uint16_t codeLen;
uint16_t code;
} huffmanTree_t;
static const huffmanTree_t huffmanTree[HUFFMAN_TREE_SIZE] = {
// Char Len Code Bitcode
{ 0x00, 2, 0x0003 }, // 11
{ 0x01, 3, 0x0005 }, // 101
{ 0x02, 4, 0x0009 }, // 1001
{ 0x03, 5, 0x0011 }, // 10001
{ 0x04, 5, 0x0010 }, // 10000
{ 0x50, 5, 0x000F }, // 01111
{ 0x05, 6, 0x001D }, // 011101
{ 0x06, 6, 0x001C }, // 011100
{ 0x07, 6, 0x001B }, // 011011
{ 0x08, 6, 0x001A }, // 011010
{ 0x10, 6, 0x0019 }, // 011001
{ 0x09, 7, 0x0031 }, // 0110001
{ 0x0A, 7, 0x0030 }, // 0110000
{ 0x0B, 7, 0x002F }, // 0101111
{ 0x0C, 7, 0x002E }, // 0101110
{ 0x0D, 7, 0x002D }, // 0101101
{ 0x0E, 7, 0x002C }, // 0101100
{ 0x0F, 7, 0x002B }, // 0101011
{ 0x11, 7, 0x002A }, // 0101010
{ 0x12, 7, 0x0029 }, // 0101001
{ 0x13, 8, 0x0051 }, // 01010001
{ 0x14, 8, 0x0050 }, // 01010000
{ 0x15, 8, 0x004F }, // 01001111
{ 0x16, 8, 0x004E }, // 01001110
{ 0x17, 8, 0x004D }, // 01001101
{ 0x18, 8, 0x004C }, // 01001100
{ 0x19, 8, 0x004B }, // 01001011
{ 0x1A, 8, 0x004A }, // 01001010
{ 0x1B, 8, 0x0049 }, // 01001001
{ 0x1C, 8, 0x0048 }, // 01001000
{ 0x1D, 8, 0x0047 }, // 01000111
{ 0x1E, 8, 0x0046 }, // 01000110
{ 0x1F, 8, 0x0045 }, // 01000101
{ 0x20, 8, 0x0044 }, // 01000100
{ 0x21, 8, 0x0043 }, // 01000011
{ 0x22, 8, 0x0042 }, // 01000010
{ 0x23, 8, 0x0041 }, // 01000001
{ 0x24, 8, 0x0040 }, // 01000000
{ 0x30, 8, 0x003F }, // 00111111
{ 0x40, 8, 0x003E }, // 00111110
{ 0xF0, 8, 0x003D }, // 00111101
{ 0x25, 9, 0x0079 }, // 001111001
{ 0x26, 9, 0x0078 }, // 001111000
{ 0x27, 9, 0x0077 }, // 001110111
{ 0x28, 9, 0x0076 }, // 001110110
{ 0x29, 9, 0x0075 }, // 001110101
{ 0x2A, 9, 0x0074 }, // 001110100
{ 0x2B, 9, 0x0073 }, // 001110011
{ 0x2C, 9, 0x0072 }, // 001110010
{ 0x2D, 9, 0x0071 }, // 001110001
{ 0x2E, 9, 0x0070 }, // 001110000
{ 0x2F, 9, 0x006F }, // 001101111
{ 0x31, 9, 0x006E }, // 001101110
{ 0x32, 9, 0x006D }, // 001101101
{ 0x33, 9, 0x006C }, // 001101100
{ 0x34, 9, 0x006B }, // 001101011
{ 0x35, 9, 0x006A }, // 001101010
{ 0x36, 9, 0x0069 }, // 001101001
{ 0x37, 9, 0x0068 }, // 001101000
{ 0x38, 9, 0x0067 }, // 001100111
{ 0x39, 9, 0x0066 }, // 001100110
{ 0x3A, 9, 0x0065 }, // 001100101
{ 0x3B, 9, 0x0064 }, // 001100100
{ 0x3C, 9, 0x0063 }, // 001100011
{ 0x3D, 9, 0x0062 }, // 001100010
{ 0x3E, 9, 0x0061 }, // 001100001
{ 0x3F, 9, 0x0060 }, // 001100000
{ 0x41, 9, 0x005F }, // 001011111
{ 0x42, 9, 0x005E }, // 001011110
{ 0x43, 9, 0x005D }, // 001011101
{ 0x44, 9, 0x005C }, // 001011100
{ 0x45, 9, 0x005B }, // 001011011
{ 0x46, 9, 0x005A }, // 001011010
{ 0x47, 9, 0x0059 }, // 001011001
{ 0x48, 9, 0x0058 }, // 001011000
{ 0x49, 9, 0x0057 }, // 001010111
{ 0x4C, 9, 0x0056 }, // 001010110
{ 0x4F, 9, 0x0055 }, // 001010101
{ 0x51, 9, 0x0054 }, // 001010100
{ 0x80, 9, 0x0053 }, // 001010011
{ 0xE0, 9, 0x0052 }, // 001010010
{ 0xF1, 9, 0x0051 }, // 001010001
{ 0xFF, 9, 0x0050 }, // 001010000
{ 0x4A, 10, 0x009F }, // 0010011111
{ 0x4B, 10, 0x009E }, // 0010011110
{ 0x4D, 10, 0x009D }, // 0010011101
{ 0x4E, 10, 0x009C }, // 0010011100
{ 0x52, 10, 0x009B }, // 0010011011
{ 0x53, 10, 0x009A }, // 0010011010
{ 0x54, 10, 0x0099 }, // 0010011001
{ 0x55, 10, 0x0098 }, // 0010011000
{ 0x56, 10, 0x0097 }, // 0010010111
{ 0x57, 10, 0x0096 }, // 0010010110
{ 0x58, 10, 0x0095 }, // 0010010101
{ 0x59, 10, 0x0094 }, // 0010010100
{ 0x5A, 10, 0x0093 }, // 0010010011
{ 0x5B, 10, 0x0092 }, // 0010010010
{ 0x5C, 10, 0x0091 }, // 0010010001
{ 0x5D, 10, 0x0090 }, // 0010010000
{ 0x5E, 10, 0x008F }, // 0010001111
{ 0x5F, 10, 0x008E }, // 0010001110
{ 0x60, 10, 0x008D }, // 0010001101
{ 0x61, 10, 0x008C }, // 0010001100
{ 0x62, 10, 0x008B }, // 0010001011
{ 0x63, 10, 0x008A }, // 0010001010
{ 0x64, 10, 0x0089 }, // 0010001001
{ 0x65, 10, 0x0088 }, // 0010001000
{ 0x66, 10, 0x0087 }, // 0010000111
{ 0x67, 10, 0x0086 }, // 0010000110
{ 0x68, 10, 0x0085 }, // 0010000101
{ 0x69, 10, 0x0084 }, // 0010000100
{ 0x6A, 10, 0x0083 }, // 0010000011
{ 0x6B, 10, 0x0082 }, // 0010000010
{ 0x6C, 10, 0x0081 }, // 0010000001
{ 0x6D, 10, 0x0080 }, // 0010000000
{ 0x6E, 10, 0x007F }, // 0001111111
{ 0x6F, 10, 0x007E }, // 0001111110
{ 0x70, 10, 0x007D }, // 0001111101
{ 0x71, 10, 0x007C }, // 0001111100
{ 0x72, 10, 0x007B }, // 0001111011
{ 0x73, 10, 0x007A }, // 0001111010
{ 0x74, 10, 0x0079 }, // 0001111001
{ 0x75, 10, 0x0078 }, // 0001111000
{ 0x76, 10, 0x0077 }, // 0001110111
{ 0x77, 10, 0x0076 }, // 0001110110
{ 0x78, 10, 0x0075 }, // 0001110101
{ 0x79, 10, 0x0074 }, // 0001110100
{ 0x7A, 10, 0x0073 }, // 0001110011
{ 0x7B, 10, 0x0072 }, // 0001110010
{ 0x7C, 10, 0x0071 }, // 0001110001
{ 0x7D, 10, 0x0070 }, // 0001110000
{ 0x7E, 10, 0x006F }, // 0001101111
{ 0x7F, 10, 0x006E }, // 0001101110
{ 0x81, 10, 0x006D }, // 0001101101
{ 0x82, 10, 0x006C }, // 0001101100
{ 0x83, 10, 0x006B }, // 0001101011
{ 0x84, 10, 0x006A }, // 0001101010
{ 0x85, 10, 0x0069 }, // 0001101001
{ 0x86, 10, 0x0068 }, // 0001101000
{ 0x87, 10, 0x0067 }, // 0001100111
{ 0x88, 10, 0x0066 }, // 0001100110
{ 0x89, 10, 0x0065 }, // 0001100101
{ 0x8A, 10, 0x0064 }, // 0001100100
{ 0x8B, 10, 0x0063 }, // 0001100011
{ 0x8C, 10, 0x0062 }, // 0001100010
{ 0x8D, 10, 0x0061 }, // 0001100001
{ 0x8E, 10, 0x0060 }, // 0001100000
{ 0x8F, 10, 0x005F }, // 0001011111
{ 0x90, 10, 0x005E }, // 0001011110
{ 0x91, 10, 0x005D }, // 0001011101
{ 0x92, 10, 0x005C }, // 0001011100
{ 0x93, 10, 0x005B }, // 0001011011
{ 0x94, 10, 0x005A }, // 0001011010
{ 0x95, 10, 0x0059 }, // 0001011001
{ 0x96, 10, 0x0058 }, // 0001011000
{ 0x97, 10, 0x0057 }, // 0001010111
{ 0x98, 10, 0x0056 }, // 0001010110
{ 0x99, 10, 0x0055 }, // 0001010101
{ 0x9A, 10, 0x0054 }, // 0001010100
{ 0x9B, 10, 0x0053 }, // 0001010011
{ 0x9C, 10, 0x0052 }, // 0001010010
{ 0x9D, 10, 0x0051 }, // 0001010001
{ 0x9E, 10, 0x0050 }, // 0001010000
{ 0x9F, 10, 0x004F }, // 0001001111
{ 0xA0, 10, 0x004E }, // 0001001110
{ 0xA1, 10, 0x004D }, // 0001001101
{ 0xA2, 10, 0x004C }, // 0001001100
{ 0xA3, 10, 0x004B }, // 0001001011
{ 0xA4, 10, 0x004A }, // 0001001010
{ 0xA5, 10, 0x0049 }, // 0001001001
{ 0xA6, 10, 0x0048 }, // 0001001000
{ 0xA7, 10, 0x0047 }, // 0001000111
{ 0xA8, 10, 0x0046 }, // 0001000110
{ 0xA9, 10, 0x0045 }, // 0001000101
{ 0xAA, 10, 0x0044 }, // 0001000100
{ 0xAB, 10, 0x0043 }, // 0001000011
{ 0xAC, 10, 0x0042 }, // 0001000010
{ 0xAD, 10, 0x0041 }, // 0001000001
{ 0xAE, 10, 0x0040 }, // 0001000000
{ 0xAF, 10, 0x003F }, // 0000111111
{ 0xB0, 10, 0x003E }, // 0000111110
{ 0xB1, 10, 0x003D }, // 0000111101
{ 0xB2, 10, 0x003C }, // 0000111100
{ 0xB3, 10, 0x003B }, // 0000111011
{ 0xB4, 10, 0x003A }, // 0000111010
{ 0xB5, 10, 0x0039 }, // 0000111001
{ 0xB6, 10, 0x0038 }, // 0000111000
{ 0xB7, 10, 0x0037 }, // 0000110111
{ 0xB8, 10, 0x0036 }, // 0000110110
{ 0xB9, 10, 0x0035 }, // 0000110101
{ 0xBA, 10, 0x0034 }, // 0000110100
{ 0xBB, 10, 0x0033 }, // 0000110011
{ 0xBC, 10, 0x0032 }, // 0000110010
{ 0xBD, 10, 0x0031 }, // 0000110001
{ 0xBE, 10, 0x0030 }, // 0000110000
{ 0xBF, 10, 0x002F }, // 0000101111
{ 0xC0, 10, 0x002E }, // 0000101110
{ 0xC1, 10, 0x002D }, // 0000101101
{ 0xC2, 10, 0x002C }, // 0000101100
{ 0xC3, 10, 0x002B }, // 0000101011
{ 0xC4, 10, 0x002A }, // 0000101010
{ 0xC5, 10, 0x0029 }, // 0000101001
{ 0xC6, 10, 0x0028 }, // 0000101000
{ 0xC7, 10, 0x0027 }, // 0000100111
{ 0xC8, 10, 0x0026 }, // 0000100110
{ 0xC9, 10, 0x0025 }, // 0000100101
{ 0xCA, 10, 0x0024 }, // 0000100100
{ 0xCB, 10, 0x0023 }, // 0000100011
{ 0xCC, 10, 0x0022 }, // 0000100010
{ 0xCD, 10, 0x0021 }, // 0000100001
{ 0xCE, 10, 0x0020 }, // 0000100000
{ 0xCF, 10, 0x001F }, // 0000011111
{ 0xD0, 10, 0x001E }, // 0000011110
{ 0xD1, 10, 0x001D }, // 0000011101
{ 0xD2, 10, 0x001C }, // 0000011100
{ 0xD3, 10, 0x001B }, // 0000011011
{ 0xD4, 10, 0x001A }, // 0000011010
{ 0xD6, 10, 0x0019 }, // 0000011001
{ 0xD7, 10, 0x0018 }, // 0000011000
{ 0xD8, 10, 0x0017 }, // 0000010111
{ 0xD9, 10, 0x0016 }, // 0000010110
{ 0xDA, 10, 0x0015 }, // 0000010101
{ 0xDB, 10, 0x0014 }, // 0000010100
{ 0xDC, 10, 0x0013 }, // 0000010011
{ 0xDE, 10, 0x0012 }, // 0000010010
{ 0xDF, 10, 0x0011 }, // 0000010001
{ 0xE1, 10, 0x0010 }, // 0000010000
{ 0xE2, 10, 0x000F }, // 0000001111
{ 0xE4, 10, 0x000E }, // 0000001110
{ 0xEF, 10, 0x000D }, // 0000001101
{ 0xD5, 11, 0x0019 }, // 00000011001
{ 0xDD, 11, 0x0018 }, // 00000011000
{ 0xE3, 11, 0x0017 }, // 00000010111
{ 0xE5, 11, 0x0016 }, // 00000010110
{ 0xE6, 11, 0x0015 }, // 00000010101
{ 0xE7, 11, 0x0014 }, // 00000010100
{ 0xE8, 11, 0x0013 }, // 00000010011
{ 0xE9, 11, 0x0012 }, // 00000010010
{ 0xEA, 11, 0x0011 }, // 00000010001
{ 0xEB, 11, 0x0010 }, // 00000010000
{ 0xEC, 11, 0x000F }, // 00000001111
{ 0xED, 11, 0x000E }, // 00000001110
{ 0xEE, 11, 0x000D }, // 00000001101
{ 0xF2, 11, 0x000C }, // 00000001100
{ 0xF3, 11, 0x000B }, // 00000001011
{ 0xF4, 11, 0x000A }, // 00000001010
{ 0xF5, 11, 0x0009 }, // 00000001001
{ 0xF6, 11, 0x0008 }, // 00000001000
{ 0xF7, 11, 0x0007 }, // 00000000111
{ 0xF8, 11, 0x0006 }, // 00000000110
{ 0xFA, 11, 0x0005 }, // 00000000101
{ 0xFB, 11, 0x0004 }, // 00000000100
{ 0xFC, 11, 0x0003 }, // 00000000011
{ 0xFD, 11, 0x0002 }, // 00000000010
{ 0xFE, 11, 0x0001 }, // 00000000001
{ 0xF9, 12, 0x0001 }, // 000000000001
{ HUFFMAN_EOF, 12, 0x0000 }, // 000000000000
};
int huffManLenIndex[HUFFMAN_TREE_SIZE];
void huffmanInitDecodeLenIndex(void)
{
// create an index of first code at each possible length
for (int ii = 0; ii < HUFFMAN_TREE_SIZE; ++ii) {
huffManLenIndex[ii] = -1;
}
for (int ii = 0; ii < HUFFMAN_TREE_SIZE; ++ii) {
if (huffManLenIndex[huffmanTree[ii].codeLen] == -1) {
huffManLenIndex[huffmanTree[ii].codeLen] = ii;
}
}
}
int huffmanDecodeBuf(uint8_t *outBuf, int outBufLen, const uint8_t *inBuf, int inBufLen, int inBufCharacterCount, const huffmanTree_t *huffmanTree)
{
static bool initialized = false;
if (!initialized) {
huffmanInitDecodeLenIndex();
initialized = true;
}
if (inBufCharacterCount > outBufLen) {
return -1;
}
uint16_t code = 0;
int codeLen = 0;
int outCount = 0;
int inCount = 0;
uint8_t testBit = 0x80;
bool eof = false;
while (!eof && outCount < outBufLen && inCount < inBufLen) {
if (outCount == inBufCharacterCount) {
// we've exhausted the input stream, discard any odd bits on the end
return outCount;
}
if (inCount >= inBufLen) {
return -1;
}
// get the next bit from the input buffer
code <<= 1;
++codeLen;
if (*inBuf & testBit) {
code |= 0x01;
}
testBit >>= 1;
if (testBit == 0) {
testBit = 0x80;
++inBuf;
++inCount;
}
// check if the code is a leaf node or an interior node
if (huffManLenIndex[codeLen] != -1) {
// look for the code in the tree, only leaf nodes are stored in the tree
for(int ii = huffManLenIndex[codeLen]; (ii < HUFFMAN_TREE_SIZE) && (huffmanTree[ii].codeLen == codeLen); ++ii) {
if (huffmanTree[ii].code == code) {
// we've found the code, so it is a leaf node
const int16_t value = huffmanTree[ii].value;
if (value == HUFFMAN_EOF) {
eof = true;
} else {
// output the value
*outBuf = (uint8_t)value;
++outBuf;
++outCount;
}
// reset the code to continue decompressing the input buffer
code = 0;
codeLen = 0;
break;
}
}
}
}
return outCount;
}
TEST(HuffmanUnittest, TestHuffmanEncode)
{
#define INBUF_LEN1 3
const uint8_t inBuf1[INBUF_LEN1] = {0,1,1};
// 11 101 101
// 1110 1101
// e d
int len = huffmanEncodeBuf(outBuf, OUTBUF_LEN, inBuf1, INBUF_LEN1, huffmanTable);
EXPECT_EQ(1, len);
EXPECT_EQ(0xed, (int)outBuf[0]);
#define INBUF_LEN2 4
const uint8_t inBuf2[INBUF_LEN2] = {0,1,2,3};
// 11 101 1001 10001
// 1110 1100 1100 01
// e c c 8
len = huffmanEncodeBuf(outBuf, OUTBUF_LEN, inBuf2, INBUF_LEN2, huffmanTable);
EXPECT_EQ(2, len);
EXPECT_EQ(0xec, (int)outBuf[0]);
EXPECT_EQ(0xc4, (int)outBuf[1]);
#define INBUF_LEN3 8
const uint8_t inBuf3[INBUF_LEN3] = {0,1,2,3,4,5,6,7};
// 11 101 1001 10001 10000 011101 011100 011011
// 1110 1100 1100 0110 0000 1110 1011 1000 1101 1
// e c c 6 0 e b 8 d 8
len = huffmanEncodeBuf(outBuf, OUTBUF_LEN, inBuf3, INBUF_LEN3, huffmanTable);
EXPECT_EQ(5, len);
EXPECT_EQ(0xec, (int)outBuf[0]);
EXPECT_EQ(0xc6, (int)outBuf[1]);
EXPECT_EQ(0x0e, (int)outBuf[2]);
EXPECT_EQ(0xb8, (int)outBuf[3]);
EXPECT_EQ(0xd8, (int)outBuf[4]);
}
TEST(HuffmanUnittest, TestHuffmanEncodeStreaming)
{
#define INBUF_LEN1 3
#define INBUF_LEN1_CHUNK1 2
#define INBUF_LEN1_CHUNK2 (INBUF_LEN1 - INBUF_LEN1_CHUNK1)
const uint8_t inBuf1[INBUF_LEN1] = {0,1,1};
// 11 101 101
// 1110 1101
// e d
huffmanState_t state1 = {
.bytesWritten = 0,
.outByte = outBuf,
.outBufLen = OUTBUF_LEN,
.outBit = 0x80,
};
*state1.outByte = 0;
int status = huffmanEncodeBufStreaming(&state1, inBuf1, INBUF_LEN1_CHUNK1, huffmanTable);
EXPECT_EQ(0, status);
status = huffmanEncodeBufStreaming(&state1, inBuf1 + INBUF_LEN1_CHUNK1, INBUF_LEN1_CHUNK2, huffmanTable);
EXPECT_EQ(0, status);
if (state1.outBit != 0x80) {
++state1.bytesWritten;
}
EXPECT_EQ(1, state1.bytesWritten);
EXPECT_EQ(0xed, (int)outBuf[0]);
#define INBUF_LEN2 4
#define INBUF_LEN2_CHUNK1 1
#define INBUF_LEN2_CHUNK2 1
#define INBUF_LEN2_CHUNK3 2
const uint8_t inBuf2[INBUF_LEN2] = {0,1,2,3};
// 11 101 1001 10001
// 1110 1100 1100 01
// e c c 8
huffmanState_t state2 = {
.bytesWritten = 0,
.outByte = outBuf,
.outBufLen = OUTBUF_LEN,
.outBit = 0x80,
};
*state2.outByte = 0;
status = huffmanEncodeBufStreaming(&state2, inBuf2, INBUF_LEN2_CHUNK1, huffmanTable);
EXPECT_EQ(0, status);
status = huffmanEncodeBufStreaming(&state2, inBuf2 + INBUF_LEN2_CHUNK1, INBUF_LEN2_CHUNK2, huffmanTable);
EXPECT_EQ(0, status);
status = huffmanEncodeBufStreaming(&state2, inBuf2 + INBUF_LEN2_CHUNK1 + INBUF_LEN2_CHUNK2, INBUF_LEN2_CHUNK3, huffmanTable);
EXPECT_EQ(0, status);
if (state2.outBit != 0x80) {
++state2.bytesWritten;
}
EXPECT_EQ(2, state2.bytesWritten);
EXPECT_EQ(0xec, (int)outBuf[0]);
EXPECT_EQ(0xc4, (int)outBuf[1]);
#define INBUF_LEN3 8
#define INBUF_LEN3_CHUNK1 4
#define INBUF_LEN3_CHUNK2 (INBUF_LEN3 - INBUF_LEN3_CHUNK1)
const uint8_t inBuf3[INBUF_LEN3] = {0,1,2,3,4,5,6,7};
// 11 101 1001 10001 10000 011101 011100 011011
// 1110 1100 1100 0110 0000 1110 1011 1000 1101 1
// e c c 6 0 e b 8 d 8
huffmanState_t state3 = {
.bytesWritten = 0,
.outByte = outBuf,
.outBufLen = OUTBUF_LEN,
.outBit = 0x80,
};
*state3.outByte = 0;
status = huffmanEncodeBufStreaming(&state3, inBuf3, INBUF_LEN3_CHUNK1, huffmanTable);
EXPECT_EQ(0, status);
status = huffmanEncodeBufStreaming(&state3, inBuf3 + INBUF_LEN3_CHUNK1, INBUF_LEN3_CHUNK2, huffmanTable);
EXPECT_EQ(0, status);
if (state3.outBit != 0x80) {
++state3.bytesWritten;
}
EXPECT_EQ(5, state3.bytesWritten);
EXPECT_EQ(0xec, (int)outBuf[0]);
EXPECT_EQ(0xc6, (int)outBuf[1]);
EXPECT_EQ(0x0e, (int)outBuf[2]);
EXPECT_EQ(0xb8, (int)outBuf[3]);
EXPECT_EQ(0xd8, (int)outBuf[4]);
}
TEST(HuffmanUnittest, TestHuffmanDecode)
{
int len;
#define HUFF_BUF_LEN1 1
#define HUFF_BUF_COUNT1 1
const uint8_t inBuf1[HUFF_BUF_LEN1] = {0xc0}; // 11
len = huffmanDecodeBuf(outBuf, OUTBUF_LEN, inBuf1, HUFF_BUF_LEN1, HUFF_BUF_COUNT1, huffmanTree);
EXPECT_EQ(1, len);
EXPECT_EQ(0x00, (int)outBuf[0]);
EXPECT_EQ(-1, huffManLenIndex[0]);
EXPECT_EQ(-1, huffManLenIndex[1]);
EXPECT_EQ(0, huffManLenIndex[2]);
EXPECT_EQ(1, huffManLenIndex[3]);
EXPECT_EQ(2, huffManLenIndex[4]);
EXPECT_EQ(3, huffManLenIndex[5]);
EXPECT_EQ(6, huffManLenIndex[6]);
EXPECT_EQ(11, huffManLenIndex[7]);
#define HUFF_BUF_LEN2 1
#define HUFF_BUF_COUNT2 3
const uint8_t inBuf2[HUFF_BUF_LEN2] = {0xed}; // 11 101 101
len = huffmanDecodeBuf(outBuf, OUTBUF_LEN, inBuf2, HUFF_BUF_LEN2, HUFF_BUF_COUNT2, huffmanTree);
EXPECT_EQ(3, len);
EXPECT_EQ(0x00, (int)outBuf[0]);
EXPECT_EQ(0x01, (int)outBuf[1]);
EXPECT_EQ(0x01, (int)outBuf[2]);
#define HUFF_BUF_LEN3 5
#define HUFF_BUF_COUNT3 8
const uint8_t inBuf3[HUFF_BUF_LEN3] = {0xec, 0xc6, 0x0e, 0xb8, 0xd8};
len = huffmanDecodeBuf(outBuf, OUTBUF_LEN, inBuf3, HUFF_BUF_LEN3, HUFF_BUF_COUNT3, huffmanTree);
EXPECT_EQ(8, len);
EXPECT_EQ(0x00, (int)outBuf[0]);
EXPECT_EQ(0x01, (int)outBuf[1]);
EXPECT_EQ(0x02, (int)outBuf[2]);
EXPECT_EQ(0x03, (int)outBuf[3]);
EXPECT_EQ(0x04, (int)outBuf[4]);
EXPECT_EQ(0x05, (int)outBuf[5]);
EXPECT_EQ(0x06, (int)outBuf[6]);
EXPECT_EQ(0x07, (int)outBuf[7]);
}
// STUBS
extern "C" {
}