Explorar el Código

[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
あく hace 2 años
padre
commit
7bf0a4786c

+ 3 - 0
.gitmodules

@@ -31,3 +31,6 @@
 [submodule "applications/external/dap_link/lib/free-dap"]
 [submodule "applications/external/dap_link/lib/free-dap"]
 	path = applications/external/dap_link/lib/free-dap
 	path = applications/external/dap_link/lib/free-dap
 	url = https://github.com/ataradov/free-dap.git
 	url = https://github.com/ataradov/free-dap.git
+[submodule "lib/heatshrink"]
+	path = lib/heatshrink
+	url = https://github.com/flipperdevices/heatshrink.git

+ 6 - 3
applications/services/gui/canvas.c

@@ -17,6 +17,7 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = {
 
 
 Canvas* canvas_init() {
 Canvas* canvas_init() {
     Canvas* canvas = malloc(sizeof(Canvas));
     Canvas* canvas = malloc(sizeof(Canvas));
+    canvas->compress_icon = compress_icon_alloc();
 
 
     // Setup u8g2
     // Setup u8g2
     u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
     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) {
 void canvas_free(Canvas* canvas) {
     furi_assert(canvas);
     furi_assert(canvas);
+    compress_icon_free(canvas->compress_icon);
     free(canvas);
     free(canvas);
 }
 }
 
 
@@ -218,7 +220,7 @@ void canvas_draw_bitmap(
     x += canvas->offset_x;
     x += canvas->offset_x;
     y += canvas->offset_y;
     y += canvas->offset_y;
     uint8_t* bitmap_data = NULL;
     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);
     u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data);
 }
 }
 
 
@@ -233,7 +235,8 @@ void canvas_draw_icon_animation(
     x += canvas->offset_x;
     x += canvas->offset_x;
     y += canvas->offset_y;
     y += canvas->offset_y;
     uint8_t* icon_data = NULL;
     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(
     u8g2_DrawXBM(
         &canvas->fb,
         &canvas->fb,
         x,
         x,
@@ -250,7 +253,7 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
     x += canvas->offset_x;
     x += canvas->offset_x;
     y += canvas->offset_y;
     y += canvas->offset_y;
     uint8_t* icon_data = NULL;
     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);
     u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data);
 }
 }
 
 

+ 2 - 0
applications/services/gui/canvas_i.h

@@ -7,6 +7,7 @@
 
 
 #include "canvas.h"
 #include "canvas.h"
 #include <u8g2.h>
 #include <u8g2.h>
+#include <toolbox/compress.h>
 
 
 /** Canvas structure
 /** Canvas structure
  */
  */
@@ -17,6 +18,7 @@ struct Canvas {
     uint8_t offset_y;
     uint8_t offset_y;
     uint8_t width;
     uint8_t width;
     uint8_t height;
     uint8_t height;
+    CompressIcon* compress_icon;
 };
 };
 
 
 /** Allocate memory and initialize canvas
 /** Allocate memory and initialize canvas

+ 1 - 0
applications/services/gui/gui.c

@@ -250,6 +250,7 @@ static void gui_redraw(Gui* gui) {
                 p->callback(
                 p->callback(
                     canvas_get_buffer(gui->canvas),
                     canvas_get_buffer(gui->canvas),
                     canvas_get_buffer_size(gui->canvas),
                     canvas_get_buffer_size(gui->canvas),
+                    canvas_get_orientation(gui->canvas),
                     p->context);
                     p->context);
             }
             }
     } while(false);
     } while(false);

+ 5 - 1
applications/services/gui/gui.h

@@ -27,7 +27,11 @@ typedef enum {
 } GuiLayer;
 } GuiLayer;
 
 
 /** Gui Canvas Commit Callback */
 /** 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"
 #define RECORD_GUI "gui"
 
 

+ 24 - 2
applications/services/rpc/rpc_gui.c

@@ -33,8 +33,18 @@ typedef struct {
     uint32_t input_counter;
     uint32_t input_counter;
 } RpcGuiSystem;
 } 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(data);
     furi_assert(context);
     furi_assert(context);
 
 
@@ -44,6 +54,8 @@ static void
     furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size);
     furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size);
 
 
     memcpy(buffer, 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);
     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;
     RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context;
 
 
+    uint32_t transmit_time = 0;
     while(true) {
     while(true) {
         uint32_t flags =
         uint32_t flags =
             furi_thread_flags_wait(RpcGuiWorkerFlagAny, FuriFlagWaitAny, FuriWaitForever);
             furi_thread_flags_wait(RpcGuiWorkerFlagAny, FuriFlagWaitAny, FuriWaitForever);
+
         if(flags & RpcGuiWorkerFlagTransmit) {
         if(flags & RpcGuiWorkerFlagTransmit) {
+            transmit_time = furi_get_tick();
             rpc_send(rpc_gui->session, rpc_gui->transmit_frame);
             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) {
         if(flags & RpcGuiWorkerFlagExit) {
             break;
             break;
         }
         }

+ 1 - 1
assets/protobuf

@@ -1 +1 @@
-Subproject commit 6460660237005d02d5c223835659b40e373bade9
+Subproject commit 1f6b4a08c5d05c2b17926a3ba79f60109638932f

+ 6 - 7
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.h,,
 Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.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_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_cortex.h,,
 Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
 Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
 Header,+,firmware/targets/furi_hal_include/furi_hal_debug.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_suspend_tick,void,
 Function,-,furi_hal_clock_switch_to_hsi,void,
 Function,-,furi_hal_clock_switch_to_hsi,void,
 Function,-,furi_hal_clock_switch_to_pll,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_disable,void,
 Function,+,furi_hal_console_enable,void,
 Function,+,furi_hal_console_enable,void,
 Function,+,furi_hal_console_init,void,
 Function,+,furi_hal_console_init,void,

+ 5 - 29
firmware/targets/f18/furi_hal/furi_hal.c

@@ -1,5 +1,6 @@
 #include <furi_hal.h>
 #include <furi_hal.h>
 #include <furi_hal_mpu.h>
 #include <furi_hal_mpu.h>
+#include <furi_hal_memory.h>
 
 
 #include <stm32wbxx_ll_cortex.h>
 #include <stm32wbxx_ll_cortex.h>
 
 
@@ -7,29 +8,20 @@
 
 
 void furi_hal_init_early() {
 void furi_hal_init_early() {
     furi_hal_cortex_init_early();
     furi_hal_cortex_init_early();
-
     furi_hal_clock_init_early();
     furi_hal_clock_init_early();
-
     furi_hal_resources_init_early();
     furi_hal_resources_init_early();
-
     furi_hal_os_init();
     furi_hal_os_init();
-
     furi_hal_spi_config_init_early();
     furi_hal_spi_config_init_early();
-
     furi_hal_i2c_init_early();
     furi_hal_i2c_init_early();
     furi_hal_light_init();
     furi_hal_light_init();
-
     furi_hal_rtc_init_early();
     furi_hal_rtc_init_early();
 }
 }
 
 
 void furi_hal_deinit_early() {
 void furi_hal_deinit_early() {
     furi_hal_rtc_deinit_early();
     furi_hal_rtc_deinit_early();
-
     furi_hal_i2c_deinit_early();
     furi_hal_i2c_deinit_early();
     furi_hal_spi_config_deinit_early();
     furi_hal_spi_config_deinit_early();
-
     furi_hal_resources_deinit_early();
     furi_hal_resources_deinit_early();
-
     furi_hal_clock_deinit_early();
     furi_hal_clock_deinit_early();
 }
 }
 
 
@@ -38,40 +30,24 @@ void furi_hal_init() {
     furi_hal_clock_init();
     furi_hal_clock_init();
     furi_hal_console_init();
     furi_hal_console_init();
     furi_hal_rtc_init();
     furi_hal_rtc_init();
-
     furi_hal_interrupt_init();
     furi_hal_interrupt_init();
-
     furi_hal_flash_init();
     furi_hal_flash_init();
-
     furi_hal_resources_init();
     furi_hal_resources_init();
-    FURI_LOG_I(TAG, "GPIO OK");
-
     furi_hal_version_init();
     furi_hal_version_init();
-
     furi_hal_spi_config_init();
     furi_hal_spi_config_init();
     furi_hal_spi_dma_init();
     furi_hal_spi_dma_init();
-
     furi_hal_speaker_init();
     furi_hal_speaker_init();
-    FURI_LOG_I(TAG, "Speaker OK");
-
     furi_hal_crypto_init();
     furi_hal_crypto_init();
-
-    // USB
-#ifndef FURI_RAM_EXEC
-    furi_hal_usb_init();
-    FURI_LOG_I(TAG, "USB OK");
-#endif
-
     furi_hal_i2c_init();
     furi_hal_i2c_init();
-
-    // High Level
     furi_hal_power_init();
     furi_hal_power_init();
     furi_hal_light_init();
     furi_hal_light_init();
+    furi_hal_bt_init();
+    furi_hal_memory_init();
+
 #ifndef FURI_RAM_EXEC
 #ifndef FURI_RAM_EXEC
+    furi_hal_usb_init();
     furi_hal_vibro_init();
     furi_hal_vibro_init();
 #endif
 #endif
-    furi_hal_bt_init();
-    furi_hal_compress_icon_init();
 }
 }
 
 
 void furi_hal_switch(void* address) {
 void furi_hal_switch(void* address) {

+ 4 - 0
firmware/targets/f18/furi_hal/furi_hal_resources.c

@@ -4,6 +4,8 @@
 #include <stm32wbxx_ll_rcc.h>
 #include <stm32wbxx_ll_rcc.h>
 #include <stm32wbxx_ll_pwr.h>
 #include <stm32wbxx_ll_pwr.h>
 
 
+#define TAG "FuriHalResources"
+
 const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8};
 const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8};
 const GpioPin ibutton_gpio = {.port = GPIOB, .pin = LL_GPIO_PIN_14};
 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_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
     NVIC_EnableIRQ(EXTI15_10_IRQn);
     NVIC_EnableIRQ(EXTI15_10_IRQn);
+
+    FURI_LOG_I(TAG, "Init OK");
 }
 }
 
 
 int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {
 int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {

+ 0 - 7
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.h,,
 Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.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_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_cortex.h,,
 Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
 Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,,
 Header,+,firmware/targets/furi_hal_include/furi_hal_debug.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_suspend_tick,void,
 Function,-,furi_hal_clock_switch_to_hsi,void,
 Function,-,furi_hal_clock_switch_to_hsi,void,
 Function,-,furi_hal_clock_switch_to_pll,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_disable,void,
 Function,+,furi_hal_console_enable,void,
 Function,+,furi_hal_console_enable,void,
 Function,+,furi_hal_console_init,void,
 Function,+,furi_hal_console_init,void,

+ 0 - 26
firmware/targets/f7/furi_hal/furi_hal.c

@@ -8,29 +8,20 @@
 
 
 void furi_hal_init_early() {
 void furi_hal_init_early() {
     furi_hal_cortex_init_early();
     furi_hal_cortex_init_early();
-
     furi_hal_clock_init_early();
     furi_hal_clock_init_early();
-
     furi_hal_resources_init_early();
     furi_hal_resources_init_early();
-
     furi_hal_os_init();
     furi_hal_os_init();
-
     furi_hal_spi_config_init_early();
     furi_hal_spi_config_init_early();
-
     furi_hal_i2c_init_early();
     furi_hal_i2c_init_early();
     furi_hal_light_init();
     furi_hal_light_init();
-
     furi_hal_rtc_init_early();
     furi_hal_rtc_init_early();
 }
 }
 
 
 void furi_hal_deinit_early() {
 void furi_hal_deinit_early() {
     furi_hal_rtc_deinit_early();
     furi_hal_rtc_deinit_early();
-
     furi_hal_i2c_deinit_early();
     furi_hal_i2c_deinit_early();
     furi_hal_spi_config_deinit_early();
     furi_hal_spi_config_deinit_early();
-
     furi_hal_resources_deinit_early();
     furi_hal_resources_deinit_early();
-
     furi_hal_clock_deinit_early();
     furi_hal_clock_deinit_early();
 }
 }
 
 
@@ -39,41 +30,24 @@ void furi_hal_init() {
     furi_hal_clock_init();
     furi_hal_clock_init();
     furi_hal_console_init();
     furi_hal_console_init();
     furi_hal_rtc_init();
     furi_hal_rtc_init();
-
     furi_hal_interrupt_init();
     furi_hal_interrupt_init();
-
     furi_hal_flash_init();
     furi_hal_flash_init();
-
     furi_hal_resources_init();
     furi_hal_resources_init();
-    FURI_LOG_I(TAG, "GPIO OK");
-
     furi_hal_version_init();
     furi_hal_version_init();
     furi_hal_region_init();
     furi_hal_region_init();
-
     furi_hal_spi_config_init();
     furi_hal_spi_config_init();
     furi_hal_spi_dma_init();
     furi_hal_spi_dma_init();
-
     furi_hal_ibutton_init();
     furi_hal_ibutton_init();
-    FURI_LOG_I(TAG, "iButton OK");
     furi_hal_speaker_init();
     furi_hal_speaker_init();
-    FURI_LOG_I(TAG, "Speaker OK");
-
     furi_hal_crypto_init();
     furi_hal_crypto_init();
-
     furi_hal_i2c_init();
     furi_hal_i2c_init();
-
-    // High Level
     furi_hal_power_init();
     furi_hal_power_init();
     furi_hal_light_init();
     furi_hal_light_init();
-
     furi_hal_bt_init();
     furi_hal_bt_init();
     furi_hal_memory_init();
     furi_hal_memory_init();
-    furi_hal_compress_icon_init();
 
 
 #ifndef FURI_RAM_EXEC
 #ifndef FURI_RAM_EXEC
-    // USB
     furi_hal_usb_init();
     furi_hal_usb_init();
-    FURI_LOG_I(TAG, "USB OK");
     furi_hal_vibro_init();
     furi_hal_vibro_init();
     furi_hal_subghz_init();
     furi_hal_subghz_init();
     furi_hal_nfc_init();
     furi_hal_nfc_init();

+ 3 - 0
firmware/targets/f7/furi_hal/furi_hal_ibutton.c

@@ -7,6 +7,7 @@
 
 
 #include <furi.h>
 #include <furi.h>
 
 
+#define TAG "FuriHalIbutton"
 #define FURI_HAL_IBUTTON_TIMER TIM1
 #define FURI_HAL_IBUTTON_TIMER TIM1
 #define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16
 #define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16
 
 
@@ -33,6 +34,8 @@ static void furi_hal_ibutton_emulate_isr() {
 void furi_hal_ibutton_init() {
 void furi_hal_ibutton_init() {
     furi_hal_ibutton = malloc(sizeof(FuriHalIbutton));
     furi_hal_ibutton = malloc(sizeof(FuriHalIbutton));
     furi_hal_ibutton->state = FuriHalIbuttonStateIdle;
     furi_hal_ibutton->state = FuriHalIbuttonStateIdle;
+
+    FURI_LOG_I(TAG, "Init OK");
 }
 }
 
 
 void furi_hal_ibutton_emulate_start(
 void furi_hal_ibutton_emulate_start(

+ 4 - 0
firmware/targets/f7/furi_hal/furi_hal_resources.c

@@ -4,6 +4,8 @@
 #include <stm32wbxx_ll_rcc.h>
 #include <stm32wbxx_ll_rcc.h>
 #include <stm32wbxx_ll_pwr.h>
 #include <stm32wbxx_ll_pwr.h>
 
 
+#define TAG "FuriHalResources"
+
 const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
 const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
 const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_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_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
     NVIC_EnableIRQ(EXTI15_10_IRQn);
     NVIC_EnableIRQ(EXTI15_10_IRQn);
+
+    FURI_LOG_I(TAG, "Init OK");
 }
 }
 
 
 int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {
 int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) {

+ 5 - 2
firmware/targets/f7/src/dfu.c

@@ -4,10 +4,11 @@
 #include <alt_boot.h>
 #include <alt_boot.h>
 #include <u8g2_glue.h>
 #include <u8g2_glue.h>
 #include <assets_icons.h>
 #include <assets_icons.h>
+#include <toolbox/compress.h>
 
 
 void flipper_boot_dfu_show_splash() {
 void flipper_boot_dfu_show_splash() {
     // Initialize
     // Initialize
-    furi_hal_compress_icon_init();
+    CompressIcon* compress_icon = compress_icon_alloc();
 
 
     u8g2_t* fb = malloc(sizeof(u8g2_t));
     u8g2_t* fb = malloc(sizeof(u8g2_t));
     memset(fb, 0, sizeof(u8g2_t));
     memset(fb, 0, sizeof(u8g2_t));
@@ -15,13 +16,15 @@ void flipper_boot_dfu_show_splash() {
     u8g2_InitDisplay(fb);
     u8g2_InitDisplay(fb);
     u8g2_SetDrawColor(fb, 0x01);
     u8g2_SetDrawColor(fb, 0x01);
     uint8_t* splash_data = NULL;
     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_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data);
     u8g2_SetFont(fb, u8g2_font_helvB08_tr);
     u8g2_SetFont(fb, u8g2_font_helvB08_tr);
     u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode");
     u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode");
     u8g2_DrawStr(fb, 2, 21, "DFU Started");
     u8g2_DrawStr(fb, 2, 21, "DFU Started");
     u8g2_SetPowerSave(fb, 0);
     u8g2_SetPowerSave(fb, 0);
     u8g2_SendBuffer(fb);
     u8g2_SendBuffer(fb);
+
+    compress_icon_free(compress_icon);
 }
 }
 
 
 void flipper_boot_dfu_exec() {
 void flipper_boot_dfu_exec() {

+ 4 - 2
firmware/targets/f7/src/recovery.c

@@ -4,6 +4,7 @@
 #include <alt_boot.h>
 #include <alt_boot.h>
 #include <u8g2_glue.h>
 #include <u8g2_glue.h>
 #include <assets_icons.h>
 #include <assets_icons.h>
+#include <toolbox/compress.h>
 
 
 #define COUNTER_VALUE (136U)
 #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_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
     u8g2_InitDisplay(fb);
     u8g2_InitDisplay(fb);
 
 
-    furi_hal_compress_icon_init();
+    CompressIcon* compress_icon = compress_icon_alloc();
     uint8_t* splash_data = NULL;
     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_ClearBuffer(fb);
     u8g2_SetDrawColor(fb, 0x01);
     u8g2_SetDrawColor(fb, 0x01);
@@ -38,6 +39,7 @@ void flipper_boot_recovery_exec() {
     u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data);
     u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data);
     u8g2_SendBuffer(fb);
     u8g2_SendBuffer(fb);
     u8g2_SetPowerSave(fb, 0);
     u8g2_SetPowerSave(fb, 0);
+    compress_icon_free(compress_icon);
 
 
     size_t counter = COUNTER_VALUE;
     size_t counter = COUNTER_VALUE;
     while(counter) {
     while(counter) {

+ 0 - 1
firmware/targets/furi_hal_include/furi_hal.h

@@ -33,7 +33,6 @@ struct STOP_EXTERNING_ME {};
 #include <furi_hal_vibro.h>
 #include <furi_hal_vibro.h>
 #include <furi_hal_usb.h>
 #include <furi_hal_usb.h>
 #include <furi_hal_usb_hid.h>
 #include <furi_hal_usb_hid.h>
-#include <furi_hal_compress.h>
 #include <furi_hal_uart.h>
 #include <furi_hal_uart.h>
 #include <furi_hal_info.h>
 #include <furi_hal_info.h>
 #include <furi_hal_random.h>
 #include <furi_hal_random.h>

+ 0 - 87
firmware/targets/furi_hal_include/furi_hal_compress.h

@@ -1,87 +0,0 @@
-/**
- * @file furi_hal_compress.h
- * LZSS based compression HAL API
- */
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stddef.h>
-
-#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

+ 4 - 0
lib/err.h

@@ -0,0 +1,4 @@
+#pragma once
+#include <furi.h>
+
+#define err(...) FURI_LOG_E("Heatshrink", "Error: %d-%s", __VA_ARGS__)

+ 1 - 0
lib/heatshrink

@@ -0,0 +1 @@
+Subproject commit 7398ccc91652a33483245200cfa1a83b073bc206

+ 0 - 20
lib/heatshrink/heatshrink_common.h

@@ -1,20 +0,0 @@
-#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

+ 0 - 28
lib/heatshrink/heatshrink_config.h

@@ -1,28 +0,0 @@
-#ifndef HEATSHRINK_CONFIG_H
-#define HEATSHRINK_CONFIG_H
-
-#include <furi.h>
-
-/* 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

+ 0 - 364
lib/heatshrink/heatshrink_decoder.c

@@ -1,364 +0,0 @@
-#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(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; 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;
-}

+ 0 - 100
lib/heatshrink/heatshrink_decoder.h

@@ -1,100 +0,0 @@
-#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(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

+ 0 - 602
lib/heatshrink/heatshrink_encoder.c

@@ -1,602 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#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 <stdio.h>
-#include <ctype.h>
-#include <assert.h>
-#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; i<end; i++) {
-        uint8_t v = data[i];
-        int16_t lv = last[v];
-        index[i] = lv;
-        last[v] = i;
-    }
-#else
-    (void)hse;
-#endif
-}
-
-static int is_finishing(heatshrink_encoder *hse) {
-    return hse->flags & 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<maxlen; len++) {
-                if (0) {
-                    LOG("  --> 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;
-}

+ 0 - 109
lib/heatshrink/heatshrink_encoder.h

@@ -1,109 +0,0 @@
-#ifndef HEATSHRINK_ENCODER_H
-#define HEATSHRINK_ENCODER_H
-
-#include <stdint.h>
-#include <stddef.h>
-#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

+ 6 - 1
lib/misc.scons

@@ -36,7 +36,6 @@ for lib in libs_recurse:
     sources += libenv.GlobRecursive("*.c*", lib)
     sources += libenv.GlobRecursive("*.c*", lib)
 
 
 libs_plain = [
 libs_plain = [
-    "heatshrink",
     "nanopb",
     "nanopb",
 ]
 ]
 
 
@@ -47,6 +46,12 @@ for lib in libs_plain:
         source=True,
         source=True,
     )
     )
 
 
+sources += Glob(
+    "heatshrink/heatshrink_*.c*",
+    exclude=GLOB_FILE_EXCLUSION,
+    source=True,
+)
+
 lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
 lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
 libenv.Install("${LIB_DIST_DIR}", lib)
 libenv.Install("${LIB_DIST_DIR}", lib)
 Return("lib")
 Return("lib")

+ 62 - 65
firmware/targets/f7/furi_hal/furi_hal_compress.c → lib/toolbox/compress.c

@@ -1,115 +1,112 @@
-#include <furi_hal_compress.h>
+#include "compress.h"
 
 
 #include <furi.h>
 #include <furi.h>
 #include <lib/heatshrink/heatshrink_encoder.h>
 #include <lib/heatshrink/heatshrink_encoder.h>
 #include <lib/heatshrink/heatshrink_decoder.h>
 #include <lib/heatshrink/heatshrink_decoder.h>
 
 
-#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 {
 typedef struct {
     uint8_t is_compressed;
     uint8_t is_compressed;
     uint8_t reserved;
     uint8_t reserved;
     uint16_t compressed_buff_size;
     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;
     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(icon_data);
     furi_assert(decoded_buff);
     furi_assert(decoded_buff);
 
 
-    FuriHalCompressHeader* header = (FuriHalCompressHeader*)icon_data;
+    CompressHeader* header = (CompressHeader*)icon_data;
     if(header->is_compressed) {
     if(header->is_compressed) {
         size_t data_processed = 0;
         size_t data_processed = 0;
         heatshrink_decoder_sink(
         heatshrink_decoder_sink(
-            icon_decoder->decoder,
-            (uint8_t*)&icon_data[4],
+            instance->decoder,
+            (uint8_t*)&icon_data[sizeof(CompressHeader)],
             header->compressed_buff_size,
             header->compressed_buff_size,
             &data_processed);
             &data_processed);
         while(1) {
         while(1) {
             HSD_poll_res res = heatshrink_decoder_poll(
             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);
                 &data_processed);
             furi_assert((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE));
             furi_assert((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE));
             if(res != HSDR_POLL_MORE) {
             if(res != HSDR_POLL_MORE) {
                 break;
                 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 {
     } else {
         *decoded_buff = (uint8_t*)&icon_data[1];
         *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->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;
     return compress;
 }
 }
 
 
-void furi_hal_compress_free(FuriHalCompress* compress) {
+void compress_free(Compress* compress) {
     furi_assert(compress);
     furi_assert(compress);
 
 
     heatshrink_encoder_free(compress->encoder);
     heatshrink_encoder_free(compress->encoder);
     heatshrink_decoder_free(compress->decoder);
     heatshrink_decoder_free(compress->decoder);
-    free(compress->compress_buff);
     free(compress);
     free(compress);
 }
 }
 
 
-bool furi_hal_compress_encode(
-    FuriHalCompress* compress,
+bool compress_encode(
+    Compress* compress,
     uint8_t* data_in,
     uint8_t* data_in,
     size_t data_in_size,
     size_t data_in_size,
     uint8_t* data_out,
     uint8_t* data_out,
@@ -126,7 +123,7 @@ bool furi_hal_compress_encode(
     HSE_finish_res finish_res;
     HSE_finish_res finish_res;
     bool encode_failed = false;
     bool encode_failed = false;
     size_t sunk = 0;
     size_t sunk = 0;
-    size_t res_buff_size = sizeof(FuriHalCompressHeader);
+    size_t res_buff_size = sizeof(CompressHeader);
 
 
     // Sink data to encoding buffer
     // Sink data to encoding buffer
     while((sunk < data_in_size) && !encode_failed) {
     while((sunk < data_in_size) && !encode_failed) {
@@ -174,7 +171,7 @@ bool furi_hal_compress_encode(
     bool result = true;
     bool result = true;
     // Write encoded data to output buffer if compression is efficient. Else - write header and original data
     // 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)) {
     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};
             .is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size};
         memcpy(data_out, &header, sizeof(header));
         memcpy(data_out, &header, sizeof(header));
         *data_res_size = res_buff_size;
         *data_res_size = res_buff_size;
@@ -186,13 +183,13 @@ bool furi_hal_compress_encode(
         *data_res_size = 0;
         *data_res_size = 0;
         result = false;
         result = false;
     }
     }
-    furi_hal_compress_reset(compress);
+    compress_reset(compress);
 
 
     return result;
     return result;
 }
 }
 
 
-bool furi_hal_compress_decode(
-    FuriHalCompress* compress,
+bool compress_decode(
+    Compress* compress,
     uint8_t* data_in,
     uint8_t* data_in,
     size_t data_in_size,
     size_t data_in_size,
     uint8_t* data_out,
     uint8_t* data_out,
@@ -212,11 +209,11 @@ bool furi_hal_compress_decode(
     size_t res_buff_size = 0;
     size_t res_buff_size = 0;
     size_t poll_size = 0;
     size_t poll_size = 0;
 
 
-    FuriHalCompressHeader* header = (FuriHalCompressHeader*)data_in;
+    CompressHeader* header = (CompressHeader*)data_in;
     if(header->is_compressed) {
     if(header->is_compressed) {
         // Sink data to decoding buffer
         // Sink data to decoding buffer
         size_t compressed_size = header->compressed_buff_size;
         size_t compressed_size = header->compressed_buff_size;
-        size_t sunk = sizeof(FuriHalCompressHeader);
+        size_t sunk = sizeof(CompressHeader);
         while(sunk < compressed_size && !decode_failed) {
         while(sunk < compressed_size && !decode_failed) {
             sink_res = heatshrink_decoder_sink(
             sink_res = heatshrink_decoder_sink(
                 compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size);
                 compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size);
@@ -258,7 +255,7 @@ bool furi_hal_compress_decode(
     } else {
     } else {
         result = false;
         result = false;
     }
     }
-    furi_hal_compress_reset(compress);
+    compress_reset(compress);
 
 
     return result;
     return result;
 }
 }

+ 96 - 0
lib/toolbox/compress.h

@@ -0,0 +1,96 @@
+/**
+ * @file compress.h
+ * LZSS based compression HAL API
+ */
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#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