This commit is contained in:
J. Nick Koston
2026-01-29 16:23:41 -06:00
parent aba8a83cba
commit 55ff740e4e
2 changed files with 20 additions and 4 deletions

View File

@@ -71,10 +71,22 @@ SerializationBuffer<> JsonBuilder::serialize() {
buf[2] = '\0';
return result;
}
size_t size = measureJson(doc_);
SerializationBuffer<> result(size);
serializeJson(doc_, result.data_writable(), size + 1);
return result;
// Intentionally avoid measureJson() - it instantiates DummyWriter templates that add ~700 bytes
// of flash. Instead, try serializing to stack buffer first. 768 bytes covers typical JSON payloads
// (sensors ~200B, lights ~170B, climate ~700B). Only entities with many options exceed this.
// serializeJson() returns actual size needed even if truncated, so we can retry with heap if needed.
constexpr size_t buf_size = SerializationBuffer<>::BUFFER_SIZE;
SerializationBuffer<> result(buf_size - 1); // Max content size (reserve 1 for null)
size_t size = serializeJson(doc_, result.data_writable(), buf_size);
if (size < buf_size) {
// Fits in stack buffer - update size to actual length
result.set_size(size);
return result;
}
// Needs heap allocation - serialize again with exact size
SerializationBuffer<> heap_result(size);
serializeJson(doc_, heap_result.data_writable(), size + 1);
return heap_result;
}
} // namespace json

View File

@@ -21,6 +21,8 @@ namespace json {
/// Supports move semantics for efficient return-by-value.
template<size_t STACK_SIZE = 768> class SerializationBuffer {
public:
static constexpr size_t BUFFER_SIZE = STACK_SIZE; ///< Stack buffer size for this instantiation
/// Construct with known size (typically from measureJson)
explicit SerializationBuffer(size_t size) : size_(size) {
if (size + 1 <= STACK_SIZE) {
@@ -84,6 +86,8 @@ template<size_t STACK_SIZE = 768> class SerializationBuffer {
size_t size() const { return size_; }
/// Get writable buffer (for serialization)
char *data_writable() { return buffer_; }
/// Set actual size after serialization (must not exceed allocated size)
void set_size(size_t size) { size_ = size; }
/// Implicit conversion to std::string for backward compatibility
operator std::string() const { return std::string(buffer_, size_); } // NOLINT(google-explicit-constructor)