[tlvs] use Tlv::Info for reading TLV values (#12322)

This change moves the TLV value reading logic from static methods in
the `Tlv` class to member methods of the nested `Tlv::Info` class.

The new methods `Tlv::Info::ReadValue()`, `Tlv::Info::ReadStringValue()`,
`Tlv::Info::ReadUintValue()`, and the templated `Tlv::Info::Read<T>()`
operate on an existing `Tlv::Info` object that has already parsed a
TLV from a message.

This improves the API design by having the read operations use the
pre-parsed and validated state within a `Tlv::Info` instance. It
avoids the need to pass the TLV offset to read functions and
eliminates redundant re-parsing of the TLV header on each read,
making the code cleaner and more efficient.

The previous static methods `Tlv::Read<T>()`, `Tlv::ReadStringTlv()`,
and `Tlv::ReadUintTlv()` are removed, and all call sites are updated
to the new pattern.
This commit is contained in:
Abtin Keshavarzian
2026-01-22 09:23:34 -08:00
committed by GitHub
parent 213745a81f
commit cda1a0cf05
5 changed files with 137 additions and 103 deletions
+22 -19
View File
@@ -36,6 +36,7 @@
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/message.hpp"
#include "common/string.hpp"
namespace ot {
@@ -162,26 +163,32 @@ exit:
return error;
}
Error Tlv::ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue)
Error Tlv::Info::ReadValue(const Message &aMessage, void *aValue, uint8_t aMinLength) const
{
Error error = kErrorNone;
Info info;
// The `Message::Read()` flavor used below (with an `OffsetRange`)
// handles the boundary check. It will return `kErrorParse` if
// the requested read length exceeds the `OffsetRange`.
SuccessOrExit(error = info.ParseFrom(aMessage, aOffset));
return aMessage.Read(mValueOffsetRange, aValue, aMinLength);
}
info.mValueOffsetRange.ShrinkLength(aMaxStringLength);
aMessage.ReadBytes(info.mValueOffsetRange, aValue);
aValue[info.mValueOffsetRange.GetLength()] = kNullChar;
Error Tlv::Info::ReadStringValue(const Message &aMessage, uint8_t aMaxStringLength, char *aValue) const
{
Error error;
uint16_t length = Min<uint16_t>(mValueOffsetRange.GetLength(), aMaxStringLength);
SuccessOrExit(error = aMessage.Read(mValueOffsetRange.GetOffset(), aValue, length));
aValue[length] = kNullChar;
exit:
return error;
}
template <typename UintType> Error Tlv::ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue)
template <typename UintType> Error Tlv::Info::ReadUintValue(const Message &aMessage, UintType &aValue) const
{
Error error;
SuccessOrExit(error = ReadTlvValue(aMessage, aOffset, &aValue, sizeof(aValue)));
SuccessOrExit(error = ReadValue(aMessage, &aValue, sizeof(aValue)));
aValue = BigEndian::HostSwap<UintType>(aValue);
exit:
@@ -189,9 +196,9 @@ exit:
}
// Explicit instantiations of `ReadUintTlv<>()`
template Error Tlv::ReadUintTlv<uint8_t>(const Message &aMessage, uint16_t aOffset, uint8_t &aValue);
template Error Tlv::ReadUintTlv<uint16_t>(const Message &aMessage, uint16_t aOffset, uint16_t &aValue);
template Error Tlv::ReadUintTlv<uint32_t>(const Message &aMessage, uint16_t aOffset, uint32_t &aValue);
template Error Tlv::Info::ReadUintValue<uint8_t>(const Message &aMessage, uint8_t &aValue) const;
template Error Tlv::Info::ReadUintValue<uint16_t>(const Message &aMessage, uint16_t &aValue) const;
template Error Tlv::Info::ReadUintValue<uint32_t>(const Message &aMessage, uint32_t &aValue) const;
Error Tlv::ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength)
{
@@ -199,11 +206,7 @@ Error Tlv::ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue,
Info info;
SuccessOrExit(error = info.ParseFrom(aMessage, aOffset));
VerifyOrExit(info.mValueOffsetRange.Contains(aMinLength), error = kErrorParse);
info.mValueOffsetRange.ShrinkLength(aMinLength);
aMessage.ReadBytes(info.mValueOffsetRange, aValue);
error = info.ReadValue(aMessage, aValue, aMinLength);
exit:
return error;
@@ -215,7 +218,7 @@ Error Tlv::FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStr
Info info;
SuccessOrExit(error = info.FindIn(aMessage, aType));
error = ReadStringTlv(aMessage, info.GetTlvOffset(), aMaxStringLength, aValue);
error = info.ReadStringValue(aMessage, aMaxStringLength, aValue);
exit:
return error;
@@ -227,7 +230,7 @@ template <typename UintType> Error Tlv::FindUintTlv(const Message &aMessage, uin
Info info;
SuccessOrExit(error = info.FindIn(aMessage, aType));
error = ReadUintTlv<UintType>(aMessage, info.GetTlvOffset(), aValue);
error = info.ReadUintValue<UintType>(aMessage, aValue);
exit:
return error;
+93 -59
View File
@@ -342,7 +342,97 @@ public:
*/
uint16_t GetValueOffset(void) const { return mValueOffsetRange.GetOffset(); }
/**
* Reads the TLV's value in a message expecting a minimum length for the value.
*
* This method can parse both standard and extended TLVs. It uses the current `GetValueOffsetRange()` to read
* from. It ensures that the TLV value has at least `aMinLength` bytes, otherwise `kErrorParse` is returned.
*
* @param[in] aMessage The message to read from.
* @param[out] aValue A buffer to output the TLV's value, must contain (at least) @p aMinLength bytes.
* @param[in] aMinLength The minimum expected length of TLV and number of bytes to copy into @p aValue
* buffer.
*
* @retval kErrorNone Successfully read the TLV and copied @p aMinLength into @p aValue.
* @retval kErrorParse The TLV was not well-formed and could not be parsed.
*/
Error ReadValue(const Message &aMessage, void *aValue, uint8_t aMinLength) const;
/**
* Reads a simple TLV with a single non-integral value in a message.
*
* This method can parse both standard and extended TLVs. It uses the current `GetValueOffsetRange()` to read
* from. It ensures that the TLV value has at least the minimum expected length, otherwise `kErrorParse` is
* returned.
*
* @note This method does not check if `GetType()` matches the `SimpleTlvType`. It is caller's responsibility
* to validate this before calling this method.
*
* @tparam SimpleTlvType The simple TLV type to read (must be a sub-class of `SimpleTlvInfo`).
*
* @param[in] aMessage The message to read from.
* @param[out] aValue A reference to the value object to output the read value.
*
* @retval kErrorNone Successfully read the TLV and updated the @p aValue.
* @retval kErrorParse The TLV was not well-formed and could not be parsed.
*/
template <typename SimpleTlvType>
Error Read(const Message &aMessage, typename SimpleTlvType::ValueType &aValue) const
{
return ReadValue(aMessage, &aValue, sizeof(aValue));
}
/**
* Reads a simple TLV with a single integral value in a message.
*
* This method can parse both standard and extended TLVs. It uses the current `GetValueOffsetRange()` to read
* from. It ensures that the TLV value has at least the minimum expected length, otherwise `kErrorParse` is
* returned.
*
* @note This method does not check if `GetType()` matches the `UintTlvType`. It is caller's responsibility to
* validate this before calling this method.
*
* @tparam UintTlvType The simple TLV type to read (must be a sub-class of `UintTlvInfo`).
*
* @param[in] aMessage The message to read from.
* @param[out] aValue A reference to an unsigned int to output the read value.
*
* @retval kErrorNone Successfully read the TLV and updated the @p aValue.
* @retval kErrorParse The TLV was not well-formed and could not be parsed.
*/
template <typename UintTlvType>
Error Read(const Message &aMessage, typename UintTlvType::UintValueType &aValue) const
{
return ReadUintValue(aMessage, aValue);
}
/**
* Reads a simple TLV with a UTF-8 string value in a message.
*
* This method can parse both standard and extended TLVs. It uses the current `GetValueOffsetRange()` to read
* from. The returned string in @p aValue is always null-terminated.
*
* @note This method does not check if `GetType()` matches the `StringTlvType`. It is caller's responsibility
* to validate this before calling this method.
*
* @tparam StringTlvType The simple TLV type to read (must be a sub-class of `StringTlvInfo`).
*
* @param[in] aMessage The message to read from.
* @param[out] aValue A reference to the string buffer to output the read value.
*
* @retval kErrorNone Successfully read the TLV and updated the @p aValue.
* @retval kErrorParse The TLV was not well-formed and could not be parsed.
*/
template <typename StringTlvType>
Error Read(const Message &aMessage, typename StringTlvType::StringType &aValue) const
{
return ReadStringValue(aMessage, StringTlvType::kMaxStringLength, aValue);
}
private:
template <typename UintType> Error ReadUintValue(const Message &aMessage, UintType &aValue) const;
Error ReadStringValue(const Message &aMessage, uint8_t aMaxStringLength, char *aValue) const;
uint8_t mType;
bool mIsExtended;
OffsetRange mTlvOffsetRange;
@@ -364,60 +454,6 @@ public:
*/
static Error ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength);
/**
* Reads a simple TLV with a single non-integral value in a message at a given offset.
*
* @tparam SimpleTlvType The simple TLV type to read (must be a sub-class of `SimpleTlvInfo`).
*
* @param[in] aMessage The message to read from.
* @param[in] aOffset The offset into the message pointing to the start of the TLV.
* @param[out] aValue A reference to the value object to output the read value.
*
* @retval kErrorNone Successfully read the TLV and updated the @p aValue.
* @retval kErrorParse The TLV was not well-formed and could not be parsed.
*/
template <typename SimpleTlvType>
static Error Read(const Message &aMessage, uint16_t aOffset, typename SimpleTlvType::ValueType &aValue)
{
return ReadTlvValue(aMessage, aOffset, &aValue, sizeof(aValue));
}
/**
* Reads a simple TLV with a single integral value in a message at a given offset.
*
* @tparam UintTlvType The simple TLV type to read (must be a sub-class of `UintTlvInfo`).
*
* @param[in] aMessage The message to read from.
* @param[in] aOffset The offset into the message pointing to the start of the TLV.
* @param[out] aValue A reference to an unsigned int to output the read value.
*
* @retval kErrorNone Successfully read the TLV and updated the @p aValue.
* @retval kErrorParse The TLV was not well-formed and could not be parsed.
*/
template <typename UintTlvType>
static Error Read(const Message &aMessage, uint16_t aOffset, typename UintTlvType::UintValueType &aValue)
{
return ReadUintTlv(aMessage, aOffset, aValue);
}
/**
* Reads a simple TLV with a UTF-8 string value in a message at a given offset.
*
* @tparam StringTlvType The simple TLV type to read (must be a sub-class of `StringTlvInfo`).
*
* @param[in] aMessage The message to read from.
* @param[in] aOffset The offset into the message pointing to the start of the TLV.
* @param[out] aValue A reference to the string buffer to output the read value.
*
* @retval kErrorNone Successfully read the TLV and updated the @p aValue.
* @retval kErrorParse The TLV was not well-formed and could not be parsed.
*/
template <typename StringTlvType>
static Error Read(const Message &aMessage, uint16_t aOffset, typename StringTlvType::StringType &aValue)
{
return ReadStringTlv(aMessage, aOffset, StringTlvType::kMaxStringLength, aValue);
}
/**
* Searches for and reads a requested TLV out of a given message.
*
@@ -865,12 +901,10 @@ protected:
private:
static Error FindTlv(const Message &aMessage, uint8_t aType, void *aValue, uint16_t aLength);
static Error ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue);
static Error FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, char *aValue);
static Error AppendStringTlv(Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, const char *aValue);
static Error ValidateStringTlvValue(uint8_t aMaxStringLength, const char *aStringValue);
static Error UpdateTlv(Message &aMessage, Bookmark aBookmark, bool aShouldWriteLength);
template <typename UintType> static Error ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue);
template <typename UintType> static Error FindUintTlv(const Message &aMessage, uint8_t aType, UintType &aValue);
template <typename UintType> static Error AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue);
@@ -961,7 +995,7 @@ public:
* Defines constants and types for a simple TLV with an unsigned int value type.
*
* This class and its sub-classes are intended to be used as the template type in `Tlv::Append<UintTlvType>()`, and
* the related `Tlv::Find<UintTlvType>()` and `Tlv::Read<UintTlvType>()`.
* the related `Tlv::Find<UintTlvType>()` and `Tlv::Info::Read<UintTlvType>()`.
*
* @tparam kTlvTypeValue The TLV Type value.
* @tparam UintType The TLV Value's type (must be an unsigned int, i.e. uint8_t, uint16_t, or uint32_t).
@@ -978,7 +1012,7 @@ public:
* Defines constants and types for a simple TLV with a single value.
*
* This class and its sub-classes are intended to be used as the template type in `Tlv::Append<SimpleTlvType>()`,
* and the related `Tlv::Find<SimpleTlvType>()` and `Tlv::Read<SimpleTlvType>()`.
* and the related `Tlv::Find<SimpleTlvType>()` and `Tlv::Info::Read<SimpleTlvType>()`.
*
* @tparam kTlvTypeValue The TLV Type value.
* @tparam TlvValueType The TLV Value's type (must not be an integral type).
@@ -997,7 +1031,7 @@ public:
* Defines constants and types for a simple TLV with a UTF-8 string value.
*
* This class and its sub-classes are intended to be used as the template type in `Tlv::Append<StringTlvType>()`,
* and the related `Tlv::Find<StringTlvType>()` and `Tlv::Read<StringTlvType>()`.
* and the related `Tlv::Find<StringTlvType>()` and `Tlv::Info::Read<StringTlvType>()`.
*
* @tparam kTlvTypeValue The TLV Type value.
* @tparam kTlvMaxValueLength The maximum allowed string length (as TLV value).
+3 -3
View File
@@ -142,7 +142,7 @@ void Initiator::HandleReport(const Message &aMessage, OffsetRange &aOffsetRange,
{
case StatusSubTlv::kType:
VerifyOrExit(!hasStatus && !hasReport, error = kErrorDrop);
SuccessOrExit(error = Tlv::Read<StatusSubTlv>(aMessage, aOffsetRange.GetOffset(), status));
SuccessOrExit(error = tlvInfo.Read<StatusSubTlv>(aMessage, status));
hasStatus = true;
break;
@@ -310,7 +310,7 @@ Error Initiator::HandleManagementResponse(const Message &aMessage, const Ip6::Ad
{
case StatusSubTlv::kType:
VerifyOrExit(!hasStatus, error = kErrorParse);
SuccessOrExit(error = Tlv::Read<StatusSubTlv>(aMessage, offsetRange.GetOffset(), status));
SuccessOrExit(error = tlvInfo.Read<StatusSubTlv>(aMessage, status));
hasStatus = true;
break;
@@ -431,7 +431,7 @@ Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, N
switch (tlvInfo.GetType())
{
case SubTlv::kQueryId:
SuccessOrExit(error = Tlv::Read<QueryIdSubTlv>(aRequestMessage, tlvInfo.GetTlvOffset(), queryId));
SuccessOrExit(error = tlvInfo.Read<QueryIdSubTlv>(aRequestMessage, queryId));
hasQueryId = true;
break;
+3 -4
View File
@@ -2731,14 +2731,13 @@ void Mle::HandleDiscoveryRequest(RxInfo &aRxInfo)
switch (tlvInfo.GetType())
{
case MeshCoP::Tlv::kDiscoveryRequest:
SuccessOrExit(error = Tlv::Read<MeshCoP::DiscoveryRequestTlv>(aRxInfo.mMessage, offsetRange.GetOffset(),
discoveryRequestTlvValue));
SuccessOrExit(error =
tlvInfo.Read<MeshCoP::DiscoveryRequestTlv>(aRxInfo.mMessage, discoveryRequestTlvValue));
parsedDiscoveryRequestTlv = true;
break;
case MeshCoP::Tlv::kExtendedPanId:
SuccessOrExit(
error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, offsetRange.GetOffset(), extPanId));
SuccessOrExit(error = tlvInfo.Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, extPanId));
VerifyOrExit(Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId() != extPanId, error = kErrorDrop);
break;
+16 -18
View File
@@ -1290,25 +1290,24 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator,
switch (tlvInfo.GetType())
{
case Tlv::kExtMacAddress:
SuccessOrExit(error =
Tlv::Read<ExtMacAddressTlv>(aMessage, offset, AsCoreType(&aDiagTlv.mData.mExtAddress)));
SuccessOrExit(error = tlvInfo.Read<ExtMacAddressTlv>(aMessage, AsCoreType(&aDiagTlv.mData.mExtAddress)));
break;
case Tlv::kAddress16:
SuccessOrExit(error = Tlv::Read<Address16Tlv>(aMessage, offset, aDiagTlv.mData.mAddr16));
SuccessOrExit(error = tlvInfo.Read<Address16Tlv>(aMessage, aDiagTlv.mData.mAddr16));
break;
case Tlv::kMode:
{
uint8_t mode;
SuccessOrExit(error = Tlv::Read<ModeTlv>(aMessage, offset, mode));
SuccessOrExit(error = tlvInfo.Read<ModeTlv>(aMessage, mode));
Mle::DeviceMode(mode).Get(aDiagTlv.mData.mMode);
break;
}
case Tlv::kTimeout:
SuccessOrExit(error = Tlv::Read<TimeoutTlv>(aMessage, offset, aDiagTlv.mData.mTimeout));
SuccessOrExit(error = tlvInfo.Read<TimeoutTlv>(aMessage, aDiagTlv.mData.mTimeout));
break;
case Tlv::kConnectivity:
@@ -1340,7 +1339,7 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator,
{
LeaderDataTlvValue tlvValue;
SuccessOrExit(error = Tlv::Read<LeaderDataTlv>(aMessage, offset, tlvValue));
SuccessOrExit(error = tlvInfo.Read<LeaderDataTlv>(aMessage, tlvValue));
tlvValue.Get(AsCoreType(&aDiagTlv.mData.mLeaderData));
break;
}
@@ -1379,11 +1378,11 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator,
}
case Tlv::kBatteryLevel:
SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aDiagTlv.mData.mBatteryLevel));
SuccessOrExit(error = tlvInfo.Read<BatteryLevelTlv>(aMessage, aDiagTlv.mData.mBatteryLevel));
break;
case Tlv::kSupplyVoltage:
SuccessOrExit(error = Tlv::Read<SupplyVoltageTlv>(aMessage, offset, aDiagTlv.mData.mSupplyVoltage));
SuccessOrExit(error = tlvInfo.Read<SupplyVoltageTlv>(aMessage, aDiagTlv.mData.mSupplyVoltage));
break;
case Tlv::kChildTable:
@@ -1429,36 +1428,35 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator,
break;
case Tlv::kMaxChildTimeout:
SuccessOrExit(error = Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aDiagTlv.mData.mMaxChildTimeout));
SuccessOrExit(error = tlvInfo.Read<MaxChildTimeoutTlv>(aMessage, aDiagTlv.mData.mMaxChildTimeout));
break;
case Tlv::kEui64:
SuccessOrExit(error = Tlv::Read<Eui64Tlv>(aMessage, offset, AsCoreType(&aDiagTlv.mData.mEui64)));
SuccessOrExit(error = tlvInfo.Read<Eui64Tlv>(aMessage, AsCoreType(&aDiagTlv.mData.mEui64)));
break;
case Tlv::kVersion:
SuccessOrExit(error = Tlv::Read<VersionTlv>(aMessage, offset, aDiagTlv.mData.mVersion));
SuccessOrExit(error = tlvInfo.Read<VersionTlv>(aMessage, aDiagTlv.mData.mVersion));
break;
case Tlv::kVendorName:
SuccessOrExit(error = Tlv::Read<VendorNameTlv>(aMessage, offset, aDiagTlv.mData.mVendorName));
SuccessOrExit(error = tlvInfo.Read<VendorNameTlv>(aMessage, aDiagTlv.mData.mVendorName));
break;
case Tlv::kVendorModel:
SuccessOrExit(error = Tlv::Read<VendorModelTlv>(aMessage, offset, aDiagTlv.mData.mVendorModel));
SuccessOrExit(error = tlvInfo.Read<VendorModelTlv>(aMessage, aDiagTlv.mData.mVendorModel));
break;
case Tlv::kVendorSwVersion:
SuccessOrExit(error = Tlv::Read<VendorSwVersionTlv>(aMessage, offset, aDiagTlv.mData.mVendorSwVersion));
SuccessOrExit(error = tlvInfo.Read<VendorSwVersionTlv>(aMessage, aDiagTlv.mData.mVendorSwVersion));
break;
case Tlv::kVendorAppUrl:
SuccessOrExit(error = Tlv::Read<VendorAppUrlTlv>(aMessage, offset, aDiagTlv.mData.mVendorAppUrl));
SuccessOrExit(error = tlvInfo.Read<VendorAppUrlTlv>(aMessage, aDiagTlv.mData.mVendorAppUrl));
break;
case Tlv::kThreadStackVersion:
SuccessOrExit(error =
Tlv::Read<ThreadStackVersionTlv>(aMessage, offset, aDiagTlv.mData.mThreadStackVersion));
SuccessOrExit(error = tlvInfo.Read<ThreadStackVersionTlv>(aMessage, aDiagTlv.mData.mThreadStackVersion));
break;
case Tlv::kNonPreferredChannels:
@@ -1470,7 +1468,7 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator,
{
uint8_t state;
SuccessOrExit(error = Tlv::Read<BrStateTlv>(aMessage, offset, state));
SuccessOrExit(error = tlvInfo.Read<BrStateTlv>(aMessage, state));
aDiagTlv.mData.mBrState = static_cast<BrState>(state);
break;
}