mirror of
https://github.com/alexhopeoconnor/DFTE.git
synced 2026-10-13 15:51:18 +10:00
277 lines
10 KiB
C++
277 lines
10 KiB
C++
#include "DeviceFrameworkTemplateContext.h"
|
|
#include "DeviceFrameworkTemplateEngineDebug.h"
|
|
|
|
DeviceFrameworkTemplateContext::DeviceFrameworkTemplateContext()
|
|
: state(TemplateRenderState::TEXT), renderingDepth(0), placeholderPos(0),
|
|
bufferPos(0), bufferLen(0), bufferOffset(0),
|
|
registry(nullptr),
|
|
totalBytesProcessed(0), startTime(0) {
|
|
memset(placeholderName, 0, sizeof(placeholderName));
|
|
for (int i = 0; i < MAX_RENDERING_DEPTH; ++i) {
|
|
renderingStack[i] = RenderingContext();
|
|
}
|
|
}
|
|
|
|
DeviceFrameworkTemplateContext::~DeviceFrameworkTemplateContext() {
|
|
while (renderingDepth > 0) {
|
|
popContext();
|
|
}
|
|
}
|
|
|
|
void DeviceFrameworkTemplateContext::reset() {
|
|
// An interrupted render may own an iterator handle. Pop through the stack
|
|
// before overwriting it so every descriptor receives its close callback.
|
|
while (renderingDepth > 0) {
|
|
popContext();
|
|
}
|
|
|
|
state = TemplateRenderState::TEXT;
|
|
renderingDepth = 0;
|
|
placeholderPos = 0;
|
|
bufferPos = 0;
|
|
bufferLen = 0;
|
|
bufferOffset = 0;
|
|
totalBytesProcessed = 0;
|
|
startTime = millis();
|
|
memset(placeholderName, 0, sizeof(placeholderName));
|
|
for (int i = 0; i < MAX_RENDERING_DEPTH; ++i) {
|
|
renderingStack[i] = RenderingContext();
|
|
}
|
|
}
|
|
|
|
// Unified stack management methods
|
|
bool DeviceFrameworkTemplateContext::pushContext(RenderingContextType type, const char* name) {
|
|
if (renderingDepth >= MAX_RENDERING_DEPTH) {
|
|
DFTE_LOG_ERROR("Rendering stack overflow! Depth=" + String(renderingDepth));
|
|
state = TemplateRenderState::ERROR;
|
|
return false;
|
|
}
|
|
|
|
// Save current buffer state if we're currently in a template context
|
|
if (renderingDepth > 0) {
|
|
RenderingContext* currentCtx = getCurrentContext();
|
|
if (currentCtx && currentCtx->type == RenderingContextType::TEMPLATE) {
|
|
// Save buffer state to current template context
|
|
currentCtx->context.templateCtx.bufferPos = bufferPos;
|
|
currentCtx->context.templateCtx.bufferLen = bufferLen;
|
|
currentCtx->context.templateCtx.bufferOffset = bufferOffset;
|
|
}
|
|
}
|
|
|
|
RenderingContext& ctx = renderingStack[renderingDepth];
|
|
ctx.type = type;
|
|
ctx.name = name;
|
|
|
|
// Initialize buffer state for new template context
|
|
if (type == RenderingContextType::TEMPLATE) {
|
|
ctx.context.templateCtx.bufferPos = 0;
|
|
ctx.context.templateCtx.bufferLen = 0;
|
|
ctx.context.templateCtx.bufferOffset = 0;
|
|
ctx.context.templateCtx.iteratorPlaceholders = nullptr;
|
|
ctx.context.templateCtx.iteratorPlaceholderCount = 0;
|
|
bufferPos = 0;
|
|
bufferLen = 0;
|
|
bufferOffset = 0;
|
|
}
|
|
|
|
renderingDepth++;
|
|
return true;
|
|
}
|
|
|
|
void DeviceFrameworkTemplateContext::popContext() {
|
|
if (renderingDepth <= 0) {
|
|
DFTE_LOG_ERROR("Rendering stack underflow!");
|
|
state = TemplateRenderState::ERROR;
|
|
return;
|
|
}
|
|
|
|
renderingDepth--;
|
|
RenderingContext& ctx = renderingStack[renderingDepth];
|
|
|
|
if (ctx.type == RenderingContextType::PLACEHOLDER_ITERATOR) {
|
|
const IteratorDescriptor* descriptor = ctx.context.iterator.descriptor;
|
|
if (descriptor && descriptor->close && ctx.context.iterator.handleOpen) {
|
|
descriptor->close(ctx.context.iterator.handle);
|
|
}
|
|
}
|
|
|
|
// Restore buffer state from parent template context if it exists
|
|
if (renderingDepth > 0) {
|
|
RenderingContext* parentCtx = getCurrentContext();
|
|
if (parentCtx && parentCtx->type == RenderingContextType::TEMPLATE) {
|
|
auto& parentTemplateCtx = parentCtx->context.templateCtx;
|
|
// Invalidate shared buffer so parent refill starts fresh
|
|
bufferPos = 0;
|
|
bufferLen = 0;
|
|
bufferOffset = parentTemplateCtx.position;
|
|
|
|
// Clear cached buffer state stored on the parent template context
|
|
parentTemplateCtx.bufferPos = 0;
|
|
parentTemplateCtx.bufferLen = 0;
|
|
parentTemplateCtx.bufferOffset = parentTemplateCtx.position;
|
|
} else {
|
|
// No parent template context, reset buffer state
|
|
bufferPos = 0;
|
|
bufferLen = 0;
|
|
bufferOffset = 0;
|
|
}
|
|
} else {
|
|
// No more contexts, reset buffer state
|
|
bufferPos = 0;
|
|
bufferLen = 0;
|
|
bufferOffset = 0;
|
|
}
|
|
|
|
// Clear the popped context
|
|
ctx = RenderingContext();
|
|
}
|
|
|
|
RenderingContext* DeviceFrameworkTemplateContext::getCurrentContext() {
|
|
if (renderingDepth == 0) return nullptr;
|
|
return &renderingStack[renderingDepth - 1];
|
|
}
|
|
|
|
RenderingContext* DeviceFrameworkTemplateContext::getContext(int depth) {
|
|
if (depth < 0 || depth >= renderingDepth) return nullptr;
|
|
return &renderingStack[depth];
|
|
}
|
|
|
|
bool DeviceFrameworkTemplateContext::isRenderingTemplate() const {
|
|
if (renderingDepth == 0) return false;
|
|
return renderingStack[renderingDepth - 1].type == RenderingContextType::TEMPLATE;
|
|
}
|
|
|
|
bool DeviceFrameworkTemplateContext::isRenderingPlaceholder() const {
|
|
if (renderingDepth == 0) return false;
|
|
RenderingContextType type = renderingStack[renderingDepth - 1].type;
|
|
return type == RenderingContextType::PLACEHOLDER_DATA ||
|
|
type == RenderingContextType::PLACEHOLDER_TEMPLATE;
|
|
}
|
|
|
|
RenderingContextType DeviceFrameworkTemplateContext::getCurrentContextType() const {
|
|
if (renderingDepth == 0) return RenderingContextType::TEMPLATE; // Default
|
|
return renderingStack[renderingDepth - 1].type;
|
|
}
|
|
|
|
bool DeviceFrameworkTemplateContext::isComplete() const {
|
|
return state == TemplateRenderState::COMPLETE || state == TemplateRenderState::ERROR;
|
|
}
|
|
|
|
bool DeviceFrameworkTemplateContext::hasError() const {
|
|
return state == TemplateRenderState::ERROR;
|
|
}
|
|
|
|
String DeviceFrameworkTemplateContext::getStateString() const {
|
|
switch (state) {
|
|
case TemplateRenderState::TEXT: return "TEXT";
|
|
case TemplateRenderState::BUILDING_PLACEHOLDER: return "BUILDING_PLACEHOLDER";
|
|
case TemplateRenderState::RENDERING_CONTEXT: return "RENDERING_CONTEXT";
|
|
case TemplateRenderState::COMPLETE: return "COMPLETE";
|
|
case TemplateRenderState::ERROR: return "ERROR";
|
|
default: return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
String DeviceFrameworkTemplateContext::getStackTrace() const {
|
|
String trace = "Stack trace (depth=" + String(renderingDepth) + "):\n";
|
|
for (int i = 0; i < renderingDepth; i++) {
|
|
const RenderingContext& ctx = renderingStack[i];
|
|
String typeStr;
|
|
switch (ctx.type) {
|
|
case RenderingContextType::TEMPLATE: typeStr = "TEMPLATE"; break;
|
|
case RenderingContextType::PLACEHOLDER_DATA: typeStr = "PLACEHOLDER_DATA"; break;
|
|
case RenderingContextType::PLACEHOLDER_TEMPLATE: typeStr = "PLACEHOLDER_TEMPLATE"; break;
|
|
default: typeStr = "UNKNOWN"; break;
|
|
}
|
|
trace += " [" + String(i) + "] " + String(ctx.name) + " (type=" + typeStr + ")";
|
|
if (ctx.type == RenderingContextType::TEMPLATE) {
|
|
trace += " at pos " + String(ctx.context.templateCtx.position);
|
|
} else if (ctx.type == RenderingContextType::PLACEHOLDER_DATA) {
|
|
trace += " at offset " + String(ctx.context.data.offset);
|
|
}
|
|
trace += "\n";
|
|
}
|
|
return trace;
|
|
}
|
|
|
|
bool DeviceFrameworkTemplateContext::refillBuffer() {
|
|
RenderingContext* currentCtx = getCurrentContext();
|
|
if (!currentCtx || currentCtx->type != RenderingContextType::TEMPLATE) {
|
|
return false;
|
|
}
|
|
|
|
auto& templateCtx = currentCtx->context.templateCtx;
|
|
if (templateCtx.position >= templateCtx.templateLen) {
|
|
return false;
|
|
}
|
|
|
|
bufferLen = min(BUFFER_SIZE, templateCtx.templateLen - templateCtx.position);
|
|
|
|
if (bufferLen > 0) {
|
|
if (templateCtx.isProgmem) {
|
|
memcpy_P(readBuffer, templateCtx.templateData + templateCtx.position, bufferLen);
|
|
} else {
|
|
memcpy(readBuffer, templateCtx.templateData + templateCtx.position, bufferLen);
|
|
}
|
|
bufferPos = 0;
|
|
bufferOffset = templateCtx.position;
|
|
// Save buffer state to template context
|
|
templateCtx.bufferPos = bufferPos;
|
|
templateCtx.bufferLen = bufferLen;
|
|
templateCtx.bufferOffset = bufferOffset;
|
|
// Don't advance templateCtx.position here - it will be updated as we read characters
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
char DeviceFrameworkTemplateContext::getNextChar() {
|
|
RenderingContext* currentCtx = getCurrentContext();
|
|
if (!currentCtx || currentCtx->type != RenderingContextType::TEMPLATE) {
|
|
return '\0';
|
|
}
|
|
|
|
// Restore buffer state from template context if needed
|
|
auto& templateCtx = currentCtx->context.templateCtx;
|
|
if (bufferPos == 0 && bufferLen == 0 && templateCtx.bufferLen > 0) {
|
|
// Buffer state might be stale, restore from context
|
|
bufferPos = templateCtx.bufferPos;
|
|
bufferLen = templateCtx.bufferLen;
|
|
bufferOffset = templateCtx.bufferOffset;
|
|
}
|
|
|
|
if (bufferPos >= bufferLen) {
|
|
if (!refillBuffer()) {
|
|
return '\0'; // End of template
|
|
}
|
|
}
|
|
char c = readBuffer[bufferPos++];
|
|
// Update template position to reflect current position
|
|
templateCtx.position = bufferOffset + bufferPos;
|
|
// Save buffer state to template context
|
|
templateCtx.bufferPos = bufferPos;
|
|
templateCtx.bufferLen = bufferLen;
|
|
templateCtx.bufferOffset = bufferOffset;
|
|
return c;
|
|
}
|
|
|
|
size_t DeviceFrameworkTemplateContext::getAvailableBytes() const {
|
|
return bufferLen - bufferPos;
|
|
}
|
|
|
|
bool DeviceFrameworkTemplateContext::hasMoreData() const {
|
|
RenderingContext* currentCtx = const_cast<DeviceFrameworkTemplateContext*>(this)->getCurrentContext();
|
|
if (!currentCtx || currentCtx->type != RenderingContextType::TEMPLATE) {
|
|
return false;
|
|
}
|
|
const auto& templateCtx = currentCtx->context.templateCtx;
|
|
return templateCtx.position < templateCtx.templateLen || bufferPos < bufferLen;
|
|
}
|
|
|
|
void DeviceFrameworkTemplateContext::resetPlaceholder() {
|
|
placeholderPos = 0;
|
|
memset(placeholderName, 0, sizeof(placeholderName));
|
|
}
|
|
|