From d723c1024d3ea25ff4b38c7ba9103b44b68eeec0 Mon Sep 17 00:00:00 2001 From: cjkas Date: Sun, 11 Oct 2026 13:29:30 +0200 Subject: [PATCH] refactor: move JsonFormatter into JsonFormatter.cpp JsonFormatter and BufferedJsonFormatter move verbatim out of WResp.cpp/WResp.h, which now include JsonFormatter.h. They don't need the web server, so on their own they can be unit tested on the PC; the native env builds the new file. AsyncJsonResp and JsonSockEvent stay in WResp. No behaviour change. Co-Authored-By: Claude Opus 5.5 --- platformio.ini | 2 +- src/JsonFormatter.cpp | 152 ++++++++++++++++++++++++++++++++++++++++++ src/JsonFormatter.h | 61 +++++++++++++++++ src/WResp.cpp | 151 ----------------------------------------- src/WResp.h | 55 +-------------- 5 files changed, 215 insertions(+), 206 deletions(-) create mode 100644 src/JsonFormatter.cpp create mode 100644 src/JsonFormatter.h diff --git a/platformio.ini b/platformio.ini index 04f0f12..432f0d9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -101,7 +101,7 @@ lib_deps = platform = native test_framework = unity test_build_src = yes -build_src_filter = -<*> + + +build_src_filter = -<*> + + + build_flags = -std=gnu++17 -Isrc diff --git a/src/JsonFormatter.cpp b/src/JsonFormatter.cpp new file mode 100644 index 0000000..714f64b --- /dev/null +++ b/src/JsonFormatter.cpp @@ -0,0 +1,152 @@ +#include "JsonFormatter.h" + +void BufferedJsonFormatter::setBuffer(char *b, size_t sz) { + this->buff = b; + this->buffSize = sz; + this->buff[0] = 0; + this->_nocomma = true; + this->_objects = 0; + this->_arrays = 0; +} +size_t BufferedJsonFormatter::length() const { + return strlen(this->buff); +} +void JsonFormatter::beginObject(const char *name) { + if(name && strlen(name) > 0) this->appendElem(name); + else if(!this->_nocomma) this->_safecat(","); + this->_safecat("{"); + this->_objects++; + this->_nocomma = true; +} +void JsonFormatter::endObject() { + //if(strlen(this->buff) + 1 > this->buffSize - 1) this->send(); + this->_safecat("}"); + this->_objects--; + this->_nocomma = false; +} +void JsonFormatter::beginArray(const char *name) { + if(name && strlen(name) > 0) this->appendElem(name); + else if(!this->_nocomma) this->_safecat(","); + this->_safecat("["); + this->_arrays++; + this->_nocomma = true; +} +void JsonFormatter::endArray() { + //if(strlen(this->buff) + 1 > this->buffSize - 1) this->send(); + this->_safecat("]"); + this->_arrays--; + this->_nocomma = false; +} + +void JsonFormatter::appendElem(const char *name) { + if(!this->_nocomma) this->_safecat(","); + if(name && strlen(name) > 0) { + this->_safecat(name, true); + this->_safecat(":"); + } + this->_nocomma = false; +} + +void JsonFormatter::addElem(const char *name, const char *val) { + if(!val) return; + this->appendElem(name); + this->_safecat(val, true); +} +void JsonFormatter::addElem(const char *val) { this->addElem(nullptr, val); } +void JsonFormatter::addElem(float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(nullptr); } + +/* +void JsonFormatter::addElem(int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(nullptr); } +*/ +void JsonFormatter::addElem(bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(nullptr); } + +void JsonFormatter::addElem(const char *name, float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(name); } + +/* +void JsonFormatter::addElem(const char *name, int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(name); } +*/ +void JsonFormatter::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); } + +void JsonFormatter::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); } + +void JsonFormatter::_safecat(const char *val, bool escape) { + size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff); + if(escape) len += 2; + if(len >= this->buffSize) { + return; + } + if(escape) strcat(this->buff, "\""); + if(escape) this->escapeString(val, &this->buff[strlen(this->buff)]); + else strcat(this->buff, val); + if(escape) strcat(this->buff, "\""); +} +void JsonFormatter::_appendNumber(const char *name) { this->appendElem(name); this->_safecat(this->_numbuff); } +uint32_t JsonFormatter::calcEscapedLength(const char *raw) { + uint32_t len = 0; + for(size_t i = strlen(raw); i > 0; i--) { + switch(raw[i]) { + case '"': + case '/': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\\': + len += 2; + break; + default: + len++; + break; + } + } + return len; +} +void JsonFormatter::escapeString(const char *raw, char *escaped) { + for(uint32_t i = 0; i < strlen(raw); i++) { + switch(raw[i]) { + case '"': + strcat(escaped, "\\\""); + break; + case '/': + strcat(escaped, "\\/"); + break; + case '\b': + strcat(escaped, "\\b"); + break; + case '\f': + strcat(escaped, "\\f"); + break; + case '\n': + strcat(escaped, "\\n"); + break; + case '\r': + strcat(escaped, "\\r"); + break; + case '\t': + strcat(escaped, "\\t"); + break; + case '\\': + strcat(escaped, "\\\\"); + break; + default: + size_t len = strlen(escaped); + escaped[len] = raw[i]; + escaped[len+1] = 0x00; + break; + } + } +} diff --git a/src/JsonFormatter.h b/src/JsonFormatter.h new file mode 100644 index 0000000..5afe9c4 --- /dev/null +++ b/src/JsonFormatter.h @@ -0,0 +1,61 @@ +#ifndef json_formatter_h +#define json_formatter_h +#include + +// Streams JSON into a fixed buffer. Free of the web server so it can be unit +// tested on the PC (pio test -e native). +class JsonFormatter { + protected: + char *buff; + size_t buffSize; + bool _headersSent = false; + uint8_t _objects = 0; + uint8_t _arrays = 0; + bool _nocomma = true; + char _numbuff[25] = {0}; + virtual void _safecat(const char *val, bool escape = false); + void _appendNumber(const char *name); + public: + void escapeString(const char *raw, char *escaped); + uint32_t calcEscapedLength(const char *raw); + void beginObject(const char *name = nullptr); + void endObject(); + void beginArray(const char *name = nullptr); + void endArray(); + void appendElem(const char *name = nullptr); + + void addElem(const char* val); + void addElem(float fval); + void addElem(int8_t nval); + void addElem(uint8_t nval); + /* + void addElem(int32_t nval); + void addElem(int16_t nval); + void addElem(uint16_t nval); + void addElem(unsigned int nval); + */ + void addElem(int32_t lval); + void addElem(uint32_t lval); + void addElem(bool bval); + + void addElem(const char* name, float fval); + void addElem(const char* name, int8_t nval); + void addElem(const char* name, uint8_t nval); + /* + void addElem(const char* name, int nval); + void addElem(const char* name, int16_t nval); + void addElem(const char* name, uint16_t nval); + void addElem(const char* name, unsigned int nval); + */ + void addElem(const char* name, int32_t lval); + void addElem(const char* name, uint32_t lval); + void addElem(const char* name, bool bval); + void addElem(const char *name, const char *val); + void addElem(const char* name, uint64_t lval); +}; +class BufferedJsonFormatter : public JsonFormatter { + public: + void setBuffer(char *b, size_t sz); + size_t length() const; +}; +#endif diff --git a/src/WResp.cpp b/src/WResp.cpp index c40a028..64fda3b 100644 --- a/src/WResp.cpp +++ b/src/WResp.cpp @@ -35,17 +35,6 @@ void JsonSockEvent::_safecat(const char *val, bool escape) { else strcat(this->buff, val); if(escape) strcat(this->buff, "\""); } -void BufferedJsonFormatter::setBuffer(char *b, size_t sz) { - this->buff = b; - this->buffSize = sz; - this->buff[0] = 0; - this->_nocomma = true; - this->_objects = 0; - this->_arrays = 0; -} -size_t BufferedJsonFormatter::length() const { - return strlen(this->buff); -} void AsyncJsonResp::beginResponse(AsyncWebServerRequest *request, char *buff, size_t buffSize) { this->_request = request; this->buff = buff; @@ -78,143 +67,3 @@ void AsyncJsonResp::_safecat(const char *val, bool escape) { else strcat(this->buff, val); if(escape) strcat(this->buff, "\""); } - -void JsonFormatter::beginObject(const char *name) { - if(name && strlen(name) > 0) this->appendElem(name); - else if(!this->_nocomma) this->_safecat(","); - this->_safecat("{"); - this->_objects++; - this->_nocomma = true; -} -void JsonFormatter::endObject() { - //if(strlen(this->buff) + 1 > this->buffSize - 1) this->send(); - this->_safecat("}"); - this->_objects--; - this->_nocomma = false; -} -void JsonFormatter::beginArray(const char *name) { - if(name && strlen(name) > 0) this->appendElem(name); - else if(!this->_nocomma) this->_safecat(","); - this->_safecat("["); - this->_arrays++; - this->_nocomma = true; -} -void JsonFormatter::endArray() { - //if(strlen(this->buff) + 1 > this->buffSize - 1) this->send(); - this->_safecat("]"); - this->_arrays--; - this->_nocomma = false; -} - -void JsonFormatter::appendElem(const char *name) { - if(!this->_nocomma) this->_safecat(","); - if(name && strlen(name) > 0) { - this->_safecat(name, true); - this->_safecat(":"); - } - this->_nocomma = false; -} - -void JsonFormatter::addElem(const char *name, const char *val) { - if(!val) return; - this->appendElem(name); - this->_safecat(val, true); -} -void JsonFormatter::addElem(const char *val) { this->addElem(nullptr, val); } -void JsonFormatter::addElem(float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(nullptr); } -void JsonFormatter::addElem(int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); } -void JsonFormatter::addElem(uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); } -void JsonFormatter::addElem(int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(nullptr); } -void JsonFormatter::addElem(uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(nullptr); } - -/* -void JsonFormatter::addElem(int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); } -void JsonFormatter::addElem(uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); } -void JsonFormatter::addElem(int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(nullptr); } -void JsonFormatter::addElem(uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(nullptr); } -*/ -void JsonFormatter::addElem(bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(nullptr); } - -void JsonFormatter::addElem(const char *name, float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(name); } -void JsonFormatter::addElem(const char *name, int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); } -void JsonFormatter::addElem(const char *name, uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); } -void JsonFormatter::addElem(const char *name, int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(name); } -void JsonFormatter::addElem(const char *name, uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(name); } - -/* -void JsonFormatter::addElem(const char *name, int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); } -void JsonFormatter::addElem(const char *name, uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); } -void JsonFormatter::addElem(const char *name, int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(name); } -*/ -void JsonFormatter::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); } - -void JsonFormatter::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); } - -void JsonFormatter::_safecat(const char *val, bool escape) { - size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff); - if(escape) len += 2; - if(len >= this->buffSize) { - return; - } - if(escape) strcat(this->buff, "\""); - if(escape) this->escapeString(val, &this->buff[strlen(this->buff)]); - else strcat(this->buff, val); - if(escape) strcat(this->buff, "\""); -} -void JsonFormatter::_appendNumber(const char *name) { this->appendElem(name); this->_safecat(this->_numbuff); } -uint32_t JsonFormatter::calcEscapedLength(const char *raw) { - uint32_t len = 0; - for(size_t i = strlen(raw); i > 0; i--) { - switch(raw[i]) { - case '"': - case '/': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - case '\\': - len += 2; - break; - default: - len++; - break; - } - } - return len; -} -void JsonFormatter::escapeString(const char *raw, char *escaped) { - for(uint32_t i = 0; i < strlen(raw); i++) { - switch(raw[i]) { - case '"': - strcat(escaped, "\\\""); - break; - case '/': - strcat(escaped, "\\/"); - break; - case '\b': - strcat(escaped, "\\b"); - break; - case '\f': - strcat(escaped, "\\f"); - break; - case '\n': - strcat(escaped, "\\n"); - break; - case '\r': - strcat(escaped, "\\r"); - break; - case '\t': - strcat(escaped, "\\t"); - break; - case '\\': - strcat(escaped, "\\\\"); - break; - default: - size_t len = strlen(escaped); - escaped[len] = raw[i]; - escaped[len+1] = 0x00; - break; - } - } -} diff --git a/src/WResp.h b/src/WResp.h index 17e6c8c..23de961 100644 --- a/src/WResp.h +++ b/src/WResp.h @@ -2,61 +2,8 @@ #include "Somfy.h" #ifndef wresp_h #define wresp_h +#include "JsonFormatter.h" -class JsonFormatter { - protected: - char *buff; - size_t buffSize; - bool _headersSent = false; - uint8_t _objects = 0; - uint8_t _arrays = 0; - bool _nocomma = true; - char _numbuff[25] = {0}; - virtual void _safecat(const char *val, bool escape = false); - void _appendNumber(const char *name); - public: - void escapeString(const char *raw, char *escaped); - uint32_t calcEscapedLength(const char *raw); - void beginObject(const char *name = nullptr); - void endObject(); - void beginArray(const char *name = nullptr); - void endArray(); - void appendElem(const char *name = nullptr); - - void addElem(const char* val); - void addElem(float fval); - void addElem(int8_t nval); - void addElem(uint8_t nval); - /* - void addElem(int32_t nval); - void addElem(int16_t nval); - void addElem(uint16_t nval); - void addElem(unsigned int nval); - */ - void addElem(int32_t lval); - void addElem(uint32_t lval); - void addElem(bool bval); - - void addElem(const char* name, float fval); - void addElem(const char* name, int8_t nval); - void addElem(const char* name, uint8_t nval); - /* - void addElem(const char* name, int nval); - void addElem(const char* name, int16_t nval); - void addElem(const char* name, uint16_t nval); - void addElem(const char* name, unsigned int nval); - */ - void addElem(const char* name, int32_t lval); - void addElem(const char* name, uint32_t lval); - void addElem(const char* name, bool bval); - void addElem(const char *name, const char *val); - void addElem(const char* name, uint64_t lval); -}; -class BufferedJsonFormatter : public JsonFormatter { - public: - void setBuffer(char *b, size_t sz); - size_t length() const; -}; class AsyncJsonResp : public JsonFormatter { protected: void _safecat(const char *val, bool escape = false) override;