mirror of
https://github.com/alexhopeoconnor/DFTE.git
synced 2026-10-03 22:21:59 +10:00
Release v1.1.0
This commit is contained in:
@@ -3,6 +3,7 @@ name: Build
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
tags: ["v*"]
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.1.0
|
||||||
|
|
||||||
|
- Replace layout-affecting compile-time storage overrides with per-context,
|
||||||
|
caller-selected rendering depth and read-buffer capacity. This keeps the
|
||||||
|
public ABI stable across translation units while allowing constrained
|
||||||
|
responses to use smaller storage.
|
||||||
|
- Pin ESP32 tests to the Arduino 3-compatible pioarduino platform release.
|
||||||
|
|
||||||
## 1.0.2
|
## 1.0.2
|
||||||
|
|
||||||
- Ensure open iterator handles are closed when rendering resets, stalls, or
|
- Ensure open iterator handles are closed when rendering resets, stalls, or
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ void loop() {
|
|||||||
|
|
||||||
```ini
|
```ini
|
||||||
lib_deps =
|
lib_deps =
|
||||||
DeviceFrameworkTemplateEngine=https://github.com/alexhopeoconnor/DFTE.git#v1.0.2
|
DeviceFrameworkTemplateEngine=https://github.com/alexhopeoconnor/DFTE.git#v1.1.0
|
||||||
```
|
```
|
||||||
|
|
||||||
PlatformIO checks out the Git ref after `#`; GitHub Release assets are unrelated. DFTE’s supported release targets are ESP8266 and ESP32.
|
PlatformIO checks out the Git ref after `#`; GitHub Release assets are unrelated. DFTE’s supported release targets are ESP8266 and ESP32.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Read the [documentation index](docs/README.md) for template syntax, async web responses, ABI-safe build flags, examples, tests, and releases.
|
Read the [documentation index](docs/README.md) for template syntax, async web responses, per-context memory sizing, examples, tests, and releases.
|
||||||
|
|
||||||
## Development and releases
|
## Development and releases
|
||||||
|
|
||||||
|
|||||||
+33
-17
@@ -1,25 +1,41 @@
|
|||||||
# Configuration and memory limits
|
# Configuration and memory limits
|
||||||
|
|
||||||
DFTE object layouts depend on fixed `DFTE_*` compile-time limits. Define any changes through shared PlatformIO `build_flags` so the library and every consuming translation unit agree on the same layout.
|
DFTE has a stable public object layout. `TemplateContext` owns its rendering
|
||||||
|
stack and read buffer at runtime, so choosing capacities cannot make a consumer
|
||||||
|
and the compiled library disagree about object size.
|
||||||
|
|
||||||
```ini
|
```cpp
|
||||||
build_flags =
|
TemplateContext standard; // default: 16 stack frames, 512-byte buffer
|
||||||
-DDFTE_BUFFER_SIZE=768
|
TemplateContext pageContext(6, 128); // known shallow response, smaller allocation
|
||||||
-DDFTE_MAX_STACK_DEPTH=24
|
if (!pageContext.isReady()) {
|
||||||
-DDFTE_PLACEHOLDER_NAME_SIZE=32
|
// Allocation failed; do not start a response with this context.
|
||||||
-DDFTE_MAX_ITERATIONS=80
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
| Flag | Default | Meaning |
|
The context allocates its stack and read buffer when it is constructed. Its
|
||||||
| --- | --- | --- |
|
approximate heap use is `maxDepth * sizeof(RenderingContext) + bufferSize`, plus
|
||||||
| `DFTE_BUFFER_SIZE` | 512 | Streaming buffer size in `TemplateContext` |
|
allocator overhead; its fixed 24-byte placeholder-token storage is part of the
|
||||||
| `DFTE_MAX_STACK_DEPTH` | 16 | Render stack frames; nested templates usually need two frames each |
|
stable context object. Allocate one context per concurrently streaming request; use explicit
|
||||||
| `DFTE_PLACEHOLDER_NAME_SIZE` | 24 | Maximum token length including `%` characters |
|
small capacities for bounded pages rather than changing global definitions.
|
||||||
| `DFTE_PROGMEM_CHUNK_SIZE` | 512 | PROGMEM source copy window |
|
Nested template expansion normally consumes two stack frames per level.
|
||||||
| `DFTE_RAM_CHUNK_SIZE` | 128 | RAM source copy window |
|
|
||||||
| `DFTE_MAX_ITERATIONS` | 50 | Safety cap for one render call |
|
|
||||||
| `DFTE_MAX_PLACEHOLDERS_DEFAULT` | 16 | Default registry constructor capacity |
|
|
||||||
|
|
||||||
`DFTE_MAX_PLACEHOLDERS_DEFAULT` is only a constructor default; pass an explicit registry capacity where a device needs more. DeviceFramework runtime template parameters do not change DFTE’s compile-time object layout.
|
| Setting | Default | Meaning |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `DFTE_MAX_STACK_DEPTH` | 16 | Default depth passed by the no-argument `TemplateContext` constructor |
|
||||||
|
| `DFTE_BUFFER_SIZE` | 512 | Default read-buffer size passed by the no-argument constructor |
|
||||||
|
| `DFTE_MAX_ITERATIONS` | 50 | Safety cap for one renderer call |
|
||||||
|
| `DFTE_PROGMEM_CHUNK_SIZE` | 512 | Source-copy window for flash data |
|
||||||
|
| `DFTE_RAM_CHUNK_SIZE` | 128 | Source-copy window for RAM data |
|
||||||
|
| `DFTE_MAX_PLACEHOLDERS_DEFAULT` | 16 | Default `PlaceholderRegistry` capacity |
|
||||||
|
|
||||||
|
The first two are constructor defaults, not layout controls: a consuming
|
||||||
|
translation unit can choose them without an ABI mismatch. The source-copy and
|
||||||
|
iteration settings change renderer behaviour, so set them consistently for a
|
||||||
|
whole PlatformIO build. `DFTE_PLACEHOLDER_NAME_SIZE` is no longer a supported
|
||||||
|
setting; placeholder tokens have a fixed ABI-stable capacity of 23 characters
|
||||||
|
plus the terminator.
|
||||||
|
|
||||||
|
`DFTE_MAX_PLACEHOLDERS_DEFAULT` is only a constructor default; pass an explicit
|
||||||
|
registry capacity where a device needs more.
|
||||||
|
|
||||||
Back to [documentation](README.md) · [project overview](../README.md).
|
Back to [documentation](README.md) · [project overview](../README.md).
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ build_flags =
|
|||||||
-I$PROJECT_LIBDEPS_DIR/$PIOENV/ESPAsyncTCP/src
|
-I$PROJECT_LIBDEPS_DIR/$PIOENV/ESPAsyncTCP/src
|
||||||
|
|
||||||
[env:dashboard_esp32]
|
[env:dashboard_esp32]
|
||||||
platform = espressif32@6.13.0
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
ESP32Async/AsyncTCP
|
ESP32Async/AsyncTCP
|
||||||
build_flags =
|
build_flags =
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
|
-DSOC_WIFI_SUPPORTED=1
|
||||||
|
-I${platformio.packages_dir}/framework-arduinoespressif32/libraries/Network/src
|
||||||
-I$PROJECT_LIBDEPS_DIR/$PIOENV/AsyncTCP/src
|
-I$PROJECT_LIBDEPS_DIR/$PIOENV/AsyncTCP/src
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ platform_packages =
|
|||||||
platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#521ae60a89e64bb0d1eb7a0b7addf620ced5cad3
|
platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#521ae60a89e64bb0d1eb7a0b7addf620ced5cad3
|
||||||
|
|
||||||
[env:example_esp32]
|
[env:example_esp32]
|
||||||
platform = espressif32
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ platform_packages =
|
|||||||
platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#521ae60a89e64bb0d1eb7a0b7addf620ced5cad3
|
platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#521ae60a89e64bb0d1eb7a0b7addf620ced5cad3
|
||||||
|
|
||||||
[env:example_esp32]
|
[env:example_esp32]
|
||||||
platform = espressif32@6.13.0
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,14 @@ build_flags =
|
|||||||
-I$PROJECT_LIBDEPS_DIR/$PIOENV/ESPAsyncTCP/src
|
-I$PROJECT_LIBDEPS_DIR/$PIOENV/ESPAsyncTCP/src
|
||||||
|
|
||||||
[env:example_esp32]
|
[env:example_esp32]
|
||||||
platform = espressif32
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
ESP32Async/AsyncTCP
|
ESP32Async/AsyncTCP
|
||||||
build_flags =
|
build_flags =
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
|
-DSOC_WIFI_SUPPORTED=1
|
||||||
|
-I${platformio.packages_dir}/framework-arduinoespressif32/libraries/Network/src
|
||||||
-I$PROJECT_LIBDEPS_DIR/$PIOENV/AsyncTCP/src
|
-I$PROJECT_LIBDEPS_DIR/$PIOENV/AsyncTCP/src
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,6 @@
|
|||||||
// When DeviceFrameworkConfig is included, extern variables are declared with CONFIG_* names
|
// When DeviceFrameworkConfig is included, extern variables are declared with CONFIG_* names
|
||||||
// We use DFTE_* internal names for compile-time constants (constexpr) and default parameter values
|
// We use DFTE_* internal names for compile-time constants (constexpr) and default parameter values
|
||||||
// NEVER define CONFIG_* macros here to avoid conflicts with extern declarations in DeviceFrameworkConfig
|
// NEVER define CONFIG_* macros here to avoid conflicts with extern declarations in DeviceFrameworkConfig
|
||||||
#ifndef DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT
|
|
||||||
#define DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT 24
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef DFTE_MAX_PLACEHOLDERS_DEFAULT
|
#ifndef DFTE_MAX_PLACEHOLDERS_DEFAULT
|
||||||
#define DFTE_MAX_PLACEHOLDERS_DEFAULT 16
|
#define DFTE_MAX_PLACEHOLDERS_DEFAULT 16
|
||||||
#endif
|
#endif
|
||||||
@@ -119,7 +115,7 @@ public:
|
|||||||
static size_t getDynamicTemplateLength(const DynamicTemplateDescriptor* descriptor, const char* templateData);
|
static size_t getDynamicTemplateLength(const DynamicTemplateDescriptor* descriptor, const char* templateData);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint16_t MAX_PLACEHOLDER_NAME_SIZE = DFTE_PLACEHOLDER_NAME_SIZE;
|
static constexpr uint16_t MAX_PLACEHOLDER_NAME_SIZE = DFTE_PLACEHOLDER_NAME_CAPACITY;
|
||||||
|
|
||||||
PlaceholderEntry* placeholders; // Dynamically allocated array
|
PlaceholderEntry* placeholders; // Dynamically allocated array
|
||||||
uint16_t maxPlaceholders; // Configurable size
|
uint16_t maxPlaceholders; // Configurable size
|
||||||
|
|||||||
@@ -16,11 +16,9 @@
|
|||||||
#define DFTE_BUFFER_SIZE_DEFAULT 512
|
#define DFTE_BUFFER_SIZE_DEFAULT 512
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT
|
// These are default capacities for a general-purpose standalone context. A
|
||||||
#define DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT 24
|
// caller can select smaller per-context capacities when its template topology
|
||||||
#endif
|
// is known, without changing the public object layout across translation units.
|
||||||
|
|
||||||
// Layout-affecting settings must be supplied through build-wide DFTE_* flags.
|
|
||||||
#ifndef DFTE_MAX_STACK_DEPTH
|
#ifndef DFTE_MAX_STACK_DEPTH
|
||||||
#define DFTE_MAX_STACK_DEPTH DFTE_MAX_STACK_DEPTH_DEFAULT
|
#define DFTE_MAX_STACK_DEPTH DFTE_MAX_STACK_DEPTH_DEFAULT
|
||||||
#endif
|
#endif
|
||||||
@@ -43,20 +41,26 @@ public:
|
|||||||
// Context state
|
// Context state
|
||||||
State state;
|
State state;
|
||||||
|
|
||||||
// Unified rendering stack
|
// Standalone defaults retained for callers that use the no-argument
|
||||||
|
// constructor and for tests that exercise the maximum supported depth.
|
||||||
static constexpr int MAX_RENDERING_DEPTH = DFTE_MAX_STACK_DEPTH;
|
static constexpr int MAX_RENDERING_DEPTH = DFTE_MAX_STACK_DEPTH;
|
||||||
RenderingContext renderingStack[MAX_RENDERING_DEPTH];
|
static const size_t BUFFER_SIZE = DFTE_BUFFER_SIZE;
|
||||||
|
|
||||||
|
// Per-context storage avoids layout-affecting build flags. This also lets
|
||||||
|
// constrained applications request only the stack and read buffer they
|
||||||
|
// actually need for a specific response.
|
||||||
|
RenderingContext* renderingStack;
|
||||||
|
size_t maxRenderingDepth;
|
||||||
int renderingDepth;
|
int renderingDepth;
|
||||||
|
|
||||||
// Current placeholder being built (only valid during BUILDING_PLACEHOLDER state)
|
// Current placeholder being built (only valid during BUILDING_PLACEHOLDER state)
|
||||||
// Allocated to configured size - matches CONFIG_templatePlaceholderNameSize when DeviceFramework is present
|
// Fixed ABI-stable placeholder token storage.
|
||||||
char placeholderName[DFTE_PLACEHOLDER_NAME_SIZE];
|
char placeholderName[DFTE_PLACEHOLDER_NAME_CAPACITY];
|
||||||
size_t placeholderPos;
|
size_t placeholderPos;
|
||||||
|
|
||||||
// Centralized buffer management
|
// Centralized buffer management
|
||||||
// Allocated to configured size - matches CONFIG_templateBufferSize when DeviceFramework is present
|
uint8_t* readBuffer;
|
||||||
static const size_t BUFFER_SIZE = DFTE_BUFFER_SIZE;
|
size_t readBufferSize;
|
||||||
uint8_t readBuffer[BUFFER_SIZE];
|
|
||||||
size_t bufferPos;
|
size_t bufferPos;
|
||||||
size_t bufferLen;
|
size_t bufferLen;
|
||||||
size_t bufferOffset;
|
size_t bufferOffset;
|
||||||
@@ -68,10 +72,18 @@ public:
|
|||||||
size_t totalBytesProcessed;
|
size_t totalBytesProcessed;
|
||||||
unsigned long startTime;
|
unsigned long startTime;
|
||||||
|
|
||||||
DeviceFrameworkTemplateContext();
|
explicit DeviceFrameworkTemplateContext(
|
||||||
|
size_t maxDepth = MAX_RENDERING_DEPTH,
|
||||||
|
size_t bufferSize = BUFFER_SIZE);
|
||||||
~DeviceFrameworkTemplateContext();
|
~DeviceFrameworkTemplateContext();
|
||||||
|
DeviceFrameworkTemplateContext(const DeviceFrameworkTemplateContext&) = delete;
|
||||||
|
DeviceFrameworkTemplateContext& operator=(const DeviceFrameworkTemplateContext&) = delete;
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
bool isReady() const { return renderingStack != nullptr && readBuffer != nullptr; }
|
||||||
|
size_t getMaxRenderingDepth() const { return maxRenderingDepth; }
|
||||||
|
size_t getBufferSize() const { return readBufferSize; }
|
||||||
|
|
||||||
// Unified stack management methods
|
// Unified stack management methods
|
||||||
bool pushContext(RenderingContextType type, const char* name);
|
bool pushContext(RenderingContextType type, const char* name);
|
||||||
void popContext();
|
void popContext();
|
||||||
|
|||||||
@@ -3,18 +3,9 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
// Fallback defaults when DeviceFrameworkConfig is not available (standalone usage)
|
// Placeholder-name storage has a fixed, ABI-stable capacity.
|
||||||
// Always use internal macro names (DFTE_*) to avoid conflicts with DeviceFrameworkConfig extern declarations
|
// DFTE_PLACEHOLDER_NAME_SIZE overrides are intentionally ignored.
|
||||||
#ifndef DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT
|
static constexpr size_t DFTE_PLACEHOLDER_NAME_CAPACITY = 24;
|
||||||
#define DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT 24
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Layout-affecting settings must be passed as build-wide DFTE_* flags so every
|
|
||||||
// translation unit sees the same object layout. DeviceFramework runtime config is
|
|
||||||
// intentionally not used for fixed-size storage.
|
|
||||||
#ifndef DFTE_PLACEHOLDER_NAME_SIZE
|
|
||||||
#define DFTE_PLACEHOLDER_NAME_SIZE DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Placeholder types for template substitution
|
* Placeholder types for template substitution
|
||||||
@@ -82,7 +73,7 @@ struct ConditionalDescriptor {
|
|||||||
*/
|
*/
|
||||||
struct PlaceholderEntry {
|
struct PlaceholderEntry {
|
||||||
// Allocated to configured size - matches CONFIG_templatePlaceholderNameSize when DeviceFramework is present
|
// Allocated to configured size - matches CONFIG_templatePlaceholderNameSize when DeviceFramework is present
|
||||||
char name[DFTE_PLACEHOLDER_NAME_SIZE];
|
char name[DFTE_PLACEHOLDER_NAME_CAPACITY];
|
||||||
PlaceholderType type;
|
PlaceholderType type;
|
||||||
const void* data;
|
const void* data;
|
||||||
PlaceholderLengthGetter getLength;
|
PlaceholderLengthGetter getLength;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "DeviceFrameworkTemplateEngine",
|
"name": "DeviceFrameworkTemplateEngine",
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"description": "Memory-efficient streaming template engine for ESP8266/ESP32 with chunked rendering support. Designed for embedded web interfaces with PROGMEM template support.",
|
"description": "Memory-efficient streaming template engine for ESP8266/ESP32 with chunked rendering support. Designed for embedded web interfaces with PROGMEM template support.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"template",
|
"template",
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ test_framework = unity
|
|||||||
test_build_src = yes
|
test_build_src = yes
|
||||||
|
|
||||||
[env:test_template_engine_esp32]
|
[env:test_template_engine_esp32]
|
||||||
platform = espressif32@6.13.0
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||||
board = nodemcu-32s
|
board = nodemcu-32s
|
||||||
framework = arduino
|
framework = arduino
|
||||||
test_framework = unity
|
test_framework = unity
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
#include "DeviceFrameworkTemplateContext.h"
|
#include "DeviceFrameworkTemplateContext.h"
|
||||||
#include "DeviceFrameworkTemplateEngineDebug.h"
|
#include "DeviceFrameworkTemplateEngineDebug.h"
|
||||||
|
#include <new>
|
||||||
|
|
||||||
DeviceFrameworkTemplateContext::DeviceFrameworkTemplateContext()
|
DeviceFrameworkTemplateContext::DeviceFrameworkTemplateContext(size_t maxDepth, size_t bufferSize)
|
||||||
: state(TemplateRenderState::TEXT), renderingDepth(0), placeholderPos(0),
|
: state(TemplateRenderState::TEXT), renderingStack(nullptr), maxRenderingDepth(maxDepth),
|
||||||
bufferPos(0), bufferLen(0), bufferOffset(0),
|
renderingDepth(0), placeholderPos(0), readBuffer(nullptr), readBufferSize(bufferSize),
|
||||||
registry(nullptr),
|
bufferPos(0), bufferLen(0), bufferOffset(0), registry(nullptr),
|
||||||
totalBytesProcessed(0), startTime(0) {
|
totalBytesProcessed(0), startTime(0) {
|
||||||
memset(placeholderName, 0, sizeof(placeholderName));
|
memset(placeholderName, 0, sizeof(placeholderName));
|
||||||
for (int i = 0; i < MAX_RENDERING_DEPTH; ++i) {
|
if (maxRenderingDepth == 0 || readBufferSize == 0) {
|
||||||
renderingStack[i] = RenderingContext();
|
state = TemplateRenderState::ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderingStack = new (std::nothrow) RenderingContext[maxRenderingDepth];
|
||||||
|
readBuffer = new (std::nothrow) uint8_t[readBufferSize];
|
||||||
|
if (!isReady()) {
|
||||||
|
delete[] renderingStack;
|
||||||
|
delete[] readBuffer;
|
||||||
|
renderingStack = nullptr;
|
||||||
|
readBuffer = nullptr;
|
||||||
|
state = TemplateRenderState::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,9 +28,16 @@ DeviceFrameworkTemplateContext::~DeviceFrameworkTemplateContext() {
|
|||||||
while (renderingDepth > 0) {
|
while (renderingDepth > 0) {
|
||||||
popContext();
|
popContext();
|
||||||
}
|
}
|
||||||
|
delete[] readBuffer;
|
||||||
|
delete[] renderingStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceFrameworkTemplateContext::reset() {
|
void DeviceFrameworkTemplateContext::reset() {
|
||||||
|
if (!isReady()) {
|
||||||
|
state = TemplateRenderState::ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// An interrupted render may own an iterator handle. Pop through the stack
|
// An interrupted render may own an iterator handle. Pop through the stack
|
||||||
// before overwriting it so every descriptor receives its close callback.
|
// before overwriting it so every descriptor receives its close callback.
|
||||||
while (renderingDepth > 0) {
|
while (renderingDepth > 0) {
|
||||||
@@ -34,14 +53,14 @@ void DeviceFrameworkTemplateContext::reset() {
|
|||||||
totalBytesProcessed = 0;
|
totalBytesProcessed = 0;
|
||||||
startTime = millis();
|
startTime = millis();
|
||||||
memset(placeholderName, 0, sizeof(placeholderName));
|
memset(placeholderName, 0, sizeof(placeholderName));
|
||||||
for (int i = 0; i < MAX_RENDERING_DEPTH; ++i) {
|
for (size_t i = 0; i < maxRenderingDepth; ++i) {
|
||||||
renderingStack[i] = RenderingContext();
|
renderingStack[i] = RenderingContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unified stack management methods
|
// Unified stack management methods
|
||||||
bool DeviceFrameworkTemplateContext::pushContext(RenderingContextType type, const char* name) {
|
bool DeviceFrameworkTemplateContext::pushContext(RenderingContextType type, const char* name) {
|
||||||
if (renderingDepth >= MAX_RENDERING_DEPTH) {
|
if (!isReady() || static_cast<size_t>(renderingDepth) >= maxRenderingDepth) {
|
||||||
DFTE_LOG_ERROR("Rendering stack overflow! Depth=" + String(renderingDepth));
|
DFTE_LOG_ERROR("Rendering stack overflow! Depth=" + String(renderingDepth));
|
||||||
state = TemplateRenderState::ERROR;
|
state = TemplateRenderState::ERROR;
|
||||||
return false;
|
return false;
|
||||||
@@ -195,6 +214,11 @@ String DeviceFrameworkTemplateContext::getStackTrace() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool DeviceFrameworkTemplateContext::refillBuffer() {
|
bool DeviceFrameworkTemplateContext::refillBuffer() {
|
||||||
|
if (!isReady()) {
|
||||||
|
state = TemplateRenderState::ERROR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
RenderingContext* currentCtx = getCurrentContext();
|
RenderingContext* currentCtx = getCurrentContext();
|
||||||
if (!currentCtx || currentCtx->type != RenderingContextType::TEMPLATE) {
|
if (!currentCtx || currentCtx->type != RenderingContextType::TEMPLATE) {
|
||||||
return false;
|
return false;
|
||||||
@@ -205,7 +229,7 @@ bool DeviceFrameworkTemplateContext::refillBuffer() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferLen = min(BUFFER_SIZE, templateCtx.templateLen - templateCtx.position);
|
bufferLen = min(readBufferSize, templateCtx.templateLen - templateCtx.position);
|
||||||
|
|
||||||
if (bufferLen > 0) {
|
if (bufferLen > 0) {
|
||||||
if (templateCtx.isProgmem) {
|
if (templateCtx.isProgmem) {
|
||||||
|
|||||||
@@ -19,6 +19,27 @@ void test_template_context_initialization() {
|
|||||||
TEST_ASSERT_EQUAL_MESSAGE(0, ctx.bufferPos, "Initial bufferPos should be 0");
|
TEST_ASSERT_EQUAL_MESSAGE(0, ctx.bufferPos, "Initial bufferPos should be 0");
|
||||||
TEST_ASSERT_EQUAL_MESSAGE(0, ctx.bufferLen, "Initial bufferLen should be 0");
|
TEST_ASSERT_EQUAL_MESSAGE(0, ctx.bufferLen, "Initial bufferLen should be 0");
|
||||||
TEST_ASSERT_NULL_MESSAGE(ctx.registry, "Initial registry should be null");
|
TEST_ASSERT_NULL_MESSAGE(ctx.registry, "Initial registry should be null");
|
||||||
|
TEST_ASSERT_TRUE_MESSAGE(ctx.isReady(), "Default context storage should allocate");
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(TemplateContext::MAX_RENDERING_DEPTH, ctx.getMaxRenderingDepth());
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(TemplateContext::BUFFER_SIZE, ctx.getBufferSize());
|
||||||
|
|
||||||
|
TemplateContext constrained(3, 64);
|
||||||
|
TEST_ASSERT_TRUE_MESSAGE(constrained.isReady(), "Constrained context storage should allocate");
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(3, constrained.getMaxRenderingDepth());
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(64, constrained.getBufferSize());
|
||||||
|
TEST_ASSERT_TRUE_MESSAGE(constrained.pushContext(RenderingContextType::TEMPLATE, "%ONE%"),
|
||||||
|
"First constrained stack frame should fit");
|
||||||
|
TEST_ASSERT_TRUE_MESSAGE(constrained.pushContext(RenderingContextType::TEMPLATE, "%TWO%"),
|
||||||
|
"Second constrained stack frame should fit");
|
||||||
|
TEST_ASSERT_TRUE_MESSAGE(constrained.pushContext(RenderingContextType::TEMPLATE, "%THREE%"),
|
||||||
|
"Third constrained stack frame should fit");
|
||||||
|
TEST_ASSERT_FALSE_MESSAGE(constrained.pushContext(RenderingContextType::TEMPLATE, "%FOUR%"),
|
||||||
|
"A constrained context must reject stack overflow");
|
||||||
|
TEST_ASSERT_TRUE_MESSAGE(constrained.hasError(), "Constrained stack overflow should be reported");
|
||||||
|
|
||||||
|
TemplateContext invalid(0, 64);
|
||||||
|
TEST_ASSERT_FALSE_MESSAGE(invalid.isReady(), "A zero-depth context must fail safely");
|
||||||
|
TEST_ASSERT_TRUE_MESSAGE(invalid.hasError(), "An invalid context must report an error");
|
||||||
|
|
||||||
// Test reset
|
// Test reset
|
||||||
ctx.pushContext(RenderingContextType::TEMPLATE, "TEST");
|
ctx.pushContext(RenderingContextType::TEMPLATE, "TEST");
|
||||||
|
|||||||
Reference in New Issue
Block a user