Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2020-05-18 16:53:06 +0300
committerDavid Crocker <dcrocker@eschertech.com>2020-05-18 16:53:06 +0300
commita5583981ced8cd59e8261909693c5830bec8bb7d (patch)
tree96e4c725ce6c87e32aa11b8ae072b57922580676 /src/ObjectModel
parentf5d8bdf136292e06254fcf5da131a983c3a6b0c6 (diff)
Saved some more stack when traversing object model
Diffstat (limited to 'src/ObjectModel')
-rw-r--r--src/ObjectModel/ObjectModel.cpp423
-rw-r--r--src/ObjectModel/ObjectModel.h6
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;