mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
[dataset] add API otDatasetTlvsCompare (#12684)
Thread spec doesn't define the order of TLVs in the dataset, so that we can't call `memcmp` to compare two dataset. If we convert the dataset to otOperationalDataset and then compare each value of otOperationalDataset, we will met an issue if the new Thread spec defines new TLVs in the dataset in the future. This commit add a new dataset API `otDatasetTlvsCompare` to check whether two dataset contain the exact same set of TLVs (same types and values).
This commit is contained in:
@@ -578,6 +578,21 @@ otError otNetworkNameFromString(otNetworkName *aNetworkName, const char *aNameSt
|
||||
*/
|
||||
otError otDatasetParseTlvs(const otOperationalDatasetTlvs *aDatasetTlvs, otOperationalDataset *aDataset);
|
||||
|
||||
/**
|
||||
* Compares two Operational Dataset TLVs to determine if they contain the same set of TLVs.
|
||||
*
|
||||
* This function performs a deep comparison. It parses both @p aDatasetTlvsA and @p aDatasetTlvsB and checks if
|
||||
* they contain the exact same set of TLVs (same type and same value). The order of TLVs within the
|
||||
* `otOperationalDatasetTlvs` does not matter.
|
||||
*
|
||||
* @param[in] aDatasetTlvsA A pointer to dataset TLVs A. Must not be NULL.
|
||||
* @param[in] aDatasetTlvsB A pointer to dataset TLVs B. Must not be NULL.
|
||||
*
|
||||
* @returns TRUE if the two Operational Dataset TLVs match, FALSE otherwise (e.g., if any TLV differs,
|
||||
* is missing, or if the TLVs are not well-formed).
|
||||
*/
|
||||
bool otDatasetTlvsCompare(const otOperationalDatasetTlvs *aDatasetTlvsA, const otOperationalDatasetTlvs *aDatasetTlvsB);
|
||||
|
||||
/**
|
||||
* Converts a given Operational Dataset to `otOperationalDatasetTlvs`.
|
||||
*
|
||||
|
||||
@@ -52,7 +52,7 @@ extern "C" {
|
||||
*
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (582)
|
||||
#define OPENTHREAD_API_VERSION (583)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -168,6 +168,27 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
bool otDatasetTlvsCompare(const otOperationalDatasetTlvs *aDatasetTlvsA, const otOperationalDatasetTlvs *aDatasetTlvsB)
|
||||
{
|
||||
bool equals = false;
|
||||
MeshCoP::Dataset datasetA;
|
||||
MeshCoP::Dataset datasetB;
|
||||
|
||||
AssertPointerIsNotNull(aDatasetTlvsA);
|
||||
AssertPointerIsNotNull(aDatasetTlvsB);
|
||||
|
||||
SuccessOrExit(datasetA.SetFrom(*aDatasetTlvsA));
|
||||
SuccessOrExit(datasetB.SetFrom(*aDatasetTlvsB));
|
||||
|
||||
SuccessOrExit(datasetA.ValidateTlvs());
|
||||
SuccessOrExit(datasetB.ValidateTlvs());
|
||||
|
||||
equals = datasetA.Equals(datasetB);
|
||||
|
||||
exit:
|
||||
return equals;
|
||||
}
|
||||
|
||||
void otDatasetConvertToTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs)
|
||||
{
|
||||
MeshCoP::Dataset dataset;
|
||||
|
||||
@@ -565,6 +565,27 @@ Error Dataset::WriteTimestamp(Type aType, const Timestamp &aTimestamp)
|
||||
|
||||
void Dataset::RemoveTimestamp(Type aType) { RemoveTlv(TimestampTlvFor(aType)); }
|
||||
|
||||
bool Dataset::Equals(const Dataset &aOther) const
|
||||
{
|
||||
bool equals = false;
|
||||
|
||||
VerifyOrExit(mLength == aOther.mLength);
|
||||
|
||||
// Ensure every TLV in this dataset is present in `aOther` with the same type and value.
|
||||
for (const Tlv *tlv = GetTlvsStart(); tlv < GetTlvsEnd(); tlv = tlv->GetNext())
|
||||
{
|
||||
const Tlv *otherTlv = aOther.FindTlv(tlv->GetType());
|
||||
|
||||
VerifyOrExit(otherTlv != nullptr);
|
||||
VerifyOrExit(memcmp(tlv, otherTlv, tlv->GetSize()) == 0);
|
||||
}
|
||||
|
||||
equals = true;
|
||||
|
||||
exit:
|
||||
return equals;
|
||||
}
|
||||
|
||||
bool Dataset::IsSubsetOf(const Dataset &aOther) const
|
||||
{
|
||||
bool isSubset = false;
|
||||
|
||||
@@ -626,6 +626,22 @@ public:
|
||||
*/
|
||||
const Tlv *GetTlvsEnd(void) const { return reinterpret_cast<const Tlv *>(mTlvs + mLength); }
|
||||
|
||||
/**
|
||||
* Determines whether this Dataset equals another Dataset.
|
||||
*
|
||||
* Two datasets are considered matching if they contain the exact same set of TLVs (same types and values).
|
||||
* The order of TLVs within the datasets does not matter.
|
||||
*
|
||||
* This method assumes that both `this` and `aOther` datasets are valid and do not contain duplicate TLVs of
|
||||
* the same type. The behavior is undefined if a dataset contains duplicates.
|
||||
*
|
||||
* @param[in] aOther The other Dataset to check against.
|
||||
*
|
||||
* @retval TRUE The current Dataset equals @p aOther.
|
||||
* @retval FALSE The current Dataset does not match @p aOther.
|
||||
*/
|
||||
bool Equals(const Dataset &aOther) const;
|
||||
|
||||
/**
|
||||
* Determines whether this Dataset is a subset of another Dataset.
|
||||
*
|
||||
|
||||
@@ -259,6 +259,65 @@ void TestDataset(void)
|
||||
|
||||
VerifyOrQuit(!dataset2.IsSubsetOf(dataset));
|
||||
VerifyOrQuit(!dataset.IsSubsetOf(dataset2));
|
||||
|
||||
// Validate `Equals()`
|
||||
|
||||
SuccessOrQuit(dataset.SetFrom(kTlvBytes, sizeof(kTlvBytes)));
|
||||
SuccessOrQuit(dataset2.SetFrom(kTlvBytes, sizeof(kTlvBytes)));
|
||||
|
||||
VerifyOrQuit(dataset.Equals(dataset2));
|
||||
VerifyOrQuit(dataset2.Equals(dataset));
|
||||
|
||||
// Order of TLVs should not matter for `Equals()`
|
||||
ChannelTlvValue channel;
|
||||
channel.SetChannelAndPage(11);
|
||||
|
||||
dataset.Clear();
|
||||
SuccessOrQuit(dataset.Write<PanIdTlv>(0xface));
|
||||
SuccessOrQuit(dataset.Write<ChannelTlv>(channel));
|
||||
|
||||
dataset2.Clear();
|
||||
SuccessOrQuit(dataset2.Write<ChannelTlv>(channel));
|
||||
SuccessOrQuit(dataset2.Write<PanIdTlv>(0xface));
|
||||
|
||||
VerifyOrQuit(dataset.Equals(dataset2));
|
||||
VerifyOrQuit(dataset2.Equals(dataset));
|
||||
|
||||
// Different TLVs
|
||||
|
||||
dataset.Clear();
|
||||
SuccessOrQuit(dataset.Write<PanIdTlv>(0xface));
|
||||
|
||||
dataset2.Clear();
|
||||
SuccessOrQuit(dataset2.Write<PanIdTlv>(0xface));
|
||||
SuccessOrQuit(dataset2.Write<ChannelTlv>(channel));
|
||||
|
||||
VerifyOrQuit(!dataset.Equals(dataset2));
|
||||
VerifyOrQuit(!dataset2.Equals(dataset));
|
||||
|
||||
// Validate `Equals()` with unknown TLVs
|
||||
|
||||
{
|
||||
const uint8_t kUnknownTlvValue[] = {0x01, 0x02, 0x03, 0x04};
|
||||
const uint8_t kUnknownTlvValue2[] = {0x01, 0x02, 0x03, 0x05};
|
||||
const Tlv::Type kUnknownTlvType = static_cast<Tlv::Type>(222);
|
||||
|
||||
dataset.Clear();
|
||||
SuccessOrQuit(dataset.Write<PanIdTlv>(0xface));
|
||||
SuccessOrQuit(dataset.WriteTlv(kUnknownTlvType, kUnknownTlvValue, sizeof(kUnknownTlvValue)));
|
||||
|
||||
dataset2.Clear();
|
||||
SuccessOrQuit(dataset2.WriteTlv(kUnknownTlvType, kUnknownTlvValue, sizeof(kUnknownTlvValue)));
|
||||
SuccessOrQuit(dataset2.Write<PanIdTlv>(0xface));
|
||||
|
||||
VerifyOrQuit(dataset.Equals(dataset2));
|
||||
VerifyOrQuit(dataset2.Equals(dataset));
|
||||
|
||||
// Different values for the same unknown TLV type
|
||||
SuccessOrQuit(dataset2.WriteTlv(kUnknownTlvType, kUnknownTlvValue2, sizeof(kUnknownTlvValue2)));
|
||||
VerifyOrQuit(!dataset.Equals(dataset2));
|
||||
VerifyOrQuit(!dataset2.Equals(dataset));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MeshCoP
|
||||
|
||||
Reference in New Issue
Block a user