Added SPDX to 30 more files - spdx-48
This commit is contained in:
parent
bfe5427a4a
commit
c322561d6f
30 changed files with 856 additions and 735 deletions
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Test code for Adafruit GPS modules using MTK driver
|
||||
// such as www.adafruit.com/products/660 (discontinued)
|
||||
// For new use see www.adafruit.com/products/746 (needs different code)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Arduino "bridge" code between host computer and WS2801-based digital
|
||||
// RGB LED pixels (e.g. Adafruit product ID #322). Intended for use
|
||||
// with USB-native boards such as Teensy or Adafruit 32u4 Breakout;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/*****************************************************************************
|
||||
Sketch for testing WS2801 LED strands - lights one LED along length of strand.
|
||||
Because only one LED is lit at a time, can safely be powered from Arduino +5V.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# ATMakers HandUp
|
||||
# Listens to the USB Serial port and responds to incoming strings
|
||||
# Sets appropriate colors on the DotStar LED
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2019 Dave Astels for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Paint for PyPortal, PyBadge, PyGamer, and the like.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
|
||||
import analogio
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2021 Liz Clark for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import board
|
||||
import simpleio
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2018 Mikey Sklar for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/*------------------------------------------------------------------------
|
||||
Gemma "Firewalker Lite" sneakers sketch.
|
||||
Uses the following Adafruit parts (X2 for two shoes):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2018 Phillip Burgess for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# Gemma "Firewalker Lite" sneakers
|
||||
# - Uses the following Adafruit parts (X2 for two shoes):
|
||||
# * Gemma M0 3V microcontroller (#3501)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2020 Liz Clark for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import board
|
||||
import displayio
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2020 Liz Clark for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# This file is where you keep secret settings, passwords, and tokens!
|
||||
# If you put them in the code you risk committing that info or sharing it
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2018 Collin Cunningham for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/* MIDI Solenoid Drummer
|
||||
* for use with Adafruit Feather + Crickit Featherwing
|
||||
* assumes a 5V solenoid connected to each of Crickit's four Drive ports
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2021 Eva Herrada for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import math
|
||||
import board
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2017 Mikey Sklar for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
|
||||
import board
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-FileCopyrightText: 2020 Jeff Epler for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2020 Limor Fried for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
RGB Matrix Ocean Scroller
|
||||
Adafruit invests time and resources providing this open source code.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// On Leonardo/Micro or others with hardware serial, use those!
|
||||
// uncomment this line:
|
||||
// #define pmsSerial Serial1
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2020 Carter Nelson for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import time
|
||||
import math
|
||||
import board
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2019 Collin Cunningham for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
This code will display a random strategy from strategies.py when the
|
||||
PyPortal screen is pressed. See the original Oblique Strategies
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2019 Collin Cunningham for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
strategies = [
|
||||
"Abandon normal instruments",
|
||||
"Accept advice",
|
||||
|
|
|
|||
|
|
@ -1,222 +1,226 @@
|
|||
// Bad Apple for ESP32 with OLED SSD1306 | 2018 by Hackerspace-FFM.de | MIT-License.
|
||||
// Adapted for Sharp Memory display + Itsy Bitsy M4 - put video.hs on QSPI storage using CircuitPython
|
||||
#include "heatshrink_decoder.h"
|
||||
|
||||
#include "SdFat.h"
|
||||
#include "Adafruit_SPIFlash.h"
|
||||
#include <Adafruit_SharpMem.h>
|
||||
#define BLACK 0
|
||||
#define WHITE 1
|
||||
|
||||
#define SCALE 3
|
||||
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
#error HEATSHRINK_DYNAMIC_ALLOC must be false for static allocation test suite.
|
||||
#endif
|
||||
|
||||
static heatshrink_decoder hsd;
|
||||
|
||||
// global storage for putPixels
|
||||
int16_t curr_x = 0;
|
||||
int16_t curr_y = 0;
|
||||
|
||||
// global storage for decodeRLE
|
||||
int32_t runlength = -1;
|
||||
int32_t c_to_dup = -1;
|
||||
|
||||
uint32_t lastRefresh = 0;
|
||||
|
||||
#define SHARP_SS A5
|
||||
Adafruit_SharpMem display(&SPI, SHARP_SS, 400, 240, 3000000);
|
||||
#define X_OFFSET (400 - SCALE*128) / 2
|
||||
#define Y_OFFSET (240 - SCALE*64) / 2
|
||||
|
||||
Adafruit_FlashTransport_QSPI flashTransport;
|
||||
Adafruit_SPIFlash flash(&flashTransport);
|
||||
FatFileSystem fatfs;
|
||||
|
||||
|
||||
void putPixels(uint8_t c, int32_t len) {
|
||||
static uint8_t color;
|
||||
uint8_t b = 0;
|
||||
while(len--) {
|
||||
b = 128;
|
||||
for (int i=0; i<8; i++) {
|
||||
if (c & b) {
|
||||
color = WHITE;
|
||||
} else {
|
||||
color = BLACK;
|
||||
}
|
||||
b >>= 1;
|
||||
if (color == BLACK) {
|
||||
// we clear the buffer each frame so only black pixels need to be drawn
|
||||
display.fillRect(X_OFFSET+curr_x*SCALE, Y_OFFSET+curr_y*SCALE, SCALE, SCALE, color);
|
||||
}
|
||||
curr_x++;
|
||||
if(curr_x >= 128) {
|
||||
curr_x = 0;
|
||||
curr_y++;
|
||||
if(curr_y >= 64) {
|
||||
curr_y = 0;
|
||||
display.refresh();
|
||||
display.clearDisplayBuffer();
|
||||
// 30 fps target rate
|
||||
//if(digitalRead(0)) while((millis() - lastRefresh) < 33) ;
|
||||
//lastRefresh = millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decodeRLE(uint8_t c) {
|
||||
if(c_to_dup == -1) {
|
||||
if((c == 0x55) || (c == 0xaa)) {
|
||||
c_to_dup = c;
|
||||
} else {
|
||||
putPixels(c, 1);
|
||||
}
|
||||
} else {
|
||||
if(runlength == -1) {
|
||||
if(c == 0) {
|
||||
putPixels(c_to_dup & 0xff, 1);
|
||||
c_to_dup = -1;
|
||||
} else if((c & 0x80) == 0) {
|
||||
if(c_to_dup == 0x55) {
|
||||
putPixels(0, c);
|
||||
} else {
|
||||
putPixels(255, c);
|
||||
}
|
||||
c_to_dup = -1;
|
||||
} else {
|
||||
runlength = c & 0x7f;
|
||||
}
|
||||
} else {
|
||||
runlength = runlength | (c << 7);
|
||||
if(c_to_dup == 0x55) {
|
||||
putPixels(0, runlength);
|
||||
} else {
|
||||
putPixels(255, runlength);
|
||||
}
|
||||
c_to_dup = -1;
|
||||
runlength = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define RLEBUFSIZE 4096
|
||||
#define READBUFSIZE 2048
|
||||
void readFile(const char * path){
|
||||
static uint8_t rle_buf[RLEBUFSIZE];
|
||||
size_t rle_bufhead = 0;
|
||||
size_t rle_size = 0;
|
||||
|
||||
size_t filelen = 0;
|
||||
size_t filesize;
|
||||
static uint8_t compbuf[READBUFSIZE];
|
||||
|
||||
Serial.printf("Reading file: %s\n", path);
|
||||
File file = fatfs.open(path);
|
||||
if(!file || file.isDirectory()){
|
||||
Serial.println("Failed to open file for reading");
|
||||
display.println("File open error. Upload video.hs using CircuitPython");
|
||||
display.refresh();
|
||||
return;
|
||||
}
|
||||
filelen = file.size();
|
||||
filesize = filelen;
|
||||
Serial.printf("File size: %d\n", filelen);
|
||||
|
||||
// init display, putPixels and decodeRLE
|
||||
display.clearDisplay();
|
||||
display.refresh();
|
||||
curr_x = 0;
|
||||
curr_y = 0;
|
||||
runlength = -1;
|
||||
c_to_dup = -1;
|
||||
lastRefresh = millis();
|
||||
|
||||
// init decoder
|
||||
heatshrink_decoder_reset(&hsd);
|
||||
size_t count = 0;
|
||||
uint32_t sunk = 0;
|
||||
size_t toRead;
|
||||
size_t toSink = 0;
|
||||
uint32_t sinkHead = 0;
|
||||
|
||||
|
||||
// Go through file...
|
||||
while(filelen) {
|
||||
if(toSink == 0) {
|
||||
toRead = filelen;
|
||||
if(toRead > READBUFSIZE) toRead = READBUFSIZE;
|
||||
file.read(compbuf, toRead);
|
||||
filelen -= toRead;
|
||||
toSink = toRead;
|
||||
sinkHead = 0;
|
||||
}
|
||||
|
||||
// uncompress buffer
|
||||
HSD_sink_res sres;
|
||||
sres = heatshrink_decoder_sink(&hsd, &compbuf[sinkHead], toSink, &count);
|
||||
//Serial.print("^^ sinked ");
|
||||
//Serial.println(count);
|
||||
toSink -= count;
|
||||
sinkHead = count;
|
||||
sunk += count;
|
||||
if (sunk == filesize) {
|
||||
heatshrink_decoder_finish(&hsd);
|
||||
}
|
||||
|
||||
HSD_poll_res pres;
|
||||
do {
|
||||
rle_size = 0;
|
||||
pres = heatshrink_decoder_poll(&hsd, rle_buf, RLEBUFSIZE, &rle_size);
|
||||
//Serial.print("^^ polled ");
|
||||
//Serial.println(rle_size);
|
||||
if(pres < 0) {
|
||||
Serial.print("POLL ERR! ");
|
||||
Serial.println(pres);
|
||||
return;
|
||||
}
|
||||
|
||||
rle_bufhead = 0;
|
||||
while(rle_size) {
|
||||
rle_size--;
|
||||
if(rle_bufhead >= RLEBUFSIZE) {
|
||||
Serial.println("RLE_SIZE ERR!");
|
||||
return;
|
||||
}
|
||||
decodeRLE(rle_buf[rle_bufhead++]);
|
||||
}
|
||||
} while (pres == HSDR_POLL_MORE);
|
||||
}
|
||||
file.close();
|
||||
Serial.println("Done.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
//while (!Serial) delay(10);
|
||||
Serial.println("Bad apple");
|
||||
|
||||
flash.begin();
|
||||
// Init file system on the flash
|
||||
fatfs.begin(&flash);
|
||||
|
||||
display.begin();
|
||||
display.clearDisplay();
|
||||
display.setTextColor(BLACK, WHITE);
|
||||
display.setTextSize(2);
|
||||
display.println("Scaled Bad Apple For SHARP Memory");
|
||||
display.refresh();
|
||||
|
||||
readFile("/video.hs");
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
}
|
||||
// SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Bad Apple for ESP32 with OLED SSD1306 | 2018 by Hackerspace-FFM.de | MIT-License.
|
||||
// Adapted for Sharp Memory display + Itsy Bitsy M4 - put video.hs on QSPI storage using CircuitPython
|
||||
#include "heatshrink_decoder.h"
|
||||
|
||||
#include "SdFat.h"
|
||||
#include "Adafruit_SPIFlash.h"
|
||||
#include <Adafruit_SharpMem.h>
|
||||
#define BLACK 0
|
||||
#define WHITE 1
|
||||
|
||||
#define SCALE 3
|
||||
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
#error HEATSHRINK_DYNAMIC_ALLOC must be false for static allocation test suite.
|
||||
#endif
|
||||
|
||||
static heatshrink_decoder hsd;
|
||||
|
||||
// global storage for putPixels
|
||||
int16_t curr_x = 0;
|
||||
int16_t curr_y = 0;
|
||||
|
||||
// global storage for decodeRLE
|
||||
int32_t runlength = -1;
|
||||
int32_t c_to_dup = -1;
|
||||
|
||||
uint32_t lastRefresh = 0;
|
||||
|
||||
#define SHARP_SS A5
|
||||
Adafruit_SharpMem display(&SPI, SHARP_SS, 400, 240, 3000000);
|
||||
#define X_OFFSET (400 - SCALE*128) / 2
|
||||
#define Y_OFFSET (240 - SCALE*64) / 2
|
||||
|
||||
Adafruit_FlashTransport_QSPI flashTransport;
|
||||
Adafruit_SPIFlash flash(&flashTransport);
|
||||
FatFileSystem fatfs;
|
||||
|
||||
|
||||
void putPixels(uint8_t c, int32_t len) {
|
||||
static uint8_t color;
|
||||
uint8_t b = 0;
|
||||
while(len--) {
|
||||
b = 128;
|
||||
for (int i=0; i<8; i++) {
|
||||
if (c & b) {
|
||||
color = WHITE;
|
||||
} else {
|
||||
color = BLACK;
|
||||
}
|
||||
b >>= 1;
|
||||
if (color == BLACK) {
|
||||
// we clear the buffer each frame so only black pixels need to be drawn
|
||||
display.fillRect(X_OFFSET+curr_x*SCALE, Y_OFFSET+curr_y*SCALE, SCALE, SCALE, color);
|
||||
}
|
||||
curr_x++;
|
||||
if(curr_x >= 128) {
|
||||
curr_x = 0;
|
||||
curr_y++;
|
||||
if(curr_y >= 64) {
|
||||
curr_y = 0;
|
||||
display.refresh();
|
||||
display.clearDisplayBuffer();
|
||||
// 30 fps target rate
|
||||
//if(digitalRead(0)) while((millis() - lastRefresh) < 33) ;
|
||||
//lastRefresh = millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decodeRLE(uint8_t c) {
|
||||
if(c_to_dup == -1) {
|
||||
if((c == 0x55) || (c == 0xaa)) {
|
||||
c_to_dup = c;
|
||||
} else {
|
||||
putPixels(c, 1);
|
||||
}
|
||||
} else {
|
||||
if(runlength == -1) {
|
||||
if(c == 0) {
|
||||
putPixels(c_to_dup & 0xff, 1);
|
||||
c_to_dup = -1;
|
||||
} else if((c & 0x80) == 0) {
|
||||
if(c_to_dup == 0x55) {
|
||||
putPixels(0, c);
|
||||
} else {
|
||||
putPixels(255, c);
|
||||
}
|
||||
c_to_dup = -1;
|
||||
} else {
|
||||
runlength = c & 0x7f;
|
||||
}
|
||||
} else {
|
||||
runlength = runlength | (c << 7);
|
||||
if(c_to_dup == 0x55) {
|
||||
putPixels(0, runlength);
|
||||
} else {
|
||||
putPixels(255, runlength);
|
||||
}
|
||||
c_to_dup = -1;
|
||||
runlength = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define RLEBUFSIZE 4096
|
||||
#define READBUFSIZE 2048
|
||||
void readFile(const char * path){
|
||||
static uint8_t rle_buf[RLEBUFSIZE];
|
||||
size_t rle_bufhead = 0;
|
||||
size_t rle_size = 0;
|
||||
|
||||
size_t filelen = 0;
|
||||
size_t filesize;
|
||||
static uint8_t compbuf[READBUFSIZE];
|
||||
|
||||
Serial.printf("Reading file: %s\n", path);
|
||||
File file = fatfs.open(path);
|
||||
if(!file || file.isDirectory()){
|
||||
Serial.println("Failed to open file for reading");
|
||||
display.println("File open error. Upload video.hs using CircuitPython");
|
||||
display.refresh();
|
||||
return;
|
||||
}
|
||||
filelen = file.size();
|
||||
filesize = filelen;
|
||||
Serial.printf("File size: %d\n", filelen);
|
||||
|
||||
// init display, putPixels and decodeRLE
|
||||
display.clearDisplay();
|
||||
display.refresh();
|
||||
curr_x = 0;
|
||||
curr_y = 0;
|
||||
runlength = -1;
|
||||
c_to_dup = -1;
|
||||
lastRefresh = millis();
|
||||
|
||||
// init decoder
|
||||
heatshrink_decoder_reset(&hsd);
|
||||
size_t count = 0;
|
||||
uint32_t sunk = 0;
|
||||
size_t toRead;
|
||||
size_t toSink = 0;
|
||||
uint32_t sinkHead = 0;
|
||||
|
||||
|
||||
// Go through file...
|
||||
while(filelen) {
|
||||
if(toSink == 0) {
|
||||
toRead = filelen;
|
||||
if(toRead > READBUFSIZE) toRead = READBUFSIZE;
|
||||
file.read(compbuf, toRead);
|
||||
filelen -= toRead;
|
||||
toSink = toRead;
|
||||
sinkHead = 0;
|
||||
}
|
||||
|
||||
// uncompress buffer
|
||||
HSD_sink_res sres;
|
||||
sres = heatshrink_decoder_sink(&hsd, &compbuf[sinkHead], toSink, &count);
|
||||
//Serial.print("^^ sinked ");
|
||||
//Serial.println(count);
|
||||
toSink -= count;
|
||||
sinkHead = count;
|
||||
sunk += count;
|
||||
if (sunk == filesize) {
|
||||
heatshrink_decoder_finish(&hsd);
|
||||
}
|
||||
|
||||
HSD_poll_res pres;
|
||||
do {
|
||||
rle_size = 0;
|
||||
pres = heatshrink_decoder_poll(&hsd, rle_buf, RLEBUFSIZE, &rle_size);
|
||||
//Serial.print("^^ polled ");
|
||||
//Serial.println(rle_size);
|
||||
if(pres < 0) {
|
||||
Serial.print("POLL ERR! ");
|
||||
Serial.println(pres);
|
||||
return;
|
||||
}
|
||||
|
||||
rle_bufhead = 0;
|
||||
while(rle_size) {
|
||||
rle_size--;
|
||||
if(rle_bufhead >= RLEBUFSIZE) {
|
||||
Serial.println("RLE_SIZE ERR!");
|
||||
return;
|
||||
}
|
||||
decodeRLE(rle_buf[rle_bufhead++]);
|
||||
}
|
||||
} while (pres == HSDR_POLL_MORE);
|
||||
}
|
||||
file.close();
|
||||
Serial.println("Done.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
//while (!Serial) delay(10);
|
||||
Serial.println("Bad apple");
|
||||
|
||||
flash.begin();
|
||||
// Init file system on the flash
|
||||
fatfs.begin(&flash);
|
||||
|
||||
display.begin();
|
||||
display.clearDisplay();
|
||||
display.setTextColor(BLACK, WHITE);
|
||||
display.setTextSize(2);
|
||||
display.println("Scaled Bad Apple For SHARP Memory");
|
||||
display.refresh();
|
||||
|
||||
readFile("/video.hs");
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,24 @@
|
|||
#ifndef HEATSHRINK_H
|
||||
#define HEATSHRINK_H
|
||||
|
||||
#define HEATSHRINK_AUTHOR "Scott Vokes <vokes.s@gmail.com>"
|
||||
#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink"
|
||||
|
||||
/* Version 0.4.1 */
|
||||
#define HEATSHRINK_VERSION_MAJOR 0
|
||||
#define HEATSHRINK_VERSION_MINOR 4
|
||||
#define HEATSHRINK_VERSION_PATCH 1
|
||||
|
||||
#define HEATSHRINK_MIN_WINDOW_BITS 4
|
||||
#define HEATSHRINK_MAX_WINDOW_BITS 15
|
||||
|
||||
#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3
|
||||
|
||||
#define HEATSHRINK_LITERAL_MARKER 0x01
|
||||
#define HEATSHRINK_BACKREF_MARKER 0x00
|
||||
|
||||
#endif
|
||||
// SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef HEATSHRINK_H
|
||||
#define HEATSHRINK_H
|
||||
|
||||
#define HEATSHRINK_AUTHOR "Scott Vokes <vokes.s@gmail.com>"
|
||||
#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink"
|
||||
|
||||
/* Version 0.4.1 */
|
||||
#define HEATSHRINK_VERSION_MAJOR 0
|
||||
#define HEATSHRINK_VERSION_MINOR 4
|
||||
#define HEATSHRINK_VERSION_PATCH 1
|
||||
|
||||
#define HEATSHRINK_MIN_WINDOW_BITS 4
|
||||
#define HEATSHRINK_MAX_WINDOW_BITS 15
|
||||
|
||||
#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3
|
||||
|
||||
#define HEATSHRINK_LITERAL_MARKER 0x01
|
||||
#define HEATSHRINK_BACKREF_MARKER 0x00
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,26 +1,30 @@
|
|||
#ifndef HEATSHRINK_CONFIG_H
|
||||
#define HEATSHRINK_CONFIG_H
|
||||
|
||||
/* Should functionality assuming dynamic allocation be used? */
|
||||
#ifndef HEATSHRINK_DYNAMIC_ALLOC
|
||||
#define HEATSHRINK_DYNAMIC_ALLOC 0
|
||||
#endif
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
/* Optional replacement of malloc/free */
|
||||
#define HEATSHRINK_MALLOC(SZ) malloc(SZ)
|
||||
#define HEATSHRINK_FREE(P, SZ) free(P)
|
||||
#else
|
||||
/* Required parameters for static configuration */
|
||||
#define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 2048
|
||||
#define HEATSHRINK_STATIC_WINDOW_BITS 11
|
||||
#define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4
|
||||
#endif
|
||||
|
||||
/* Turn on logging for debugging. */
|
||||
#define HEATSHRINK_DEBUGGING_LOGS 0
|
||||
|
||||
/* Use indexing for faster compression. (This requires additional space.) */
|
||||
#define HEATSHRINK_USE_INDEX 1
|
||||
|
||||
#endif
|
||||
// SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef HEATSHRINK_CONFIG_H
|
||||
#define HEATSHRINK_CONFIG_H
|
||||
|
||||
/* Should functionality assuming dynamic allocation be used? */
|
||||
#ifndef HEATSHRINK_DYNAMIC_ALLOC
|
||||
#define HEATSHRINK_DYNAMIC_ALLOC 0
|
||||
#endif
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
/* Optional replacement of malloc/free */
|
||||
#define HEATSHRINK_MALLOC(SZ) malloc(SZ)
|
||||
#define HEATSHRINK_FREE(P, SZ) free(P)
|
||||
#else
|
||||
/* Required parameters for static configuration */
|
||||
#define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 2048
|
||||
#define HEATSHRINK_STATIC_WINDOW_BITS 11
|
||||
#define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4
|
||||
#endif
|
||||
|
||||
/* Turn on logging for debugging. */
|
||||
#define HEATSHRINK_DEBUGGING_LOGS 0
|
||||
|
||||
/* Use indexing for faster compression. (This requires additional space.) */
|
||||
#define HEATSHRINK_USE_INDEX 1
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,367 +1,371 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "heatshrink_decoder.h"
|
||||
|
||||
/* States for the polling state machine. */
|
||||
typedef enum {
|
||||
HSDS_TAG_BIT, /* tag bit */
|
||||
HSDS_YIELD_LITERAL, /* ready to yield literal byte */
|
||||
HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */
|
||||
HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */
|
||||
HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */
|
||||
HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */
|
||||
HSDS_YIELD_BACKREF, /* ready to yield back-reference */
|
||||
} HSD_state;
|
||||
|
||||
#if HEATSHRINK_DEBUGGING_LOGS
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#define LOG(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define ASSERT(X) assert(X)
|
||||
static const char *state_names[] = {
|
||||
"tag_bit",
|
||||
"yield_literal",
|
||||
"backref_index_msb",
|
||||
"backref_index_lsb",
|
||||
"backref_count_msb",
|
||||
"backref_count_lsb",
|
||||
"yield_backref",
|
||||
};
|
||||
#else
|
||||
#define LOG(...) /* no-op */
|
||||
#define ASSERT(X) /* no-op */
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buf; /* output buffer */
|
||||
size_t buf_size; /* buffer size */
|
||||
size_t *output_size; /* bytes pushed to buffer, so far */
|
||||
} output_info;
|
||||
|
||||
#define NO_BITS ((uint16_t)-1)
|
||||
|
||||
/* Forward references. */
|
||||
static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count);
|
||||
static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte);
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size,
|
||||
uint8_t window_sz2,
|
||||
uint8_t lookahead_sz2) {
|
||||
if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) ||
|
||||
(window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) ||
|
||||
(input_buffer_size == 0) ||
|
||||
(lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) ||
|
||||
(lookahead_sz2 >= window_sz2)) {
|
||||
return NULL;
|
||||
}
|
||||
size_t buffers_sz = (1 << window_sz2) + input_buffer_size;
|
||||
size_t sz = sizeof(heatshrink_decoder) + buffers_sz;
|
||||
heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz);
|
||||
if (hsd == NULL) { return NULL; }
|
||||
hsd->input_buffer_size = input_buffer_size;
|
||||
hsd->window_sz2 = window_sz2;
|
||||
hsd->lookahead_sz2 = lookahead_sz2;
|
||||
heatshrink_decoder_reset(hsd);
|
||||
LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n",
|
||||
sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size);
|
||||
return hsd;
|
||||
}
|
||||
|
||||
void heatshrink_decoder_free(heatshrink_decoder *hsd) {
|
||||
size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size;
|
||||
size_t sz = sizeof(heatshrink_decoder) + buffers_sz;
|
||||
HEATSHRINK_FREE(hsd, sz);
|
||||
(void)sz; /* may not be used by free */
|
||||
}
|
||||
#endif
|
||||
|
||||
void heatshrink_decoder_reset(heatshrink_decoder *hsd) {
|
||||
size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd);
|
||||
size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd);
|
||||
memset(hsd->buffers, 0, buf_sz + input_sz);
|
||||
hsd->state = HSDS_TAG_BIT;
|
||||
hsd->input_size = 0;
|
||||
hsd->input_index = 0;
|
||||
hsd->bit_index = 0x00;
|
||||
hsd->current_byte = 0x00;
|
||||
hsd->output_count = 0;
|
||||
hsd->output_index = 0;
|
||||
hsd->head_index = 0;
|
||||
}
|
||||
|
||||
/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */
|
||||
HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd,
|
||||
uint8_t *in_buf, size_t size, size_t *input_size) {
|
||||
if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) {
|
||||
return HSDR_SINK_ERROR_NULL;
|
||||
}
|
||||
|
||||
size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size;
|
||||
if (rem == 0) {
|
||||
*input_size = 0;
|
||||
return HSDR_SINK_FULL;
|
||||
}
|
||||
|
||||
size = rem < size ? rem : size;
|
||||
LOG("-- sinking %zd bytes\n", size);
|
||||
/* copy into input buffer (at head of buffers) */
|
||||
memcpy(&hsd->buffers[hsd->input_size], in_buf, size);
|
||||
hsd->input_size += size;
|
||||
*input_size = size;
|
||||
return HSDR_SINK_OK;
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
* Decompression *
|
||||
*****************/
|
||||
|
||||
#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD))
|
||||
#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD))
|
||||
|
||||
// States
|
||||
static HSD_state st_tag_bit(heatshrink_decoder *hsd);
|
||||
static HSD_state st_yield_literal(heatshrink_decoder *hsd,
|
||||
output_info *oi);
|
||||
static HSD_state st_backref_index_msb(heatshrink_decoder *hsd);
|
||||
static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd);
|
||||
static HSD_state st_backref_count_msb(heatshrink_decoder *hsd);
|
||||
static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd);
|
||||
static HSD_state st_yield_backref(heatshrink_decoder *hsd,
|
||||
output_info *oi);
|
||||
|
||||
HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd,
|
||||
uint8_t *out_buf, size_t out_buf_size, size_t *output_size) {
|
||||
if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) {
|
||||
return HSDR_POLL_ERROR_NULL;
|
||||
}
|
||||
*output_size = 0;
|
||||
|
||||
output_info oi;
|
||||
oi.buf = out_buf;
|
||||
oi.buf_size = out_buf_size;
|
||||
oi.output_size = output_size;
|
||||
|
||||
while (1) {
|
||||
LOG("-- poll, state is %d (%s), input_size %d\n",
|
||||
hsd->state, state_names[hsd->state], hsd->input_size);
|
||||
uint8_t in_state = hsd->state;
|
||||
switch (in_state) {
|
||||
case HSDS_TAG_BIT:
|
||||
hsd->state = st_tag_bit(hsd);
|
||||
break;
|
||||
case HSDS_YIELD_LITERAL:
|
||||
hsd->state = st_yield_literal(hsd, &oi);
|
||||
break;
|
||||
case HSDS_BACKREF_INDEX_MSB:
|
||||
hsd->state = st_backref_index_msb(hsd);
|
||||
break;
|
||||
case HSDS_BACKREF_INDEX_LSB:
|
||||
hsd->state = st_backref_index_lsb(hsd);
|
||||
break;
|
||||
case HSDS_BACKREF_COUNT_MSB:
|
||||
hsd->state = st_backref_count_msb(hsd);
|
||||
break;
|
||||
case HSDS_BACKREF_COUNT_LSB:
|
||||
hsd->state = st_backref_count_lsb(hsd);
|
||||
break;
|
||||
case HSDS_YIELD_BACKREF:
|
||||
hsd->state = st_yield_backref(hsd, &oi);
|
||||
break;
|
||||
default:
|
||||
return HSDR_POLL_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
/* If the current state cannot advance, check if input or output
|
||||
* buffer are exhausted. */
|
||||
if (hsd->state == in_state) {
|
||||
if (*output_size == out_buf_size) { return HSDR_POLL_MORE; }
|
||||
return HSDR_POLL_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static HSD_state st_tag_bit(heatshrink_decoder *hsd) {
|
||||
uint32_t bits = get_bits(hsd, 1); // get tag bit
|
||||
if (bits == NO_BITS) {
|
||||
return HSDS_TAG_BIT;
|
||||
} else if (bits) {
|
||||
return HSDS_YIELD_LITERAL;
|
||||
} else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) {
|
||||
return HSDS_BACKREF_INDEX_MSB;
|
||||
} else {
|
||||
hsd->output_index = 0;
|
||||
return HSDS_BACKREF_INDEX_LSB;
|
||||
}
|
||||
}
|
||||
|
||||
static HSD_state st_yield_literal(heatshrink_decoder *hsd,
|
||||
output_info *oi) {
|
||||
/* Emit a repeated section from the window buffer, and add it (again)
|
||||
* to the window buffer. (Note that the repetition can include
|
||||
* itself.)*/
|
||||
if (*oi->output_size < oi->buf_size) {
|
||||
uint16_t byte = get_bits(hsd, 8);
|
||||
if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */
|
||||
uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
|
||||
uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1;
|
||||
uint8_t c = byte & 0xFF;
|
||||
LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.');
|
||||
buf[hsd->head_index++ & mask] = c;
|
||||
push_byte(hsd, oi, c);
|
||||
return HSDS_TAG_BIT;
|
||||
} else {
|
||||
return HSDS_YIELD_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) {
|
||||
uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
|
||||
ASSERT(bit_ct > 8);
|
||||
uint16_t bits = get_bits(hsd, bit_ct - 8);
|
||||
LOG("-- backref index (msb), got 0x%04x (+1)\n", bits);
|
||||
if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; }
|
||||
hsd->output_index = bits << 8;
|
||||
return HSDS_BACKREF_INDEX_LSB;
|
||||
}
|
||||
|
||||
static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) {
|
||||
uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
|
||||
uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8);
|
||||
LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits);
|
||||
if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; }
|
||||
hsd->output_index |= bits;
|
||||
hsd->output_index++;
|
||||
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
|
||||
hsd->output_count = 0;
|
||||
return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB;
|
||||
}
|
||||
|
||||
static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) {
|
||||
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
|
||||
ASSERT(br_bit_ct > 8);
|
||||
uint16_t bits = get_bits(hsd, br_bit_ct - 8);
|
||||
LOG("-- backref count (msb), got 0x%04x (+1)\n", bits);
|
||||
if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; }
|
||||
hsd->output_count = bits << 8;
|
||||
return HSDS_BACKREF_COUNT_LSB;
|
||||
}
|
||||
|
||||
static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) {
|
||||
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
|
||||
uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8);
|
||||
LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits);
|
||||
if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; }
|
||||
hsd->output_count |= bits;
|
||||
hsd->output_count++;
|
||||
return HSDS_YIELD_BACKREF;
|
||||
}
|
||||
|
||||
static HSD_state st_yield_backref(heatshrink_decoder *hsd,
|
||||
output_info *oi) {
|
||||
size_t count = oi->buf_size - *oi->output_size;
|
||||
if (count > 0) {
|
||||
size_t i = 0;
|
||||
if (hsd->output_count < count) count = hsd->output_count;
|
||||
uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
|
||||
uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1;
|
||||
uint16_t neg_offset = hsd->output_index;
|
||||
LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset);
|
||||
ASSERT(neg_offset <= mask + 1);
|
||||
ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd)));
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
uint8_t c = buf[(hsd->head_index - neg_offset) & mask];
|
||||
push_byte(hsd, oi, c);
|
||||
buf[hsd->head_index & mask] = c;
|
||||
hsd->head_index++;
|
||||
LOG(" -- ++ 0x%02x\n", c);
|
||||
}
|
||||
hsd->output_count -= count;
|
||||
if (hsd->output_count == 0) { return HSDS_TAG_BIT; }
|
||||
}
|
||||
return HSDS_YIELD_BACKREF;
|
||||
}
|
||||
|
||||
/* Get the next COUNT bits from the input buffer, saving incremental progress.
|
||||
* Returns NO_BITS on end of input, or if more than 15 bits are requested. */
|
||||
static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) {
|
||||
uint16_t accumulator = 0;
|
||||
int i = 0;
|
||||
if (count > 15) { return NO_BITS; }
|
||||
LOG("-- popping %u bit(s)\n", count);
|
||||
|
||||
/* If we aren't able to get COUNT bits, suspend immediately, because we
|
||||
* don't track how many bits of COUNT we've accumulated before suspend. */
|
||||
if (hsd->input_size == 0) {
|
||||
if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; }
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (hsd->bit_index == 0x00) {
|
||||
if (hsd->input_size == 0) {
|
||||
LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n",
|
||||
accumulator, accumulator);
|
||||
return NO_BITS;
|
||||
}
|
||||
hsd->current_byte = hsd->buffers[hsd->input_index++];
|
||||
LOG(" -- pulled byte 0x%02x\n", hsd->current_byte);
|
||||
if (hsd->input_index == hsd->input_size) {
|
||||
hsd->input_index = 0; /* input is exhausted */
|
||||
hsd->input_size = 0;
|
||||
}
|
||||
hsd->bit_index = 0x80;
|
||||
}
|
||||
accumulator <<= 1;
|
||||
if (hsd->current_byte & hsd->bit_index) {
|
||||
accumulator |= 0x01;
|
||||
if (0) {
|
||||
LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n",
|
||||
accumulator, hsd->bit_index);
|
||||
}
|
||||
} else {
|
||||
if (0) {
|
||||
LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n",
|
||||
accumulator, hsd->bit_index);
|
||||
}
|
||||
}
|
||||
hsd->bit_index >>= 1;
|
||||
}
|
||||
|
||||
if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); }
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) {
|
||||
if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; }
|
||||
switch (hsd->state) {
|
||||
case HSDS_TAG_BIT:
|
||||
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
|
||||
|
||||
/* If we want to finish with no input, but are in these states, it's
|
||||
* because the 0-bit padding to the last byte looks like a backref
|
||||
* marker bit followed by all 0s for index and count bits. */
|
||||
case HSDS_BACKREF_INDEX_LSB:
|
||||
case HSDS_BACKREF_INDEX_MSB:
|
||||
case HSDS_BACKREF_COUNT_LSB:
|
||||
case HSDS_BACKREF_COUNT_MSB:
|
||||
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
|
||||
|
||||
/* If the output stream is padded with 0xFFs (possibly due to being in
|
||||
* flash memory), also explicitly check the input size rather than
|
||||
* uselessly returning MORE but yielding 0 bytes when polling. */
|
||||
case HSDS_YIELD_LITERAL:
|
||||
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
|
||||
|
||||
default:
|
||||
return HSDR_FINISH_MORE;
|
||||
}
|
||||
}
|
||||
|
||||
static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) {
|
||||
LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.');
|
||||
oi->buf[(*oi->output_size)++] = byte;
|
||||
(void)hsd;
|
||||
}
|
||||
// SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "heatshrink_decoder.h"
|
||||
|
||||
/* States for the polling state machine. */
|
||||
typedef enum {
|
||||
HSDS_TAG_BIT, /* tag bit */
|
||||
HSDS_YIELD_LITERAL, /* ready to yield literal byte */
|
||||
HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */
|
||||
HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */
|
||||
HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */
|
||||
HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */
|
||||
HSDS_YIELD_BACKREF, /* ready to yield back-reference */
|
||||
} HSD_state;
|
||||
|
||||
#if HEATSHRINK_DEBUGGING_LOGS
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#define LOG(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define ASSERT(X) assert(X)
|
||||
static const char *state_names[] = {
|
||||
"tag_bit",
|
||||
"yield_literal",
|
||||
"backref_index_msb",
|
||||
"backref_index_lsb",
|
||||
"backref_count_msb",
|
||||
"backref_count_lsb",
|
||||
"yield_backref",
|
||||
};
|
||||
#else
|
||||
#define LOG(...) /* no-op */
|
||||
#define ASSERT(X) /* no-op */
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buf; /* output buffer */
|
||||
size_t buf_size; /* buffer size */
|
||||
size_t *output_size; /* bytes pushed to buffer, so far */
|
||||
} output_info;
|
||||
|
||||
#define NO_BITS ((uint16_t)-1)
|
||||
|
||||
/* Forward references. */
|
||||
static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count);
|
||||
static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte);
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size,
|
||||
uint8_t window_sz2,
|
||||
uint8_t lookahead_sz2) {
|
||||
if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) ||
|
||||
(window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) ||
|
||||
(input_buffer_size == 0) ||
|
||||
(lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) ||
|
||||
(lookahead_sz2 >= window_sz2)) {
|
||||
return NULL;
|
||||
}
|
||||
size_t buffers_sz = (1 << window_sz2) + input_buffer_size;
|
||||
size_t sz = sizeof(heatshrink_decoder) + buffers_sz;
|
||||
heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz);
|
||||
if (hsd == NULL) { return NULL; }
|
||||
hsd->input_buffer_size = input_buffer_size;
|
||||
hsd->window_sz2 = window_sz2;
|
||||
hsd->lookahead_sz2 = lookahead_sz2;
|
||||
heatshrink_decoder_reset(hsd);
|
||||
LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n",
|
||||
sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size);
|
||||
return hsd;
|
||||
}
|
||||
|
||||
void heatshrink_decoder_free(heatshrink_decoder *hsd) {
|
||||
size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size;
|
||||
size_t sz = sizeof(heatshrink_decoder) + buffers_sz;
|
||||
HEATSHRINK_FREE(hsd, sz);
|
||||
(void)sz; /* may not be used by free */
|
||||
}
|
||||
#endif
|
||||
|
||||
void heatshrink_decoder_reset(heatshrink_decoder *hsd) {
|
||||
size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd);
|
||||
size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd);
|
||||
memset(hsd->buffers, 0, buf_sz + input_sz);
|
||||
hsd->state = HSDS_TAG_BIT;
|
||||
hsd->input_size = 0;
|
||||
hsd->input_index = 0;
|
||||
hsd->bit_index = 0x00;
|
||||
hsd->current_byte = 0x00;
|
||||
hsd->output_count = 0;
|
||||
hsd->output_index = 0;
|
||||
hsd->head_index = 0;
|
||||
}
|
||||
|
||||
/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */
|
||||
HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd,
|
||||
uint8_t *in_buf, size_t size, size_t *input_size) {
|
||||
if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) {
|
||||
return HSDR_SINK_ERROR_NULL;
|
||||
}
|
||||
|
||||
size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size;
|
||||
if (rem == 0) {
|
||||
*input_size = 0;
|
||||
return HSDR_SINK_FULL;
|
||||
}
|
||||
|
||||
size = rem < size ? rem : size;
|
||||
LOG("-- sinking %zd bytes\n", size);
|
||||
/* copy into input buffer (at head of buffers) */
|
||||
memcpy(&hsd->buffers[hsd->input_size], in_buf, size);
|
||||
hsd->input_size += size;
|
||||
*input_size = size;
|
||||
return HSDR_SINK_OK;
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
* Decompression *
|
||||
*****************/
|
||||
|
||||
#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD))
|
||||
#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD))
|
||||
|
||||
// States
|
||||
static HSD_state st_tag_bit(heatshrink_decoder *hsd);
|
||||
static HSD_state st_yield_literal(heatshrink_decoder *hsd,
|
||||
output_info *oi);
|
||||
static HSD_state st_backref_index_msb(heatshrink_decoder *hsd);
|
||||
static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd);
|
||||
static HSD_state st_backref_count_msb(heatshrink_decoder *hsd);
|
||||
static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd);
|
||||
static HSD_state st_yield_backref(heatshrink_decoder *hsd,
|
||||
output_info *oi);
|
||||
|
||||
HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd,
|
||||
uint8_t *out_buf, size_t out_buf_size, size_t *output_size) {
|
||||
if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) {
|
||||
return HSDR_POLL_ERROR_NULL;
|
||||
}
|
||||
*output_size = 0;
|
||||
|
||||
output_info oi;
|
||||
oi.buf = out_buf;
|
||||
oi.buf_size = out_buf_size;
|
||||
oi.output_size = output_size;
|
||||
|
||||
while (1) {
|
||||
LOG("-- poll, state is %d (%s), input_size %d\n",
|
||||
hsd->state, state_names[hsd->state], hsd->input_size);
|
||||
uint8_t in_state = hsd->state;
|
||||
switch (in_state) {
|
||||
case HSDS_TAG_BIT:
|
||||
hsd->state = st_tag_bit(hsd);
|
||||
break;
|
||||
case HSDS_YIELD_LITERAL:
|
||||
hsd->state = st_yield_literal(hsd, &oi);
|
||||
break;
|
||||
case HSDS_BACKREF_INDEX_MSB:
|
||||
hsd->state = st_backref_index_msb(hsd);
|
||||
break;
|
||||
case HSDS_BACKREF_INDEX_LSB:
|
||||
hsd->state = st_backref_index_lsb(hsd);
|
||||
break;
|
||||
case HSDS_BACKREF_COUNT_MSB:
|
||||
hsd->state = st_backref_count_msb(hsd);
|
||||
break;
|
||||
case HSDS_BACKREF_COUNT_LSB:
|
||||
hsd->state = st_backref_count_lsb(hsd);
|
||||
break;
|
||||
case HSDS_YIELD_BACKREF:
|
||||
hsd->state = st_yield_backref(hsd, &oi);
|
||||
break;
|
||||
default:
|
||||
return HSDR_POLL_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
/* If the current state cannot advance, check if input or output
|
||||
* buffer are exhausted. */
|
||||
if (hsd->state == in_state) {
|
||||
if (*output_size == out_buf_size) { return HSDR_POLL_MORE; }
|
||||
return HSDR_POLL_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static HSD_state st_tag_bit(heatshrink_decoder *hsd) {
|
||||
uint32_t bits = get_bits(hsd, 1); // get tag bit
|
||||
if (bits == NO_BITS) {
|
||||
return HSDS_TAG_BIT;
|
||||
} else if (bits) {
|
||||
return HSDS_YIELD_LITERAL;
|
||||
} else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) {
|
||||
return HSDS_BACKREF_INDEX_MSB;
|
||||
} else {
|
||||
hsd->output_index = 0;
|
||||
return HSDS_BACKREF_INDEX_LSB;
|
||||
}
|
||||
}
|
||||
|
||||
static HSD_state st_yield_literal(heatshrink_decoder *hsd,
|
||||
output_info *oi) {
|
||||
/* Emit a repeated section from the window buffer, and add it (again)
|
||||
* to the window buffer. (Note that the repetition can include
|
||||
* itself.)*/
|
||||
if (*oi->output_size < oi->buf_size) {
|
||||
uint16_t byte = get_bits(hsd, 8);
|
||||
if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */
|
||||
uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
|
||||
uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1;
|
||||
uint8_t c = byte & 0xFF;
|
||||
LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.');
|
||||
buf[hsd->head_index++ & mask] = c;
|
||||
push_byte(hsd, oi, c);
|
||||
return HSDS_TAG_BIT;
|
||||
} else {
|
||||
return HSDS_YIELD_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) {
|
||||
uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
|
||||
ASSERT(bit_ct > 8);
|
||||
uint16_t bits = get_bits(hsd, bit_ct - 8);
|
||||
LOG("-- backref index (msb), got 0x%04x (+1)\n", bits);
|
||||
if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; }
|
||||
hsd->output_index = bits << 8;
|
||||
return HSDS_BACKREF_INDEX_LSB;
|
||||
}
|
||||
|
||||
static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) {
|
||||
uint8_t bit_ct = BACKREF_INDEX_BITS(hsd);
|
||||
uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8);
|
||||
LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits);
|
||||
if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; }
|
||||
hsd->output_index |= bits;
|
||||
hsd->output_index++;
|
||||
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
|
||||
hsd->output_count = 0;
|
||||
return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB;
|
||||
}
|
||||
|
||||
static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) {
|
||||
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
|
||||
ASSERT(br_bit_ct > 8);
|
||||
uint16_t bits = get_bits(hsd, br_bit_ct - 8);
|
||||
LOG("-- backref count (msb), got 0x%04x (+1)\n", bits);
|
||||
if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; }
|
||||
hsd->output_count = bits << 8;
|
||||
return HSDS_BACKREF_COUNT_LSB;
|
||||
}
|
||||
|
||||
static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) {
|
||||
uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd);
|
||||
uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8);
|
||||
LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits);
|
||||
if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; }
|
||||
hsd->output_count |= bits;
|
||||
hsd->output_count++;
|
||||
return HSDS_YIELD_BACKREF;
|
||||
}
|
||||
|
||||
static HSD_state st_yield_backref(heatshrink_decoder *hsd,
|
||||
output_info *oi) {
|
||||
size_t count = oi->buf_size - *oi->output_size;
|
||||
if (count > 0) {
|
||||
size_t i = 0;
|
||||
if (hsd->output_count < count) count = hsd->output_count;
|
||||
uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)];
|
||||
uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1;
|
||||
uint16_t neg_offset = hsd->output_index;
|
||||
LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset);
|
||||
ASSERT(neg_offset <= mask + 1);
|
||||
ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd)));
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
uint8_t c = buf[(hsd->head_index - neg_offset) & mask];
|
||||
push_byte(hsd, oi, c);
|
||||
buf[hsd->head_index & mask] = c;
|
||||
hsd->head_index++;
|
||||
LOG(" -- ++ 0x%02x\n", c);
|
||||
}
|
||||
hsd->output_count -= count;
|
||||
if (hsd->output_count == 0) { return HSDS_TAG_BIT; }
|
||||
}
|
||||
return HSDS_YIELD_BACKREF;
|
||||
}
|
||||
|
||||
/* Get the next COUNT bits from the input buffer, saving incremental progress.
|
||||
* Returns NO_BITS on end of input, or if more than 15 bits are requested. */
|
||||
static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) {
|
||||
uint16_t accumulator = 0;
|
||||
int i = 0;
|
||||
if (count > 15) { return NO_BITS; }
|
||||
LOG("-- popping %u bit(s)\n", count);
|
||||
|
||||
/* If we aren't able to get COUNT bits, suspend immediately, because we
|
||||
* don't track how many bits of COUNT we've accumulated before suspend. */
|
||||
if (hsd->input_size == 0) {
|
||||
if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; }
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (hsd->bit_index == 0x00) {
|
||||
if (hsd->input_size == 0) {
|
||||
LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n",
|
||||
accumulator, accumulator);
|
||||
return NO_BITS;
|
||||
}
|
||||
hsd->current_byte = hsd->buffers[hsd->input_index++];
|
||||
LOG(" -- pulled byte 0x%02x\n", hsd->current_byte);
|
||||
if (hsd->input_index == hsd->input_size) {
|
||||
hsd->input_index = 0; /* input is exhausted */
|
||||
hsd->input_size = 0;
|
||||
}
|
||||
hsd->bit_index = 0x80;
|
||||
}
|
||||
accumulator <<= 1;
|
||||
if (hsd->current_byte & hsd->bit_index) {
|
||||
accumulator |= 0x01;
|
||||
if (0) {
|
||||
LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n",
|
||||
accumulator, hsd->bit_index);
|
||||
}
|
||||
} else {
|
||||
if (0) {
|
||||
LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n",
|
||||
accumulator, hsd->bit_index);
|
||||
}
|
||||
}
|
||||
hsd->bit_index >>= 1;
|
||||
}
|
||||
|
||||
if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); }
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) {
|
||||
if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; }
|
||||
switch (hsd->state) {
|
||||
case HSDS_TAG_BIT:
|
||||
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
|
||||
|
||||
/* If we want to finish with no input, but are in these states, it's
|
||||
* because the 0-bit padding to the last byte looks like a backref
|
||||
* marker bit followed by all 0s for index and count bits. */
|
||||
case HSDS_BACKREF_INDEX_LSB:
|
||||
case HSDS_BACKREF_INDEX_MSB:
|
||||
case HSDS_BACKREF_COUNT_LSB:
|
||||
case HSDS_BACKREF_COUNT_MSB:
|
||||
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
|
||||
|
||||
/* If the output stream is padded with 0xFFs (possibly due to being in
|
||||
* flash memory), also explicitly check the input size rather than
|
||||
* uselessly returning MORE but yielding 0 bytes when polling. */
|
||||
case HSDS_YIELD_LITERAL:
|
||||
return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE;
|
||||
|
||||
default:
|
||||
return HSDR_FINISH_MORE;
|
||||
}
|
||||
}
|
||||
|
||||
static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) {
|
||||
LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.');
|
||||
oi->buf[(*oi->output_size)++] = byte;
|
||||
(void)hsd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +1,104 @@
|
|||
#ifndef HEATSHRINK_DECODER_H
|
||||
#define HEATSHRINK_DECODER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "heatshrink_common.h"
|
||||
#include "heatshrink_config.h"
|
||||
|
||||
typedef enum {
|
||||
HSDR_SINK_OK, /* data sunk, ready to poll */
|
||||
HSDR_SINK_FULL, /* out of space in internal buffer */
|
||||
HSDR_SINK_ERROR_NULL=-1, /* NULL argument */
|
||||
} HSD_sink_res;
|
||||
|
||||
typedef enum {
|
||||
HSDR_POLL_EMPTY, /* input exhausted */
|
||||
HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */
|
||||
HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */
|
||||
HSDR_POLL_ERROR_UNKNOWN=-2,
|
||||
} HSD_poll_res;
|
||||
|
||||
typedef enum {
|
||||
HSDR_FINISH_DONE, /* output is done */
|
||||
HSDR_FINISH_MORE, /* more output remains */
|
||||
HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */
|
||||
} HSD_finish_res;
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \
|
||||
((BUF)->input_buffer_size)
|
||||
#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \
|
||||
((BUF)->window_sz2)
|
||||
#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
|
||||
((BUF)->lookahead_sz2)
|
||||
#else
|
||||
#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \
|
||||
HEATSHRINK_STATIC_INPUT_BUFFER_SIZE
|
||||
#define HEATSHRINK_DECODER_WINDOW_BITS(_) \
|
||||
(HEATSHRINK_STATIC_WINDOW_BITS)
|
||||
#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
|
||||
(HEATSHRINK_STATIC_LOOKAHEAD_BITS)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint16_t input_size; /* bytes in input buffer */
|
||||
uint16_t input_index; /* offset to next unprocessed input byte */
|
||||
uint16_t output_count; /* how many bytes to output */
|
||||
uint16_t output_index; /* index for bytes to output */
|
||||
uint16_t head_index; /* head of window buffer */
|
||||
uint8_t state; /* current state machine node */
|
||||
uint8_t current_byte; /* current byte of input */
|
||||
uint8_t bit_index; /* current bit index */
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
/* Fields that are only used if dynamically allocated. */
|
||||
uint8_t window_sz2; /* window buffer bits */
|
||||
uint8_t lookahead_sz2; /* lookahead bits */
|
||||
uint16_t input_buffer_size; /* input buffer size */
|
||||
|
||||
/* Input buffer, then expansion window buffer */
|
||||
uint8_t buffers[];
|
||||
#else
|
||||
/* Input buffer, then expansion window buffer */
|
||||
uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_))
|
||||
+ HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)];
|
||||
#endif
|
||||
} heatshrink_decoder;
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes,
|
||||
* an expansion buffer size of 2^WINDOW_SZ2, and a lookahead
|
||||
* size of 2^lookahead_sz2. (The window buffer and lookahead sizes
|
||||
* must match the settings used when the data was compressed.)
|
||||
* Returns NULL on error. */
|
||||
heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size,
|
||||
uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2);
|
||||
|
||||
/* Free a decoder. */
|
||||
void heatshrink_decoder_free(heatshrink_decoder *hsd);
|
||||
#endif
|
||||
|
||||
/* Reset a decoder. */
|
||||
void heatshrink_decoder_reset(heatshrink_decoder *hsd);
|
||||
|
||||
/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to
|
||||
* indicate how many bytes were actually sunk (in case a buffer was filled). */
|
||||
HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd,
|
||||
uint8_t *in_buf, size_t size, size_t *input_size);
|
||||
|
||||
/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into
|
||||
* OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */
|
||||
HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd,
|
||||
uint8_t *out_buf, size_t out_buf_size, size_t *output_size);
|
||||
|
||||
/* Notify the dencoder that the input stream is finished.
|
||||
* If the return value is HSDR_FINISH_MORE, there is still more output, so
|
||||
* call heatshrink_decoder_poll and repeat. */
|
||||
HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd);
|
||||
|
||||
#endif
|
||||
// SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef HEATSHRINK_DECODER_H
|
||||
#define HEATSHRINK_DECODER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "heatshrink_common.h"
|
||||
#include "heatshrink_config.h"
|
||||
|
||||
typedef enum {
|
||||
HSDR_SINK_OK, /* data sunk, ready to poll */
|
||||
HSDR_SINK_FULL, /* out of space in internal buffer */
|
||||
HSDR_SINK_ERROR_NULL=-1, /* NULL argument */
|
||||
} HSD_sink_res;
|
||||
|
||||
typedef enum {
|
||||
HSDR_POLL_EMPTY, /* input exhausted */
|
||||
HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */
|
||||
HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */
|
||||
HSDR_POLL_ERROR_UNKNOWN=-2,
|
||||
} HSD_poll_res;
|
||||
|
||||
typedef enum {
|
||||
HSDR_FINISH_DONE, /* output is done */
|
||||
HSDR_FINISH_MORE, /* more output remains */
|
||||
HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */
|
||||
} HSD_finish_res;
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \
|
||||
((BUF)->input_buffer_size)
|
||||
#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \
|
||||
((BUF)->window_sz2)
|
||||
#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
|
||||
((BUF)->lookahead_sz2)
|
||||
#else
|
||||
#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \
|
||||
HEATSHRINK_STATIC_INPUT_BUFFER_SIZE
|
||||
#define HEATSHRINK_DECODER_WINDOW_BITS(_) \
|
||||
(HEATSHRINK_STATIC_WINDOW_BITS)
|
||||
#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \
|
||||
(HEATSHRINK_STATIC_LOOKAHEAD_BITS)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint16_t input_size; /* bytes in input buffer */
|
||||
uint16_t input_index; /* offset to next unprocessed input byte */
|
||||
uint16_t output_count; /* how many bytes to output */
|
||||
uint16_t output_index; /* index for bytes to output */
|
||||
uint16_t head_index; /* head of window buffer */
|
||||
uint8_t state; /* current state machine node */
|
||||
uint8_t current_byte; /* current byte of input */
|
||||
uint8_t bit_index; /* current bit index */
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
/* Fields that are only used if dynamically allocated. */
|
||||
uint8_t window_sz2; /* window buffer bits */
|
||||
uint8_t lookahead_sz2; /* lookahead bits */
|
||||
uint16_t input_buffer_size; /* input buffer size */
|
||||
|
||||
/* Input buffer, then expansion window buffer */
|
||||
uint8_t buffers[];
|
||||
#else
|
||||
/* Input buffer, then expansion window buffer */
|
||||
uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_))
|
||||
+ HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)];
|
||||
#endif
|
||||
} heatshrink_decoder;
|
||||
|
||||
#if HEATSHRINK_DYNAMIC_ALLOC
|
||||
/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes,
|
||||
* an expansion buffer size of 2^WINDOW_SZ2, and a lookahead
|
||||
* size of 2^lookahead_sz2. (The window buffer and lookahead sizes
|
||||
* must match the settings used when the data was compressed.)
|
||||
* Returns NULL on error. */
|
||||
heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size,
|
||||
uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2);
|
||||
|
||||
/* Free a decoder. */
|
||||
void heatshrink_decoder_free(heatshrink_decoder *hsd);
|
||||
#endif
|
||||
|
||||
/* Reset a decoder. */
|
||||
void heatshrink_decoder_reset(heatshrink_decoder *hsd);
|
||||
|
||||
/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to
|
||||
* indicate how many bytes were actually sunk (in case a buffer was filled). */
|
||||
HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd,
|
||||
uint8_t *in_buf, size_t size, size_t *input_size);
|
||||
|
||||
/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into
|
||||
* OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */
|
||||
HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd,
|
||||
uint8_t *out_buf, size_t out_buf_size, size_t *output_size);
|
||||
|
||||
/* Notify the dencoder that the input stream is finished.
|
||||
* If the return value is HSDR_FINISH_MORE, there is still more output, so
|
||||
* call heatshrink_decoder_poll and repeat. */
|
||||
HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2018 Mikey Sklar for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <FastLED.h>
|
||||
|
||||
#define LED_PIN 0
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2018 Mikey Sklar for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import board
|
||||
import neopixel
|
||||
import adafruit_fancyled.adafruit_fancyled as fancy
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2021 Carter Nelson for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import serial
|
||||
|
||||
# open serial port
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2021 Carter Nelson for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import json
|
||||
import serial
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2021 Carter Nelson for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import serial
|
||||
|
||||
# how many bytes to read?
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Carter Nelson for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#include "OPTIGATrustM.h"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue