diff options
author | David Crocker <dcrocker@eschertech.com> | 2020-05-18 16:53:06 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2020-05-18 16:53:06 +0300 |
commit | a5583981ced8cd59e8261909693c5830bec8bb7d (patch) | |
tree | 96e4c725ce6c87e32aa11b8ae072b57922580676 /src/ObjectModel | |
parent | f5d8bdf136292e06254fcf5da131a983c3a6b0c6 (diff) |
Saved some more stack when traversing object model
Diffstat (limited to 'src/ObjectModel')
-rw-r--r-- | src/ObjectModel/ObjectModel.cpp | 423 | ||||
-rw-r--r-- | src/ObjectModel/ObjectModel.h | 6 |
2 files changed, 229 insertions, 200 deletions
diff --git a/src/ObjectModel/ObjectModel.cpp b/src/ObjectModel/ObjectModel.cpp index ecb57f25..93eca965 100644 --- a/src/ObjectModel/ObjectModel.cpp +++ b/src/ObjectModel/ObjectModel.cpp @@ -166,7 +166,7 @@ void ExpressionValue::ExtractRequestedPart(const StringRef& rslt) const noexcept #endif -void ObjectExplorationContext::AddIndex(int32_t index) +void ObjectExplorationContext::AddIndex(int32_t index) THROWS(GCodeException) { if (numIndicesCounted == MaxIndices) { @@ -176,7 +176,7 @@ void ObjectExplorationContext::AddIndex(int32_t index) ++numIndicesCounted; } -void ObjectExplorationContext::AddIndex() +void ObjectExplorationContext::AddIndex() THROWS(GCodeException) { if (numIndicesCounted == numIndicesProvided) { @@ -185,7 +185,7 @@ void ObjectExplorationContext::AddIndex() ++numIndicesCounted; } -void ObjectExplorationContext::RemoveIndex() +void ObjectExplorationContext::RemoveIndex() THROWS(GCodeException) { if (numIndicesCounted == 0) { @@ -194,7 +194,7 @@ void ObjectExplorationContext::RemoveIndex() --numIndicesCounted; } -void ObjectExplorationContext::ProvideIndex(int32_t index) +void ObjectExplorationContext::ProvideIndex(int32_t index) THROWS(GCodeException) { if (numIndicesProvided == MaxIndices) { @@ -252,7 +252,7 @@ ObjectExplorationContext::ObjectExplorationContext(const char *reportFlags, bool } } -int32_t ObjectExplorationContext::GetIndex(size_t n) const +int32_t ObjectExplorationContext::GetIndex(size_t n) const THROWS(GCodeException) { if (n < numIndicesCounted) { @@ -261,7 +261,7 @@ int32_t ObjectExplorationContext::GetIndex(size_t n) const THROW_INTERNAL_ERROR; } -int32_t ObjectExplorationContext::GetLastIndex() const +int32_t ObjectExplorationContext::GetLastIndex() const THROWS(GCodeException) { if (numIndicesCounted != 0) { @@ -287,7 +287,8 @@ GCodeException ObjectExplorationContext::ConstructParseException(const char *msg } // Report this object -void ObjectModel::ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, uint8_t tableNumber, const char* filter) const +void ObjectModel::ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, + uint8_t tableNumber, const char* filter) const THROWS(GCodeException) { if (context.IncreaseDepth()) { @@ -349,7 +350,7 @@ void ObjectModel::ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& cont } // Construct a JSON representation of those parts of the object model requested by the user. This version is called on the root of the tree. -void ObjectModel::ReportAsJson(OutputBuffer *buf, const char *filter, const char *reportFlags, bool wantArrayLength) const +void ObjectModel::ReportAsJson(OutputBuffer *buf, const char *filter, const char *reportFlags, bool wantArrayLength) const THROWS(GCodeException) { const unsigned int defaultMaxDepth = (wantArrayLength) ? 99 : (filter[0] == 0) ? 1 : 99; ObjectExplorationContext context(reportFlags, wantArrayLength, defaultMaxDepth); @@ -357,257 +358,281 @@ void ObjectModel::ReportAsJson(OutputBuffer *buf, const char *filter, const char } // Function to report a value or object as JSON -// This function is recursive, so keep its stack usage low -void ObjectModel::ReportItemAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ExpressionValue& val, const char *filter) const +// This function is recursive, so keep its stack usage low. +// Most recursive calls are for non-array object values, so handle object values inline to reduce stack usage. +// This saves about 240 bytes of stack space but costs 272 bytes of flash memory. +inline void ObjectModel::ReportItemAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, + const ExpressionValue& val, const char *filter) const THROWS(GCodeException) { if (context.WantArrayLength() && *filter == 0) { - // We have been asked for the length of an array and we have reached the end of the filter, so the value should be an array - switch (val.GetType()) + ReportArrayLengthAsJson(buf, context, val); + } + else if (val.GetType() == TypeCode::ObjectModel) + { + if (*filter == '.') { - case TypeCode::Array: - buf->catf("%u", val.omadVal->GetNumElements(this, context)); - break; + ++filter; + } + else if (*filter != 0) + { + buf->cat("null"); // error, should have reached the end of the filter or a '.' + return; + } + val.omVal->ReportAsJson(buf, context, (val.omVal == this) ? classDescriptor : nullptr, val.param, filter); + } + else + { + ReportItemAsJsonFull(buf, context, classDescriptor, val, filter); + } +} - case TypeCode::Bitmap16: - case TypeCode::Bitmap32: - buf->catf("%u", Bitmap<uint32_t>::MakeFromRaw(val.uVal).CountSetBits()); - break; +void ObjectModel::ReportArrayLengthAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ExpressionValue& val) const noexcept +{ + switch (val.GetType()) + { + case TypeCode::Array: + buf->catf("%u", val.omadVal->GetNumElements(this, context)); + break; - case TypeCode::Bitmap64: - buf->catf("%u", Bitmap<uint64_t>::MakeFromRaw(val.Get56BitValue()).CountSetBits()); - break; + case TypeCode::Bitmap16: + case TypeCode::Bitmap32: + buf->catf("%u", Bitmap<uint32_t>::MakeFromRaw(val.uVal).CountSetBits()); + break; - default: - buf->cat("null"); - break; - } + case TypeCode::Bitmap64: + buf->catf("%u", Bitmap<uint64_t>::MakeFromRaw(val.Get56BitValue()).CountSetBits()); + break; + + case TypeCode::CString: + buf->catf("%u", strlen(val.sVal)); + break; + + default: + buf->cat("null"); + break; } - else +} + +// Function to report a value or object as JSON +// This function is recursive, so keep its stack usage low +void ObjectModel::ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, + const ExpressionValue& val, const char *filter) const THROWS(GCodeException) +{ + switch (val.GetType()) { - switch (val.GetType()) + case TypeCode::Array: + if (*filter == '[') { - case TypeCode::Array: - if (*filter == '[') + ++filter; + if (*filter == ']') // if reporting on [parts of] all elements in the array { - ++filter; - if (*filter == ']') // if reporting on [parts of] all elements in the array + ReportArrayAsJson(buf, context, classDescriptor, val.omadVal, filter + 1); + } + else + { + const char *endptr; + const int32_t index = StrToI32(filter, &endptr); + if (endptr == filter || *endptr != ']' || index < 0 || (size_t)index >= val.omadVal->GetNumElements(this, context)) { - ReportArrayAsJson(buf, context, classDescriptor, val.omadVal, filter + 1); + buf->cat("null"); // avoid returning badly-formed JSON + break; // invalid syntax, or index out of range } - else + if (*filter == 0) { - const char *endptr; - const int32_t index = StrToI32(filter, &endptr); - if (endptr == filter || *endptr != ']' || index < 0 || (size_t)index >= val.omadVal->GetNumElements(this, context)) - { - buf->cat("null"); // avoid returning badly-formed JSON - break; // invalid syntax, or index out of range - } - if (*filter == 0) - { - buf->cat('['); - } - context.AddIndex(index); - { - ReadLocker lock(val.omadVal->lockPointer); - const ExpressionValue element = val.omadVal->GetElement(this, context); - ReportItemAsJson(buf, context, classDescriptor, element, endptr + 1); - } - context.RemoveIndex(); - if (*filter == 0) - { - buf->cat(']'); - } + buf->cat('['); + } + context.AddIndex(index); + { + // As at release 3.1.1 this next block uses the most stack of this entire function + ReadLocker lock(val.omadVal->lockPointer); + const ExpressionValue element = val.omadVal->GetElement(this, context); + ReportItemAsJson(buf, context, classDescriptor, element, endptr + 1); + } + context.RemoveIndex(); + if (*filter == 0) + { + buf->cat(']'); } } - else if (*filter == 0) // else reporting on all subparts of all elements in the array, or just the length - { - ReportArrayAsJson(buf, context, classDescriptor, val.omadVal, filter); - } - else - { - buf->cat("null"); - } - break; - - case TypeCode::ObjectModel: - if (*filter == '.') - { - ++filter; - } - else if (*filter != 0) - { - buf->cat("null"); // error, should have reached the end of the filter or a '.' - break; - } - val.omVal->ReportAsJson(buf, context, (val.omVal == this) ? classDescriptor : nullptr, val.param, filter); - break; + } + else if (*filter == 0) // else reporting on all subparts of all elements in the array, or just the length + { + ReportArrayAsJson(buf, context, classDescriptor, val.omadVal, filter); + } + else + { + buf->cat("null"); + } + break; - case TypeCode::Float: - ReportFloat(buf, val); - break; + case TypeCode::Float: + ReportFloat(buf, val); + break; - case TypeCode::Uint32: - buf->catf("%" PRIu32, val.uVal); - break; + case TypeCode::Uint32: + buf->catf("%" PRIu32, val.uVal); + break; - case TypeCode::Uint64: - buf->catf("%" PRIu64, ((uint64_t)val.param << 32) | val.uVal); // convert unsigned integer to string - break; + case TypeCode::Uint64: + buf->catf("%" PRIu64, ((uint64_t)val.param << 32) | val.uVal); // convert unsigned integer to string + break; - case TypeCode::Int32: - buf->catf("%" PRIi32, val.iVal); - break; + case TypeCode::Int32: + buf->catf("%" PRIi32, val.iVal); + break; - case TypeCode::CString: - buf->EncodeString(val.sVal, true); - break; + case TypeCode::CString: + buf->EncodeString(val.sVal, true); + break; #ifdef DUET3 - case TypeCode::CanExpansionBoardDetails: - ReportExpansionBoardDetail(buf, val); - break; + case TypeCode::CanExpansionBoardDetails: + ReportExpansionBoardDetail(buf, val); + break; #endif - case TypeCode::Bitmap16: - case TypeCode::Bitmap32: - if (*filter == '[') + case TypeCode::Bitmap16: + case TypeCode::Bitmap32: + if (*filter == '[') + { + ++filter; + if (*filter == ']') // if reporting on all elements in the array { ++filter; - if (*filter == ']') // if reporting on all elements in the array - { - ++filter; - } - else - { - const char *endptr; - const int32_t index = StrToI32(filter, &endptr); - if (endptr == filter || *endptr != ']' || index < 0 || (size_t)index >= val.omadVal->GetNumElements(this, context)) - { - buf->cat("null"); // avoid returning badly-formed JSON - break; // invalid syntax, or index out of range - } - const auto bm = Bitmap<uint32_t>::MakeFromRaw(val.uVal); - buf->catf("%u", bm.GetSetBitNumber(index)); - break; - } - } - else if (context.ShortFormReport()) - { - buf->catf("%" PRIu32, val.uVal); - break; } - - // If we get here then we want a long form report - ReportBitmap1632Long(buf, val); - break; - - case TypeCode::Bitmap64: - if (*filter == '[') + else { - ++filter; - if (*filter == ']') // if reporting on all elements in the array - { - ++filter; - } - else + const char *endptr; + const int32_t index = StrToI32(filter, &endptr); + if (endptr == filter || *endptr != ']' || index < 0 || (size_t)index >= val.omadVal->GetNumElements(this, context)) { - const char *endptr; - const int32_t index = StrToI32(filter, &endptr); - if (endptr == filter || *endptr != ']' || index < 0 || (size_t)index >= val.omadVal->GetNumElements(this, context)) - { - buf->cat("null"); // avoid returning badly-formed JSON - break; // invalid syntax, or index out of range - } - const auto bm = Bitmap<uint64_t>::MakeFromRaw(val.uVal); - buf->catf("%u", bm.GetSetBitNumber(index)); - break; + buf->cat("null"); // avoid returning badly-formed JSON + break; // invalid syntax, or index out of range } - } - else if (context.ShortFormReport()) - { - buf->catf("%" PRIu64, val.Get56BitValue()); + const auto bm = Bitmap<uint32_t>::MakeFromRaw(val.uVal); + buf->catf("%u", bm.GetSetBitNumber(index)); break; } - - // If we get here then we want a long form report - ReportBitmap64Long(buf, val); + } + else if (context.ShortFormReport()) + { + buf->catf("%" PRIu32, val.uVal); break; + } - case TypeCode::Enum32: - if (context.ShortFormReport()) + // If we get here then we want a long form report + ReportBitmap1632Long(buf, val); + break; + + case TypeCode::Bitmap64: + if (*filter == '[') + { + ++filter; + if (*filter == ']') // if reporting on all elements in the array { - buf->catf("%" PRIu32, val.uVal); + ++filter; } else { - buf->cat("\"unimplemented\""); - // TODO append the real name + const char *endptr; + const int32_t index = StrToI32(filter, &endptr); + if (endptr == filter || *endptr != ']' || index < 0 || (size_t)index >= val.omadVal->GetNumElements(this, context)) + { + buf->cat("null"); // avoid returning badly-formed JSON + break; // invalid syntax, or index out of range + } + const auto bm = Bitmap<uint64_t>::MakeFromRaw(val.uVal); + buf->catf("%u", bm.GetSetBitNumber(index)); + break; } + } + else if (context.ShortFormReport()) + { + buf->catf("%" PRIu64, val.Get56BitValue()); break; + } - case TypeCode::Bool: - buf->cat((val.bVal) ? "true" : "false"); - break; + // If we get here then we want a long form report + ReportBitmap64Long(buf, val); + break; - case TypeCode::Char: - buf->cat('"'); - buf->EncodeChar(val.cVal); - buf->cat('"'); - break; + case TypeCode::Enum32: + if (context.ShortFormReport()) + { + buf->catf("%" PRIu32, val.uVal); + } + else + { + buf->cat("\"unimplemented\""); + // TODO append the real name + } + break; + + case TypeCode::Bool: + buf->cat((val.bVal) ? "true" : "false"); + break; - case TypeCode::IPAddress: + case TypeCode::Char: + buf->cat('"'); + buf->EncodeChar(val.cVal); + buf->cat('"'); + break; + + case TypeCode::IPAddress: + { + const IPAddress ipVal(val.uVal); + char sep = '"'; + for (unsigned int q = 0; q < 4; ++q) { - const IPAddress ipVal(val.uVal); - char sep = '"'; - for (unsigned int q = 0; q < 4; ++q) - { - buf->catf("%c%u", sep, ipVal.GetQuad(q)); - sep = '.'; - } - buf->cat('"'); + buf->catf("%c%u", sep, ipVal.GetQuad(q)); + sep = '.'; } - break; + buf->cat('"'); + } + break; - case TypeCode::DateTime: - ReportDateTime(buf, val); - break; + case TypeCode::DateTime: + ReportDateTime(buf, val); + break; - case TypeCode::DriverId: + case TypeCode::DriverId: #if SUPPORT_CAN_EXPANSION - buf->catf("\"%u.%u\"", (unsigned int)(val.uVal >> 8), (unsigned int)(val.uVal & 0xFF)); + buf->catf("\"%u.%u\"", (unsigned int)(val.uVal >> 8), (unsigned int)(val.uVal & 0xFF)); #else - buf->catf("\"%u\"", (unsigned int)val.uVal); + buf->catf("\"%u\"", (unsigned int)val.uVal); #endif - break; + break; - case TypeCode::MacAddress: - buf->catf("\"%02x:%02x:%02x:%02x:%02x:%02x\"", - (unsigned int)(val.uVal & 0xFF), (unsigned int)((val.uVal >> 8) & 0xFF), (unsigned int)((val.uVal >> 16) & 0xFF), (unsigned int)((val.uVal >> 24) & 0xFF), - (unsigned int)(val.param & 0xFF), (unsigned int)((val.param >> 8) & 0xFF)); - break; + case TypeCode::MacAddress: + buf->catf("\"%02x:%02x:%02x:%02x:%02x:%02x\"", + (unsigned int)(val.uVal & 0xFF), (unsigned int)((val.uVal >> 8) & 0xFF), (unsigned int)((val.uVal >> 16) & 0xFF), (unsigned int)((val.uVal >> 24) & 0xFF), + (unsigned int)(val.param & 0xFF), (unsigned int)((val.param >> 8) & 0xFF)); + break; - case TypeCode::Special: + case TypeCode::Special: #if HAS_MASS_STORAGE - switch ((ExpressionValue::SpecialType)val.param) - { - case ExpressionValue::SpecialType::sysDir: - reprap.GetPlatform().EncodeSysDir(buf); - break; - } -#endif - break; - - case TypeCode::None: - buf->cat("null"); + switch ((ExpressionValue::SpecialType)val.param) + { + case ExpressionValue::SpecialType::sysDir: + reprap.GetPlatform().EncodeSysDir(buf); break; } +#endif + break; + + case TypeCode::None: + buf->cat("null"); + break; + + case TypeCode::ObjectModel: + break; // we already handled this case in the inline part } } // Report an entire array as JSON -void ObjectModel::ReportArrayAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ObjectModelArrayDescriptor *omad, const char *filter) const +void ObjectModel::ReportArrayAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, + const ObjectModelArrayDescriptor *omad, const char *filter) const THROWS(GCodeException) { ReadLocker lock(omad->lockPointer); diff --git a/src/ObjectModel/ObjectModel.h b/src/ObjectModel/ObjectModel.h index 748e379c..321b6740 100644 --- a/src/ObjectModel/ObjectModel.h +++ b/src/ObjectModel/ObjectModel.h @@ -227,7 +227,8 @@ public: ExpressionValue GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, const char *idString, uint8_t tableNumber = 0) const THROWS(GCodeException); // Function to report a value or object as JSON - void ReportItemAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ExpressionValue& val, const char *filter) const THROWS(GCodeException); + void ReportItemAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, + const ExpressionValue& val, const char *filter) const THROWS(GCodeException); // Skip the current element in the ID or filter string static const char* GetNextElement(const char *id) noexcept; @@ -249,6 +250,9 @@ protected: private: // These functions have been separated from ReportItemAsJson to avoid high stack usage in the recursive functions, therefore they must not be inlined + __attribute__ ((noinline)) void ReportArrayLengthAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ExpressionValue& val) const noexcept; + __attribute__ ((noinline)) void ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, + const ExpressionValue& val, const char *filter) const THROWS(GCodeException); __attribute__ ((noinline)) static void ReportDateTime(OutputBuffer *buf, const ExpressionValue& val) noexcept; __attribute__ ((noinline)) static void ReportFloat(OutputBuffer *buf, const ExpressionValue& val) noexcept; __attribute__ ((noinline)) static void ReportBitmap1632Long(OutputBuffer *buf, const ExpressionValue& val) noexcept; |