diff --git a/Espressif/examples/esphttpd/Makefile b/Espressif/examples/esphttpd/Makefile index c067dce..d80ca63 100644 --- a/Espressif/examples/esphttpd/Makefile +++ b/Espressif/examples/esphttpd/Makefile @@ -121,7 +121,7 @@ MODULES = user EXTRA_INCDIR = include libesphttpd/include # libraries used in this project, mainly provided by the SDK -LIBS = c gcc hal phy pp net80211 wpa main lwip crypto +LIBS = c gcc hal phy pp net80211 wpa main lwip #Add in esphttpd lib LIBS += esphttpd diff --git a/Espressif/examples/esphttpd/html/cats/junge-katze-iv.jpg b/Espressif/examples/esphttpd/html/cats/junge-katze-iv.jpg new file mode 100644 index 0000000..c3cf70b Binary files /dev/null and b/Espressif/examples/esphttpd/html/cats/junge-katze-iv.jpg differ diff --git a/Espressif/examples/esphttpd/html/cats/kitten-loves-toy.jpg b/Espressif/examples/esphttpd/html/cats/kitten-loves-toy.jpg new file mode 100644 index 0000000..569ff56 Binary files /dev/null and b/Espressif/examples/esphttpd/html/cats/kitten-loves-toy.jpg differ diff --git a/Espressif/examples/esphttpd/html/index.tpl b/Espressif/examples/esphttpd/html/index.tpl index 1c7a5ec..cfd4ec3 100644 --- a/Espressif/examples/esphttpd/html/index.tpl +++ b/Espressif/examples/esphttpd/html/index.tpl @@ -12,12 +12,15 @@ been loaded %counter% times.
  • If you haven't connected this device to your WLAN network now, you can do so.
  • You can also control the LED.
  • You can download the raw contents of the SPI flash rom
  • +
  • Esphttpd now also supports websockets.
  • And because I can, here's a link to my website

    And because we're on the Internets now, here are the required pictures of cats:

    +
    +

    diff --git a/Espressif/examples/esphttpd/html/websocket/index.html b/Espressif/examples/esphttpd/html/websocket/index.html new file mode 100644 index 0000000..d7423ec --- /dev/null +++ b/Espressif/examples/esphttpd/html/websocket/index.html @@ -0,0 +1,69 @@ + + + + +WebSocket Test + + + +

    WebSocket Test

    + +
    diff --git a/Espressif/examples/esphttpd/include/user_config.h b/Espressif/examples/esphttpd/include/user_config.h index da06979..8b13789 100644 --- a/Espressif/examples/esphttpd/include/user_config.h +++ b/Espressif/examples/esphttpd/include/user_config.h @@ -1,11 +1 @@ -#ifndef __USER_CONFIG_H__ -#define __USER_CONFIG_H__ -#define USE_WIFI_MODE STATIONAP_MODE -#define WIFI_CLIENTSSID "MYAP" -#define WIFI_CLIENTPASSWORD "00000000" -#define WIFI_AP_NAME "ESP8266" -#define WIFI_AP_PASSWORD "00000000" -#define PLATFORM_DEBUG true - -#endif diff --git a/Espressif/examples/esphttpd/lib/heatshrink/LICENSE b/Espressif/examples/esphttpd/lib/heatshrink/LICENSE deleted file mode 100644 index 31ec3df..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (c) 2013, Scott Vokes -All rights reserved. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Espressif/examples/esphttpd/lib/heatshrink/Makefile b/Espressif/examples/esphttpd/lib/heatshrink/Makefile deleted file mode 100644 index d8e6875..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -PROJECT = heatshrink -#OPTIMIZE = -O0 -#OPTIMIZE = -Os -OPTIMIZE = -O3 -WARN = -Wall -Wextra -pedantic #-Werror -CFLAGS += -std=c99 -g ${WARN} ${OPTIMIZE} -CFLAGS += -Wmissing-prototypes -CFLAGS += -Wstrict-prototypes -CFLAGS += -Wmissing-declarations - -# If libtheft is available, build additional property-based tests. -# Uncomment these to use it in test_heatshrink_dynamic. -#CFLAGS += -DHEATSHRINK_HAS_THEFT -#LDFLAGS += -ltheft - -all: - @echo "For tests, make test_heatshrink_dynamic (default) or change the" - @echo "config.h to disable static memory and build test_heatshrink_static." - @echo "For the standalone command-line tool, make heatshrink." - -${PROJECT}: heatshrink.c - -OBJS= heatshrink_encoder.o \ - heatshrink_decoder.o \ - -heatshrink: ${OBJS} -test_heatshrink_dynamic: ${OBJS} test_heatshrink_dynamic_theft.o -test_heatshrink_static: ${OBJS} - -*.o: Makefile heatshrink_config.h - -heatshrink_decoder.o: heatshrink_decoder.h heatshrink_common.h -heatshrink_encoder.o: heatshrink_encoder.h heatshrink_common.h - -tags: TAGS - -TAGS: - etags *.[ch] - -diagrams: dec_sm.png enc_sm.png - -dec_sm.png: dec_sm.dot - dot -o $@ -Tpng $< - -enc_sm.png: enc_sm.dot - dot -o $@ -Tpng $< - -clean: - rm -f ${PROJECT} test_heatshrink_{dynamic,static} *.o *.core {dec,enc}_sm.png TAGS diff --git a/Espressif/examples/esphttpd/lib/heatshrink/README.md b/Espressif/examples/esphttpd/lib/heatshrink/README.md deleted file mode 100644 index ab150ee..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# heatshrink - -A data compression/decompression library for embedded/real-time systems. - -## Key Features: - -- **Low memory usage (as low as 50 bytes)** - It is useful for some cases with less than 50 bytes, and useful - for many general cases with < 300 bytes. -- **Incremental, bounded CPU use** - You can chew on input data in arbitrarily tiny bites. - This is a useful property in hard real-time environments. -- **Can use either static or dynamic memory allocation** - The library doesn't impose any constraints on memory management. -- **ISC license** - You can use it freely, even for commercial purposes. - -## Getting Started: - -There is a standalone command-line program, `heatshrink`, but the -encoder and decoder can also be used as libraries, independent of each -other. To do so, copy `heatshrink_common.h`, `heatshrink_config.h`, and -either `heatshrink_encoder.c` or `heatshrink_decoder.c` (and their -respective header) into your project. - -Dynamic allocation is used by default, but in an embedded context, you -probably want to statically allocate the encoder/decoder. Set -`HEATSHRINK_DYNAMIC_ALLOC` to 0 in `heatshrink_config.h`. - -## More Information and Benchmarks: - -heatshrink is based on [LZSS], since it's particularly suitable for -compression in small amounts of memory. It can use an optional, small -[index] to make compression significantly faster, but otherwise can run -in under 100 bytes of memory. The index currently adds 2^(window size+1) -bytes to memory usage for compression, and temporarily allocates 512 -bytes on the stack during index construction. - -For more information, see the [blog post] for an overview, and the -`heatshrink_encoder.h` / `heatshrink_decoder.h` header files for API -documentation. - -[blog post]: http://spin.atomicobject.com/2013/03/14/heatshrink-embedded-data-compression/ -[index]: http://spin.atomicobject.com/2014/01/13/lightweight-indexing-for-embedded-systems/ -[LZSS]: http://en.wikipedia.org/wiki/Lempel-Ziv-Storer-Szymanski - -## Build Status - - [![Build Status](https://travis-ci.org/atomicobject/heatshrink.png)](http://travis-ci.org/atomicobject/heatshrink) diff --git a/Espressif/examples/esphttpd/lib/heatshrink/dec_sm.dot b/Espressif/examples/esphttpd/lib/heatshrink/dec_sm.dot deleted file mode 100644 index 470012f..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/dec_sm.dot +++ /dev/null @@ -1,52 +0,0 @@ -digraph { - graph [label="Decoder state machine", labelloc="t"] - Start [style="invis", shape="point"] - empty - input_available - yield_literal - backref_index_msb - backref_index_lsb - backref_count_msb - backref_count_lsb - yield_backref - check_for_more_input - done [peripheries=2] - - empty->input_available [label="sink()", color="blue", weight=10] - Start->empty - - input_available->yield_literal [label="pop 1-bit"] - input_available->backref_index_msb [label="pop 0-bit", weight=10] - input_available->backref_index_lsb [label="pop 0-bit, index <8 bits", weight=10] - - yield_literal->yield_literal [label="sink()", color="blue"] - yield_literal->yield_literal [label="poll()", color="red"] - yield_literal->check_for_more_input [label="poll(), done", color="red"] - - backref_index_msb->backref_index_msb [label="sink()", color="blue"] - backref_index_msb->backref_index_lsb [label="pop index, upper bits", weight=10] - backref_index_msb->done [label="finish()", color="blue"] - - backref_index_lsb->backref_index_lsb [label="sink()", color="blue"] - backref_index_lsb->backref_count_msb [label="pop index, lower bits", weight=10] - backref_index_lsb->backref_count_lsb [label="pop index, count <=8 bits", weight=10] - backref_index_lsb->done [label="finish()", color="blue"] - - backref_count_msb->backref_count_msb [label="sink()", color="blue"] - backref_count_msb->backref_count_lsb [label="pop count, upper bits", weight=10] - backref_count_msb->done [label="finish()", color="blue"] - - backref_count_lsb->backref_count_lsb [label="sink()", color="blue"] - backref_count_lsb->yield_backref [label="pop count, lower bits", weight=10] - backref_count_lsb->done [label="finish()", color="blue"] - - yield_backref->yield_backref [label="sink()", color="blue"] - yield_backref->yield_backref [label="poll()", color="red"] - yield_backref->check_for_more_input [label="poll(), done", - color="red", weight=10] - - check_for_more_input->empty [label="no"] - check_for_more_input->input_available [label="yes"] - - empty->done [label="finish()", color="blue"] -} diff --git a/Espressif/examples/esphttpd/lib/heatshrink/enc_sm.dot b/Espressif/examples/esphttpd/lib/heatshrink/enc_sm.dot deleted file mode 100644 index 6d3030f..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/enc_sm.dot +++ /dev/null @@ -1,51 +0,0 @@ -digraph { - graph [label="Encoder state machine", labelloc="t"] - start [style="invis", shape="point"] - not_full - filled - search - yield_tag_bit - yield_literal - yield_br_length - yield_br_index - save_backlog - flush_bits - done [peripheries=2] - - start->not_full [label="start"] - - not_full->not_full [label="sink(), not full", color="blue"] - not_full->filled [label="sink(), buffer is full", color="blue"] - not_full->filled [label="finish(), set is_finished", color="blue"] - - filled->search [label="indexing (if any)"] - - search->search [label="step"] - search->yield_tag_bit [label="literal"] - search->yield_tag_bit [label="match found"] - search->save_backlog [label="input exhausted"] - - yield_tag_bit->yield_tag_bit [label="poll(), full buf", color="red"] - yield_tag_bit->yield_literal [label="poll(), literal", color="red"] - yield_tag_bit->yield_br_index [label="poll(), no literal", color="red"] - yield_tag_bit->flush_bits [label="finishing, no literal"] - - yield_literal->yield_literal [label="poll(), full buf", color="red"] - yield_literal->search [label="poll(), no match", color="red"] - yield_literal->yield_tag_bit [label="poll(), match", color="red"] - yield_literal->flush_bits [label="poll(), final literal", color="red"] - - yield_br_index->yield_br_index [label="poll(), full buf", color="red"] - yield_br_index->yield_br_length [label="poll()", color="red"] - - yield_br_length->yield_br_length [label="poll(), full buf", color="red"] - yield_br_length->search [label="done"] - - save_backlog->flush_bits [label="finishing, no literal"] - save_backlog->yield_tag_bit [label="finishing, literal"] - save_backlog->not_full [label="expect more input"] - - flush_bits->flush_bits [label="poll(), full buf", color="red"] - flush_bits->done [label="poll(), flushed", color="red"] - flush_bits->done [label="no more output"] -} diff --git a/Espressif/examples/esphttpd/lib/heatshrink/greatest.h b/Espressif/examples/esphttpd/lib/heatshrink/greatest.h deleted file mode 100644 index a92c642..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/greatest.h +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright (c) 2011 Scott Vokes - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef GREATEST_H -#define GREATEST_H - -#define GREATEST_VERSION_MAJOR 0 -#define GREATEST_VERSION_MINOR 9 -#define GREATEST_VERSION_PATCH 3 - -/* A unit testing system for C, contained in 1 file. - * It doesn't use dynamic allocation or depend on anything - * beyond ANSI C89. */ - - -/********************************************************************* - * Minimal test runner template - *********************************************************************/ -#if 0 - -#include "greatest.h" - -TEST foo_should_foo() { - PASS(); -} - -static void setup_cb(void *data) { - printf("setup callback for each test case\n"); -} - -static void teardown_cb(void *data) { - printf("teardown callback for each test case\n"); -} - -SUITE(suite) { - /* Optional setup/teardown callbacks which will be run before/after - * every test case in the suite. - * Cleared when the suite finishes. */ - SET_SETUP(setup_cb, voidp_to_callback_data); - SET_TEARDOWN(teardown_cb, voidp_to_callback_data); - - RUN_TEST(foo_should_foo); -} - -/* Add all the definitions that need to be in the test runner's main file. */ -GREATEST_MAIN_DEFS(); - -int main(int argc, char **argv) { - GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ - RUN_SUITE(suite); - GREATEST_MAIN_END(); /* display results */ -} - -#endif -/*********************************************************************/ - - -#include -#include -#include -#include - - -/*********** - * Options * - ***********/ - -/* Default column width for non-verbose output. */ -#ifndef GREATEST_DEFAULT_WIDTH -#define GREATEST_DEFAULT_WIDTH 72 -#endif - -/* FILE *, for test logging. */ -#ifndef GREATEST_STDOUT -#define GREATEST_STDOUT stdout -#endif - -/* Remove GREATEST_ prefix from most commonly used symbols? */ -#ifndef GREATEST_USE_ABBREVS -#define GREATEST_USE_ABBREVS 1 -#endif - - -/********* - * Types * - *********/ - -/* Info for the current running suite. */ -typedef struct greatest_suite_info { - unsigned int tests_run; - unsigned int passed; - unsigned int failed; - unsigned int skipped; - - /* timers, pre/post running suite and individual tests */ - clock_t pre_suite; - clock_t post_suite; - clock_t pre_test; - clock_t post_test; -} greatest_suite_info; - -/* Type for a suite function. */ -typedef void (greatest_suite_cb)(void); - -/* Types for setup/teardown callbacks. If non-NULL, these will be run - * and passed the pointer to their additional data. */ -typedef void (greatest_setup_cb)(void *udata); -typedef void (greatest_teardown_cb)(void *udata); - -typedef enum { - GREATEST_FLAG_VERBOSE = 0x01, - GREATEST_FLAG_FIRST_FAIL = 0x02, - GREATEST_FLAG_LIST_ONLY = 0x04 -} GREATEST_FLAG; - -typedef struct greatest_run_info { - unsigned int flags; - unsigned int tests_run; /* total test count */ - - /* Overall pass/fail/skip counts. */ - unsigned int passed; - unsigned int failed; - unsigned int skipped; - - /* currently running test suite */ - greatest_suite_info suite; - - /* info to print about the most recent failure */ - const char *fail_file; - unsigned int fail_line; - const char *msg; - - /* current setup/teardown hooks and userdata */ - greatest_setup_cb *setup; - void *setup_udata; - greatest_teardown_cb *teardown; - void *teardown_udata; - - /* formatting info for ".....s...F"-style output */ - unsigned int col; - unsigned int width; - - /* only run a specific suite or test */ - char *suite_filter; - char *test_filter; - - /* overall timers */ - clock_t begin; - clock_t end; -} greatest_run_info; - -/* Global var for the current testing context. - * Initialized by GREATEST_MAIN_DEFS(). */ -extern greatest_run_info greatest_info; - - -/********************** - * Exported functions * - **********************/ - -void greatest_do_pass(const char *name); -void greatest_do_fail(const char *name); -void greatest_do_skip(const char *name); -int greatest_pre_test(const char *name); -void greatest_post_test(const char *name, int res); -void greatest_usage(const char *name); -void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); -void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); - - -/********** - * Macros * - **********/ - -/* Define a suite. */ -#define GREATEST_SUITE(NAME) void NAME(void) - -/* Start defining a test function. - * The arguments are not included, to allow parametric testing. */ -#define GREATEST_TEST static int - -/* Run a suite. */ -#define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) - -/* Run a test in the current suite. */ -#define GREATEST_RUN_TEST(TEST) \ - do { \ - if (greatest_pre_test(#TEST) == 1) { \ - int res = TEST(); \ - greatest_post_test(#TEST, res); \ - } else if (GREATEST_LIST_ONLY()) { \ - fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ - } \ - } while (0) - -/* Run a test in the current suite with one void* argument, - * which can be a pointer to a struct with multiple arguments. */ -#define GREATEST_RUN_TEST1(TEST, ENV) \ - do { \ - if (greatest_pre_test(#TEST) == 1) { \ - int res = TEST(ENV); \ - greatest_post_test(#TEST, res); \ - } else if (GREATEST_LIST_ONLY()) { \ - fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ - } \ - } while (0) - -/* If __VA_ARGS__ (C99) is supported, allow parametric testing - * without needing to manually manage the argument struct. */ -#if __STDC_VERSION__ >= 19901L -#define GREATEST_RUN_TESTp(TEST, ...) \ - do { \ - if (greatest_pre_test(#TEST) == 1) { \ - int res = TEST(__VA_ARGS__); \ - greatest_post_test(#TEST, res); \ - } else if (GREATEST_LIST_ONLY()) { \ - fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ - } \ - } while (0) -#endif - - -/* Check if the test runner is in verbose mode. */ -#define GREATEST_IS_VERBOSE() (greatest_info.flags & GREATEST_FLAG_VERBOSE) -#define GREATEST_LIST_ONLY() (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) -#define GREATEST_FIRST_FAIL() (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) -#define GREATEST_FAILURE_ABORT() (greatest_info.suite.failed > 0 && GREATEST_FIRST_FAIL()) - -/* Message-less forms. */ -#define GREATEST_PASS() GREATEST_PASSm(NULL) -#define GREATEST_FAIL() GREATEST_FAILm(NULL) -#define GREATEST_SKIP() GREATEST_SKIPm(NULL) -#define GREATEST_ASSERT(COND) GREATEST_ASSERTm(#COND, COND) -#define GREATEST_ASSERT_FALSE(COND) GREATEST_ASSERT_FALSEm(#COND, COND) -#define GREATEST_ASSERT_EQ(EXP, GOT) GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) -#define GREATEST_ASSERT_STR_EQ(EXP, GOT) GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) - -/* The following forms take an additional message argument first, - * to be displayed by the test runner. */ - -/* Fail if a condition is not true, with message. */ -#define GREATEST_ASSERTm(MSG, COND) \ - do { \ - greatest_info.msg = MSG; \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - if (!(COND)) return -1; \ - greatest_info.msg = NULL; \ - } while (0) - -#define GREATEST_ASSERT_FALSEm(MSG, COND) \ - do { \ - greatest_info.msg = MSG; \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - if ((COND)) return -1; \ - greatest_info.msg = NULL; \ - } while (0) - -#define GREATEST_ASSERT_EQm(MSG, EXP, GOT) \ - do { \ - greatest_info.msg = MSG; \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - if ((EXP) != (GOT)) return -1; \ - greatest_info.msg = NULL; \ - } while (0) - -#define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ - do { \ - const char *exp_s = (EXP); \ - const char *got_s = (GOT); \ - greatest_info.msg = MSG; \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - if (0 != strcmp(exp_s, got_s)) { \ - fprintf(GREATEST_STDOUT, \ - "Expected:\n####\n%s\n####\n", exp_s); \ - fprintf(GREATEST_STDOUT, \ - "Got:\n####\n%s\n####\n", got_s); \ - return -1; \ - } \ - greatest_info.msg = NULL; \ - } while (0) - -#define GREATEST_PASSm(MSG) \ - do { \ - greatest_info.msg = MSG; \ - return 0; \ - } while (0) - -#define GREATEST_FAILm(MSG) \ - do { \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - greatest_info.msg = MSG; \ - return -1; \ - } while (0) - -#define GREATEST_SKIPm(MSG) \ - do { \ - greatest_info.msg = MSG; \ - return 1; \ - } while (0) - -#define GREATEST_SET_TIME(NAME) \ - NAME = clock(); \ - if (NAME == (clock_t) -1) { \ - fprintf(GREATEST_STDOUT, \ - "clock error: %s\n", #NAME); \ - exit(EXIT_FAILURE); \ - } - -#define GREATEST_CLOCK_DIFF(C1, C2) \ - fprintf(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ - (long unsigned int) (C2) - (C1), \ - (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) \ - -/* Include several function definitions in the main test file. */ -#define GREATEST_MAIN_DEFS() \ - \ -/* Is FILTER a subset of NAME? */ \ -static int greatest_name_match(const char *name, \ - const char *filter) { \ - size_t offset = 0; \ - size_t filter_len = strlen(filter); \ - while (name[offset] != '\0') { \ - if (name[offset] == filter[0]) { \ - if (0 == strncmp(&name[offset], filter, filter_len)) { \ - return 1; \ - } \ - } \ - offset++; \ - } \ - \ - return 0; \ -} \ - \ -int greatest_pre_test(const char *name) { \ - if (!GREATEST_LIST_ONLY() \ - && (!GREATEST_FIRST_FAIL() || greatest_info.suite.failed == 0) \ - && (greatest_info.test_filter == NULL || \ - greatest_name_match(name, greatest_info.test_filter))) { \ - GREATEST_SET_TIME(greatest_info.suite.pre_test); \ - if (greatest_info.setup) { \ - greatest_info.setup(greatest_info.setup_udata); \ - } \ - return 1; /* test should be run */ \ - } else { \ - return 0; /* skipped */ \ - } \ -} \ - \ -void greatest_post_test(const char *name, int res) { \ - GREATEST_SET_TIME(greatest_info.suite.post_test); \ - if (greatest_info.teardown) { \ - void *udata = greatest_info.teardown_udata; \ - greatest_info.teardown(udata); \ - } \ - \ - if (res < 0) { \ - greatest_do_fail(name); \ - } else if (res > 0) { \ - greatest_do_skip(name); \ - } else if (res == 0) { \ - greatest_do_pass(name); \ - } \ - greatest_info.suite.tests_run++; \ - greatest_info.col++; \ - if (GREATEST_IS_VERBOSE()) { \ - GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ - greatest_info.suite.post_test); \ - fprintf(GREATEST_STDOUT, "\n"); \ - } else if (greatest_info.col % greatest_info.width == 0) { \ - fprintf(GREATEST_STDOUT, "\n"); \ - greatest_info.col = 0; \ - } \ - if (GREATEST_STDOUT == stdout) fflush(stdout); \ -} \ - \ -static void greatest_run_suite(greatest_suite_cb *suite_cb, \ - const char *suite_name) { \ - if (greatest_info.suite_filter && \ - !greatest_name_match(suite_name, greatest_info.suite_filter)) \ - return; \ - if (GREATEST_FIRST_FAIL() && greatest_info.failed > 0) return; \ - greatest_info.suite.tests_run = 0; \ - greatest_info.suite.failed = 0; \ - greatest_info.suite.passed = 0; \ - greatest_info.suite.skipped = 0; \ - greatest_info.suite.pre_suite = 0; \ - greatest_info.suite.post_suite = 0; \ - greatest_info.suite.pre_test = 0; \ - greatest_info.suite.post_test = 0; \ - greatest_info.col = 0; \ - fprintf(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ - GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ - suite_cb(); \ - GREATEST_SET_TIME(greatest_info.suite.post_suite); \ - if (greatest_info.suite.tests_run > 0) { \ - fprintf(GREATEST_STDOUT, \ - "\n%u tests - %u pass, %u fail, %u skipped", \ - greatest_info.suite.tests_run, \ - greatest_info.suite.passed, \ - greatest_info.suite.failed, \ - greatest_info.suite.skipped); \ - GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ - greatest_info.suite.post_suite); \ - fprintf(GREATEST_STDOUT, "\n"); \ - } \ - greatest_info.setup = NULL; \ - greatest_info.setup_udata = NULL; \ - greatest_info.teardown = NULL; \ - greatest_info.teardown_udata = NULL; \ - greatest_info.passed += greatest_info.suite.passed; \ - greatest_info.failed += greatest_info.suite.failed; \ - greatest_info.skipped += greatest_info.suite.skipped; \ - greatest_info.tests_run += greatest_info.suite.tests_run; \ -} \ - \ -void greatest_do_pass(const char *name) { \ - if (GREATEST_IS_VERBOSE()) { \ - fprintf(GREATEST_STDOUT, "PASS %s: %s", \ - name, greatest_info.msg ? greatest_info.msg : ""); \ - } else { \ - fprintf(GREATEST_STDOUT, "."); \ - } \ - greatest_info.suite.passed++; \ -} \ - \ -void greatest_do_fail(const char *name) { \ - if (GREATEST_IS_VERBOSE()) { \ - fprintf(GREATEST_STDOUT, \ - "FAIL %s: %s (%s:%u)", \ - name, greatest_info.msg ? greatest_info.msg : "", \ - greatest_info.fail_file, greatest_info.fail_line); \ - } else { \ - fprintf(GREATEST_STDOUT, "F"); \ - /* add linebreak if in line of '.'s */ \ - if (greatest_info.col % greatest_info.width != 0) \ - fprintf(GREATEST_STDOUT, "\n"); \ - greatest_info.col = 0; \ - fprintf(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ - name, \ - greatest_info.msg ? greatest_info.msg : "", \ - greatest_info.fail_file, greatest_info.fail_line); \ - } \ - greatest_info.suite.failed++; \ -} \ - \ -void greatest_do_skip(const char *name) { \ - if (GREATEST_IS_VERBOSE()) { \ - fprintf(GREATEST_STDOUT, "SKIP %s: %s", \ - name, \ - greatest_info.msg ? \ - greatest_info.msg : "" ); \ - } else { \ - fprintf(GREATEST_STDOUT, "s"); \ - } \ - greatest_info.suite.skipped++; \ -} \ - \ -void greatest_usage(const char *name) { \ - fprintf(GREATEST_STDOUT, \ - "Usage: %s [-hlfv] [-s SUITE] [-t TEST]\n" \ - " -h print this Help\n" \ - " -l List suites and their tests, then exit\n" \ - " -f Stop runner after first failure\n" \ - " -v Verbose output\n" \ - " -s SUITE only run suite named SUITE\n" \ - " -t TEST only run test named TEST\n", \ - name); \ -} \ - \ -void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ - greatest_info.setup = cb; \ - greatest_info.setup_udata = udata; \ -} \ - \ -void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, \ - void *udata) { \ - greatest_info.teardown = cb; \ - greatest_info.teardown_udata = udata; \ -} \ - \ -greatest_run_info greatest_info - -/* Handle command-line arguments, etc. */ -#define GREATEST_MAIN_BEGIN() \ - do { \ - int i = 0; \ - memset(&greatest_info, 0, sizeof(greatest_info)); \ - if (greatest_info.width == 0) { \ - greatest_info.width = GREATEST_DEFAULT_WIDTH; \ - } \ - for (i = 1; i < argc; i++) { \ - if (0 == strcmp("-t", argv[i])) { \ - if (argc <= i + 1) { \ - greatest_usage(argv[0]); \ - exit(EXIT_FAILURE); \ - } \ - greatest_info.test_filter = argv[i+1]; \ - i++; \ - } else if (0 == strcmp("-s", argv[i])) { \ - if (argc <= i + 1) { \ - greatest_usage(argv[0]); \ - exit(EXIT_FAILURE); \ - } \ - greatest_info.suite_filter = argv[i+1]; \ - i++; \ - } else if (0 == strcmp("-f", argv[i])) { \ - greatest_info.flags |= GREATEST_FLAG_FIRST_FAIL; \ - } else if (0 == strcmp("-v", argv[i])) { \ - greatest_info.flags |= GREATEST_FLAG_VERBOSE; \ - } else if (0 == strcmp("-l", argv[i])) { \ - greatest_info.flags |= GREATEST_FLAG_LIST_ONLY; \ - } else if (0 == strcmp("-h", argv[i])) { \ - greatest_usage(argv[0]); \ - exit(EXIT_SUCCESS); \ - } else { \ - fprintf(GREATEST_STDOUT, \ - "Unknown argument '%s'\n", argv[i]); \ - greatest_usage(argv[0]); \ - exit(EXIT_FAILURE); \ - } \ - } \ - } while (0); \ - GREATEST_SET_TIME(greatest_info.begin) - -#define GREATEST_MAIN_END() \ - do { \ - if (!GREATEST_LIST_ONLY()) { \ - GREATEST_SET_TIME(greatest_info.end); \ - fprintf(GREATEST_STDOUT, \ - "\nTotal: %u tests", greatest_info.tests_run); \ - GREATEST_CLOCK_DIFF(greatest_info.begin, \ - greatest_info.end); \ - fprintf(GREATEST_STDOUT, "\n"); \ - fprintf(GREATEST_STDOUT, \ - "Pass: %u, fail: %u, skip: %u.\n", \ - greatest_info.passed, \ - greatest_info.failed, greatest_info.skipped); \ - } \ - return (greatest_info.failed > 0 \ - ? EXIT_FAILURE : EXIT_SUCCESS); \ - } while (0) - -/* Make abbreviations without the GREATEST_ prefix for the - * most commonly used symbols. */ -#if GREATEST_USE_ABBREVS -#define TEST GREATEST_TEST -#define SUITE GREATEST_SUITE -#define RUN_TEST GREATEST_RUN_TEST -#define RUN_TEST1 GREATEST_RUN_TEST1 -#define RUN_SUITE GREATEST_RUN_SUITE -#define ASSERT GREATEST_ASSERT -#define ASSERTm GREATEST_ASSERTm -#define ASSERT_FALSE GREATEST_ASSERT_FALSE -#define ASSERT_EQ GREATEST_ASSERT_EQ -#define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ -#define ASSERT_FALSEm GREATEST_ASSERT_FALSEm -#define ASSERT_EQm GREATEST_ASSERT_EQm -#define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm -#define PASS GREATEST_PASS -#define FAIL GREATEST_FAIL -#define SKIP GREATEST_SKIP -#define PASSm GREATEST_PASSm -#define FAILm GREATEST_FAILm -#define SKIPm GREATEST_SKIPm -#define SET_SETUP GREATEST_SET_SETUP_CB -#define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB - -#if __STDC_VERSION__ >= 19901L -#endif /* C99 */ -#define RUN_TESTp GREATEST_RUN_TESTp -#endif /* USE_ABBREVS */ - -#endif diff --git a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink.c b/Espressif/examples/esphttpd/lib/heatshrink/heatshrink.c deleted file mode 100644 index 9de553f..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink.c +++ /dev/null @@ -1,446 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "heatshrink_encoder.h" -#include "heatshrink_decoder.h" - -#define DEF_WINDOW_SZ2 11 -#define DEF_LOOKAHEAD_SZ2 4 -#define DEF_DECODER_INPUT_BUFFER_SIZE 256 -#define DEF_BUFFER_SIZE (64 * 1024) - -#if 0 -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#else -#define LOG(...) /* NO-OP */ -#endif - -static const int version_major = HEATSHRINK_VERSION_MAJOR; -static const int version_minor = HEATSHRINK_VERSION_MINOR; -static const int version_patch = HEATSHRINK_VERSION_PATCH; -static const char author[] = HEATSHRINK_AUTHOR; -static const char url[] = HEATSHRINK_URL; - -static void usage(void) { - fprintf(stderr, "heatshrink version %u.%u.%u by %s\n", - version_major, version_minor, version_patch, author); - fprintf(stderr, "Home page: %s\n\n", url); - fprintf(stderr, - "Usage:\n" - " heatshrink [-h] [-e|-d] [-v] [-w SIZE] [-l BITS] [IN_FILE] [OUT_FILE]\n" - "\n" - "heatshrink compresses or uncompresses byte streams using LZSS, and is\n" - "designed especially for embedded, low-memory, and/or hard real-time\n" - "systems.\n" - "\n" - " -h print help\n" - " -e encode (compress, default)\n" - " -d decode (uncompress)\n" - " -v verbose (print input & output sizes, compression ratio, etc.)\n" - "\n" - " -w SIZE Base-2 log of LZSS sliding window size\n" - "\n" - " A larger value allows searches a larger history of the data for repeated\n" - " patterns, potentially compressing more effectively, but will use\n" - " more memory and processing time.\n" - " Recommended default: -w 8 (embedded systems), -w 10 (elsewhere)\n" - " \n" - " -l BITS Number of bits used for back-reference lengths\n" - "\n" - " A larger value allows longer substitutions, but since all\n" - " back-references must use -w + -l bits, larger -w or -l can be\n" - " counterproductive if most patterns are small and/or local.\n" - " Recommended default: -l 4\n" - "\n" - " If IN_FILE or OUT_FILE are unspecified, they will default to\n" - " \"-\" for standard input and standard output, respectively.\n"); - exit(1); -} - -typedef enum { IO_READ, IO_WRITE, } IO_mode; -typedef enum { OP_ENC, OP_DEC, } Operation; - -typedef struct { - int fd; /* file descriptor */ - IO_mode mode; - size_t fill; /* fill index */ - size_t read; /* read index */ - size_t size; - size_t total; - uint8_t buf[]; -} io_handle; - -typedef struct { - uint8_t window_sz2; - uint8_t lookahead_sz2; - size_t decoder_input_buffer_size; - size_t buffer_size; - uint8_t verbose; - Operation cmd; - char *in_fname; - char *out_fname; - io_handle *in; - io_handle *out; -} config; - -static void die(char *msg) { - fprintf(stderr, "%s\n", msg); - exit(EXIT_FAILURE); -} - -static void report(config *cfg); - -/* Open an IO handle. Returns NULL on error. */ -static io_handle *handle_open(char *fname, IO_mode m, size_t buf_sz) { - io_handle *io = NULL; - io = malloc(sizeof(*io) + buf_sz); - if (io == NULL) { return NULL; } - memset(io, 0, sizeof(*io) + buf_sz); - io->fd = -1; - io->size = buf_sz; - io->mode = m; - - if (m == IO_READ) { - if (0 == strcmp("-", fname)) { - io->fd = STDIN_FILENO; - } else { - io->fd = open(fname, O_RDONLY); - } - } else if (m == IO_WRITE) { - if (0 == strcmp("-", fname)) { - io->fd = STDOUT_FILENO; - } else { - io->fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC /*| O_EXCL*/, 0644); - } - } - - if (io->fd == -1) { /* failed to open */ - free(io); - err(1, "open"); - return NULL; - } - - return io; -} - -/* Read SIZE bytes from an IO handle and return a pointer to the content. - * BUF contains at least size_t bytes. Returns 0 on EOF, -1 on error. */ -static ssize_t handle_read(io_handle *io, size_t size, uint8_t **buf) { - LOG("@ read %zd\n", size); - if (buf == NULL) { return -1; } - if (size > io->size) { - printf("size %zd, io->size %zd\n", size, io->size); - return -1; - } - if (io->mode != IO_READ) { return -1; } - - size_t rem = io->fill - io->read; - if (rem >= size) { - *buf = &io->buf[io->read]; - return size; - } else { /* read and replenish */ - if (io->fd == -1) { /* already closed, return what we've got */ - *buf = &io->buf[io->read]; - return rem; - } - - memmove(io->buf, &io->buf[io->read], rem); - io->fill -= io->read; - io->read = 0; - ssize_t read_sz = read(io->fd, &io->buf[io->fill], io->size - io->fill); - if (read_sz < 0) { err(1, "read"); } - io->total += read_sz; - if (read_sz == 0) { /* EOF */ - if (close(io->fd) < 0) { err(1, "close"); } - io->fd = -1; - } - io->fill += read_sz; - *buf = io->buf; - return io->fill > size ? size : io->fill; - } -} - -/* Drop the oldest SIZE bytes from the buffer. Returns <0 on error. */ -static int handle_drop(io_handle *io, size_t size) { - LOG("@ drop %zd\n", size); - if (io->read + size <= io->fill) { - io->read += size; - } else { - return -1; - } - if (io->read == io->fill) { - io->read = 0; - io->fill = 0; - } - return 0; -} - -/* Sink SIZE bytes from INPUT into the io handle. Returns the number of - * bytes written, or -1 on error. */ -static ssize_t handle_sink(io_handle *io, size_t size, uint8_t *input) { - LOG("@ sink %zd\n", size); - if (size > io->size) { return -1; } - if (io->mode != IO_WRITE) { return -1; } - - if (io->fill + size > io->size) { - ssize_t written = write(io->fd, io->buf, io->fill); - LOG("@ flushing %zd, wrote %zd\n", io->fill, written); - io->total += written; - if (written == -1) { err(1, "write"); } - memmove(io->buf, &io->buf[written], io->fill - written); - io->fill -= written; - } - memcpy(&io->buf[io->fill], input, size); - io->fill += size; - return size; -} - -static void handle_close(io_handle *io) { - if (io->fd != -1) { - if (io->mode == IO_WRITE) { - ssize_t written = write(io->fd, io->buf, io->fill); - io->total += written; - LOG("@ close: flushing %zd, wrote %zd\n", io->fill, written); - if (written == -1) { err(1, "write"); } - } - close(io->fd); - io->fd = -1; - } -} - -static void close_and_report(config *cfg) { - handle_close(cfg->in); - handle_close(cfg->out); - if (cfg->verbose) { report(cfg); } - free(cfg->in); - free(cfg->out); -} - -static int encoder_sink_read(config *cfg, heatshrink_encoder *hse, - uint8_t *data, size_t data_sz) { - size_t out_sz = 4096; - uint8_t out_buf[out_sz]; - memset(out_buf, 0, out_sz); - size_t sink_sz = 0; - size_t poll_sz = 0; - HSE_sink_res sres; - HSE_poll_res pres; - HSE_finish_res fres; - io_handle *out = cfg->out; - - size_t sunk = 0; - do { - if (data_sz > 0) { - sres = heatshrink_encoder_sink(hse, &data[sunk], data_sz - sunk, &sink_sz); - if (sres < 0) { die("sink"); } - sunk += sink_sz; - } - - do { - pres = heatshrink_encoder_poll(hse, out_buf, out_sz, &poll_sz); - if (pres < 0) { die("poll"); } - if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink"); - } while (pres == HSER_POLL_MORE); - - if (poll_sz == 0 && data_sz == 0) { - fres = heatshrink_encoder_finish(hse); - if (fres < 0) { die("finish"); } - if (fres == HSER_FINISH_DONE) { return 1; } - } - } while (sunk < data_sz); - return 0; -} - -static int encode(config *cfg) { - uint8_t window_sz2 = cfg->window_sz2; - size_t window_sz = 1 << window_sz2; - heatshrink_encoder *hse = heatshrink_encoder_alloc(window_sz2, cfg->lookahead_sz2); - if (hse == NULL) { die("failed to init encoder: bad settings"); } - ssize_t read_sz = 0; - io_handle *in = cfg->in; - - /* Process input until end of stream */ - while (1) { - uint8_t *input = NULL; - read_sz = handle_read(in, window_sz, &input); - if (input == NULL) { - printf("handle read failure\n"); - die("read"); - } - if (read_sz < 0) { die("read"); } - - /* Pass read to encoder and check if input is fully processed. */ - if (encoder_sink_read(cfg, hse, input, read_sz)) break; - - if (handle_drop(in, read_sz) < 0) { die("drop"); } - }; - - if (read_sz == -1) { err(1, "read"); } - - heatshrink_encoder_free(hse); - close_and_report(cfg); - return 0; -} - -static int decoder_sink_read(config *cfg, heatshrink_decoder *hsd, - uint8_t *data, size_t data_sz) { - io_handle *out = cfg->out; - size_t sink_sz = 0; - size_t poll_sz = 0; - size_t out_sz = 4096; - uint8_t out_buf[out_sz]; - memset(out_buf, 0, out_sz); - - HSD_sink_res sres; - HSD_poll_res pres; - HSD_finish_res fres; - - size_t sunk = 0; - do { - if (data_sz > 0) { - sres = heatshrink_decoder_sink(hsd, &data[sunk], data_sz - sunk, &sink_sz); - if (sres < 0) { die("sink"); } - sunk += sink_sz; - } - - do { - pres = heatshrink_decoder_poll(hsd, out_buf, out_sz, &poll_sz); - if (pres < 0) { die("poll"); } - if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink"); - } while (pres == HSDR_POLL_MORE); - - if (data_sz == 0 && poll_sz == 0) { - fres = heatshrink_decoder_finish(hsd); - if (fres < 0) { die("finish"); } - if (fres == HSDR_FINISH_DONE) { return 1; } - } - } while (sunk < data_sz); - - return 0; -} - -static int decode(config *cfg) { - uint8_t window_sz2 = cfg->window_sz2; - size_t window_sz = 1 << window_sz2; - size_t ibs = cfg->decoder_input_buffer_size; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(ibs, - window_sz2, cfg->lookahead_sz2); - if (hsd == NULL) { die("failed to init decoder"); } - - ssize_t read_sz = 0; - - io_handle *in = cfg->in; - - HSD_finish_res fres; - - /* Process input until end of stream */ - while (1) { - uint8_t *input = NULL; - read_sz = handle_read(in, window_sz, &input); - if (input == NULL) { - printf("handle read failure\n"); - die("read"); - } - if (read_sz == 0) { - fres = heatshrink_decoder_finish(hsd); - if (fres < 0) { die("finish"); } - if (fres == HSDR_FINISH_DONE) break; - } else if (read_sz < 0) { - die("read"); - } else { - if (decoder_sink_read(cfg, hsd, input, read_sz)) { break; } - if (handle_drop(in, read_sz) < 0) { die("drop"); } - } - } - if (read_sz == -1) { err(1, "read"); } - - heatshrink_decoder_free(hsd); - close_and_report(cfg); - return 0; -} - -static void report(config *cfg) { - size_t inb = cfg->in->total; - size_t outb = cfg->out->total; - fprintf(cfg->out->fd == STDOUT_FILENO ? stderr : stdout, - "%s %0.2f %%\t %zd -> %zd (-w %u -l %u)\n", - cfg->in_fname, 100.0 - (100.0 * outb) / inb, inb, outb, - cfg->window_sz2, cfg->lookahead_sz2); -} - -static void proc_args(config *cfg, int argc, char **argv) { - cfg->window_sz2 = DEF_WINDOW_SZ2; - cfg->lookahead_sz2 = DEF_LOOKAHEAD_SZ2; - cfg->buffer_size = DEF_BUFFER_SIZE; - cfg->decoder_input_buffer_size = DEF_DECODER_INPUT_BUFFER_SIZE; - cfg->cmd = OP_ENC; - cfg->verbose = 0; - cfg->in_fname = "-"; - cfg->out_fname = "-"; - - int a = 0; - while ((a = getopt(argc, argv, "hedi:w:l:v")) != -1) { - switch (a) { - case 'h': /* help */ - usage(); - case 'e': /* encode */ - cfg->cmd = OP_ENC; break; - case 'd': /* decode */ - cfg->cmd = OP_DEC; break; - case 'i': /* input buffer size */ - cfg->decoder_input_buffer_size = atoi(optarg); - break; - case 'w': /* window bits */ - cfg->window_sz2 = atoi(optarg); - break; - case 'l': /* lookahead bits */ - cfg->lookahead_sz2 = atoi(optarg); - break; - case 'v': /* verbosity++ */ - cfg->verbose++; - break; - case '?': /* unknown argument */ - default: - usage(); - } - } - argc -= optind; - argv += optind; - if (argc > 0) { - cfg->in_fname = argv[0]; - argc--; - argv++; - } - if (argc > 0) { cfg->out_fname = argv[0]; } -} - -int main(int argc, char **argv) { - config cfg; - memset(&cfg, 0, sizeof(cfg)); - proc_args(&cfg, argc, argv); - - if (0 == strcmp(cfg.in_fname, cfg.out_fname) - && (0 != strcmp("-", cfg.in_fname))) { - printf("Refusing to overwrite file '%s' with itself.\n", cfg.in_fname); - exit(1); - } - - cfg.in = handle_open(cfg.in_fname, IO_READ, cfg.buffer_size); - if (cfg.in == NULL) { die("Failed to open input file for read"); } - cfg.out = handle_open(cfg.out_fname, IO_WRITE, cfg.buffer_size); - if (cfg.out == NULL) { die("Failed to open output file for write"); } - - if (cfg.cmd == OP_ENC) { - return encode(&cfg); - } else if (cfg.cmd == OP_DEC) { - return decode(&cfg); - } else { - usage(); - } -} diff --git a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_common.h b/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_common.h deleted file mode 100644 index 0d396b9..0000000 --- a/Espressif/examples/esphttpd/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.3.1 */ -#define HEATSHRINK_VERSION_MAJOR 0 -#define HEATSHRINK_VERSION_MINOR 3 -#define HEATSHRINK_VERSION_PATCH 1 - -#define HEATSHRINK_MIN_WINDOW_BITS 4 -#define HEATSHRINK_MAX_WINDOW_BITS 15 - -#define HEATSHRINK_MIN_LOOKAHEAD_BITS 2 - -#define HEATSHRINK_LITERAL_MARKER 0x01 -#define HEATSHRINK_BACKREF_MARKER 0x00 - -#endif diff --git a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_config.h b/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_config.h deleted file mode 100644 index 51d4772..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_config.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef HEATSHRINK_CONFIG_H -#define HEATSHRINK_CONFIG_H - -/* Should functionality assuming dynamic allocation be used? */ -#define HEATSHRINK_DYNAMIC_ALLOC 1 - -#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 32 - #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/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_decoder.c b/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_decoder.c deleted file mode 100644 index b92be13..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_decoder.c +++ /dev/null @@ -1,382 +0,0 @@ -#include -#include -#include "heatshrink_decoder.h" - -/* States for the polling state machine. */ -typedef enum { - HSDS_EMPTY, /* no input to process */ - HSDS_INPUT_AVAILABLE, /* new input, completely unprocessed */ - 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 */ - HSDS_CHECK_FOR_MORE_INPUT, /* check if input is exhausted */ -} 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[] = { - "empty", - "input_available", - "yield_literal", - "backref_index", - "backref_count", - "yield_backref", - "check_for_more_input", -}; -#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 ((uint32_t)-1) - -/* Forward references. */ -static uint32_t get_bits(heatshrink_decoder *hsd, uint8_t count); -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, - uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (input_buffer_size == 0) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 > window_sz2)) { - return NULL; - } - size_t buffers_sz = (1 << window_sz2) + input_buffer_size; - size_t sz = sizeof(heatshrink_decoder) + buffers_sz; - heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); - if (hsd == NULL) { return NULL; } - hsd->input_buffer_size = input_buffer_size; - hsd->window_sz2 = window_sz2; - hsd->lookahead_sz2 = lookahead_sz2; - heatshrink_decoder_reset(hsd); - LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", - sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); - return hsd; -} - -void heatshrink_decoder_free(heatshrink_decoder *hsd) { - size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size; - size_t sz = sizeof(heatshrink_decoder) + buffers_sz; - HEATSHRINK_FREE(hsd, sz); - (void)sz; /* may not be used by free */ -} -#endif - -void heatshrink_decoder_reset(heatshrink_decoder *hsd) { - size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd); - size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd); - memset(hsd->buffers, 0, buf_sz + input_sz); - hsd->state = HSDS_EMPTY; - 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; - hsd->bit_accumulator = 0x00000000; -} - -/* 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; - if (hsd->state == HSDS_EMPTY) { - hsd->state = HSDS_INPUT_AVAILABLE; - hsd->input_index = 0; - } - *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_input_available(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); -static HSD_state st_check_for_input(heatshrink_decoder *hsd); - -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_EMPTY: - return HSDR_POLL_EMPTY; - case HSDS_INPUT_AVAILABLE: - hsd->state = st_input_available(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; - case HSDS_CHECK_FOR_MORE_INPUT: - hsd->state = st_check_for_input(hsd); - 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_input_available(heatshrink_decoder *hsd) { - uint32_t bits = get_bits(hsd, 1); // get tag bit - 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) { - uint32_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_CHECK_FOR_MORE_INPUT; - } 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); - uint32_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); - uint32_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); - uint32_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); - uint32_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) { - 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 <= 1 << BACKREF_COUNT_BITS(hsd)); - - for (size_t 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_CHECK_FOR_MORE_INPUT; } - } - return HSDS_YIELD_BACKREF; -} - -static HSD_state st_check_for_input(heatshrink_decoder *hsd) { - return (hsd->input_size == 0) ? HSDS_EMPTY : HSDS_INPUT_AVAILABLE; -} - -/* Get the next COUNT bits from the input buffer, saving incremental progress. - * Returns NO_BITS on end of input, or if more than 31 bits are requested. */ -static uint32_t get_bits(heatshrink_decoder *hsd, uint8_t count) { - if (count > 31) { 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 (int 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", - hsd->bit_accumulator, hsd->bit_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; - } - hsd->bit_accumulator <<= 1; - if (hsd->current_byte & hsd->bit_index) { - hsd->bit_accumulator |= 0x01; - if (0) { - LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", - hsd->bit_accumulator, hsd->bit_index); - } - } else { - if (0) { - LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", - hsd->bit_accumulator, hsd->bit_index); - } - } - hsd->bit_index >>= 1; - } - - uint32_t res = 0; - res = hsd->bit_accumulator; - hsd->bit_accumulator = 0x00000000; - if (count > 1) { LOG(" -- accumulated %08x\n", res); } - return res; -} - -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) { - if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; } - switch (hsd->state) { - case HSDS_EMPTY: - return HSDR_FINISH_DONE; - - /* 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/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_decoder.h b/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_decoder.h deleted file mode 100644 index c1eb144..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_decoder.h +++ /dev/null @@ -1,101 +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 */ - uint16_t bit_accumulator; - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of input */ - uint8_t bit_index; /* current bit index */ - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Fields that are only used if dynamically allocated. */ - uint8_t window_sz2; /* window buffer bits */ - uint8_t lookahead_sz2; /* lookahead bits */ - uint16_t input_buffer_size; /* input buffer size */ - - /* Input buffer, then expansion window buffer */ - uint8_t buffers[]; -#else - /* Input buffer, then expansion window buffer */ - uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) - + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; -#endif -} heatshrink_decoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, - * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead - * size of 2^lookahead_sz2. (The window buffer and lookahead sizes - * must match the settings used when the data was compressed.) - * Returns NULL on error. */ -heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, - uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); - -/* Free a decoder. */ -void heatshrink_decoder_free(heatshrink_decoder *hsd); -#endif - -/* Reset a decoder. */ -void heatshrink_decoder_reset(heatshrink_decoder *hsd); - -/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to - * indicate how many bytes were actually sunk (in case a buffer was filled). */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the dencoder that the input stream is finished. - * If the return value is HSDR_FINISH_MORE, there is still more output, so - * call heatshrink_decoder_poll and repeat. */ -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); - -#endif diff --git a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_encoder.c b/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_encoder.c deleted file mode 100644 index ede5f60..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_encoder.c +++ /dev/null @@ -1,650 +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, - FLAG_HAS_LITERAL = 0x02, - FLAG_ON_FINAL_LITERAL = 0x04, - FLAG_BACKLOG_IS_PARTIAL = 0x08, - FLAG_BACKLOG_IS_FILLED = 0x10, -}; - -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 int backlog_is_partial(heatshrink_encoder *hse); -static int backlog_is_filled(heatshrink_encoder *hse); -static int on_final_literal(heatshrink_encoder *hse); -static void save_backlog(heatshrink_encoder *hse); -static int has_literal(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 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) + buf_sz); - if (hse == NULL) { return NULL; } - hse->window_sz2 = window_sz2; - hse->lookahead_sz2 = lookahead_sz2; - 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) { - size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(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) + buf_sz); - (void)buf_sz; -} -#endif - -void heatshrink_encoder_reset(heatshrink_encoder *hse) { - size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - memset(hse->buffer, 0, buf_sz); - 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); - 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 ? 0 : lookahead_sz)) { - /* Current search buffer is exhausted, copy it into the - * backlog and await more input. */ - LOG("-- end of search @ %d, saving backlog\n", msi); - return HSES_SAVE_BACKLOG; - } - - uint16_t input_offset = get_input_offset(hse); - uint16_t end = input_offset + msi; - - uint16_t start = 0; - if (backlog_is_filled(hse)) { /* last WINDOW_LENGTH bytes */ - start = end - window_length + 1; - } else if (backlog_is_partial(hse)) { /* clamp to available data */ - start = end - window_length + 1; - if (start < lookahead_sz) { start = lookahead_sz; } - } else { /* only scan available input */ - start = input_offset; - } - - 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->flags |= FLAG_HAS_LITERAL; - 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 << hse->window_sz2 /*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); - hse->flags &= ~FLAG_HAS_LITERAL; - if (on_final_literal(hse)) { return HSES_FLUSH_BITS; } - return hse->match_length > 0 ? HSES_YIELD_TAG_BIT : 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) { - if (is_finishing(hse)) { - /* copy remaining literal (if necessary) */ - if (has_literal(hse)) { - hse->flags |= FLAG_ON_FINAL_LITERAL; - return HSES_YIELD_TAG_BIT; - } else { - return HSES_FLUSH_BITS; - } - } else { - 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); - uint16_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 backlog_is_partial(heatshrink_encoder *hse) { - return hse->flags & FLAG_BACKLOG_IS_PARTIAL; -} - -static int backlog_is_filled(heatshrink_encoder *hse) { - return hse->flags & FLAG_BACKLOG_IS_FILLED; -} - -static int on_final_literal(heatshrink_encoder *hse) { - return hse->flags & FLAG_ON_FINAL_LITERAL; -} - -static int has_literal(heatshrink_encoder *hse) { - return (hse->flags & FLAG_HAS_LITERAL); -} - -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; - const uint16_t break_even_point = 3; - 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 >= start) { - 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 >= start; 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 - - if (match_maxlen >= break_even_point) { - 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); - - if (backlog_is_partial(hse)) { - /* The whole backlog is filled in now, so include it in scans. */ - hse->flags |= FLAG_BACKLOG_IS_FILLED; - } else { - /* Include backlog, except for the first lookahead_sz bytes, which - * are still undefined. */ - hse->flags |= FLAG_BACKLOG_IS_PARTIAL; - } - hse->match_scan_index = 0; - hse->input_size -= input_buf_sz - rem; -} diff --git a/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_encoder.h b/Espressif/examples/esphttpd/lib/heatshrink/heatshrink_encoder.h deleted file mode 100644 index 18c1773..0000000 --- a/Espressif/examples/esphttpd/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 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/Espressif/examples/esphttpd/lib/heatshrink/test_heatshrink_dynamic.c b/Espressif/examples/esphttpd/lib/heatshrink/test_heatshrink_dynamic.c deleted file mode 100644 index 1e18a69..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/test_heatshrink_dynamic.c +++ /dev/null @@ -1,999 +0,0 @@ -#include -#include -#include - -#include "heatshrink_encoder.h" -#include "heatshrink_decoder.h" -#include "greatest.h" - -#if !HEATSHRINK_DYNAMIC_ALLOC -#error Must set HEATSHRINK_DYNAMIC_ALLOC to 1 for dynamic allocation test suite. -#endif - -SUITE(encoding); -SUITE(decoding); -SUITE(integration); - -#ifdef HEATSHRINK_HAS_THEFT -SUITE(properties); -#endif - -static void dump_buf(char *name, uint8_t *buf, uint16_t count) { - for (int i=0; iinput_size, 6); - ASSERT_EQ(hsd->input_index, 0); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_return_empty_if_empty(void) { - uint8_t output[256]; - size_t out_sz = 0; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, - HEATSHRINK_MIN_WINDOW_BITS, 4); - HSD_poll_res res = heatshrink_decoder_poll(hsd, output, 256, &out_sz); - ASSERT_EQ(HSDR_POLL_EMPTY, res); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_reject_null_hsd(void) { - uint8_t output[256]; - size_t out_sz = 0; - HSD_poll_res res = heatshrink_decoder_poll(NULL, output, 256, &out_sz); - ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); - PASS(); -} - -TEST decoder_poll_should_reject_null_output_buffer(void) { - size_t out_sz = 0; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, - HEATSHRINK_MIN_WINDOW_BITS, 4); - HSD_poll_res res = heatshrink_decoder_poll(hsd, NULL, 256, &out_sz); - ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_reject_null_output_size_pointer(void) { - uint8_t output[256]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, - HEATSHRINK_MIN_WINDOW_BITS, 4); - HSD_poll_res res = heatshrink_decoder_poll(hsd, output, 256, NULL); - ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_expand_short_literal(void) { - uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0 }; //"foo" - uint8_t output[4]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 3); - size_t count = 0; - - HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); - ASSERT_EQ(HSDR_SINK_OK, sres); - - size_t out_sz = 0; - HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, 4, &out_sz); - ASSERT_EQ(HSDR_POLL_EMPTY, pres); - ASSERT_EQ(3, out_sz); - ASSERT_EQ('f', output[0]); - ASSERT_EQ('o', output[1]); - ASSERT_EQ('o', output[2]); - - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_expand_short_literal_and_backref(void) { - uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0, 0x40, 0x80}; //"foofoo" - uint8_t output[6]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 7); - memset(output, 0, sizeof(*output)); - size_t count = 0; - - HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); - ASSERT_EQ(HSDR_SINK_OK, sres); - - size_t out_sz = 0; - (void)heatshrink_decoder_poll(hsd, output, 6, &out_sz); - - if (0) dump_buf("output", output, out_sz); - ASSERT_EQ(6, out_sz); - ASSERT_EQ('f', output[0]); - ASSERT_EQ('o', output[1]); - ASSERT_EQ('o', output[2]); - ASSERT_EQ('f', output[3]); - ASSERT_EQ('o', output[4]); - ASSERT_EQ('o', output[5]); - - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_expand_short_self_overlapping_backref(void) { - /* "aaaaa" == (literal, 1), ('a'), (backref, 1 back, 4 bytes) */ - uint8_t input[] = {0xb0, 0x80, 0x01, 0x80}; - uint8_t output[6]; - uint8_t expected[] = {'a', 'a', 'a', 'a', 'a'}; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 7); - size_t count = 0; - - HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); - ASSERT_EQ(HSDR_SINK_OK, sres); - - size_t out_sz = 0; - (void)heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz); - - if (0) dump_buf("output", output, out_sz); - ASSERT_EQ(sizeof(expected), out_sz); - for (size_t i=0; iwindow_sz2, - cfg->lookahead_sz2); - heatshrink_decoder *hsd = heatshrink_decoder_alloc(cfg->decoder_input_buffer_size, - cfg->window_sz2, cfg->lookahead_sz2); - size_t comp_sz = input_size + (input_size/2) + 4; - size_t decomp_sz = input_size + (input_size/2) + 4; - uint8_t *comp = malloc(comp_sz); - uint8_t *decomp = malloc(decomp_sz); - if (comp == NULL) FAILm("malloc fail"); - if (decomp == NULL) FAILm("malloc fail"); - memset(comp, 0, comp_sz); - memset(decomp, 0, decomp_sz); - - size_t count = 0; - - if (cfg->log_lvl > 1) { - printf("\n^^ COMPRESSING\n"); - dump_buf("input", input, input_size); - } - - size_t sunk = 0; - size_t polled = 0; - while (sunk < input_size) { - ASSERT(heatshrink_encoder_sink(hse, &input[sunk], input_size - sunk, &count) >= 0); - sunk += count; - if (cfg->log_lvl > 1) printf("^^ sunk %zd\n", count); - if (sunk == input_size) { - ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); - } - - HSE_poll_res pres; - do { /* "turn the crank" */ - pres = heatshrink_encoder_poll(hse, &comp[polled], comp_sz - polled, &count); - ASSERT(pres >= 0); - polled += count; - if (cfg->log_lvl > 1) printf("^^ polled %zd\n", count); - } while (pres == HSER_POLL_MORE); - ASSERT_EQ(HSER_POLL_EMPTY, pres); - if (polled >= comp_sz) FAILm("compression should never expand that much"); - if (sunk == input_size) { - ASSERT_EQ(HSER_FINISH_DONE, heatshrink_encoder_finish(hse)); - } - } - if (cfg->log_lvl > 0) printf("in: %u compressed: %zu ", input_size, polled); - size_t compressed_size = polled; - sunk = 0; - polled = 0; - - if (cfg->log_lvl > 1) { - printf("\n^^ DECOMPRESSING\n"); - dump_buf("comp", comp, compressed_size); - } - while (sunk < compressed_size) { - ASSERT(heatshrink_decoder_sink(hsd, &comp[sunk], compressed_size - sunk, &count) >= 0); - sunk += count; - if (cfg->log_lvl > 1) printf("^^ sunk %zd\n", count); - if (sunk == compressed_size) { - ASSERT_EQ(HSDR_FINISH_MORE, heatshrink_decoder_finish(hsd)); - } - - HSD_poll_res pres; - do { - pres = heatshrink_decoder_poll(hsd, &decomp[polled], - decomp_sz - polled, &count); - ASSERT(pres >= 0); - ASSERT(count > 0); - polled += count; - if (cfg->log_lvl > 1) printf("^^ polled %zd\n", count); - } while (pres == HSDR_POLL_MORE); - ASSERT_EQ(HSDR_POLL_EMPTY, pres); - if (sunk == compressed_size) { - HSD_finish_res fres = heatshrink_decoder_finish(hsd); - ASSERT_EQ(HSDR_FINISH_DONE, fres); - } - - if (polled > input_size) { - printf("\nExpected %zd, got %zu\n", (size_t)input_size, polled); - FAILm("Decompressed data is larger than original input"); - } - } - if (cfg->log_lvl > 0) printf("decompressed: %zu\n", polled); - if (polled != input_size) { - FAILm("Decompressed length does not match original input length"); - } - - if (cfg->log_lvl > 1) dump_buf("decomp", decomp, polled); - for (uint32_t i=0; i out[%d] == 0x%02x ('%c') %c\n", - j, input[j], isprint(input[j]) ? input[j] : '.', - j, decomp[j], isprint(decomp[j]) ? decomp[j] : '.', - input[j] == decomp[j] ? ' ' : 'X'); - } - } - } - ASSERT_EQ(input[i], decomp[i]); - } - free(comp); - free(decomp); - heatshrink_encoder_free(hse); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST data_without_duplication_should_match(void) { - uint8_t input[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', - 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', - 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 8; - cfg.lookahead_sz2 = 3; - cfg.decoder_input_buffer_size = 256; - return compress_and_expand_and_check(input, sizeof(input), &cfg); -} - -TEST data_with_simple_repetition_should_compress_and_decompress_properly(void) { - uint8_t input[] = {'a', 'b', 'c', 'a', 'b', 'c', 'd', 'a', 'b', - 'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e', 'f', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'a', 'b', - 'c', 'd', 'e', 'f', 'g', 'h'}; - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 8; - cfg.lookahead_sz2 = 3; - cfg.decoder_input_buffer_size = 256; - return compress_and_expand_and_check(input, sizeof(input), &cfg); -} - -TEST data_without_duplication_should_match_with_absurdly_tiny_buffers(void) { - heatshrink_encoder *hse = heatshrink_encoder_alloc(8, 3); - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 3); - uint8_t input[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', - 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', - 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; - uint8_t comp[60]; - uint8_t decomp[60]; - size_t count = 0; - int log = 0; - - if (log) dump_buf("input", input, sizeof(input)); - for (uint32_t i=0; i= 0); - } - ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); - - size_t packed_count = 0; - do { - ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0); - packed_count += count; - } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE); - - if (log) dump_buf("comp", comp, packed_count); - for (uint32_t i=0; i= 0); - } - - for (uint32_t i=0; i= 0); - } - - if (log) dump_buf("decomp", decomp, sizeof(input)); - for (uint32_t i=0; i= 0); - } - ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); - - size_t packed_count = 0; - do { - ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0); - packed_count += count; - } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE); - - if (log) dump_buf("comp", comp, packed_count); - for (uint32_t i=0; i= 0); - } - - for (uint32_t i=0; i= 0); - } - - if (log) dump_buf("decomp", decomp, sizeof(input)); - for (uint32_t i=0; ilog_lvl > 0) { - printf("\n-- size %u, seed %u, input buf %zu\n", - size, seed, cfg->decoder_input_buffer_size); - } - fill_with_pseudorandom_letters(input, size, seed); - return compress_and_expand_and_check(input, size, cfg); -} - -TEST small_input_buffer_should_not_impact_decoder_correctness(void) { - int size = 5; - uint8_t input[size]; - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 8; - cfg.lookahead_sz2 = 3; - cfg.decoder_input_buffer_size = 5; - for (uint16_t i=0; i= 19901L - printf("\n\nFuzzing (single-byte sizes):\n"); - for (uint8_t lsize=3; lsize < 8; lsize++) { - for (uint32_t size=1; size < 128*1024L; size <<= 1) { - if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); - for (uint16_t ibs=32; ibs<=8192; ibs <<= 1) { /* input buffer size */ - if (GREATEST_IS_VERBOSE()) printf(" -- input buffer %u\n", ibs); - for (uint32_t seed=1; seed<=10; seed++) { - if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 8; - cfg.lookahead_sz2 = lsize; - cfg.decoder_input_buffer_size = ibs; - RUN_TESTp(pseudorandom_data_should_match, size, seed, &cfg); - } - } - } - } - - printf("\nFuzzing (multi-byte sizes):\n"); - for (uint8_t lsize=6; lsize < 9; lsize++) { - for (uint32_t size=1; size < 128*1024L; size <<= 1) { - if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); - for (uint16_t ibs=32; ibs<=8192; ibs <<= 1) { /* input buffer size */ - if (GREATEST_IS_VERBOSE()) printf(" -- input buffer %u\n", ibs); - for (uint32_t seed=1; seed<=10; seed++) { - if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 11; - cfg.lookahead_sz2 = lsize; - cfg.decoder_input_buffer_size = ibs; - RUN_TESTp(pseudorandom_data_should_match, size, seed, &cfg); - } - } - } - } - -#endif -} - -/* Add all the definitions that need to be in the test runner's main file. */ -GREATEST_MAIN_DEFS(); - -int main(int argc, char **argv) { - GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ - RUN_SUITE(encoding); - RUN_SUITE(decoding); - RUN_SUITE(integration); - #ifdef HEATSHRINK_HAS_THEFT - RUN_SUITE(properties); - #endif - GREATEST_MAIN_END(); /* display results */ -} diff --git a/Espressif/examples/esphttpd/lib/heatshrink/test_heatshrink_dynamic_theft.c b/Espressif/examples/esphttpd/lib/heatshrink/test_heatshrink_dynamic_theft.c deleted file mode 100644 index 2752886..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/test_heatshrink_dynamic_theft.c +++ /dev/null @@ -1,521 +0,0 @@ -#include "heatshrink_config.h" -#ifdef HEATSHRINK_HAS_THEFT - -#include -#include -#include -#include -#include - -#include "heatshrink_encoder.h" -#include "heatshrink_decoder.h" -#include "greatest.h" -#include "theft.h" -#include "greatest_theft.h" - -#if !HEATSHRINK_DYNAMIC_ALLOC -#error Must set HEATSHRINK_DYNAMIC_ALLOC to 1 for this test suite. -#endif - -SUITE(properties); - -typedef struct { - int limit; - int fails; - int dots; -} test_env; - -typedef struct { - size_t size; - uint8_t buf[]; -} rbuf; - -static void *rbuf_alloc_cb(struct theft *t, theft_hash seed, void *env) { - test_env *te = (test_env *)env; - //printf("seed is 0x%016llx\n", seed); - - size_t sz = (size_t)(seed % te->limit) + 1; - rbuf *r = malloc(sizeof(rbuf) + sz); - if (r == NULL) { return THEFT_ERROR; } - r->size = sz; - - for (size_t i = 0; i < sz; i += sizeof(theft_hash)) { - theft_hash s = theft_random(t); - for (uint8_t b = 0; b < sizeof(theft_hash); b++) { - if (i + b >= sz) { break; } - r->buf[i + b] = (uint8_t) (s >> (8*b)) & 0xff; - } - } - - return r; -} - -static void rbuf_free_cb(void *instance, void *env) { - free(instance); - (void)env; -} - -static uint64_t rbuf_hash_cb(void *instance, void *env) { - rbuf *r = (rbuf *)instance; - (void)env; - return theft_hash_onepass(r->buf, r->size); -} - -/* Make a copy of a buffer, keeping NEW_SZ bytes starting at OFFSET. */ -static void *copy_rbuf_subset(rbuf *cur, size_t new_sz, size_t byte_offset) { - if (new_sz == 0) { return THEFT_DEAD_END; } - rbuf *nr = malloc(sizeof(rbuf) + new_sz); - if (nr == NULL) { return THEFT_ERROR; } - nr->size = new_sz; - memcpy(nr->buf, &cur->buf[byte_offset], new_sz); - /* printf("%zu -> %zu\n", cur->size, new_sz); */ - return nr; -} - -/* Make a copy of a buffer, but only PORTION, starting OFFSET in - * (e.g. the third quarter is (0.25 at +0.75). Rounds to ints. */ -static void *copy_rbuf_percent(rbuf *cur, float portion, float offset) { - size_t new_sz = cur->size * portion; - size_t byte_offset = (size_t)(cur->size * offset); - return copy_rbuf_subset(cur, new_sz, byte_offset); -} - -/* How to shrink a random buffer to a simpler one. */ -static void *rbuf_shrink_cb(void *instance, uint32_t tactic, void *env) { - rbuf *cur = (rbuf *)instance; - - if (tactic == 0) { /* first half */ - return copy_rbuf_percent(cur, 0.5, 0); - } else if (tactic == 1) { /* second half */ - return copy_rbuf_percent(cur, 0.5, 0.5); - } else if (tactic <= 18) { /* drop 1-16 bytes at start */ - const int last_tactic = 1; - const size_t drop = tactic - last_tactic; - if (cur->size < drop) { return THEFT_DEAD_END; } - return copy_rbuf_subset(cur, cur->size - drop, drop); - } else if (tactic <= 34) { /* drop 1-16 bytes at end */ - const int last_tactic = 18; - const size_t drop = tactic - last_tactic; - if (cur->size < drop) { return THEFT_DEAD_END; } - return copy_rbuf_subset(cur, cur->size - drop, 0); - } else if (tactic == 35) { - /* Divide every byte by 2, saturating at 0 */ - rbuf *cp = copy_rbuf_percent(cur, 1, 0); - if (cp == NULL) { return THEFT_ERROR; } - for (size_t i = 0; i < cp->size; i++) { cp->buf[i] /= 2; } - return cp; - } else if (tactic == 36) { - /* subtract 1 from every byte, saturating at 0 */ - rbuf *cp = copy_rbuf_percent(cur, 1, 0); - if (cp == NULL) { return THEFT_ERROR; } - for (size_t i = 0; i < cp->size; i++) { - if (cp->buf[i] > 0) { cp->buf[i]--; } - } - return cp; - } else { - (void)env; - return THEFT_NO_MORE_TACTICS; - } - - return THEFT_NO_MORE_TACTICS; -} - -static void rbuf_print_cb(FILE *f, void *instance, void *env) { - rbuf *r = (rbuf *)instance; - (void)env; - fprintf(f, "buf[%zd]:\n ", r->size); - uint8_t bytes = 0; - for (size_t i = 0; i < r->size; i++) { - fprintf(f, "%02x", r->buf[i]); - bytes++; - if (bytes == 16) { - fprintf(f, "\n "); - bytes = 0; - } - } - fprintf(f, "\n"); -} - -static struct theft_type_info rbuf_info = { - .alloc = rbuf_alloc_cb, - .free = rbuf_free_cb, - .hash = rbuf_hash_cb, - .shrink = rbuf_shrink_cb, - .print = rbuf_print_cb, -}; - -static theft_progress_callback_res -progress_cb(struct theft_trial_info *info, void *env) { - test_env *te = (test_env *)env; - if ((info->trial & 0xff) == 0) { - printf("."); - fflush(stdout); - te->dots++; - if (te->dots == 64) { - printf("\n"); - te->dots = 0; - } - } - - if (info->status == THEFT_TRIAL_FAIL) { - te->fails++; - rbuf *cur = info->args[0]; - if (cur->size < 5) { return THEFT_PROGRESS_HALT; } - } - - if (te->fails > 10) { - return THEFT_PROGRESS_HALT; - } - return THEFT_PROGRESS_CONTINUE; -} - -/* For an arbitrary input buffer, it should never get stuck in a - * state where the data has been sunk but no data can be polled. */ -static theft_trial_res prop_should_not_get_stuck(void *input) { - /* Make a buffer large enough for the output: 4 KB of input with - * each 16 bits becoming up to 16 bytes will fit in a 64 KB buffer. - * (4 KB of input comes from `env.limit = 1 << 12;` below.) */ - uint8_t output[64 * 1024]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc((64 * 1024L) - 1, 12, 4); - if (hsd == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)input; - - size_t count = 0; - HSD_sink_res sres = heatshrink_decoder_sink(hsd, r->buf, r->size, &count); - if (sres != HSDR_SINK_OK) { return THEFT_TRIAL_ERROR; } - - size_t out_sz = 0; - HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz); - if (pres != HSDR_POLL_EMPTY) { return THEFT_TRIAL_FAIL; } - - HSD_finish_res fres = heatshrink_decoder_finish(hsd); - heatshrink_decoder_free(hsd); - if (fres != HSDR_FINISH_DONE) { return THEFT_TRIAL_FAIL; } - - return THEFT_TRIAL_PASS; -} - -static bool get_time_seed(theft_seed *seed) -{ - struct timeval tv; - if (-1 == gettimeofday(&tv, NULL)) { return false; } - *seed = (theft_seed)((tv.tv_sec << 32) | tv.tv_usec); - /* printf("seed is 0x%016llx\n", *seed); */ - return true; -} - -TEST decoder_fuzzing_should_not_detect_stuck_state(void) { - // Get a random number seed based on the time - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - /* Pass the max buffer size for this property (4 KB) in a closure */ - test_env env = { .limit = 1 << 12 }; - - theft_seed always_seeds = { 0xe87bb1f61032a061 }; - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_should_not_get_stuck, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 100000, - .progress_cb = progress_cb, - .env = &env, - - .always_seeds = &always_seeds, - .always_seed_count = 1, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - GREATEST_ASSERT_EQm("should_not_get_stuck", THEFT_RUN_PASS, res); - PASS(); -} - -static theft_trial_res prop_encoded_and_decoded_data_should_match(void *input) { - uint8_t e_output[64 * 1024]; - uint8_t d_output[64 * 1024]; - heatshrink_encoder *hse = heatshrink_encoder_alloc(12, 4); - if (hse == NULL) { return THEFT_TRIAL_ERROR; } - heatshrink_decoder *hsd = heatshrink_decoder_alloc(4096, 12, 4); - if (hsd == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)input; - - size_t e_input_size = 0; - HSE_sink_res esres = heatshrink_encoder_sink(hse, - r->buf, r->size, &e_input_size); - if (esres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } - if (e_input_size != r->size) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - HSE_finish_res efres = heatshrink_encoder_finish(hse); - if (efres != HSER_FINISH_MORE) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - size_t e_output_size = 0; - HSE_poll_res epres = heatshrink_encoder_poll(hse, - e_output, sizeof(e_output), &e_output_size); - if (epres != HSER_POLL_EMPTY) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - size_t count = 0; - HSD_sink_res sres = heatshrink_decoder_sink(hsd, e_output, e_output_size, &count); - if (sres != HSDR_SINK_OK) { return THEFT_TRIAL_ERROR; } - - size_t d_output_size = 0; - HSD_poll_res pres = heatshrink_decoder_poll(hsd, d_output, - sizeof(d_output), &d_output_size); - if (pres != HSDR_POLL_EMPTY) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - if (d_output_size != r->size) { - printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; - } - - if (0 != memcmp(d_output, r->buf, d_output_size)) { - return THEFT_TRIAL_FAIL; - } - - HSD_finish_res fres = heatshrink_decoder_finish(hsd); - if (fres != HSDR_FINISH_DONE) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - heatshrink_encoder_free(hse); - heatshrink_decoder_free(hsd); - return THEFT_TRIAL_PASS; -} - - -TEST encoded_and_decoded_data_should_match(void) { - test_env env = { .limit = 1 << 11 }; - - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_encoded_and_decoded_data_should_match, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 1000000, - .env = &env, - .progress_cb = progress_cb, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - ASSERT_EQ(THEFT_RUN_PASS, res); - PASS(); -} - -static size_t ceil_nine_eighths(size_t sz) { - return sz + sz/8 + (sz & 0x07 ? 1 : 0); -} - -static theft_trial_res -prop_encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst(void *input) { - uint8_t output[32 * 1024]; - heatshrink_encoder *hse = heatshrink_encoder_alloc(12, 4); - if (hse == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)input; - - size_t input_size = 0; - HSE_sink_res esres = heatshrink_encoder_sink(hse, - r->buf, r->size, &input_size); - if (esres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } - /* Assumes data fits in one sink, failure here means buffer must be larger. */ - if (input_size != r->size) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - HSE_finish_res efres = heatshrink_encoder_finish(hse); - if (efres != HSER_FINISH_MORE) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - size_t output_size = 0; - HSE_poll_res epres = heatshrink_encoder_poll(hse, - output, sizeof(output), &output_size); - if (epres != HSER_POLL_EMPTY) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - size_t ceil_9_8s = ceil_nine_eighths(r->size); - if (output_size > ceil_9_8s) { - return THEFT_TRIAL_FAIL; - } - - heatshrink_encoder_free(hse); - return THEFT_TRIAL_PASS; -} - -TEST encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst(void) { - test_env env = { .limit = 1 << 11 }; - - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 10000, - .env = &env, - .progress_cb = progress_cb, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - ASSERT_EQ(THEFT_RUN_PASS, res); - PASS(); -} - -static theft_trial_res -prop_encoder_should_always_make_progress(void *instance) { - uint8_t output[64 * 1024]; - heatshrink_encoder *hse = heatshrink_encoder_alloc(8, 4); - if (hse == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)instance; - - size_t sunk = 0; - int no_progress = 0; - - while (1) { - if (sunk < r->size) { - size_t input_size = 0; - HSE_sink_res esres = heatshrink_encoder_sink(hse, - &r->buf[sunk], r->size - sunk, &input_size); - if (esres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } - sunk += input_size; - } else { - HSE_finish_res efres = heatshrink_encoder_finish(hse); - if (efres == HSER_FINISH_DONE) { - break; - } else if (efres != HSER_FINISH_MORE) { - printf("FAIL %d\n", __LINE__); - return THEFT_TRIAL_FAIL; - } - } - - size_t output_size = 0; - HSE_poll_res epres = heatshrink_encoder_poll(hse, - output, sizeof(output), &output_size); - if (epres < 0) { return THEFT_TRIAL_ERROR; } - if (output_size == 0 && sunk == r->size) { - no_progress++; - if (no_progress > 2) { - return THEFT_TRIAL_FAIL; - } - } else { - no_progress = 0; - } - } - - heatshrink_encoder_free(hse); - return THEFT_TRIAL_PASS; -} - -TEST encoder_should_always_make_progress(void) { - test_env env = { .limit = 1 << 15 }; - - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_encoder_should_always_make_progress, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 10000, - .env = &env, - .progress_cb = progress_cb, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - ASSERT_EQ(THEFT_RUN_PASS, res); - PASS(); -} - -static theft_trial_res -prop_decoder_should_always_make_progress(void *instance) { - uint8_t output[64 * 1024]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(512, 8, 4); - if (hsd == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)instance; - - size_t sunk = 0; - int no_progress = 0; - - while (1) { - if (sunk < r->size) { - size_t input_size = 0; - HSD_sink_res sres = heatshrink_decoder_sink(hsd, - &r->buf[sunk], r->size - sunk, &input_size); - if (sres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } - sunk += input_size; - } else { - HSD_finish_res fres = heatshrink_decoder_finish(hsd); - if (fres == HSDR_FINISH_DONE) { - break; - } else if (fres != HSDR_FINISH_MORE) { - printf("FAIL %d\n", __LINE__); - return THEFT_TRIAL_FAIL; - } - } - - size_t output_size = 0; - HSD_poll_res pres = heatshrink_decoder_poll(hsd, - output, sizeof(output), &output_size); - if (pres < 0) { return THEFT_TRIAL_ERROR; } - if (output_size == 0 && sunk == r->size) { - no_progress++; - if (no_progress > 2) { - return THEFT_TRIAL_FAIL; - } - } else { - no_progress = 0; - } - } - - heatshrink_decoder_free(hsd); - return THEFT_TRIAL_PASS; -} - -TEST decoder_should_always_make_progress(void) { - test_env env = { .limit = 1 << 15 }; - - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_decoder_should_always_make_progress, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 10000, - .env = &env, - .progress_cb = progress_cb, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - ASSERT_EQ(THEFT_RUN_PASS, res); - PASS(); -} - -SUITE(properties) { - RUN_TEST(decoder_fuzzing_should_not_detect_stuck_state); - RUN_TEST(encoded_and_decoded_data_should_match); - RUN_TEST(encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst); - RUN_TEST(encoder_should_always_make_progress); - RUN_TEST(decoder_should_always_make_progress); -} -#else -struct because_iso_c_requires_at_least_one_declaration; -#endif diff --git a/Espressif/examples/esphttpd/lib/heatshrink/test_heatshrink_static.c b/Espressif/examples/esphttpd/lib/heatshrink/test_heatshrink_static.c deleted file mode 100644 index e9c9754..0000000 --- a/Espressif/examples/esphttpd/lib/heatshrink/test_heatshrink_static.c +++ /dev/null @@ -1,167 +0,0 @@ -#include -#include - -#include "heatshrink_encoder.h" -#include "heatshrink_decoder.h" -#include "greatest.h" - -#if HEATSHRINK_DYNAMIC_ALLOC -#error HEATSHRINK_DYNAMIC_ALLOC must be false for static allocation test suite. -#endif - -SUITE(integration); - -/* The majority of the tests are in test_heatshrink_dynamic, because that allows - * instantiating encoders/decoders with different settings at run-time. */ - -static heatshrink_encoder hse; -static heatshrink_decoder hsd; - -static void fill_with_pseudorandom_letters(uint8_t *buf, uint16_t size, uint32_t seed) { - uint64_t rn = 9223372036854775783; /* prime under 2^64 */ - for (int i=0; i 1) { - printf("\n^^ COMPRESSING\n"); - dump_buf("input", input, input_size); - } - - uint32_t sunk = 0; - uint32_t polled = 0; - while (sunk < input_size) { - ASSERT(heatshrink_encoder_sink(&hse, &input[sunk], input_size - sunk, &count) >= 0); - sunk += count; - if (log_lvl > 1) printf("^^ sunk %zd\n", count); - if (sunk == input_size) { - ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(&hse)); - } - - HSE_poll_res pres; - do { /* "turn the crank" */ - pres = heatshrink_encoder_poll(&hse, &comp[polled], comp_sz - polled, &count); - ASSERT(pres >= 0); - polled += count; - if (log_lvl > 1) printf("^^ polled %zd\n", count); - } while (pres == HSER_POLL_MORE); - ASSERT_EQ(HSER_POLL_EMPTY, pres); - if (polled >= comp_sz) FAILm("compression should never expand that much"); - if (sunk == input_size) { - ASSERT_EQ(HSER_FINISH_DONE, heatshrink_encoder_finish(&hse)); - } - } - if (log_lvl > 0) printf("in: %u compressed: %u ", input_size, polled); - uint32_t compressed_size = polled; - sunk = 0; - polled = 0; - - if (log_lvl > 1) { - printf("\n^^ DECOMPRESSING\n"); - dump_buf("comp", comp, compressed_size); - } - while (sunk < compressed_size) { - ASSERT(heatshrink_decoder_sink(&hsd, &comp[sunk], compressed_size - sunk, &count) >= 0); - sunk += count; - if (log_lvl > 1) printf("^^ sunk %zd\n", count); - if (sunk == compressed_size) { - ASSERT_EQ(HSDR_FINISH_MORE, heatshrink_decoder_finish(&hsd)); - } - - HSD_poll_res pres; - do { - pres = heatshrink_decoder_poll(&hsd, &decomp[polled], - decomp_sz - polled, &count); - ASSERT(pres >= 0); - polled += count; - if (log_lvl > 1) printf("^^ polled %zd\n", count); - } while (pres == HSDR_POLL_MORE); - ASSERT_EQ(HSDR_POLL_EMPTY, pres); - if (sunk == compressed_size) { - HSD_finish_res fres = heatshrink_decoder_finish(&hsd); - ASSERT_EQ(HSDR_FINISH_DONE, fres); - } - - if (polled > input_size) { - FAILm("Decompressed data is larger than original input"); - } - } - if (log_lvl > 0) printf("decompressed: %u\n", polled); - if (polled != input_size) { - FAILm("Decompressed length does not match original input length"); - } - - if (log_lvl > 1) dump_buf("decomp", decomp, polled); - for (size_t i=0; i out[%zd] == 0x%02x ('%c')\n", - j, input[j], isprint(input[j]) ? input[j] : '.', - j, decomp[j], isprint(decomp[j]) ? decomp[j] : '.'); - } - } - } - ASSERT_EQ(input[i], decomp[i]); - } - free(comp); - free(decomp); - PASS(); -} - -TEST pseudorandom_data_should_match(uint32_t size, uint32_t seed) { - uint8_t input[size]; - fill_with_pseudorandom_letters(input, size, seed); - return compress_and_expand_and_check(input, size, 0); -} - -SUITE(integration) { -#if __STDC_VERSION__ >= 19901L - for (uint32_t size=1; size < 64*1024; size <<= 1) { - if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); - for (uint32_t seed=1; seed<=100; seed++) { - if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); - RUN_TESTp(pseudorandom_data_should_match, size, seed); - } - } -#endif -} - -/* Add all the definitions that need to be in the test runner's main file. */ -GREATEST_MAIN_DEFS(); - -int main(int argc, char **argv) { - GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ - printf("INPUT_BUFFER_SIZE: %u\n", HEATSHRINK_STATIC_INPUT_BUFFER_SIZE); - printf("WINDOW_BITS: %u\n", HEATSHRINK_STATIC_WINDOW_BITS); - printf("LOOKAHEAD_BITS: %u\n", HEATSHRINK_STATIC_LOOKAHEAD_BITS); - - printf("sizeof(heatshrink_encoder): %zd\n", sizeof(heatshrink_encoder)); - printf("sizeof(heatshrink_decoder): %zd\n", sizeof(heatshrink_decoder)); - RUN_SUITE(integration); - GREATEST_MAIN_END(); /* display results */ -} diff --git a/Espressif/examples/esphttpd/libesphttpd/core/base64.c b/Espressif/examples/esphttpd/libesphttpd/core/base64.c index 9d97a26..259bbb2 100644 --- a/Espressif/examples/esphttpd/libesphttpd/core/base64.c +++ b/Espressif/examples/esphttpd/libesphttpd/core/base64.c @@ -66,22 +66,20 @@ int ICACHE_FLASH_ATTR base64_decode(size_t in_len, const char *in, size_t out_le return io; } -//Only need decode functions for now. -#if 0 - static const uint8_t base64enc_tab[64]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#if 0 void base64encode(const unsigned char in[3], unsigned char out[4], int count) { out[0]=base64enc_tab[(in[0]>>2)]; out[1]=base64enc_tab[((in[0]&3)<<4)|(in[1]>>4)]; out[2]=count<2 ? '=' : base64enc_tab[((in[1]&15)<<2)|(in[2]>>6)]; out[3]=count<3 ? '=' : base64enc_tab[(in[2]&63)]; } +#endif - -int base64_encode(size_t in_len, const unsigned char *in, size_t out_len, char *out) { +int ICACHE_FLASH_ATTR base64_encode(size_t in_len, const unsigned char *in, size_t out_len, char *out) { unsigned ii, io; - uint_least32_t v; + uint32_t v; unsigned rem; for(io=0,ii=0,v=0,rem=0;iiproto.tcp->remote_port && + os_memcmp(connData[i].remote_ip, espconn->proto.tcp->remote_ip, 4) == 0) { + if (arg != connData[i].conn) connData[i].conn = arg; // yes, this happens!? + return &connData[i]; + } } //Shouldn't happen. - os_printf("FindConnData: Huh? Couldn't find connection for %p\n", arg); + os_printf("*** Unknown connection 0x%p\n", arg); return NULL; } + //Retires a connection for re-use static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) { if (conn->post->buff!=NULL) os_free(conn->post->buff); conn->post->buff=NULL; conn->cgi=NULL; conn->conn=NULL; + conn->remote_port=0; + os_memset(conn->remote_ip, 0, 4); } //Stupid li'l helper function that returns the value of a hex char. -static int httpdHexVal(char c) { +static int ICACHE_FLASH_ATTR httpdHexVal(char c) { if (c>='0' && c<='9') return c-'0'; if (c>='A' && c<='F') return c-'A'+10; if (c>='a' && c<='f') return c-'a'+10; @@ -110,7 +117,7 @@ static int httpdHexVal(char c) { //Takes the valLen bytes stored in val, and converts it into at most retLen bytes that //are stored in the ret buffer. Returns the actual amount of bytes used in ret. Also //zero-terminates the ret buffer. -int httpdUrlDecode(char *val, int valLen, char *ret, int retLen) { +int ICACHE_FLASH_ATTR httpdUrlDecode(char *val, int valLen, char *ret, int retLen) { int s=0, d=0; int esced=0, escVal=0; while (spriv->sendBuff -static void ICACHE_FLASH_ATTR xmitSendBuff(HttpdConnData *conn) { +//Function to send any data in conn->priv->sendBuff. Do not use in CGIs unless you know what you +//are doing! +void ICACHE_FLASH_ATTR httpdFlushSendBuffer(HttpdConnData *conn) { if (conn->priv->sendBuffLen!=0) { espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen); conn->priv->sendBuffLen=0; @@ -317,7 +325,7 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { os_printf("Conn %p is done. Closing.\n", conn->conn); espconn_disconnect(conn->conn); httpdRetireConn(conn); - return; //No need to call xmitSendBuff. + return; //No need to call httpdFlushSendBuffer. } r=conn->cgi(conn); //Execute cgi fn. @@ -328,7 +336,7 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { os_printf("ERROR! CGI fn returns code %d after sending data! Bad CGI!\n", r); conn->cgi=NULL; //mark for destruction. } - xmitSendBuff(conn); + httpdFlushSendBuffer(conn); } static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nServer: esp8266-httpd/"HTTPDVER"\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nNot Found.\r\n"; @@ -368,7 +376,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) { //generate a built-in 404 to handle this. os_printf("%s not found. 404!\n", conn->url); httpdSend(conn, httpNotFoundHeader, -1); - xmitSendBuff(conn); + httpdFlushSendBuffer(conn); conn->cgi=NULL; //mark for destruction return; } @@ -378,11 +386,11 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) { r=conn->cgi(conn); if (r==HTTPD_CGI_MORE) { //Yep, it's happy to do so and has more data to send. - xmitSendBuff(conn); + httpdFlushSendBuffer(conn); return; } else if (r==HTTPD_CGI_DONE) { //Yep, it's happy to do so and already is done sending data. - xmitSendBuff(conn); + httpdFlushSendBuffer(conn); conn->cgi=NULL; //mark conn for destruction return; } else if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) { @@ -396,21 +404,21 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) { //Parse a line of header data and modify the connection data accordingly. static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { int i; - char first_line = false; + char firstLine=0; if (os_strncmp(h, "GET ", 4)==0) { conn->requestType = HTTPD_METHOD_GET; - first_line = true; + firstLine=1; } else if (os_strncmp(h, "Host:", 5)==0) { i=5; while (h[i]==' ') i++; conn->hostName=&h[i]; } else if (os_strncmp(h, "POST ", 5)==0) { conn->requestType = HTTPD_METHOD_POST; - first_line = true; + firstLine=1; } - if (first_line) { + if (firstLine) { char *e; //Skip past the space after POST/GET @@ -519,6 +527,12 @@ static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short httpdProcessRequest(conn); conn->post->buffLen = 0; } + } else { + //Let cgi handle data if it registered a recvHdl callback. If not, ignore. + if (conn->recvHdl) { + conn->recvHdl(conn, data+x, len-x); + break; + } } } } @@ -571,6 +585,8 @@ static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) { connData[i].post->received=0; connData[i].post->len=-1; connData[i].hostName=NULL; + connData[i].remote_port=conn->proto.tcp->remote_port; + os_memcpy(connData[i].remote_ip, conn->proto.tcp->remote_ip, 4); espconn_regist_recvcb(conn, httpdRecvCb); espconn_regist_reconcb(conn, httpdReconCb); @@ -594,4 +610,5 @@ void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) { os_printf("Httpd init, conn=%p\n", &httpdConn); espconn_regist_connectcb(&httpdConn, httpdConnectCb); espconn_accept(&httpdConn); + espconn_tcp_set_max_con_allow(&httpdConn, MAX_CONN); } diff --git a/Espressif/examples/esphttpd/libesphttpd/core/sha1.c b/Espressif/examples/esphttpd/libesphttpd/core/sha1.c new file mode 100644 index 0000000..062db2c --- /dev/null +++ b/Espressif/examples/esphttpd/libesphttpd/core/sha1.c @@ -0,0 +1,168 @@ +/* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ +// gcc -Wall -DSHA1TEST -o sha1test sha1.c && ./sha1test + +#include +#include +#include + +#include "sha1.h" + +//according to http://ip.cadence.com/uploads/pdf/xtensalx_overview_handbook.pdf +// the cpu is normally defined as little ending, but can be big endian too. +// for the esp this seems to work +//#define SHA_BIG_ENDIAN + + + +/* code */ +#define SHA1_K0 0x5a827999 +#define SHA1_K20 0x6ed9eba1 +#define SHA1_K40 0x8f1bbcdc +#define SHA1_K60 0xca62c1d6 + +void ICACHE_FLASH_ATTR sha1_init(sha1nfo *s) { + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->byteCount = 0; + s->bufferOffset = 0; +} + +uint32_t ICACHE_FLASH_ATTR sha1_rol32(uint32_t number, uint8_t bits) { + return ((number << bits) | (number >> (32-bits))); +} + +void ICACHE_FLASH_ATTR sha1_hashBlock(sha1nfo *s) { + uint8_t i; + uint32_t a,b,c,d,e,t; + + a=s->state[0]; + b=s->state[1]; + c=s->state[2]; + d=s->state[3]; + e=s->state[4]; + for (i=0; i<80; i++) { + if (i>=16) { + t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15]; + s->buffer[i&15] = sha1_rol32(t,1); + } + if (i<20) { + t = (d ^ (b & (c ^ d))) + SHA1_K0; + } else if (i<40) { + t = (b ^ c ^ d) + SHA1_K20; + } else if (i<60) { + t = ((b & c) | (d & (b | c))) + SHA1_K40; + } else { + t = (b ^ c ^ d) + SHA1_K60; + } + t+=sha1_rol32(a,5) + e + s->buffer[i&15]; + e=d; + d=c; + c=sha1_rol32(b,30); + b=a; + a=t; + } + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + s->state[4] += e; +} + +void ICACHE_FLASH_ATTR sha1_addUncounted(sha1nfo *s, uint8_t data) { + uint8_t * const b = (uint8_t*) s->buffer; +#ifdef SHA_BIG_ENDIAN + b[s->bufferOffset] = data; +#else + b[s->bufferOffset ^ 3] = data; +#endif + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + sha1_hashBlock(s); + s->bufferOffset = 0; + } +} + +void ICACHE_FLASH_ATTR sha1_writebyte(sha1nfo *s, uint8_t data) { + ++s->byteCount; + sha1_addUncounted(s, data); +} + +void ICACHE_FLASH_ATTR sha1_write(sha1nfo *s, const char *data, size_t len) { + for (;len--;) sha1_writebyte(s, (uint8_t) *data++); +} + +void ICACHE_FLASH_ATTR sha1_pad(sha1nfo *s) { + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); +} + +uint8_t* ICACHE_FLASH_ATTR sha1_result(sha1nfo *s) { + // Pad to complete the last block + sha1_pad(s); + +#ifndef SHA_BIG_ENDIAN + // Swap byte order back + int i; + for (i=0; i<5; i++) { + s->state[i]= + (((s->state[i])<<24)& 0xff000000) + | (((s->state[i])<<8) & 0x00ff0000) + | (((s->state[i])>>8) & 0x0000ff00) + | (((s->state[i])>>24)& 0x000000ff); + } +#endif + + // Return pointer to hash (20 characters) + return (uint8_t*) s->state; +} + +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c + +void ICACHE_FLASH_ATTR sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength) { + uint8_t i; + memset(s->keyBuffer, 0, BLOCK_LENGTH); + if (keyLength > BLOCK_LENGTH) { + // Hash long keys + sha1_init(s); + for (;keyLength--;) sha1_writebyte(s, *key++); + memcpy(s->keyBuffer, sha1_result(s), HASH_LENGTH); + } else { + // Block length keys are used as is + memcpy(s->keyBuffer, key, keyLength); + } + // Start inner hash + sha1_init(s); + for (i=0; ikeyBuffer[i] ^ HMAC_IPAD); + } +} + +uint8_t* ICACHE_FLASH_ATTR sha1_resultHmac(sha1nfo *s) { + uint8_t i; + // Complete inner hash + memcpy(s->innerHash,sha1_result(s),HASH_LENGTH); + // Calculate outer hash + sha1_init(s); + for (i=0; ikeyBuffer[i] ^ HMAC_OPAD); + for (i=0; iinnerHash[i]); + return sha1_result(s); +} diff --git a/Espressif/examples/esphttpd/libesphttpd/espfs/espfs.c b/Espressif/examples/esphttpd/libesphttpd/espfs/espfs.c index b14a589..dac47c5 100644 --- a/Espressif/examples/esphttpd/libesphttpd/espfs/espfs.c +++ b/Espressif/examples/esphttpd/libesphttpd/espfs/espfs.c @@ -74,6 +74,10 @@ a memory exception, crashing the program. */ EspFsInitResult ICACHE_FLASH_ATTR espFsInit(void *flashAddress) { + if((uint32_t)flashAddress > 0x40200000) { + flashAddress = (void*)((uint32_t)flashAddress-0x40200000); + } + // base address must be aligned to 4 bytes if (((int)flashAddress & 3) != 0) { return ESPFS_INIT_RESULT_BAD_ALIGN; @@ -81,7 +85,7 @@ EspFsInitResult ICACHE_FLASH_ATTR espFsInit(void *flashAddress) { // check if there is valid header at address EspFsHeader testHeader; - os_memcpy(&testHeader, flashAddress, sizeof(EspFsHeader)); + spi_flash_read((uint32)flashAddress, (uint32*)&testHeader, sizeof(EspFsHeader)); if (testHeader.magic != ESPFS_MAGIC) { return ESPFS_INIT_RESULT_NO_IMAGE; } @@ -95,21 +99,16 @@ EspFsInitResult ICACHE_FLASH_ATTR espFsInit(void *flashAddress) { //ToDo: perhaps os_memcpy also does unaligned accesses? #ifdef __ets__ -void ICACHE_FLASH_ATTR memcpyAligned(char *dst, char *src, int len) { - int x; - int w, b; - for (x=0; x>0); - if (b==1) *dst=(w>>8); - if (b==2) *dst=(w>>16); - if (b==3) *dst=(w>>24); - dst++; src++; - } +void ICACHE_FLASH_ATTR readFlashUnaligned(char *dst, char *src, int len) { + uint8_t src_offset = ((uint32_t)src) & 3; + uint32_t src_address = ((uint32_t)src) - src_offset; + + uint32_t tmp_buf[len/4 + 2]; + spi_flash_read((uint32)src_address, (uint32*)tmp_buf, len+src_offset); + os_memcpy(dst, ((uint8_t*)tmp_buf)+src_offset, len); } #else -#define memcpyAligned memcpy +#define readFlashUnaligned memcpy #endif // Returns flags of opened file. @@ -120,7 +119,7 @@ int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) { } int8_t flags; - memcpyAligned((char*)&flags, (char*)&fh->header->flags, 1); + readFlashUnaligned((char*)&flags, (char*)&fh->header->flags, 1); return (int)flags; } @@ -141,7 +140,8 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { while(1) { hpos=p; //Grab the next file header. - os_memcpy(&h, p, sizeof(EspFsHeader)); + spi_flash_read((uint32)p, (uint32*)&h, sizeof(EspFsHeader)); + if (h.magic!=ESPFS_MAGIC) { os_printf("Magic mismatch. EspFS image broken.\n"); return NULL; @@ -152,7 +152,7 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { } //Grab the name of the file. p+=sizeof(EspFsHeader); - os_memcpy(namebuf, p, sizeof(namebuf)); + spi_flash_read((uint32)p, (uint32*)&namebuf, sizeof(namebuf)); // os_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n", // namebuf, (unsigned int)h.nameLen, (unsigned int)h.fileLenComp, h.compression, h.flags); if (os_strcmp(namebuf, fileName)==0) { @@ -174,7 +174,7 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { char parm; heatshrink_decoder *dec; //Decoder params are stored in 1st byte. - memcpyAligned(&parm, r->posComp, 1); + readFlashUnaligned(&parm, r->posComp, 1); r->posComp++; os_printf("Heatshrink compressed file; decode parms = %x\n", parm); dec=heatshrink_decoder_alloc(16, (parm>>4)&0xf, parm&0xf); @@ -196,22 +196,23 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) { int flen, fdlen; if (fh==NULL) return 0; + + readFlashUnaligned((char*)&flen, (char*)&fh->header->fileLenComp, 4); //Cache file length. - memcpyAligned((char*)&flen, (char*)&fh->header->fileLenComp, 4); - memcpyAligned((char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4); //Do stuff depending on the way the file is compressed. if (fh->decompressor==COMPRESS_NONE) { int toRead; toRead=flen-(fh->posComp-fh->posStart); if (len>toRead) len=toRead; // os_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp); - memcpyAligned(buff, fh->posComp, len); + readFlashUnaligned(buff, fh->posComp, len); fh->posDecomp+=len; fh->posComp+=len; // os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp); return len; #ifdef ESPFS_HEATSHRINK } else if (fh->decompressor==COMPRESS_HEATSHRINK) { + readFlashUnaligned((char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4); int decoded=0; size_t elen, rlen; char ebuff[16]; @@ -230,7 +231,7 @@ int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) { //ToDo: Check ret val of heatshrink fns for errors elen=flen-(fh->posComp - fh->posStart); if (elen>0) { - memcpyAligned(ebuff, fh->posComp, 16); + readFlashUnaligned(ebuff, fh->posComp, 16); heatshrink_decoder_sink(dec, (uint8_t *)ebuff, (elen>16)?16:elen, &rlen); fh->posComp+=rlen; } diff --git a/Espressif/examples/esphttpd/libesphttpd/espfs/mkespfsimage/espfsformat.h b/Espressif/examples/esphttpd/libesphttpd/espfs/mkespfsimage/espfsformat.h deleted file mode 100644 index 8ce5549..0000000 --- a/Espressif/examples/esphttpd/libesphttpd/espfs/mkespfsimage/espfsformat.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef ESPROFSFORMAT_H -#define ESPROFSFORMAT_H - -/* -Stupid cpio-like tool to make read-only 'filesystems' that live on the flash SPI chip of the module. -Can (will) use lzf compression (when I come around to it) to make shit quicker. Aligns names, files, -headers on 4-byte boundaries so the SPI abstraction hardware in the ESP8266 doesn't crap on itself -when trying to do a <4byte or unaligned read. -*/ - -/* -The idea 'borrows' from cpio: it's basically a concatenation of {header, filename, file} data. -Header, filename and file data is 32-bit aligned. The last file is indicated by data-less header -with the FLAG_LASTFILE flag set. -*/ - - -#define FLAG_LASTFILE (1<<0) -#define FLAG_GZIP (1<<1) -#define COMPRESS_NONE 0 -#define COMPRESS_HEATSHRINK 1 -#define ESPFS_MAGIC 0x73665345 - -typedef struct { - int32_t magic; - int8_t flags; - int8_t compression; - int16_t nameLen; - int32_t fileLenComp; - int32_t fileLenDecomp; -} __attribute__((packed)) EspFsHeader; - -#endif \ No newline at end of file diff --git a/Espressif/examples/esphttpd/libesphttpd/include/cgiwebsocket.h b/Espressif/examples/esphttpd/libesphttpd/include/cgiwebsocket.h new file mode 100644 index 0000000..f42e269 --- /dev/null +++ b/Espressif/examples/esphttpd/libesphttpd/include/cgiwebsocket.h @@ -0,0 +1,37 @@ +#ifndef CGIWEBSOCKET_H +#define CGIWEBSOCKET_H + +#include "httpd.h" + +#define WEBSOCK_FLAG_NONE 0 +#define WEBSOCK_FLAG_CONT (1<<0) //Set if the data is not the final data in the message; more follows +#define WEBSOCK_FLAG_BIN (1<<1) //Set if the data is binary instead of text + + + +typedef struct Websock Websock; +typedef struct WebsockPriv WebsockPriv; + +typedef void(*WsConnectedCb)(Websock *ws); +typedef void(*WsRecvCb)(Websock *ws, char *data, int len, int flags); +typedef void(*WsSentCb)(Websock *ws); +typedef void(*WsCloseCb)(Websock *ws); + +struct Websock { + void *userData; + HttpdConnData *conn; + uint8_t status; + WsRecvCb recvCb; + WsSentCb sentCb; + WsCloseCb closeCb; + WebsockPriv *priv; +}; + +int ICACHE_FLASH_ATTR cgiWebsocket(HttpdConnData *connData); +int ICACHE_FLASH_ATTR cgiWebsocketSend(Websock *ws, char *data, int len, int flags); +void ICACHE_FLASH_ATTR cgiWebsocketClose(Websock *ws); +int ICACHE_FLASH_ATTR cgiWebSocketRecv(HttpdConnData *connData, char *data, int len); +int ICACHE_FLASH_ATTR cgiWebsockBroadcast(char *resource, char *data, int len, int flags); + + +#endif \ No newline at end of file diff --git a/Espressif/examples/esphttpd/libesphttpd/include/espmissingincludes.h b/Espressif/examples/esphttpd/libesphttpd/include/espmissingincludes.h index 27e6652..b115309 100644 --- a/Espressif/examples/esphttpd/libesphttpd/include/espmissingincludes.h +++ b/Espressif/examples/esphttpd/libesphttpd/include/espmissingincludes.h @@ -33,12 +33,7 @@ void ets_update_cpu_frequency(int freqmhz); int os_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format (printf, 3, 4))); int os_printf_plus(const char *format, ...) __attribute__ ((format (printf, 1, 2))); -void pvPortFree(void *ptr); -void *pvPortMalloc(size_t xWantedSize); -void *pvPortZalloc(size_t); void uart_div_modify(int no, unsigned int freq); -void vPortFree(void *ptr); -void *vPortMalloc(size_t xWantedSize); uint8 wifi_get_opmode(void); uint32 system_get_time(); int rand(void); @@ -46,6 +41,23 @@ void ets_bzero(void *s, size_t n); void ets_delay_us(int ms); +//Hack: this is defined in SDK 1.4.0 and undefined in 1.3.0. It's only used for this, the symbol itself +//has no meaning here. +#ifndef RC_LIMIT_P2P_11N +//Defs for SDK <1.4.0 +void *pvPortMalloc(size_t xWantedSize); +void *pvPortZalloc(size_t); +void vPortFree(void *ptr); +void *vPortMalloc(size_t xWantedSize); +void pvPortFree(void *ptr); +#else +void *pvPortMalloc(size_t xWantedSize, const char *file, int line); +void *pvPortZalloc(size_t, const char *file, int line); +void vPortFree(void *ptr, const char *file, int line); +void *vPortMalloc(size_t xWantedSize, const char *file, int line); +void pvPortFree(void *ptr, const char *file, int line); +#endif + //Standard PIN_FUNC_SELECT gives a warning. Replace by a non-warning one. #ifdef PIN_FUNC_SELECT #undef PIN_FUNC_SELECT diff --git a/Espressif/examples/esphttpd/libesphttpd/include/httpd.h b/Espressif/examples/esphttpd/libesphttpd/include/httpd.h index 14ecb7c..5ad6bcf 100644 --- a/Espressif/examples/esphttpd/libesphttpd/include/httpd.h +++ b/Espressif/examples/esphttpd/libesphttpd/include/httpd.h @@ -11,12 +11,12 @@ #define HTTPD_METHOD_GET 1 #define HTTPD_METHOD_POST 2 - typedef struct HttpdPriv HttpdPriv; typedef struct HttpdConnData HttpdConnData; typedef struct HttpdPostData HttpdPostData; typedef int (* cgiSendCallback)(HttpdConnData *connData); +typedef int (* cgiRecvHandler)(HttpdConnData *connData, char *data, int len); //A struct describing a http connection. This gets passed to cgi functions. struct HttpdConnData { @@ -30,7 +30,10 @@ struct HttpdConnData { char *hostName; HttpdPriv *priv; cgiSendCallback cgi; + cgiRecvHandler recvHdl; HttpdPostData *post; + int remote_port; + uint8 remote_ip[4]; }; //A struct describing the POST data sent inside the http connection. This is used by the CGI functions @@ -64,5 +67,6 @@ void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn); int ICACHE_FLASH_ATTR httpdGetHeader(HttpdConnData *conn, char *header, char *ret, int retLen); int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len); +void ICACHE_FLASH_ATTR httpdFlushSendBuffer(HttpdConnData *conn); #endif \ No newline at end of file diff --git a/Espressif/examples/esphttpd/libesphttpd/include/sha1.h b/Espressif/examples/esphttpd/libesphttpd/include/sha1.h new file mode 100644 index 0000000..69148de --- /dev/null +++ b/Espressif/examples/esphttpd/libesphttpd/include/sha1.h @@ -0,0 +1,39 @@ +/* header */ + +#ifndef __SHA1_H__ +#define __SHA1_H__ + +#define HASH_LENGTH 20 +#define BLOCK_LENGTH 64 + +typedef struct sha1nfo { + uint32_t buffer[BLOCK_LENGTH/4]; + uint32_t state[HASH_LENGTH/4]; + uint32_t byteCount; + uint8_t bufferOffset; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; +} sha1nfo; + +/* public API - prototypes - TODO: doxygen*/ + +/** + */ +void sha1_init(sha1nfo *s); +/** + */ +void sha1_writebyte(sha1nfo *s, uint8_t data); +/** + */ +void sha1_write(sha1nfo *s, const char *data, size_t len); +/** + */ +uint8_t* sha1_result(sha1nfo *s); +/** + */ +void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength); +/** + */ +uint8_t* sha1_resultHmac(sha1nfo *s); + +#endif diff --git a/Espressif/examples/esphttpd/libesphttpd/util/cgiwebsocket.c b/Espressif/examples/esphttpd/libesphttpd/util/cgiwebsocket.c new file mode 100644 index 0000000..40e752e --- /dev/null +++ b/Espressif/examples/esphttpd/libesphttpd/util/cgiwebsocket.c @@ -0,0 +1,278 @@ +#include +#include "httpd.h" +#include "sha1.h" +#include "base64.h" +#include "cgiwebsocket.h" + +#define WS_KEY_IDENTIFIER "Sec-WebSocket-Key: " +#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + +/* from IEEE RFC6455 sec 5.2 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ +*/ + +#define FLAG_FIN (1 << 7) + +#define OPCODE_CONTINUE 0x0 +#define OPCODE_TEXT 0x1 +#define OPCODE_BINARY 0x2 +#define OPCODE_CLOSE 0x8 +#define OPCODE_PING 0x9 +#define OPCODE_PONG 0xA + +#define FLAGS_MASK ((uint8_t)0xF0) +#define OPCODE_MASK ((uint8_t)0x0F) +#define IS_MASKED ((uint8_t)(1<<7)) +#define PAYLOAD_MASK ((uint8_t)0x7F) + +typedef struct WebsockFrame WebsockFrame; + +#define ST_FLAGS 0 +#define ST_LEN0 1 +#define ST_LEN1 2 +#define ST_LEN2 3 +//... +#define ST_LEN8 9 +#define ST_MASK1 10 +#define ST_MASK4 13 +#define ST_PAYLOAD 14 + +struct WebsockFrame { + uint8_t flags; + uint8_t len8; + uint64_t len; + uint8_t mask[4]; +}; + +struct WebsockPriv { + struct WebsockFrame fr; + uint8_t maskCtr; + uint8 frameCont; + uint8 mustClose; + int wsStatus; + Websock *next; //in linked list +}; + +static Websock *llStart=NULL; + +static int ICACHE_FLASH_ATTR sendFrameHead(Websock *ws, int opcode, int len) { + char buf[14]; + int i=0; + buf[i++]=opcode; + if (len>65535) { + buf[i++]=127; + buf[i++]=0; buf[i++]=0; buf[i++]=0; buf[i++]=0; + buf[i++]=len>>24; + buf[i++]=len>>16; + buf[i++]=len>>8; + buf[i++]=len; + } else if (len>125) { + buf[i++]=126; + buf[i++]=len>>8; + buf[i++]=len; + } else { + buf[i++]=len; + } + return httpdSend(ws->conn, buf, i); +} + +int ICACHE_FLASH_ATTR cgiWebsocketSend(Websock *ws, char *data, int len, int flags) { + int r; + int fl=0; + if (flags&WEBSOCK_FLAG_BIN) fl=OPCODE_BINARY; else fl=OPCODE_TEXT; + if (!(flags&WEBSOCK_FLAG_CONT)) fl|=FLAG_FIN; + sendFrameHead(ws, fl, len); + r=httpdSend(ws->conn, data, len); + httpdFlushSendBuffer(ws->conn); + return r; +} + +//Broadcast data to all websockets at a specific url. Returns the amount of connections sent to. +int ICACHE_FLASH_ATTR cgiWebsockBroadcast(char *resource, char *data, int len, int flags) { + Websock *lw=llStart; + int ret=0; + while (lw!=NULL) { + if (os_strcmp(lw->conn->url, resource)==0) { + cgiWebsocketSend(lw, data, len, flags); + ret++; + } + lw=lw->priv->next; + } + return ret; +} + + +void ICACHE_FLASH_ATTR cgiWebsocketClose(Websock *ws) { + sendFrameHead(ws, FLAG_FIN|OPCODE_CLOSE, 0); + httpdFlushSendBuffer(ws->conn); +} + + +int ICACHE_FLASH_ATTR cgiWebSocketRecv(HttpdConnData *connData, char *data, int len) { + int i, j, sl; + Websock *ws=(Websock*)connData->cgiPrivData; + for (i=0; ipriv->wsStatus, data[i]); + if (ws->priv->wsStatus==ST_FLAGS) { + ws->priv->maskCtr=0; + ws->priv->frameCont=0; + ws->priv->fr.flags=(uint8_t)data[i]; + ws->priv->wsStatus=ST_LEN0; + } else if (ws->priv->wsStatus==ST_LEN0) { + ws->priv->fr.len8=(uint8_t)data[i]; + if ((ws->priv->fr.len8&127)>=126) { + ws->priv->fr.len=0; + ws->priv->wsStatus=ST_LEN1; + } else { + ws->priv->fr.len=ws->priv->fr.len8&127; + ws->priv->wsStatus=(ws->priv->fr.len8&IS_MASKED)?ST_MASK1:ST_PAYLOAD; + } + } else if (ws->priv->wsStatus<=ST_LEN8) { + ws->priv->fr.len=(ws->priv->fr.len<<8)|data[i]; + if (((ws->priv->fr.len8&127)==126 && ws->priv->wsStatus==ST_LEN2) || ws->priv->wsStatus==ST_LEN8) { + if (ws->priv->fr.len8&IS_MASKED) ws->priv->wsStatus=ST_MASK1; else ws->priv->wsStatus=ST_PAYLOAD; + } else { + ws->priv->wsStatus++; + } + } else if (ws->priv->wsStatus<=ST_MASK4) { + ws->priv->fr.mask[ws->priv->wsStatus-ST_MASK1]=data[i]; + ws->priv->wsStatus++; + } else if (ws->priv->wsStatus==ST_PAYLOAD) { + //Okay, header is in; this is a data byte. We're going to process all the data bytes we have + //received here at the same time; no more byte iterations till the end of this frame. + //First, unmask the data + sl=len-i; + for (j=0; jpriv->fr.mask[(ws->priv->maskCtr++)&3]); + + //Inspect the header to see what we need to do. + if ((ws->priv->fr.flags&OPCODE_MASK)==OPCODE_PING) { + if (!ws->priv->frameCont) sendFrameHead(ws, OPCODE_PONG, 0); + if (ws->priv->fr.len>0) httpdSend(ws->conn, data+i, sl); + } else if ((ws->priv->fr.flags&OPCODE_MASK)==OPCODE_TEXT || + (ws->priv->fr.flags&OPCODE_MASK)==OPCODE_BINARY || + (ws->priv->fr.flags&OPCODE_MASK)==OPCODE_CONTINUE) { + if (sl>ws->priv->fr.len) sl=ws->priv->fr.len; + if (!(ws->priv->fr.len8&IS_MASKED)) { + //We're a server; client should send us masked packets. + cgiWebsocketClose(ws); + } else { + int flags=0; + if ((ws->priv->fr.flags&OPCODE_MASK)==OPCODE_BINARY) flags|=WEBSOCK_FLAG_BIN; + if ((ws->priv->fr.flags&FLAG_FIN)==0) flags|=WEBSOCK_FLAG_CONT; + if (ws->recvCb) ws->recvCb(ws, data+i, sl, flags); + } + } else if ((ws->priv->fr.flags&OPCODE_MASK)==OPCODE_CLOSE) { + ws->priv->mustClose=1; + } + ws->priv->frameCont=1; //next payload is continuation of this one. + i+=sl; //skip remaining bytes of framepayload, we have handled this + ws->priv->fr.len-=sl; + if (ws->priv->fr.len==0) ws->priv->wsStatus=ST_FLAGS; + } + } + httpdFlushSendBuffer(ws->conn); + return 0; +} + +//Websocket 'cgi' implementation +int ICACHE_FLASH_ATTR cgiWebsocket(HttpdConnData *connData) { + char buff[256]; + int i; + sha1nfo s; + if (connData->conn==NULL) { + //Connection aborted. Clean up. +// os_printf("WS: Cleanup\n"); + if (connData->cgiPrivData) { + Websock *ws=(Websock*)connData->cgiPrivData; + if (ws->closeCb) ws->closeCb(ws); + //Clean up linked list + if (llStart==ws) { + llStart=ws->priv->next; + } else if (llStart) { + Websock *lws=llStart; + //Find ws that links to this one. + while (lws!=NULL && lws->priv->next!=ws) lws=lws->priv->next; + if (lws!=NULL) lws->priv->next=ws->priv->next; + } + if (ws->priv) os_free(ws->priv); + os_free(connData->cgiPrivData); + connData->cgiPrivData=NULL; + } + return HTTPD_CGI_DONE; + } + + if (connData->cgiPrivData==NULL) { +// os_printf("WS: First call\n"); + //First call here. Check if client headers are OK, send server header. + i=httpdGetHeader(connData, "Upgrade", buff, sizeof(buff)-1); +// os_printf("WS: Upgrade: %s\n", buff); + if (i && os_strcmp(buff, "websocket")==0) { + i=httpdGetHeader(connData, "Sec-WebSocket-Key", buff, sizeof(buff)-1); + if (i) { +// os_printf("WS: Key: %s\n", buff); + //Seems like a WebSocket connection. + // Alloc structs + connData->cgiPrivData=os_malloc(sizeof(Websock)); + os_memset(connData->cgiPrivData, 0, sizeof(Websock)); + Websock *ws=(Websock*)connData->cgiPrivData; + ws->priv=os_malloc(sizeof(WebsockPriv)); + os_memset(ws->priv, 0, sizeof(WebsockPriv)); + ws->conn=connData; + //Reply with the right headers. + os_strcat(buff, WS_GUID); + sha1_init(&s); + sha1_write(&s, buff, os_strlen(buff)); + httpdStartResponse(connData, 101); + httpdHeader(connData, "Upgrade", "websocket"); + httpdHeader(connData, "Connection", "upgrade"); + base64_encode(20, sha1_result(&s), sizeof(buff), buff); + httpdHeader(connData, "Sec-WebSocket-Accept", buff); + httpdEndHeaders(connData); + //Set data receive handler + connData->recvHdl=cgiWebSocketRecv; + //Inform CGI function we have a connection + WsConnectedCb connCb=connData->cgiArg; + connCb(ws); + //Insert ws into linked list + if (llStart==NULL) { + llStart=ws; + } else { + Websock *lw=llStart; + while (lw->priv->next) lw=lw->priv->next; + lw->priv->next=ws; + } + return HTTPD_CGI_MORE; + } + } + //No valid websocket connection + httpdStartResponse(connData, 500); + httpdEndHeaders(connData); + return HTTPD_CGI_DONE; + } + + //Sending is done. Call the sent callback if we have one. + Websock *ws=(Websock*)connData->cgiPrivData; + if (ws && ws->sentCb) ws->sentCb(ws); + + if (ws && ws->priv->mustClose) return HTTPD_CGI_DONE; + + return HTTPD_CGI_MORE; +} + diff --git a/Espressif/examples/esphttpd/user/io.c b/Espressif/examples/esphttpd/user/io.c index a342dbd..2a4cd5c 100644 --- a/Espressif/examples/esphttpd/user/io.c +++ b/Espressif/examples/esphttpd/user/io.c @@ -46,6 +46,6 @@ void ioInit() { gpio_output_set(0, 0, (1<recvCb=myWebsocketRecv; + cgiWebsocketSend(ws, "Hi, Websocket!", 14, WEBSOCK_FLAG_NONE); +} + + #ifdef ESPFS_POS CgiUploadFlashDef uploadParams={ .type=CGIFLASH_TYPE_ESPFS, @@ -102,6 +131,8 @@ HttpdBuiltInUrl builtInUrls[]={ {"/wifi/connstatus.cgi", cgiWiFiConnStatus, NULL}, {"/wifi/setmode.cgi", cgiWiFiSetMode, NULL}, + {"/websocket/ws.cgi", cgiWebsocket, myWebsocketConnect}, + {"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem {NULL, NULL, NULL} }; @@ -134,6 +165,9 @@ void user_init(void) { os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); os_timer_arm(&prHeapTimer, 3000, 1); #endif + os_timer_disarm(&websockTimer); + os_timer_setfn(&websockTimer, websockTimerCb, NULL); + os_timer_arm(&websockTimer, 1000, 1); os_printf("\nReady\n"); }