diff --git a/include/DeviceFrameworkPlaceholderRegistry.h b/include/DeviceFrameworkPlaceholderRegistry.h index 400cea3..af26bcd 100644 --- a/include/DeviceFrameworkPlaceholderRegistry.h +++ b/include/DeviceFrameworkPlaceholderRegistry.h @@ -90,6 +90,7 @@ public: * @return true if registered successfully */ bool registerRamData(const char* name, PlaceholderDataGetter getter); + bool registerDynamicData(const char* name, const DynamicDataDescriptor* descriptor); bool registerDynamicTemplate(const char* name, const DynamicTemplateDescriptor* descriptor); bool registerConditional(const char* name, const ConditionalDescriptor* descriptor); bool registerIterator(const char* name, const IteratorDescriptor* descriptor); @@ -124,6 +125,7 @@ public: // Static helper functions for length calculation static size_t getProgmemLength(const void* data); static size_t getRamLength(const void* data); + static size_t getDynamicDataLength(const DynamicDataDescriptor* descriptor, const char* data); static size_t getDynamicTemplateLength(const DynamicTemplateDescriptor* descriptor, const char* templateData); private: @@ -138,6 +140,8 @@ private: uint8_t* dest, size_t maxLen); static size_t copyRamData(PlaceholderDataGetter getter, size_t offset, uint8_t* dest, size_t maxLen); + static size_t copyDynamicData(const DynamicDataDescriptor* descriptor, size_t offset, + uint8_t* dest, size_t maxLen); }; #endif // DEVICEFRAMEWORK_PLACEHOLDER_REGISTRY_H diff --git a/include/DeviceFrameworkTemplateTypes.h b/include/DeviceFrameworkTemplateTypes.h index aa92426..9e4198a 100644 --- a/include/DeviceFrameworkTemplateTypes.h +++ b/include/DeviceFrameworkTemplateTypes.h @@ -31,6 +31,7 @@ enum class PlaceholderType { PROGMEM_DATA, // Large PROGMEM data (CSS, JS, base64 images) PROGMEM_TEMPLATE, // Nested template in PROGMEM RAM_DATA, // Dynamic RAM data via getter functions + DYNAMIC_DATA, // Dynamic RAM data via getter + userData, rendered as raw bytes DYNAMIC_TEMPLATE, CONDITIONAL, ITERATOR @@ -64,6 +65,12 @@ typedef void* (*IteratorOpenHandler)(void* userData); typedef IteratorStepResult (*IteratorNextHandler)(void* handle, struct IteratorItemView& view); typedef void (*IteratorCloseHandler)(void* handle); +struct DynamicDataDescriptor { + DynamicTemplateGetter getter; + DynamicTemplateLengthGetter getLength; + void* userData; +}; + struct DynamicTemplateDescriptor { DynamicTemplateGetter getter; DynamicTemplateLengthGetter getLength; diff --git a/src/DeviceFrameworkPlaceholderRegistry.cpp b/src/DeviceFrameworkPlaceholderRegistry.cpp index f6112ca..79feddf 100644 --- a/src/DeviceFrameworkPlaceholderRegistry.cpp +++ b/src/DeviceFrameworkPlaceholderRegistry.cpp @@ -125,6 +125,39 @@ bool DeviceFrameworkPlaceholderRegistry::registerRamData(const char* name, Place return true; } +bool DeviceFrameworkPlaceholderRegistry::registerDynamicData(const char* name, const DynamicDataDescriptor* descriptor) { + if (placeholders == nullptr || maxPlaceholders == 0) { + DFTE_LOG_ERROR("Placeholder registry not initialized"); + return false; + } + + if (count >= maxPlaceholders) { + DFTE_LOG_ERROR("Placeholder registry full, cannot register: " + String(name)); + return false; + } + + if (!validatePlaceholderName(name)) { + return false; + } + + if (descriptor == nullptr || descriptor->getter == nullptr) { + DFTE_LOG_ERROR("Invalid dynamic data descriptor for placeholder: " + String(name)); + return false; + } + + PlaceholderEntry& entry = placeholders[count]; + strncpy(entry.name, name, sizeof(entry.name) - 1); + entry.name[sizeof(entry.name) - 1] = '\0'; + entry.type = PlaceholderType::DYNAMIC_DATA; + entry.data = descriptor; + entry.getLength = nullptr; + entry.cachedLength = 0; + entry.hasCachedLength = false; + + count++; + return true; +} + bool DeviceFrameworkPlaceholderRegistry::registerDynamicTemplate(const char* name, const DynamicTemplateDescriptor* descriptor) { if (placeholders == nullptr || maxPlaceholders == 0) { DFTE_LOG_ERROR("Placeholder registry not initialized"); @@ -296,6 +329,9 @@ size_t DeviceFrameworkPlaceholderRegistry::renderPlaceholder(const PlaceholderEn case PlaceholderType::RAM_DATA: return copyRamData((PlaceholderDataGetter)entry->data, offset, buffer, maxLen); + case PlaceholderType::DYNAMIC_DATA: + return copyDynamicData(static_cast(entry->data), offset, buffer, maxLen); + case PlaceholderType::DYNAMIC_TEMPLATE: case PlaceholderType::CONDITIONAL: case PlaceholderType::ITERATOR: @@ -323,6 +359,18 @@ size_t DeviceFrameworkPlaceholderRegistry::getRamLength(const void* data) { return strlen(str); } +size_t DeviceFrameworkPlaceholderRegistry::getDynamicDataLength(const DynamicDataDescriptor* descriptor, const char* data) { + if (descriptor == nullptr || data == nullptr) { + return 0; + } + + if (descriptor->getLength) { + return descriptor->getLength(data, descriptor->userData); + } + + return strlen(data); +} + size_t DeviceFrameworkPlaceholderRegistry::getDynamicTemplateLength(const DynamicTemplateDescriptor* descriptor, const char* templateData) { if (descriptor == nullptr || templateData == nullptr) { return 0; @@ -370,3 +418,25 @@ size_t DeviceFrameworkPlaceholderRegistry::copyRamData(PlaceholderDataGetter get return chunkSize; } +size_t DeviceFrameworkPlaceholderRegistry::copyDynamicData(const DynamicDataDescriptor* descriptor, + size_t offset, + uint8_t* dest, + size_t maxLen) { + if (descriptor == nullptr || descriptor->getter == nullptr || maxLen == 0) return 0; + + const char* data = descriptor->getter(descriptor->userData); + if (data == nullptr) { + return 0; + } + + size_t dataLen = getDynamicDataLength(descriptor, data); + if (offset >= dataLen) return 0; + + size_t remaining = dataLen - offset; + constexpr size_t MAX_CHUNK = DFTE_RAM_CHUNK_SIZE; + size_t chunkSize = min(min(maxLen, remaining), MAX_CHUNK); + + memcpy(dest, data + offset, chunkSize); + return chunkSize; +} + diff --git a/src/DeviceFrameworkTemplateRenderer.cpp b/src/DeviceFrameworkTemplateRenderer.cpp index 68543e4..95d12a8 100644 --- a/src/DeviceFrameworkTemplateRenderer.cpp +++ b/src/DeviceFrameworkTemplateRenderer.cpp @@ -24,6 +24,15 @@ static bool pushPlaceholderEntry(DeviceFrameworkTemplateContext& ctx, const Plac dataCtx->context.data.offset = 0; return true; } + case PlaceholderType::DYNAMIC_DATA: { + if (!ctx.pushContext(RenderingContextType::PLACEHOLDER_DATA, name)) { + return false; + } + RenderingContext* dataCtx = ctx.getCurrentContext(); + dataCtx->context.data.entry = entry; + dataCtx->context.data.offset = 0; + return true; + } case PlaceholderType::PROGMEM_TEMPLATE: { if (!ctx.pushContext(RenderingContextType::PLACEHOLDER_TEMPLATE, name)) { return false; @@ -659,6 +668,7 @@ DeviceFrameworkTemplateRenderer::RenderOutcome DeviceFrameworkTemplateRenderer:: switch (entry->type) { case PlaceholderType::PROGMEM_DATA: case PlaceholderType::RAM_DATA: + case PlaceholderType::DYNAMIC_DATA: outcome.pushContext.type = RenderingContextType::PLACEHOLDER_DATA; outcome.pushContext.entry = entry; break; @@ -768,7 +778,11 @@ DeviceFrameworkTemplateRenderer::RenderOutcome DeviceFrameworkTemplateRenderer:: } size_t totalLength = 0; - if (entry->hasCachedLength) { + if (entry->type == PlaceholderType::DYNAMIC_DATA) { + const auto* descriptor = static_cast(entry->data); + const char* data = (descriptor && descriptor->getter) ? descriptor->getter(descriptor->userData) : nullptr; + totalLength = DeviceFrameworkPlaceholderRegistry::getDynamicDataLength(descriptor, data); + } else if (entry->hasCachedLength) { totalLength = entry->cachedLength; } else if (entry->getLength != nullptr) { totalLength = entry->getLength(entry->data);