From b7068e162e36206ce1740a461c1b6608ef783fa2 Mon Sep 17 00:00:00 2001 From: Bill Vaglienti Date: Mon, 13 Apr 2020 15:10:08 -0700 Subject: [PATCH] Added logic to specify an include directive in the source file for a code tag. Improved field limiting logic by account for floating point rounding error and actual maximum enumeration size. --- README.md | 1 + encodable.h | 3 +++ enumcreator.cpp | 14 ++++++++------ enumcreator.h | 6 ++++++ exampleprotocol.xml | 2 +- protocolcode.cpp | 12 ++++++++++++ protocolcode.h | 4 ++++ protocolfield.cpp | 36 +++++++++++++++++++++++++----------- protocolfield.h | 1 + protocolpacket.cpp | 18 ++++++------------ protocolparser.cpp | 16 +++++++++------- protocolparser.h | 2 +- protocolstructure.cpp | 18 +++++++++++++++++- protocolstructure.h | 3 +++ protocolstructuremodule.cpp | 31 ++++++++++++++++++++++++------- protocolstructuremodule.h | 3 +++ 16 files changed, 124 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 8db655c..9deefd6 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,7 @@ Code subtag attributes: - `encode` : A verbatim code snippet that is inserted in the encode function. - `decode` : A verbatim code snippet that is inserted in the decode function. - `comment` : A one line comment above the code snippet. +- `include` : The name of a file to #include in the source code of the function that contains the structure encoding/decoding functions. This is useful to support calling external functions from the Code subtag. Packet tag ---------- diff --git a/encodable.h b/encodable.h index 66a55ae..f27b48f 100644 --- a/encodable.h +++ b/encodable.h @@ -43,6 +43,9 @@ class Encodable : public ProtocolDocumentation //! Return the include directives needed for this encodable virtual void getIncludeDirectives(QStringList& list) const {Q_UNUSED(list);} + //! Return the include directives that go into source code needed for this encodable + virtual void getSourceIncludeDirectives(QStringList& list) const {Q_UNUSED(list);} + //! Return the include directives needed for this encodable's init and verify functions virtual void getInitAndVerifyIncludeDirectives(QStringList& list) const {Q_UNUSED(list);} diff --git a/enumcreator.cpp b/enumcreator.cpp index 00a97f4..be8b110 100644 --- a/enumcreator.cpp +++ b/enumcreator.cpp @@ -94,6 +94,7 @@ QString EnumElement::getDeclaration() const EnumCreator::EnumCreator(ProtocolParser* parse, QString Parent, ProtocolSupport supported) : ProtocolDocumentation(parse, Parent, supported), minbitwidth(0), + maxvalue(0), hidden(false), lookup(false), lookupTitle(false), @@ -122,6 +123,7 @@ void EnumCreator::clear(void) filepath.clear(); sourceOutput.clear(); minbitwidth = 0; + maxvalue = 0; hidden = false; lookup = false; lookupTitle = false; @@ -482,7 +484,7 @@ void EnumCreator::checkAgainstKeywords(void) void EnumCreator::computeNumberList(void) { // Attempt to get a list of numbers that represents the value of each enumeration - int maxValue = 1; + maxvalue = 1; int value = -1; QString baseString; @@ -524,7 +526,7 @@ void EnumCreator::computeNumberList(void) if (!ok) { replaceEnumerationNameWithValue(stringValue); - parser->replaceEnumerationNameWithValue(stringValue); + stringValue = parser->replaceEnumerationNameWithValue(stringValue); // If this string is a composite of numbers, add them together if we can stringValue = EncodedLength::collapseLengthString(stringValue, true); @@ -549,8 +551,8 @@ void EnumCreator::computeNumberList(void) }// if we got a string from the xml // keep track of maximum value - if(value > maxValue) - maxValue = value; + if(value > maxvalue) + maxvalue = value; // Remember the value element.number = stringValue; @@ -558,10 +560,10 @@ void EnumCreator::computeNumberList(void) }// for the whole list of value strings // Its possible we have no idea, so go with 8 bits in that case - if(maxValue > 0) + if(maxvalue > 0) { // Figure out the number of bits needed to encode the maximum value - minbitwidth = (int)ceil(log2(maxValue + 1)); + minbitwidth = (int)ceil(log2(maxvalue + 1)); } else minbitwidth = 8; diff --git a/enumcreator.h b/enumcreator.h index a340c0b..8d53345 100644 --- a/enumcreator.h +++ b/enumcreator.h @@ -112,6 +112,9 @@ class EnumCreator : public ProtocolDocumentation //! Return the minimum number of bits needed to encode the enumeration int getMinBitWidth(void) const {return minbitwidth;} + //! Return the maximum value used by the enumeration + int getMaximumValue(void) const {return maxvalue;} + //! Return true if this enumeration is hidden from the documentation virtual bool isHidden (void) const Q_DECL_OVERRIDE {return hidden;} @@ -159,6 +162,9 @@ class EnumCreator : public ProtocolDocumentation //! Minimum number of bits needed to encode the enumeration int minbitwidth; + //! Maximum value of this enumeration + int maxvalue; + //! Determines if this enum will be hidden from the documentation bool hidden; diff --git a/exampleprotocol.xml b/exampleprotocol.xml index daeb19b..47effb5 100644 --- a/exampleprotocol.xml +++ b/exampleprotocol.xml @@ -182,7 +182,7 @@ efficiently encode data for little or big endian architectures."> - + diff --git a/protocolcode.cpp b/protocolcode.cpp index a25dad5..56d329c 100644 --- a/protocolcode.cpp +++ b/protocolcode.cpp @@ -21,6 +21,7 @@ void ProtocolCode::clear(void) encode.clear(); decode.clear(); + include.clear(); } @@ -55,6 +56,8 @@ void ProtocolCode::parse(void) decode = attr.value().trimmed(); else if(attrname.compare("comment", Qt::CaseInsensitive) == 0) comment = attr.value().trimmed(); + else if(attrname.compare("include", Qt::CaseInsensitive) == 0) + include = attr.value().trimmed(); else if(support.disableunrecognized == false) emitWarning("Unrecognized attribute \"" + attrname + "\""); @@ -124,3 +127,12 @@ QString ProtocolCode::getDecodeString(bool isBigEndian, int* bitcount, bool isSt return output; } + +//! Return the include directives that go into source code needed for this encodable +void ProtocolCode::getSourceIncludeDirectives(QStringList& list) const +{ + if(!include.isEmpty()) + list.append(include); +} + + diff --git a/protocolcode.h b/protocolcode.h index 7d2894f..208ec92 100644 --- a/protocolcode.h +++ b/protocolcode.h @@ -34,6 +34,9 @@ class ProtocolCode : public Encodable //! Return the string that is used to declare this encodable virtual QString getDeclaration(void) const Q_DECL_OVERRIDE {return QString();} + //! Return the include directives that go into source code needed for this encodable + virtual void getSourceIncludeDirectives(QStringList& list) const Q_DECL_OVERRIDE; + //! Return the signature of this field in an encode function signature virtual QString getEncodeSignature(void) const Q_DECL_OVERRIDE {return QString();} @@ -97,6 +100,7 @@ class ProtocolCode : public Encodable protected: QString encode; QString decode; + QString include; }; #endif // PROTOCOLCODE_H diff --git a/protocolfield.cpp b/protocolfield.cpp index d37b534..6763263 100644 --- a/protocolfield.cpp +++ b/protocolfield.cpp @@ -14,7 +14,6 @@ TypeData::TypeData(ProtocolSupport sup) : isStruct(false), isSigned(false), isBitfield(false), - isFloat(false), isEnum(false), isString(false), @@ -22,6 +21,7 @@ TypeData::TypeData(ProtocolSupport sup) : isNull(false), bits(8), sigbits(0), + enummax(0), support(sup) { } @@ -39,6 +39,7 @@ TypeData::TypeData(const TypeData& that) : isNull(that.isNull), bits(that.bits), sigbits(that.sigbits), + enummax(that.enummax), support(that.support) { } @@ -60,6 +61,7 @@ void TypeData::clear(void) isNull = false; bits = 8; sigbits = 0; + enummax = 0; } @@ -218,6 +220,10 @@ double TypeData::getMaximumFloatValue(void) const { if(isString || isStruct) return 0; + else if(isEnum) + { + return enummax; + } else if(isFloat) { // Float encodings use float rules @@ -286,6 +292,10 @@ uint64_t TypeData::getMaximumIntegerValue(void) const { if(isString || isStruct) return 0; + else if(isEnum) + { + return enummax; + } else if(isFloat) { return (uint64_t)getMaximumFloatValue(); @@ -1074,11 +1084,15 @@ void ProtocolField::parse(void) else { int minbits = 8; + inMemoryType.enummax = 255; // Figure out the minimum number of bits for the enumeration const EnumCreator* creator = parser->lookUpEnumeration(enumName); if(creator != 0) + { minbits = creator->getMinBitWidth(); + inMemoryType.enummax = creator->getMaximumValue(); + } if(encodedTypeString.isEmpty()) { @@ -1392,7 +1406,7 @@ void ProtocolField::parse(void) // scaler or maxString. if(!minString.isEmpty()) { - encodedMin = ShuntingYard::computeInfix(minString, &ok); + encodedMin = ShuntingYard::computeInfix(parser->replaceEnumerationNameWithValue(minString), &ok); if(!ok) { @@ -1414,7 +1428,7 @@ void ProtocolField::parse(void) // is specified then one will be computed based on maxString. if(!maxString.isEmpty()) { - encodedMax = ShuntingYard::computeInfix(maxString, &ok); + encodedMax = ShuntingYard::computeInfix(parser->replaceEnumerationNameWithValue(maxString), &ok); if(!ok) { emitWarning("max is not a number, 1.0 assumed"); @@ -1472,7 +1486,7 @@ void ProtocolField::parse(void) // produce the encoded value. Unlike minString and maxString it can // apply even if the encodedType is floating point. Hence it is // possible for encodedMax to be 0 while the scaler is not 1.0. - scaler = ShuntingYard::computeInfix(scalerString, &ok); + scaler = ShuntingYard::computeInfix(parser->replaceEnumerationNameWithValue(scalerString), &ok); if(!ok) { @@ -1608,7 +1622,7 @@ void ProtocolField::parse(void) else if(!verifyMaxString.isEmpty()) { // Compute the verify max value if we can - verifyMaxValue = ShuntingYard::computeInfix(verifyMaxString, &hasVerifyMaxValue); + verifyMaxValue = ShuntingYard::computeInfix(parser->replaceEnumerationNameWithValue(verifyMaxString), &hasVerifyMaxValue); } // Now handle the verify minimum value @@ -1620,7 +1634,7 @@ void ProtocolField::parse(void) else if(!verifyMinString.isEmpty()) { // Compute the verify min value if we can - verifyMinValue = ShuntingYard::computeInfix(verifyMinString, &hasVerifyMinValue); + verifyMinValue = ShuntingYard::computeInfix(parser->replaceEnumerationNameWithValue(verifyMinString), &hasVerifyMinValue); } // Support the case where a numeric string uses "pi" or "e". @@ -2046,7 +2060,7 @@ void ProtocolField::getDocumentationDetails(QList& outline, QString& startB return; // See if we can replace any enumeration names with values - parser->replaceEnumerationNameWithValue(maxEncodedLength); + maxEncodedLength = parser->replaceEnumerationNameWithValue(maxEncodedLength); // The byte after this one QString nextStartByte = EncodedLength::collapseLengthString(startByte + "+" + maxEncodedLength); @@ -3574,8 +3588,8 @@ QString ProtocolField::getLimitedArgument(QString argument) const maxstring = verifyMaxString; } - // Now check if this max value is less than the in-Memory maximum. If it is then we must apply the max limit - if(maxvalue < inMemoryType.getMaximumFloatValue()) + // Now check if this max value is less than the in-Memory maximum. If it is then we must apply the max limit. We allow one lsb of fiddle. + if(std::nextafter(maxvalue, DBL_MAX) < inMemoryType.getMaximumFloatValue()) skipmax = false; }// else if we can evaluate the verify value @@ -3596,8 +3610,8 @@ QString ProtocolField::getLimitedArgument(QString argument) const minstring = verifyMinString; } - // Now check if this min value is more than the in-Memory minimum. If it is then we must apply the min limit - if(minvalue > inMemoryType.getMinimumFloatValue()) + // Now check if this min value is more than the in-Memory minimum. If it is then we must apply the min limit. We allow one lsb of fiddle. + if(std::nextafter(minvalue, -DBL_MAX) > inMemoryType.getMinimumFloatValue()) skipmin = false; }// else if we can evaluate the verify value diff --git a/protocolfield.h b/protocolfield.h index d584118..42afa3b 100644 --- a/protocolfield.h +++ b/protocolfield.h @@ -32,6 +32,7 @@ class TypeData bool isNull; //!< true if type is null, i.e not in memory OR not encoded int bits; //!< number of bits used by type int sigbits; //!< number of bits for the significand of a float16 or float24 + int enummax; //!< maximum value of the enumeration if isEnum is true //! Pull a positive integer value from a string static int extractPositiveInt(const QString& string, bool* ok = 0); diff --git a/protocolpacket.cpp b/protocolpacket.cpp index 263d940..b9fa844 100644 --- a/protocolpacket.cpp +++ b/protocolpacket.cpp @@ -120,14 +120,8 @@ void ProtocolPacket::parse(void) // Warning about maximum data size, only applies to packets if(support.maxdatasize > 0) { - // The length strings, which may include enumerated identiers such as "N3D" - QString maxLength = encodedLength.maxEncodedLength; - - // Replace any defined enumerations with their actual value - parser->replaceEnumerationNameWithValue(maxLength); - // maxdatasize will be zero if the length string cannot be computed - int maxdatasize = (int)(ShuntingYard::computeInfix(maxLength) + 0.5); + int maxdatasize = (int)(ShuntingYard::computeInfix(parser->replaceEnumerationNameWithValue(encodedLength.maxEncodedLength)) + 0.5); // Warn the user if the packet might be too big if(maxdatasize > support.maxdatasize) @@ -1189,7 +1183,7 @@ QString ProtocolPacket::getTopLevelMarkdown(bool global, const QStringList& pack if(!ids.at(0).isEmpty()) { // In case the packet identifer is an enumeration we know - parser->replaceEnumerationNameWithValue(idvalue); + idvalue = parser->replaceEnumerationNameWithValue(idvalue); if(id.compare(idvalue) == 0) output += "- packet identifier: `" + id + "`\n"; @@ -1220,7 +1214,7 @@ QString ProtocolPacket::getTopLevelMarkdown(bool global, const QStringList& pack QString idvalue = id; // In case the packet identifer is an enumeration we know - parser->replaceEnumerationNameWithValue(idvalue); + idvalue = parser->replaceEnumerationNameWithValue(idvalue); // Put the link here in this case if(id.compare(idvalue) == 0) @@ -1239,7 +1233,7 @@ QString ProtocolPacket::getTopLevelMarkdown(bool global, const QStringList& pack QString minLength = EncodedLength::collapseLengthString(encodedLength.minEncodedLength, true).replace("1*", ""); // Replace any defined enumerations with their actual value - parser->replaceEnumerationNameWithValue(minLength); + minLength = parser->replaceEnumerationNameWithValue(minLength); // Re-collapse, perhaps we can solve it now minLength = EncodedLength::collapseLengthString(minLength, true); @@ -1256,8 +1250,8 @@ QString ProtocolPacket::getTopLevelMarkdown(bool global, const QStringList& pack QString minLength = EncodedLength::collapseLengthString(encodedLength.minEncodedLength, true).replace("1*", ""); // Replace any defined enumerations with their actual value - parser->replaceEnumerationNameWithValue(maxLength); - parser->replaceEnumerationNameWithValue(minLength); + maxLength = parser->replaceEnumerationNameWithValue(maxLength); + minLength = parser->replaceEnumerationNameWithValue(minLength); // Re-collapse, perhaps we can solve it now maxLength = EncodedLength::collapseLengthString(maxLength, true); diff --git a/protocolparser.cpp b/protocolparser.cpp index 839dd58..e3b4c08 100644 --- a/protocolparser.cpp +++ b/protocolparser.cpp @@ -17,7 +17,7 @@ #include // The version of the protocol generator is set here -const QString ProtocolParser::genVersion = "2.21.a"; +const QString ProtocolParser::genVersion = "2.22.b"; /*! * \brief ProtocolParser::ProtocolParser @@ -1122,22 +1122,24 @@ const EnumCreator* ProtocolParser::lookUpEnumeration(const QString& enumName) co /*! * Replace any text that matches an enumeration name with the value of that enumeration - * \param text is modified to replace names with numbers - * \return a reference to text + * \param text is the source text to search, which won't be modified + * \return A new string that replaces any enumeration names with the value of the enumeration */ -QString& ProtocolParser::replaceEnumerationNameWithValue(QString& text) const +QString ProtocolParser::replaceEnumerationNameWithValue(const QString& text) const { + QString replace = text; + for(int i = 0; i < globalEnums.size(); i++) { - globalEnums.at(i)->replaceEnumerationNameWithValue(text); + globalEnums.at(i)->replaceEnumerationNameWithValue(replace); } for(int i = 0; i < enums.size(); i++) { - enums.at(i)->replaceEnumerationNameWithValue(text); + enums.at(i)->replaceEnumerationNameWithValue(replace); } - return text; + return replace; } diff --git a/protocolparser.h b/protocolparser.h index 251b18c..4972158 100644 --- a/protocolparser.h +++ b/protocolparser.h @@ -107,7 +107,7 @@ class ProtocolParser const EnumCreator* lookUpEnumeration(const QString& enumName) const; //! Replace any text that matches an enumeration name with the value of that enumeration - QString& replaceEnumerationNameWithValue(QString& text) const; + QString replaceEnumerationNameWithValue(const QString& text) const; //! Determine if text is part of an enumeration. QString getEnumerationNameForEnumValue(const QString& text) const; diff --git a/protocolstructure.cpp b/protocolstructure.cpp index 01044ac..f8978d1 100644 --- a/protocolstructure.cpp +++ b/protocolstructure.cpp @@ -582,6 +582,22 @@ void ProtocolStructure::getIncludeDirectives(QStringList& list) const } list.removeDuplicates(); + +}// ProtocolStructure::getIncludeDirectives + + +/*! + * Append the include directives in source code for this encodable. Mostly this is empty, + * but code encodables may have source code includes. + * \param list is appended with any directives this encodable requires. + */ +void ProtocolStructure::getSourceIncludeDirectives(QStringList& list) const +{ + // Includes that our encodable members may need + for(int i = 0; i < encodables.length(); i++) + encodables.at(i)->getSourceIncludeDirectives(list); + + list.removeDuplicates(); } @@ -2549,7 +2565,7 @@ void ProtocolStructure::getDocumentationDetails(QList& outline, QString& st QString maxEncodedLength = encodedLength.maxEncodedLength; // See if we can replace any enumeration names with values - parser->replaceEnumerationNameWithValue(maxEncodedLength); + maxEncodedLength = parser->replaceEnumerationNameWithValue(maxEncodedLength); // The byte after this one QString nextStartByte = EncodedLength::collapseLengthString(startByte + "+" + maxEncodedLength); diff --git a/protocolstructure.h b/protocolstructure.h index 0bba462..870ff37 100644 --- a/protocolstructure.h +++ b/protocolstructure.h @@ -157,6 +157,9 @@ class ProtocolStructure : public Encodable //! Return the include directives needed for this encodable virtual void getIncludeDirectives(QStringList& list) const Q_DECL_OVERRIDE; + //! Return the include directives that go into source code for this encodable + virtual void getSourceIncludeDirectives(QStringList& list) const Q_DECL_OVERRIDE; + //! Return the include directives needed for this encodable's init and verify functions virtual void getInitAndVerifyIncludeDirectives(QStringList& list) const Q_DECL_OVERRIDE; diff --git a/protocolstructuremodule.cpp b/protocolstructuremodule.cpp index 7e46a6d..ea675e1 100644 --- a/protocolstructuremodule.cpp +++ b/protocolstructuremodule.cpp @@ -488,13 +488,9 @@ void ProtocolStructureModule::setupFiles(QString moduleName, verifyheaderfile->makeLineSeparator(); } - if(support.specialFloat) - source.writeIncludeDirective("floatspecial.h"); - - source.writeIncludeDirective("fielddecode.h"); - source.writeIncludeDirective("fieldencode.h"); - source.writeIncludeDirective("scaleddecode.h"); - source.writeIncludeDirective("scaledencode.h"); + list.clear(); + getSourceIncludeDirectives(list); + source.writeIncludeDirectives(list); // Outputs for the enumerations in source file, if any for(int i = 0; i < enumList.count(); i++) @@ -544,6 +540,27 @@ void ProtocolStructureModule::getIncludeDirectives(QStringList& list) const } +/*! + * Return the include directives that go into source code for this encodable + * \param list is appended with any directives for source code. + */ +void ProtocolStructureModule::getSourceIncludeDirectives(QStringList& list) const +{ + if(support.specialFloat) + list.append("floatspecial.h"); + + list.append("fielddecode.h"); + list.append("fieldencode.h"); + list.append("scaleddecode.h"); + list.append("scaledencode.h"); + + // And any of our children's headers + ProtocolStructure::getSourceIncludeDirectives(list); + + list.removeDuplicates(); +} + + /*! * Return the include directives needed for this encodable's init and verify functions * \param list is appended with any directives this encodable requires. diff --git a/protocolstructuremodule.h b/protocolstructuremodule.h index 4c1ccf7..122b9f5 100644 --- a/protocolstructuremodule.h +++ b/protocolstructuremodule.h @@ -30,6 +30,9 @@ class ProtocolStructureModule : public ProtocolStructure //! Return the include directives needed for this encodable virtual void getIncludeDirectives(QStringList& list) const Q_DECL_OVERRIDE; + //! Return the include directives that go into source code for this encodable + virtual void getSourceIncludeDirectives(QStringList& list) const Q_DECL_OVERRIDE; + //! Return the include directives needed for this encodable's init and verify functions virtual void getInitAndVerifyIncludeDirectives(QStringList& list) const Q_DECL_OVERRIDE;