From 7bf0a4786c6987b756683f6ee0af866833a8b4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 23 Mar 2023 02:00:48 +0900 Subject: [PATCH] [FL-3152] Screen streaming improvements (#2498) * Rpc: reserve some bandwidth when screen streaming * Move furi_hal_compress to toolbox/comporess * Lib: heatshrink as external submodule, compile warnings fixes, better buffer management * Lib: cleanup compressor definitions * Rpc: add canvas orientation support * Format Sources --- .gitmodules | 3 + applications/services/gui/canvas.c | 9 +- applications/services/gui/canvas_i.h | 2 + applications/services/gui/gui.c | 1 + applications/services/gui/gui.h | 6 +- applications/services/rpc/rpc_gui.c | 26 +- assets/protobuf | 2 +- firmware/targets/f18/api_symbols.csv | 13 +- firmware/targets/f18/furi_hal/furi_hal.c | 34 +- .../targets/f18/furi_hal/furi_hal_resources.c | 4 + firmware/targets/f7/api_symbols.csv | 7 - firmware/targets/f7/furi_hal/furi_hal.c | 26 - .../targets/f7/furi_hal/furi_hal_ibutton.c | 3 + .../targets/f7/furi_hal/furi_hal_resources.c | 4 + firmware/targets/f7/src/dfu.c | 7 +- firmware/targets/f7/src/recovery.c | 6 +- firmware/targets/furi_hal_include/furi_hal.h | 1 - .../furi_hal_include/furi_hal_compress.h | 87 --- lib/err.h | 4 + lib/heatshrink | 1 + lib/heatshrink/heatshrink_common.h | 20 - lib/heatshrink/heatshrink_config.h | 28 - lib/heatshrink/heatshrink_decoder.c | 364 ----------- lib/heatshrink/heatshrink_decoder.h | 100 --- lib/heatshrink/heatshrink_encoder.c | 602 ------------------ lib/heatshrink/heatshrink_encoder.h | 109 ---- lib/misc.scons | 7 +- .../toolbox/compress.c | 127 ++-- lib/toolbox/compress.h | 96 +++ 29 files changed, 242 insertions(+), 1457 deletions(-) delete mode 100644 firmware/targets/furi_hal_include/furi_hal_compress.h create mode 100644 lib/err.h create mode 160000 lib/heatshrink delete mode 100644 lib/heatshrink/heatshrink_common.h delete mode 100644 lib/heatshrink/heatshrink_config.h delete mode 100644 lib/heatshrink/heatshrink_decoder.c delete mode 100644 lib/heatshrink/heatshrink_decoder.h delete mode 100644 lib/heatshrink/heatshrink_encoder.c delete mode 100644 lib/heatshrink/heatshrink_encoder.h rename firmware/targets/f7/furi_hal/furi_hal_compress.c => lib/toolbox/compress.c (67%) create mode 100644 lib/toolbox/compress.h diff --git a/.gitmodules b/.gitmodules index 56368cd58..3a15177bd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "applications/external/dap_link/lib/free-dap"] path = applications/external/dap_link/lib/free-dap url = https://github.com/ataradov/free-dap.git +[submodule "lib/heatshrink"] + path = lib/heatshrink + url = https://github.com/flipperdevices/heatshrink.git diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 9c29a39fd..40797c086 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -17,6 +17,7 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = { Canvas* canvas_init() { Canvas* canvas = malloc(sizeof(Canvas)); + canvas->compress_icon = compress_icon_alloc(); // Setup u8g2 u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); @@ -35,6 +36,7 @@ Canvas* canvas_init() { void canvas_free(Canvas* canvas) { furi_assert(canvas); + compress_icon_free(canvas->compress_icon); free(canvas); } @@ -218,7 +220,7 @@ void canvas_draw_bitmap( x += canvas->offset_x; y += canvas->offset_y; uint8_t* bitmap_data = NULL; - furi_hal_compress_icon_decode(compressed_bitmap_data, &bitmap_data); + compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data); u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data); } @@ -233,7 +235,8 @@ void canvas_draw_icon_animation( x += canvas->offset_x; y += canvas->offset_y; uint8_t* icon_data = NULL; - furi_hal_compress_icon_decode(icon_animation_get_data(icon_animation), &icon_data); + compress_icon_decode( + canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data); u8g2_DrawXBM( &canvas->fb, x, @@ -250,7 +253,7 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) { x += canvas->offset_x; y += canvas->offset_y; uint8_t* icon_data = NULL; - furi_hal_compress_icon_decode(icon_get_data(icon), &icon_data); + compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data); } diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index 12cabfa7d..39e7021bc 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -7,6 +7,7 @@ #include "canvas.h" #include +#include /** Canvas structure */ @@ -17,6 +18,7 @@ struct Canvas { uint8_t offset_y; uint8_t width; uint8_t height; + CompressIcon* compress_icon; }; /** Allocate memory and initialize canvas diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 24b48a837..392011620 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -250,6 +250,7 @@ static void gui_redraw(Gui* gui) { p->callback( canvas_get_buffer(gui->canvas), canvas_get_buffer_size(gui->canvas), + canvas_get_orientation(gui->canvas), p->context); } } while(false); diff --git a/applications/services/gui/gui.h b/applications/services/gui/gui.h index d7d73f27b..1b5987eda 100644 --- a/applications/services/gui/gui.h +++ b/applications/services/gui/gui.h @@ -27,7 +27,11 @@ typedef enum { } GuiLayer; /** Gui Canvas Commit Callback */ -typedef void (*GuiCanvasCommitCallback)(uint8_t* data, size_t size, void* context); +typedef void (*GuiCanvasCommitCallback)( + uint8_t* data, + size_t size, + CanvasOrientation orientation, + void* context); #define RECORD_GUI "gui" diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index c2af425e9..0c70702cf 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -33,8 +33,18 @@ typedef struct { uint32_t input_counter; } RpcGuiSystem; -static void - rpc_system_gui_screen_stream_frame_callback(uint8_t* data, size_t size, void* context) { +static const PB_Gui_ScreenOrientation rpc_system_gui_screen_orientation_map[] = { + [CanvasOrientationHorizontal] = PB_Gui_ScreenOrientation_HORIZONTAL, + [CanvasOrientationHorizontalFlip] = PB_Gui_ScreenOrientation_HORIZONTAL_FLIP, + [CanvasOrientationVertical] = PB_Gui_ScreenOrientation_VERTICAL, + [CanvasOrientationVerticalFlip] = PB_Gui_ScreenOrientation_VERTICAL_FLIP, +}; + +static void rpc_system_gui_screen_stream_frame_callback( + uint8_t* data, + size_t size, + CanvasOrientation orientation, + void* context) { furi_assert(data); furi_assert(context); @@ -44,6 +54,8 @@ static void furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size); memcpy(buffer, data, size); + rpc_gui->transmit_frame->content.gui_screen_frame.orientation = + rpc_system_gui_screen_orientation_map[orientation]; furi_thread_flags_set(furi_thread_get_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagTransmit); } @@ -53,12 +65,22 @@ static int32_t rpc_system_gui_screen_stream_frame_transmit_thread(void* context) RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context; + uint32_t transmit_time = 0; while(true) { uint32_t flags = furi_thread_flags_wait(RpcGuiWorkerFlagAny, FuriFlagWaitAny, FuriWaitForever); + if(flags & RpcGuiWorkerFlagTransmit) { + transmit_time = furi_get_tick(); rpc_send(rpc_gui->session, rpc_gui->transmit_frame); + transmit_time = furi_get_tick() - transmit_time; + + // Guaranteed bandwidth reserve + uint32_t extra_delay = transmit_time / 20; + if(extra_delay > 500) extra_delay = 500; + if(extra_delay) furi_delay_tick(extra_delay); } + if(flags & RpcGuiWorkerFlagExit) { break; } diff --git a/assets/protobuf b/assets/protobuf index 646066023..1f6b4a08c 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 6460660237005d02d5c223835659b40e373bade9 +Subproject commit 1f6b4a08c5d05c2b17926a3ba79f60109638932f diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index e6fae33ee..40e23a747 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -57,7 +57,6 @@ Header,+,firmware/targets/furi_hal_include/furi_hal.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_serial.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_compress.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,, @@ -876,12 +875,12 @@ Function,-,furi_hal_clock_resume_tick,void, Function,-,furi_hal_clock_suspend_tick,void, Function,-,furi_hal_clock_switch_to_hsi,void, Function,-,furi_hal_clock_switch_to_pll,void, -Function,-,furi_hal_compress_alloc,FuriHalCompress*,uint16_t -Function,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,furi_hal_compress_encode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,furi_hal_compress_free,void,FuriHalCompress* -Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**" -Function,-,furi_hal_compress_icon_init,void, +Function,-,compress_alloc,Compress*,uint16_t +Function,-,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,compress_free,void,Compress* +Function,-,compress_icon_decode,void,"const uint8_t*, uint8_t**" +Function,-,compress_icon_init,void, Function,+,furi_hal_console_disable,void, Function,+,furi_hal_console_enable,void, Function,+,furi_hal_console_init,void, diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c index 2c255fa0d..4064dd647 100644 --- a/firmware/targets/f18/furi_hal/furi_hal.c +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -1,5 +1,6 @@ #include #include +#include #include @@ -7,29 +8,20 @@ void furi_hal_init_early() { furi_hal_cortex_init_early(); - furi_hal_clock_init_early(); - furi_hal_resources_init_early(); - furi_hal_os_init(); - furi_hal_spi_config_init_early(); - furi_hal_i2c_init_early(); furi_hal_light_init(); - furi_hal_rtc_init_early(); } void furi_hal_deinit_early() { furi_hal_rtc_deinit_early(); - furi_hal_i2c_deinit_early(); furi_hal_spi_config_deinit_early(); - furi_hal_resources_deinit_early(); - furi_hal_clock_deinit_early(); } @@ -38,40 +30,24 @@ void furi_hal_init() { furi_hal_clock_init(); furi_hal_console_init(); furi_hal_rtc_init(); - furi_hal_interrupt_init(); - furi_hal_flash_init(); - furi_hal_resources_init(); - FURI_LOG_I(TAG, "GPIO OK"); - furi_hal_version_init(); - furi_hal_spi_config_init(); furi_hal_spi_dma_init(); - furi_hal_speaker_init(); - FURI_LOG_I(TAG, "Speaker OK"); - furi_hal_crypto_init(); - - // USB -#ifndef FURI_RAM_EXEC - furi_hal_usb_init(); - FURI_LOG_I(TAG, "USB OK"); -#endif - furi_hal_i2c_init(); - - // High Level furi_hal_power_init(); furi_hal_light_init(); + furi_hal_bt_init(); + furi_hal_memory_init(); + #ifndef FURI_RAM_EXEC + furi_hal_usb_init(); furi_hal_vibro_init(); #endif - furi_hal_bt_init(); - furi_hal_compress_icon_init(); } void furi_hal_switch(void* address) { diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index 41cc80bfb..abb258cb1 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -4,6 +4,8 @@ #include #include +#define TAG "FuriHalResources" + const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; const GpioPin ibutton_gpio = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; @@ -198,6 +200,8 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); + + FURI_LOG_I(TAG, "Init OK"); } int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7ac9a2459..8b1d29b1c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -62,7 +62,6 @@ Header,+,firmware/targets/furi_hal_include/furi_hal.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_serial.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_compress.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,, @@ -1057,12 +1056,6 @@ Function,-,furi_hal_clock_resume_tick,void, Function,-,furi_hal_clock_suspend_tick,void, Function,-,furi_hal_clock_switch_to_hsi,void, Function,-,furi_hal_clock_switch_to_pll,void, -Function,-,furi_hal_compress_alloc,FuriHalCompress*,uint16_t -Function,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,furi_hal_compress_encode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" -Function,-,furi_hal_compress_free,void,FuriHalCompress* -Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**" -Function,-,furi_hal_compress_icon_init,void, Function,+,furi_hal_console_disable,void, Function,+,furi_hal_console_enable,void, Function,+,furi_hal_console_init,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 5840a697e..1b710bb96 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -8,29 +8,20 @@ void furi_hal_init_early() { furi_hal_cortex_init_early(); - furi_hal_clock_init_early(); - furi_hal_resources_init_early(); - furi_hal_os_init(); - furi_hal_spi_config_init_early(); - furi_hal_i2c_init_early(); furi_hal_light_init(); - furi_hal_rtc_init_early(); } void furi_hal_deinit_early() { furi_hal_rtc_deinit_early(); - furi_hal_i2c_deinit_early(); furi_hal_spi_config_deinit_early(); - furi_hal_resources_deinit_early(); - furi_hal_clock_deinit_early(); } @@ -39,41 +30,24 @@ void furi_hal_init() { furi_hal_clock_init(); furi_hal_console_init(); furi_hal_rtc_init(); - furi_hal_interrupt_init(); - furi_hal_flash_init(); - furi_hal_resources_init(); - FURI_LOG_I(TAG, "GPIO OK"); - furi_hal_version_init(); furi_hal_region_init(); - furi_hal_spi_config_init(); furi_hal_spi_dma_init(); - furi_hal_ibutton_init(); - FURI_LOG_I(TAG, "iButton OK"); furi_hal_speaker_init(); - FURI_LOG_I(TAG, "Speaker OK"); - furi_hal_crypto_init(); - furi_hal_i2c_init(); - - // High Level furi_hal_power_init(); furi_hal_light_init(); - furi_hal_bt_init(); furi_hal_memory_init(); - furi_hal_compress_icon_init(); #ifndef FURI_RAM_EXEC - // USB furi_hal_usb_init(); - FURI_LOG_I(TAG, "USB OK"); furi_hal_vibro_init(); furi_hal_subghz_init(); furi_hal_nfc_init(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c index c05cd69a8..f19fd0a0e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c @@ -7,6 +7,7 @@ #include +#define TAG "FuriHalIbutton" #define FURI_HAL_IBUTTON_TIMER TIM1 #define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16 @@ -33,6 +34,8 @@ static void furi_hal_ibutton_emulate_isr() { void furi_hal_ibutton_init() { furi_hal_ibutton = malloc(sizeof(FuriHalIbutton)); furi_hal_ibutton->state = FuriHalIbuttonStateIdle; + + FURI_LOG_I(TAG, "Init OK"); } void furi_hal_ibutton_emulate_start( diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index c0eb9ee67..d0d85cb2d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -4,6 +4,8 @@ #include #include +#define TAG "FuriHalResources" + const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; @@ -190,6 +192,8 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); + + FURI_LOG_I(TAG, "Init OK"); } int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { diff --git a/firmware/targets/f7/src/dfu.c b/firmware/targets/f7/src/dfu.c index f32ac2ac4..b060bc8d2 100644 --- a/firmware/targets/f7/src/dfu.c +++ b/firmware/targets/f7/src/dfu.c @@ -4,10 +4,11 @@ #include #include #include +#include void flipper_boot_dfu_show_splash() { // Initialize - furi_hal_compress_icon_init(); + CompressIcon* compress_icon = compress_icon_alloc(); u8g2_t* fb = malloc(sizeof(u8g2_t)); memset(fb, 0, sizeof(u8g2_t)); @@ -15,13 +16,15 @@ void flipper_boot_dfu_show_splash() { u8g2_InitDisplay(fb); u8g2_SetDrawColor(fb, 0x01); uint8_t* splash_data = NULL; - furi_hal_compress_icon_decode(icon_get_data(&I_DFU_128x50), &splash_data); + compress_icon_decode(compress_icon, icon_get_data(&I_DFU_128x50), &splash_data); u8g2_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data); u8g2_SetFont(fb, u8g2_font_helvB08_tr); u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode"); u8g2_DrawStr(fb, 2, 21, "DFU Started"); u8g2_SetPowerSave(fb, 0); u8g2_SendBuffer(fb); + + compress_icon_free(compress_icon); } void flipper_boot_dfu_exec() { diff --git a/firmware/targets/f7/src/recovery.c b/firmware/targets/f7/src/recovery.c index db538b0d5..d037e8118 100644 --- a/firmware/targets/f7/src/recovery.c +++ b/firmware/targets/f7/src/recovery.c @@ -4,6 +4,7 @@ #include #include #include +#include #define COUNTER_VALUE (136U) @@ -27,9 +28,9 @@ void flipper_boot_recovery_exec() { u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); u8g2_InitDisplay(fb); - furi_hal_compress_icon_init(); + CompressIcon* compress_icon = compress_icon_alloc(); uint8_t* splash_data = NULL; - furi_hal_compress_icon_decode(icon_get_data(&I_Erase_pin_128x64), &splash_data); + compress_icon_decode(compress_icon, icon_get_data(&I_Erase_pin_128x64), &splash_data); u8g2_ClearBuffer(fb); u8g2_SetDrawColor(fb, 0x01); @@ -38,6 +39,7 @@ void flipper_boot_recovery_exec() { u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data); u8g2_SendBuffer(fb); u8g2_SetPowerSave(fb, 0); + compress_icon_free(compress_icon); size_t counter = COUNTER_VALUE; while(counter) { diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index ad4340dd4..2eb4688d4 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -33,7 +33,6 @@ struct STOP_EXTERNING_ME {}; #include #include #include -#include #include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_compress.h b/firmware/targets/furi_hal_include/furi_hal_compress.h deleted file mode 100644 index f80aee516..000000000 --- a/firmware/targets/furi_hal_include/furi_hal_compress.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @file furi_hal_compress.h - * LZSS based compression HAL API - */ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Defines encoder and decoder window size */ -#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG (8) - -/** Defines encoder and decoder lookahead buffer size */ -#define FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4) - -/** FuriHalCompress control structure */ -typedef struct FuriHalCompress FuriHalCompress; - -/** Initialize icon decoder - */ -void furi_hal_compress_icon_init(); - -/** Icon decoder - * - * @param icon_data pointer to icon data - * @param decoded_buff pointer to decoded buffer - */ -void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff); - -/** Allocate encoder and decoder - * - * @param compress_buff_size size of decoder and encoder buffer to allocate - * - * @return FuriHalCompress instance - */ -FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size); - -/** Free encoder and decoder - * - * @param compress FuriHalCompress instance - */ -void furi_hal_compress_free(FuriHalCompress* compress); - -/** Encode data - * - * @param compress FuriHalCompress instance - * @param data_in pointer to input data - * @param data_in_size size of input data - * @param data_out maximum size of output data - * @param data_res_size pointer to result output data size - * - * @return true on success - */ -bool furi_hal_compress_encode( - FuriHalCompress* compress, - uint8_t* data_in, - size_t data_in_size, - uint8_t* data_out, - size_t data_out_size, - size_t* data_res_size); - -/** Decode data - * - * @param compress FuriHalCompress instance - * @param data_in pointer to input data - * @param data_in_size size of input data - * @param data_out maximum size of output data - * @param data_res_size pointer to result output data size - * - * @return true on success - */ -bool furi_hal_compress_decode( - FuriHalCompress* compress, - uint8_t* data_in, - size_t data_in_size, - uint8_t* data_out, - size_t data_out_size, - size_t* data_res_size); - -#ifdef __cplusplus -} -#endif diff --git a/lib/err.h b/lib/err.h new file mode 100644 index 000000000..a0e93874e --- /dev/null +++ b/lib/err.h @@ -0,0 +1,4 @@ +#pragma once +#include + +#define err(...) FURI_LOG_E("Heatshrink", "Error: %d-%s", __VA_ARGS__) \ No newline at end of file diff --git a/lib/heatshrink b/lib/heatshrink new file mode 160000 index 000000000..7398ccc91 --- /dev/null +++ b/lib/heatshrink @@ -0,0 +1 @@ +Subproject commit 7398ccc91652a33483245200cfa1a83b073bc206 diff --git a/lib/heatshrink/heatshrink_common.h b/lib/heatshrink/heatshrink_common.h deleted file mode 100644 index 243f44702..000000000 --- a/lib/heatshrink/heatshrink_common.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HEATSHRINK_H -#define HEATSHRINK_H - -#define HEATSHRINK_AUTHOR "Scott Vokes " -#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 diff --git a/lib/heatshrink/heatshrink_config.h b/lib/heatshrink/heatshrink_config.h deleted file mode 100644 index 7f2373c0d..000000000 --- a/lib/heatshrink/heatshrink_config.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef HEATSHRINK_CONFIG_H -#define HEATSHRINK_CONFIG_H - -#include - -/* Should functionality assuming dynamic allocation be used? */ -#ifndef HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DYNAMIC_ALLOC 1 -#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 1024 - #define HEATSHRINK_STATIC_WINDOW_BITS 8 - #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 diff --git a/lib/heatshrink/heatshrink_decoder.c b/lib/heatshrink/heatshrink_decoder.c deleted file mode 100644 index 287828367..000000000 --- a/lib/heatshrink/heatshrink_decoder.c +++ /dev/null @@ -1,364 +0,0 @@ -#include -#include -#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 -#include -#include -#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(uint8_t* buffer, - 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 sz = sizeof(heatshrink_decoder); - 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; - hsd->buffers = buffer; - 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 sz = sizeof(heatshrink_decoder); - HEATSHRINK_FREE(hsd, sz); - (void)sz; /* may not be used by free */ -} -#endif - -void heatshrink_decoder_reset(heatshrink_decoder *hsd) { - 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; ihead_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; -} diff --git a/lib/heatshrink/heatshrink_decoder.h b/lib/heatshrink/heatshrink_decoder.h deleted file mode 100644 index 687b0806b..000000000 --- a/lib/heatshrink/heatshrink_decoder.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef HEATSHRINK_DECODER_H -#define HEATSHRINK_DECODER_H - -#include -#include -#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(uint8_t* buffer, 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 diff --git a/lib/heatshrink/heatshrink_encoder.c b/lib/heatshrink/heatshrink_encoder.c deleted file mode 100644 index 98f27dff8..000000000 --- a/lib/heatshrink/heatshrink_encoder.c +++ /dev/null @@ -1,602 +0,0 @@ -#include -#include -#include -#include "heatshrink_encoder.h" - -typedef enum { - HSES_NOT_FULL, /* input buffer not full enough */ - HSES_FILLED, /* buffer is full */ - HSES_SEARCH, /* searching for patterns */ - HSES_YIELD_TAG_BIT, /* yield tag bit */ - HSES_YIELD_LITERAL, /* emit literal byte */ - HSES_YIELD_BR_INDEX, /* yielding backref index */ - HSES_YIELD_BR_LENGTH, /* yielding backref length */ - HSES_SAVE_BACKLOG, /* copying buffer to backlog */ - HSES_FLUSH_BITS, /* flush bit buffer */ - HSES_DONE, /* done */ -} HSE_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "not_full", - "filled", - "search", - "yield_tag_bit", - "yield_literal", - "yield_br_index", - "yield_br_length", - "save_backlog", - "flush_bits", - "done", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -// Encoder flags -enum { - FLAG_IS_FINISHING = 0x01, -}; - -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 MATCH_NOT_FOUND ((uint16_t)-1) - -static uint16_t get_input_offset(heatshrink_encoder *hse); -static uint16_t get_input_buffer_size(heatshrink_encoder *hse); -static uint16_t get_lookahead_size(heatshrink_encoder *hse); -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag); -static int can_take_byte(output_info *oi); -static int is_finishing(heatshrink_encoder *hse); -static void save_backlog(heatshrink_encoder *hse); - -/* Push COUNT (max 8) bits to the output buffer, which has room. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi); -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi); -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 >= window_sz2)) { - return NULL; - } - - /* Note: 2 * the window size is used because the buffer needs to fit - * (1 << window_sz2) bytes for the current input, and an additional - * (1 << window_sz2) bytes for the previous buffer of input, which - * will be scanned for useful backreferences. */ - size_t buf_sz = (2 << window_sz2); - - heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse)); - if (hse == NULL) { return NULL; } - hse->window_sz2 = window_sz2; - hse->lookahead_sz2 = lookahead_sz2; - hse->buffer = buffer; - heatshrink_encoder_reset(hse); - -#if HEATSHRINK_USE_INDEX - size_t index_sz = buf_sz*sizeof(uint16_t); - hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index)); - if (hse->search_index == NULL) { - HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz); - return NULL; - } - hse->search_index->size = index_sz; -#endif - - LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n", - buf_sz, get_input_buffer_size(hse)); - return hse; -} - -void heatshrink_encoder_free(heatshrink_encoder *hse) { -#if HEATSHRINK_USE_INDEX - size_t index_sz = sizeof(struct hs_index) + hse->search_index->size; - HEATSHRINK_FREE(hse->search_index, index_sz); - (void)index_sz; -#endif - HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder)); -} -#endif - -void heatshrink_encoder_reset(heatshrink_encoder *hse) { - hse->input_size = 0; - hse->state = HSES_NOT_FULL; - hse->match_scan_index = 0; - hse->flags = 0; - hse->bit_index = 0x80; - hse->current_byte = 0x00; - hse->match_length = 0; - - hse->outgoing_bits = 0x0000; - hse->outgoing_bits_count = 0; - - #ifdef LOOP_DETECT - hse->loop_detect = (uint32_t)-1; - #endif -} - -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSER_SINK_ERROR_NULL; - } - - /* Sinking more content after saying the content is done, tsk tsk */ - if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; } - - /* Sinking more content before processing is done */ - if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; } - - uint16_t write_offset = get_input_offset(hse) + hse->input_size; - uint16_t ibs = get_input_buffer_size(hse); - uint16_t rem = ibs - hse->input_size; - uint16_t cp_sz = rem < size ? rem : size; - - memcpy(&hse->buffer[write_offset], in_buf, cp_sz); - *input_size = cp_sz; - hse->input_size += cp_sz; - - LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n", - cp_sz, size, write_offset, hse->input_size); - if (cp_sz == rem) { - LOG("-- internal buffer is now full\n"); - hse->state = HSES_FILLED; - } - - return HSER_SINK_OK; -} - - -/*************** - * Compression * - ***************/ - -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length); -static void do_indexing(heatshrink_encoder *hse); - -static HSE_state st_step_search(heatshrink_encoder *hse); -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_save_backlog(heatshrink_encoder *hse); -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi); - -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSER_POLL_ERROR_NULL; - } - if (out_buf_size == 0) { - LOG("-- MISUSE: output buffer size is 0\n"); - return HSER_POLL_ERROR_MISUSE; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- polling, state %u (%s), flags 0x%02x\n", - hse->state, state_names[hse->state], hse->flags); - - uint8_t in_state = hse->state; - switch (in_state) { - case HSES_NOT_FULL: - return HSER_POLL_EMPTY; - case HSES_FILLED: - do_indexing(hse); - hse->state = HSES_SEARCH; - break; - case HSES_SEARCH: - hse->state = st_step_search(hse); - break; - case HSES_YIELD_TAG_BIT: - hse->state = st_yield_tag_bit(hse, &oi); - break; - case HSES_YIELD_LITERAL: - hse->state = st_yield_literal(hse, &oi); - break; - case HSES_YIELD_BR_INDEX: - hse->state = st_yield_br_index(hse, &oi); - break; - case HSES_YIELD_BR_LENGTH: - hse->state = st_yield_br_length(hse, &oi); - break; - case HSES_SAVE_BACKLOG: - hse->state = st_save_backlog(hse); - break; - case HSES_FLUSH_BITS: - hse->state = st_flush_bit_buffer(hse, &oi); - /* fall through */ - case HSES_DONE: - return HSER_POLL_EMPTY; - default: - LOG("-- bad state %s\n", state_names[hse->state]); - return HSER_POLL_ERROR_MISUSE; - } - - if (hse->state == in_state) { - /* Check if output buffer is exhausted. */ - if (*output_size == out_buf_size) return HSER_POLL_MORE; - } - } -} - -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) { - if (hse == NULL) { return HSER_FINISH_ERROR_NULL; } - LOG("-- setting is_finishing flag\n"); - hse->flags |= FLAG_IS_FINISHING; - if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; } - return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE; -} - -static HSE_state st_step_search(heatshrink_encoder *hse) { - uint16_t window_length = get_input_buffer_size(hse); - uint16_t lookahead_sz = get_lookahead_size(hse); - uint16_t msi = hse->match_scan_index; - LOG("## step_search, scan @ +%d (%d/%d), input size %d\n", - msi, hse->input_size + msi, 2*window_length, hse->input_size); - - bool fin = is_finishing(hse); - if (msi > hse->input_size - (fin ? 1 : lookahead_sz)) { - /* Current search buffer is exhausted, copy it into the - * backlog and await more input. */ - LOG("-- end of search @ %d\n", msi); - return fin ? HSES_FLUSH_BITS : HSES_SAVE_BACKLOG; - } - - uint16_t input_offset = get_input_offset(hse); - uint16_t end = input_offset + msi; - uint16_t start = end - window_length; - - uint16_t max_possible = lookahead_sz; - if (hse->input_size - msi < lookahead_sz) { - max_possible = hse->input_size - msi; - } - - uint16_t match_length = 0; - uint16_t match_pos = find_longest_match(hse, - start, end, max_possible, &match_length); - - if (match_pos == MATCH_NOT_FOUND) { - LOG("ss Match not found\n"); - hse->match_scan_index++; - hse->match_length = 0; - return HSES_YIELD_TAG_BIT; - } else { - LOG("ss Found match of %d bytes at %d\n", match_length, match_pos); - hse->match_pos = match_pos; - hse->match_length = match_length; - ASSERT(match_pos <= 1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse) /*window_length*/); - - return HSES_YIELD_TAG_BIT; - } -} - -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - if (hse->match_length == 0) { - add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER); - return HSES_YIELD_LITERAL; - } else { - add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER); - hse->outgoing_bits = hse->match_pos - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse); - return HSES_YIELD_BR_INDEX; - } - } else { - return HSES_YIELD_TAG_BIT; /* output is full, continue */ - } -} - -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - push_literal_byte(hse, oi); - return HSES_SEARCH; - } else { - return HSES_YIELD_LITERAL; - } -} - -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref index %u\n", hse->match_pos); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_INDEX; /* continue */ - } else { - hse->outgoing_bits = hse->match_length - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse); - return HSES_YIELD_BR_LENGTH; /* done */ - } - } else { - return HSES_YIELD_BR_INDEX; /* continue */ - } -} - -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref length %u\n", hse->match_length); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_LENGTH; - } else { - hse->match_scan_index += hse->match_length; - hse->match_length = 0; - return HSES_SEARCH; - } - } else { - return HSES_YIELD_BR_LENGTH; - } -} - -static HSE_state st_save_backlog(heatshrink_encoder *hse) { - LOG("-- saving backlog\n"); - save_backlog(hse); - return HSES_NOT_FULL; -} - -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi) { - if (hse->bit_index == 0x80) { - LOG("-- done!\n"); - return HSES_DONE; - } else if (can_take_byte(oi)) { - LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index); - oi->buf[(*oi->output_size)++] = hse->current_byte; - LOG("-- done!\n"); - return HSES_DONE; - } else { - return HSES_FLUSH_BITS; - } -} - -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) { - LOG("-- adding tag bit: %d\n", tag); - push_bits(hse, 1, tag, oi); -} - -static uint16_t get_input_offset(heatshrink_encoder *hse) { - return get_input_buffer_size(hse); -} - -static uint16_t get_input_buffer_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - (void)hse; -} - -static uint16_t get_lookahead_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - (void)hse; -} - -static void do_indexing(heatshrink_encoder *hse) { -#if HEATSHRINK_USE_INDEX - /* Build an index array I that contains flattened linked lists - * for the previous instances of every byte in the buffer. - * - * For example, if buf[200] == 'x', then index[200] will either - * be an offset i such that buf[i] == 'x', or a negative offset - * to indicate end-of-list. This significantly speeds up matching, - * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. - * - * Future optimization options: - * 1. Since any negative value represents end-of-list, the other - * 15 bits could be used to improve the index dynamically. - * - * 2. Likewise, the last lookahead_sz bytes of the index will - * not be usable, so temporary data could be stored there to - * dynamically improve the index. - * */ - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t last[256]; - memset(last, 0xFF, sizeof(last)); - - uint8_t * const data = hse->buffer; - int16_t * const index = hsi->index; - - const uint16_t input_offset = get_input_offset(hse); - const uint16_t end = input_offset + hse->input_size; - - for (uint16_t i=0; iflags & FLAG_IS_FINISHING; -} - -static int can_take_byte(output_info *oi) { - return *oi->output_size < oi->buf_size; -} - -/* Return the longest match for the bytes at buf[end:end+maxlen] between - * buf[start] and buf[end-1]. If no match is found, return -1. */ -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length) { - LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", - end, end + maxlen, start, end + maxlen - 1, maxlen); - uint8_t *buf = hse->buffer; - - uint16_t match_maxlen = 0; - uint16_t match_index = MATCH_NOT_FOUND; - - uint16_t len = 0; - uint8_t * const needlepoint = &buf[end]; -#if HEATSHRINK_USE_INDEX - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t pos = hsi->index[end]; - - while (pos - (int16_t)start >= 0) { - uint8_t * const pospoint = &buf[pos]; - len = 0; - - /* Only check matches that will potentially beat the current maxlen. - * This is redundant with the index if match_maxlen is 0, but the - * added branch overhead to check if it == 0 seems to be worse. */ - if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { - pos = hsi->index[pos]; - continue; - } - - for (len = 1; len < maxlen; len++) { - if (pospoint[len] != needlepoint[len]) break; - } - - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* won't find better */ - } - pos = hsi->index[pos]; - } -#else - for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) { - uint8_t * const pospoint = &buf[pos]; - if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) - && (*pospoint == *needlepoint)) { - for (len=1; len cmp buf[%d] == 0x%02x against %02x (start %u)\n", - pos + len, pospoint[len], needlepoint[len], start); - } - if (pospoint[len] != needlepoint[len]) { break; } - } - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* don't keep searching */ - } - } - } -#endif - - const size_t break_even_point = - (1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) + - HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - - /* Instead of comparing break_even_point against 8*match_maxlen, - * compare match_maxlen against break_even_point/8 to avoid - * overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and - * 3, respectively, break_even_point/8 will always be at least 1. */ - if (match_maxlen > (break_even_point / 8)) { - LOG("-- best match: %u bytes at -%u\n", - match_maxlen, end - match_index); - *match_length = match_maxlen; - return end - match_index; - } - LOG("-- none found\n"); - return MATCH_NOT_FOUND; -} - -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) { - uint8_t count = 0; - uint8_t bits = 0; - if (hse->outgoing_bits_count > 8) { - count = 8; - bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8); - } else { - count = hse->outgoing_bits_count; - bits = hse->outgoing_bits; - } - - if (count > 0) { - LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits); - push_bits(hse, count, bits, oi); - hse->outgoing_bits_count -= count; - } - return count; -} - -/* Push COUNT (max 8) bits to the output buffer, which has room. - * Bytes are set from the lowest bits, up. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi) { - ASSERT(count <= 8); - LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits); - - /* If adding a whole byte and at the start of a new output byte, - * just push it through whole and skip the bit IO loop. */ - if (count == 8 && hse->bit_index == 0x80) { - oi->buf[(*oi->output_size)++] = bits; - } else { - for (int i=count - 1; i>=0; i--) { - bool bit = bits & (1 << i); - if (bit) { hse->current_byte |= hse->bit_index; } - if (0) { - LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n", - bit ? 1 : 0, hse->bit_index, hse->current_byte); - } - hse->bit_index >>= 1; - if (hse->bit_index == 0x00) { - hse->bit_index = 0x80; - LOG(" > pushing byte 0x%02x\n", hse->current_byte); - oi->buf[(*oi->output_size)++] = hse->current_byte; - hse->current_byte = 0x00; - } - } - } -} - -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) { - uint16_t processed_offset = hse->match_scan_index - 1; - uint16_t input_offset = get_input_offset(hse) + processed_offset; - uint8_t c = hse->buffer[input_offset]; - LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n", - c, isprint(c) ? c : '.', input_offset); - push_bits(hse, 8, c, oi); -} - -static void save_backlog(heatshrink_encoder *hse) { - size_t input_buf_sz = get_input_buffer_size(hse); - - uint16_t msi = hse->match_scan_index; - - /* Copy processed data to beginning of buffer, so it can be - * used for future matches. Don't bother checking whether the - * input is less than the maximum size, because if it isn't, - * we're done anyway. */ - uint16_t rem = input_buf_sz - msi; // unprocessed bytes - uint16_t shift_sz = input_buf_sz + rem; - - memmove(&hse->buffer[0], - &hse->buffer[input_buf_sz - rem], - shift_sz); - - hse->match_scan_index = 0; - hse->input_size -= input_buf_sz - rem; -} diff --git a/lib/heatshrink/heatshrink_encoder.h b/lib/heatshrink/heatshrink_encoder.h deleted file mode 100644 index e2ccb44c7..000000000 --- a/lib/heatshrink/heatshrink_encoder.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef HEATSHRINK_ENCODER_H -#define HEATSHRINK_ENCODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSER_SINK_OK, /* data sunk into input buffer */ - HSER_SINK_ERROR_NULL=-1, /* NULL argument */ - HSER_SINK_ERROR_MISUSE=-2, /* API misuse */ -} HSE_sink_res; - -typedef enum { - HSER_POLL_EMPTY, /* input exhausted */ - HSER_POLL_MORE, /* poll again for more output */ - HSER_POLL_ERROR_NULL=-1, /* NULL argument */ - HSER_POLL_ERROR_MISUSE=-2, /* API misuse */ -} HSE_poll_res; - -typedef enum { - HSER_FINISH_DONE, /* encoding is complete */ - HSER_FINISH_MORE, /* more output remaining; use poll */ - HSER_FINISH_ERROR_NULL=-1, /* NULL argument */ -} HSE_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \ - ((HSE)->window_sz2) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \ - ((HSE)->lookahead_sz2) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - ((HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[]; -}; -#else -#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - (&(HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS]; -}; -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t match_scan_index; - uint16_t match_length; - uint16_t match_pos; - uint16_t outgoing_bits; /* enqueued outgoing bits */ - uint8_t outgoing_bits_count; - uint8_t flags; - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of output */ - uint8_t bit_index; /* current bit index */ -#if HEATSHRINK_DYNAMIC_ALLOC - uint8_t window_sz2; /* 2^n size of window */ - uint8_t lookahead_sz2; /* 2^n size of lookahead */ -#if HEATSHRINK_USE_INDEX - struct hs_index *search_index; -#endif - /* input buffer and / sliding window for expansion */ - uint8_t* buffer; -#else - #if HEATSHRINK_USE_INDEX - struct hs_index search_index; - #endif - /* input buffer and / sliding window for expansion */ - uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)]; -#endif -} heatshrink_encoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a new encoder struct and its buffers. - * Returns NULL on error. */ -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2, - uint8_t lookahead_sz2); - -/* Free an encoder. */ -void heatshrink_encoder_free(heatshrink_encoder *hse); -#endif - -/* Reset an encoder. */ -void heatshrink_encoder_reset(heatshrink_encoder *hse); - -/* Sink up to SIZE bytes from IN_BUF into the encoder. - * INPUT_SIZE is set to the number of bytes actually sunk (in case a - * buffer was filled.). */ -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the encoder that the input stream is finished. - * If the return value is HSER_FINISH_MORE, there is still more output, so - * call heatshrink_encoder_poll and repeat. */ -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); - -#endif diff --git a/lib/misc.scons b/lib/misc.scons index 49b6b61d9..b479851b1 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -36,7 +36,6 @@ for lib in libs_recurse: sources += libenv.GlobRecursive("*.c*", lib) libs_plain = [ - "heatshrink", "nanopb", ] @@ -47,6 +46,12 @@ for lib in libs_plain: source=True, ) +sources += Glob( + "heatshrink/heatshrink_*.c*", + exclude=GLOB_FILE_EXCLUSION, + source=True, +) + lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) Return("lib") diff --git a/firmware/targets/f7/furi_hal/furi_hal_compress.c b/lib/toolbox/compress.c similarity index 67% rename from firmware/targets/f7/furi_hal/furi_hal_compress.c rename to lib/toolbox/compress.c index 7e31dbbf7..0d5e1c654 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_compress.c +++ b/lib/toolbox/compress.c @@ -1,115 +1,112 @@ -#include +#include "compress.h" #include #include #include -#define TAG "FuriHalCompress" +/** Defines encoder and decoder window size */ +#define COMPRESS_EXP_BUFF_SIZE_LOG (8u) -#define FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE (2 * 512) -#define FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE (1024) +/** Defines encoder and decoder lookahead buffer size */ +#define COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4u) -#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE (1 << FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG) +/** Buffer sizes for input and output data */ +#define COMPRESS_ICON_ENCODED_BUFF_SIZE (1024u) +#define COMPRESS_ICON_DECODED_BUFF_SIZE (1024u) typedef struct { uint8_t is_compressed; uint8_t reserved; uint16_t compressed_buff_size; -} FuriHalCompressHeader; +} CompressHeader; -typedef struct { - heatshrink_decoder* decoder; - uint8_t - compress_buff[FURI_HAL_COMPRESS_EXP_BUFF_SIZE + FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE]; - uint8_t decoded_buff[FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE]; -} FuriHalCompressIcon; +_Static_assert(sizeof(CompressHeader) == 4, "Incorrect CompressHeader size"); -struct FuriHalCompress { - heatshrink_encoder* encoder; +struct CompressIcon { heatshrink_decoder* decoder; - uint8_t* compress_buff; - uint16_t compress_buff_size; + uint8_t decoded_buff[COMPRESS_ICON_DECODED_BUFF_SIZE]; }; -static FuriHalCompressIcon* icon_decoder; +CompressIcon* compress_icon_alloc() { + CompressIcon* instance = malloc(sizeof(CompressIcon)); + instance->decoder = heatshrink_decoder_alloc( + COMPRESS_ICON_ENCODED_BUFF_SIZE, + COMPRESS_EXP_BUFF_SIZE_LOG, + COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); + heatshrink_decoder_reset(instance->decoder); + memset(instance->decoded_buff, 0, sizeof(instance->decoded_buff)); -static void furi_hal_compress_reset(FuriHalCompress* compress) { - furi_assert(compress); - heatshrink_encoder_reset(compress->encoder); - heatshrink_decoder_reset(compress->decoder); - memset(compress->compress_buff, 0, compress->compress_buff_size); + return instance; } -void furi_hal_compress_icon_init() { - icon_decoder = malloc(sizeof(FuriHalCompressIcon)); - icon_decoder->decoder = heatshrink_decoder_alloc( - icon_decoder->compress_buff, - FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE, - FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, - FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); - heatshrink_decoder_reset(icon_decoder->decoder); - memset(icon_decoder->decoded_buff, 0, sizeof(icon_decoder->decoded_buff)); - FURI_LOG_I(TAG, "Init OK"); +void compress_icon_free(CompressIcon* instance) { + furi_assert(instance); + heatshrink_decoder_free(instance->decoder); + free(instance); } -void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff) { +void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff) { + furi_assert(instance); furi_assert(icon_data); furi_assert(decoded_buff); - FuriHalCompressHeader* header = (FuriHalCompressHeader*)icon_data; + CompressHeader* header = (CompressHeader*)icon_data; if(header->is_compressed) { size_t data_processed = 0; heatshrink_decoder_sink( - icon_decoder->decoder, - (uint8_t*)&icon_data[4], + instance->decoder, + (uint8_t*)&icon_data[sizeof(CompressHeader)], header->compressed_buff_size, &data_processed); while(1) { HSD_poll_res res = heatshrink_decoder_poll( - icon_decoder->decoder, - icon_decoder->decoded_buff, - sizeof(icon_decoder->decoded_buff), + instance->decoder, + instance->decoded_buff, + sizeof(instance->decoded_buff), &data_processed); furi_assert((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE)); if(res != HSDR_POLL_MORE) { break; } } - heatshrink_decoder_reset(icon_decoder->decoder); - memset(icon_decoder->compress_buff, 0, sizeof(icon_decoder->compress_buff)); - *decoded_buff = icon_decoder->decoded_buff; + heatshrink_decoder_reset(instance->decoder); + *decoded_buff = instance->decoded_buff; } else { *decoded_buff = (uint8_t*)&icon_data[1]; } } -FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size) { - FuriHalCompress* compress = malloc(sizeof(FuriHalCompress)); - compress->compress_buff = malloc(compress_buff_size + FURI_HAL_COMPRESS_EXP_BUFF_SIZE); - compress->encoder = heatshrink_encoder_alloc( - compress->compress_buff, - FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, - FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); +struct Compress { + heatshrink_encoder* encoder; + heatshrink_decoder* decoder; +}; + +static void compress_reset(Compress* compress) { + furi_assert(compress); + heatshrink_encoder_reset(compress->encoder); + heatshrink_decoder_reset(compress->decoder); +} + +Compress* compress_alloc(uint16_t compress_buff_size) { + Compress* compress = malloc(sizeof(Compress)); + compress->encoder = + heatshrink_encoder_alloc(COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); compress->decoder = heatshrink_decoder_alloc( - compress->compress_buff, - compress_buff_size, - FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, - FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); + compress_buff_size, COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); return compress; } -void furi_hal_compress_free(FuriHalCompress* compress) { +void compress_free(Compress* compress) { furi_assert(compress); heatshrink_encoder_free(compress->encoder); heatshrink_decoder_free(compress->decoder); - free(compress->compress_buff); free(compress); } -bool furi_hal_compress_encode( - FuriHalCompress* compress, +bool compress_encode( + Compress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, @@ -126,7 +123,7 @@ bool furi_hal_compress_encode( HSE_finish_res finish_res; bool encode_failed = false; size_t sunk = 0; - size_t res_buff_size = sizeof(FuriHalCompressHeader); + size_t res_buff_size = sizeof(CompressHeader); // Sink data to encoding buffer while((sunk < data_in_size) && !encode_failed) { @@ -174,7 +171,7 @@ bool furi_hal_compress_encode( bool result = true; // Write encoded data to output buffer if compression is efficient. Else - write header and original data if(!encode_failed && (res_buff_size < data_in_size + 1)) { - FuriHalCompressHeader header = { + CompressHeader header = { .is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size}; memcpy(data_out, &header, sizeof(header)); *data_res_size = res_buff_size; @@ -186,13 +183,13 @@ bool furi_hal_compress_encode( *data_res_size = 0; result = false; } - furi_hal_compress_reset(compress); + compress_reset(compress); return result; } -bool furi_hal_compress_decode( - FuriHalCompress* compress, +bool compress_decode( + Compress* compress, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, @@ -212,11 +209,11 @@ bool furi_hal_compress_decode( size_t res_buff_size = 0; size_t poll_size = 0; - FuriHalCompressHeader* header = (FuriHalCompressHeader*)data_in; + CompressHeader* header = (CompressHeader*)data_in; if(header->is_compressed) { // Sink data to decoding buffer size_t compressed_size = header->compressed_buff_size; - size_t sunk = sizeof(FuriHalCompressHeader); + size_t sunk = sizeof(CompressHeader); while(sunk < compressed_size && !decode_failed) { sink_res = heatshrink_decoder_sink( compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size); @@ -258,7 +255,7 @@ bool furi_hal_compress_decode( } else { result = false; } - furi_hal_compress_reset(compress); + compress_reset(compress); return result; } diff --git a/lib/toolbox/compress.h b/lib/toolbox/compress.h new file mode 100644 index 000000000..a18551d7f --- /dev/null +++ b/lib/toolbox/compress.h @@ -0,0 +1,96 @@ +/** + * @file compress.h + * LZSS based compression HAL API + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Compress Icon control structure */ +typedef struct CompressIcon CompressIcon; + +/** Initialize icon compressor + * + * @return Compress Icon instance + */ +CompressIcon* compress_icon_alloc(); + +/** Free icon compressor + * + * @param instance The Compress Icon instance + */ +void compress_icon_free(CompressIcon* instance); + +/** Decompress icon + * + * @warning decoded_buff pointer set by this function is valid till next + * `compress_icon_decode` or `compress_icon_free` call + * + * @param instance The Compress Icon instance + * @param icon_data pointer to icon data + * @param[in] decoded_buff pointer to decoded buffer pointer + */ +void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff); + +/** Compress control structure */ +typedef struct Compress Compress; + +/** Allocate encoder and decoder + * + * @param compress_buff_size size of decoder and encoder buffer to allocate + * + * @return Compress instance + */ +Compress* compress_alloc(uint16_t compress_buff_size); + +/** Free encoder and decoder + * + * @param compress Compress instance + */ +void compress_free(Compress* compress); + +/** Encode data + * + * @param compress Compress instance + * @param data_in pointer to input data + * @param data_in_size size of input data + * @param data_out maximum size of output data + * @param data_res_size pointer to result output data size + * + * @return true on success + */ +bool compress_encode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size); + +/** Decode data + * + * @param compress Compress instance + * @param data_in pointer to input data + * @param data_in_size size of input data + * @param data_out maximum size of output data + * @param data_res_size pointer to result output data size + * + * @return true on success + */ +bool compress_decode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size); + +#ifdef __cplusplus +} +#endif