/*
 *  Copyright (c) 2016, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   This file includes definitions for generating and processing Thread Network Data TLVs.
 */

#ifndef NETWORK_DATA_TLVS_HPP_
#define NETWORK_DATA_TLVS_HPP_

#include "openthread-core-config.h"

#include <openthread/netdata.h>

#include "common/const_cast.hpp"
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/equatable.hpp"
#include "net/ip6_address.hpp"
#include "thread/mle_types.hpp"
#include "thread/network_data_types.hpp"

namespace ot {
namespace NetworkData {

/**
 * @addtogroup core-netdata-tlvs
 *
 * @brief
 *   This module includes definitions for generating and processing Thread Network Data TLVs.
 *
 * @{
 */

class NetworkDataTlv;

/**
 * Casts a `NetworkDataTlv` pointer to a given subclass `TlvType` pointer.
 *
 * @tparam TlvType  The TLV type to cast into. MUST be a subclass of `NetworkDataTlv`.
 *
 * @param[in] aTlv   A pointer to a `NetworkDataTlv` to convert/cast to a `TlvType`.
 *
 * @returns A `TlvType` pointer to `aTlv`.
 */
template <class TlvType> TlvType *As(NetworkDataTlv *aTlv) { return static_cast<TlvType *>(aTlv); }

/**
 * Casts a `NetworkDataTlv` pointer to a given subclass `TlvType` pointer.
 *
 * @tparam TlvType  The TLV type to cast into. MUST be a subclass of `NetworkDataTlv`.
 *
 * @param[in] aTlv   A pointer to a `NetworkDataTlv` to convert/cast to a `TlvType`.
 *
 * @returns A `TlvType` pointer to `aTlv`.
 */
template <class TlvType> const TlvType *As(const NetworkDataTlv *aTlv) { return static_cast<const TlvType *>(aTlv); }

/**
 * Casts a `NetworkDataTlv` reference to a given subclass `TlvType` reference.
 *
 * @tparam TlvType  The TLV type to cast into. MUST be a subclass of `NetworkDataTlv`.
 *
 * @param[in] aTlv   A reference to a `NetworkDataTlv` to convert/cast to a `TlvType`.
 *
 * @returns A `TlvType` reference to `aTlv`.
 */
template <class TlvType> TlvType &As(NetworkDataTlv &aTlv) { return static_cast<TlvType &>(aTlv); }

/**
 * Casts a `NetworkDataTlv` reference to a given subclass `TlvType` reference.
 *
 * @tparam TlvType  The TLV type to cast into. MUST be a subclass of `NetworkDataTlv`.
 *
 * @param[in] aTlv   A reference to a `NetworkDataTlv` to convert/cast to a `TlvType`.
 *
 * @returns A `TlvType` reference to `aTlv`.
 */
template <class TlvType> const TlvType &As(const NetworkDataTlv &aTlv) { return static_cast<const TlvType &>(aTlv); }

/**
 * Implements Thread Network Data TLV generation and parsing.
 */
OT_TOOL_PACKED_BEGIN
class NetworkDataTlv
{
public:
    /**
     * Thread Network Data Type values.
     */
    enum Type : uint8_t
    {
        kTypeHasRoute          = 0, ///< Has Route TLV
        kTypePrefix            = 1, ///< Prefix TLV
        kTypeBorderRouter      = 2, ///< Border Router TLV
        kTypeContext           = 3, ///< Context TLV
        kTypeCommissioningData = 4, ///< Commissioning Dataset TLV
        kTypeService           = 5, ///< Service TLV
        kTypeServer            = 6, ///< Server TLV
    };

    /**
     * Initializes the TLV.
     */
    void Init(void)
    {
        mType   = 0;
        mLength = 0;
    }

    /**
     * Returns the Type value.
     *
     * @returns The Type value.
     */
    Type GetType(void) const { return static_cast<Type>(mType >> kTypeOffset); }

    /**
     * Sets the Type value.
     *
     * @param[in]  aType  The Type value.
     */
    void SetType(Type aType) { mType = (mType & ~kTypeMask) | ((aType << kTypeOffset) & kTypeMask); }

    /**
     * Returns the Length value.
     *
     * @returns The Length value.
     */
    uint8_t GetLength(void) const { return mLength; }

    /**
     * Sets the Length value.
     *
     * @param[in]  aLength  The Length value.
     */
    void SetLength(uint8_t aLength) { mLength = aLength; }

    /**
     * Increases the Length value by a given amount.
     *
     * @param[in]  aIncrement  The increment amount to increase the length.
     */
    void IncreaseLength(uint8_t aIncrement) { mLength += aIncrement; }

    /**
     * Decreases the Length value by a given amount.
     *
     * @param[in]  aDecrement  The decrement amount to decrease the length.
     */
    void DecreaseLength(uint8_t aDecrement) { mLength -= aDecrement; }

    /**
     * Returns the TLV's total size (number of bytes) including Type, Length, and Value fields.
     *
     * @returns The total size include Type, Length, and Value fields.
     */
    uint8_t GetSize(void) const { return sizeof(NetworkDataTlv) + mLength; }

    /**
     * Returns a pointer to the Value.
     *
     * @returns A pointer to the value.
     */
    uint8_t *GetValue(void) { return reinterpret_cast<uint8_t *>(this) + sizeof(NetworkDataTlv); }

    /**
     * Returns a pointer to the Value.
     *
     * @returns A pointer to the value.
     */
    const uint8_t *GetValue(void) const { return reinterpret_cast<const uint8_t *>(this) + sizeof(NetworkDataTlv); }

    /**
     * Returns a pointer to the next Network Data TLV.
     *
     * @returns A pointer to the next Network Data TLV.
     */
    NetworkDataTlv *GetNext(void)
    {
        return reinterpret_cast<NetworkDataTlv *>(reinterpret_cast<uint8_t *>(this) + sizeof(*this) + mLength);
    }

    /**
     * Returns a pointer to the next Network Data TLV.
     *
     * @returns A pointer to the next Network Data TLV.
     */
    const NetworkDataTlv *GetNext(void) const
    {
        return reinterpret_cast<const NetworkDataTlv *>(reinterpret_cast<const uint8_t *>(this) + sizeof(*this) +
                                                        mLength);
    }

    /**
     * Clears the Stable bit.
     */
    void ClearStable(void) { mType &= ~kStableMask; }

    /**
     * Indicates whether or not the Stable bit is set.
     *
     * @retval TRUE   If the Stable bit is set.
     * @retval FALSE  If the Stable bit is not set.
     */
    bool IsStable(void) const { return (mType & kStableMask); }

    /**
     * Sets the Stable bit.
     */
    void SetStable(void) { mType |= kStableMask; }

    /**
     * Searches in a given sequence of TLVs to find the first TLV with a given type.
     *
     * @param[in]  aStart  A pointer to the start of the sequence of TLVs to search within.
     * @param[in]  aEnd    A pointer to the end of the sequence of TLVs.
     * @param[in]  aType   The TLV type to find.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    static NetworkDataTlv *Find(NetworkDataTlv *aStart, NetworkDataTlv *aEnd, Type aType)
    {
        return AsNonConst(Find(AsConst(aStart), AsConst(aEnd), aType));
    }

    /**
     * Searches in a given sequence of TLVs to find the first TLV with a given type.
     *
     * @param[in]  aStart  A pointer to the start of the sequence of TLVs to search within.
     * @param[in]  aEnd    A pointer to the end of the sequence of TLVs.
     * @param[in]  aType   The TLV type to find.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    static const NetworkDataTlv *Find(const NetworkDataTlv *aStart, const NetworkDataTlv *aEnd, Type aType);

    /**
     * This template static method searches in a given sequence of TLVs to find the first TLV with a given type.
     *
     * @tparam     TlvType The TLV type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @param[in]  aStart  A pointer to the start of the sequence of TLVs to search within.
     * @param[in]  aEnd    A pointer to the end of the sequence of TLVs.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    template <typename TlvType> static TlvType *Find(NetworkDataTlv *aStart, NetworkDataTlv *aEnd)
    {
        return As<TlvType>(Find(aStart, aEnd, TlvType::kType));
    }

    /**
     * This template static method searches in a given sequence of TLVs to find the first TLV with a given type.
     *
     * @tparam     TlvType The TLV type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @param[in]  aStart  A pointer to the start of the sequence of TLVs to search within.
     * @param[in]  aEnd    A pointer to the end of the sequence of TLVs.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    template <typename TlvType> static const TlvType *Find(const NetworkDataTlv *aStart, const NetworkDataTlv *aEnd)
    {
        return As<TlvType>(Find(aStart, aEnd, TlvType::kType));
    }

    /**
     * Searches in a given sequence of TLVs to find the first TLV with a given TLV type and stable
     * flag.
     *
     * @param[in]  aStart  A pointer to the start of the sequence of TLVs to search within.
     * @param[in]  aEnd    A pointer to the end of the sequence of TLVs.
     * @param[in]  aType   The TLV type to find.
     * @param[in]  aStable TRUE to find a stable TLV, FALSE to find a TLV not marked as stable.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    static NetworkDataTlv *Find(NetworkDataTlv *aStart, NetworkDataTlv *aEnd, Type aType, bool aStable)
    {
        return AsNonConst(Find(AsConst(aStart), AsConst(aEnd), aType, aStable));
    }

    /**
     * Searches in a given sequence of TLVs to find the first TLV with a given TLV type and stable
     * flag.
     *
     * @param[in]  aStart  A pointer to the start of the sequence of TLVs to search within.
     * @param[in]  aEnd    A pointer to the end of the sequence of TLVs.
     * @param[in]  aType   The TLV type to find.
     * @param[in]  aStable TRUE to find a stable TLV, FALSE to find a TLV not marked as stable.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    static const NetworkDataTlv *Find(const NetworkDataTlv *aStart,
                                      const NetworkDataTlv *aEnd,
                                      Type                  aType,
                                      bool                  aStable);

    /**
     * This template static method searches in a given sequence of TLVs to find the first TLV with a given TLV type and
     * stable flag.
     *
     * @tparam     TlvType The TLV type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @param[in]  aStart  A pointer to the start of the sequence of TLVs to search within.
     * @param[in]  aEnd    A pointer to the end of the sequence of TLVs.
     * @param[in]  aStable TRUE to find a stable TLV, FALSE to find a TLV not marked as stable.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    template <typename TlvType> static TlvType *Find(NetworkDataTlv *aStart, NetworkDataTlv *aEnd, bool aStable)
    {
        return As<TlvType>(Find(aStart, aEnd, TlvType::kType, aStable));
    }

    /**
     * This template static method searches in a given sequence of TLVs to find the first TLV with a given TLV type and
     * stable flag.
     *
     * @tparam     TlvType The TLV type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @param[in]  aStart  A pointer to the start of the sequence of TLVs to search within.
     * @param[in]  aEnd    A pointer to the end of the sequence of TLVs.
     * @param[in]  aStable TRUE to find a stable TLV, FALSE to find a TLV not marked as stable.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    template <typename TlvType>
    static const TlvType *Find(const NetworkDataTlv *aStart, const NetworkDataTlv *aEnd, bool aStable)
    {
        return As<TlvType>(Find(aStart, aEnd, TlvType::kType, aStable));
    }

private:
    static constexpr uint8_t kTypeOffset = 1;
    static constexpr uint8_t kTypeMask   = 0x7f << kTypeOffset;
    static constexpr uint8_t kStableMask = 1 << 0;

    uint8_t mType;
    uint8_t mLength;
} OT_TOOL_PACKED_END;

/**
 * Implements Has Route TLV entry generation and parsing.
 */
OT_TOOL_PACKED_BEGIN
class HasRouteEntry : public Equatable<HasRouteEntry>
{
    friend class ExternalRouteConfig;

public:
    /**
     * Initializes the header.
     */
    void Init(void)
    {
        SetRloc(Mle::kInvalidRloc16);
        mFlags = 0;
    }

    /**
     * Returns the RLOC16 value.
     *
     * @returns The RLOC16 value.
     */
    uint16_t GetRloc(void) const { return BigEndian::HostSwap16(mRloc); }

    /**
     * Sets the RLOC16 value.
     *
     * @param[in]  aRloc16  The RLOC16 value.
     */
    void SetRloc(uint16_t aRloc16) { mRloc = BigEndian::HostSwap16(aRloc16); }

    /**
     * Returns the Preference value.
     *
     * @returns The preference value.
     */
    int8_t GetPreference(void) const { return PreferenceFromFlags(GetFlags()); }

    /**
     * Gets the Flags value.
     *
     * @returns The Flags value.
     */
    uint8_t GetFlags(void) const { return mFlags; }

    /**
     * Sets the Flags value.
     *
     * @param[in]  aFlags  The Flags value.
     */
    void SetFlags(uint8_t aFlags) { mFlags = aFlags; }

    /**
     * Indicates whether or not the NAT64 flag is set.
     *
     * @retval TRUE   If the NAT64 flag is set.
     * @retval FALSE  If the NAT64 flag is not set.
     */
    bool IsNat64(void) const { return (mFlags & kNat64Flag) != 0; }

    /**
     * Indicates whether or not the Advertising PIO (AP) flag is set.
     *
     * @retval TRUE   If the AP flag is set.
     * @retval FALSE  If the AP flag is not set.
     */
    bool IsAdvPio(void) const { return (mFlags & kAdvPioFlag) != 0; }

    /**
     * Returns a pointer to the next HasRouteEntry.
     *
     * @returns A pointer to the next HasRouteEntry.
     */
    HasRouteEntry *GetNext(void) { return (this + 1); }

    /**
     * Returns a pointer to the next HasRouteEntry.
     *
     * @returns A pointer to the next HasRouteEntry.
     */
    const HasRouteEntry *GetNext(void) const { return (this + 1); }

    /**
     * Returns an updated flags bitmask by removing the preference bits (sets them to zero) from a
     * given flags bitmask.
     *
     * @param[in] aFlags  The flags bitmask.
     *
     * @returns An updated version @p aFlags with preference bits cleared.
     */
    static uint8_t FlagsWithoutPreference(uint8_t aFlags) { return (aFlags & ~kPreferenceMask); }

    /**
     * Gets the preference field from a flags bitmask.
     *
     * @param[in] aFlags  The flags.
     *
     * @returns The preference field from the @p aFlags.
     */
    static int8_t PreferenceFromFlags(uint8_t aFlags) { return RoutePreferenceFromValue(aFlags >> kPreferenceOffset); }

private:
    static constexpr uint8_t kPreferenceOffset = 6;
    static constexpr uint8_t kPreferenceMask   = 3 << kPreferenceOffset;
    static constexpr uint8_t kNat64Flag        = 1 << 5;
    static constexpr uint8_t kAdvPioFlag       = 1 << 4;

    uint16_t mRloc;
    uint8_t  mFlags;
} OT_TOOL_PACKED_END;

/**
 * Implements Has Route TLV generation and parsing.
 */
OT_TOOL_PACKED_BEGIN
class HasRouteTlv : public NetworkDataTlv
{
public:
    static constexpr Type kType = kTypeHasRoute; ///< The TLV Type.

    /**
     * Initializes the TLV.
     */
    void Init(void)
    {
        NetworkDataTlv::Init();
        SetType(kTypeHasRoute);
        SetLength(0);
    }

    /**
     * Returns the number of HasRoute entries.
     *
     * @returns The number of HasRoute entries.
     */
    uint8_t GetNumEntries(void) const { return GetLength() / sizeof(HasRouteEntry); }

    /**
     * Returns a pointer to the HasRoute entry at a given index.
     *
     * @param[in]  aIndex  An index.
     *
     * @returns A pointer to the HasRoute entry at @p aIndex.
     */
    HasRouteEntry *GetEntry(uint8_t aIndex)
    {
        return reinterpret_cast<HasRouteEntry *>(GetValue() + (aIndex * sizeof(HasRouteEntry)));
    }

    /**
     * Returns a pointer to the HasRoute entry at a given index.
     *
     * @param[in]  aIndex  An index.
     *
     * @returns A pointer to the HasRoute entry at @p aIndex.
     */
    const HasRouteEntry *GetEntry(uint8_t aIndex) const
    {
        return reinterpret_cast<const HasRouteEntry *>(GetValue() + (aIndex * sizeof(HasRouteEntry)));
    }

    /**
     * Returns a pointer to the first HasRouteEntry (at index 0'th).
     *
     * @returns A pointer to the first HasRouteEntry.
     */
    HasRouteEntry *GetFirstEntry(void) { return reinterpret_cast<HasRouteEntry *>(GetValue()); }

    /**
     * Returns a pointer to the first HasRouteEntry (at index 0'th).
     *
     * @returns A pointer to the first HasRouteEntry.
     */
    const HasRouteEntry *GetFirstEntry(void) const { return reinterpret_cast<const HasRouteEntry *>(GetValue()); }

    /**
     * Returns a pointer to the last HasRouteEntry.
     *
     * If there are no entries the pointer will be invalid but guaranteed to be before the `GetFirstEntry()` pointer.
     *
     * @returns A pointer to the last HasRouteEntry.
     */
    HasRouteEntry *GetLastEntry(void)
    {
        return reinterpret_cast<HasRouteEntry *>(GetValue() + GetLength() - sizeof(HasRouteEntry));
    }

    /**
     * Returns a pointer to the last HasRouteEntry.
     *
     * If there are no entries the pointer will be invalid but guaranteed to be before the `GetFirstEntry()` pointer.
     *
     * @returns A pointer to the last HasRouteEntry.
     */
    const HasRouteEntry *GetLastEntry(void) const
    {
        return reinterpret_cast<const HasRouteEntry *>(GetValue() + GetLength() - sizeof(HasRouteEntry));
    }

} OT_TOOL_PACKED_END;

/**
 * Implements Prefix TLV generation and parsing.
 */
OT_TOOL_PACKED_BEGIN
class PrefixTlv : public NetworkDataTlv
{
public:
    static constexpr Type kType = kTypePrefix; ///< The TLV Type.

    /**
     * Initializes the TLV.
     *
     * @param[in]  aDomainId      The Domain ID.
     * @param[in]  aPrefixLength  The Prefix Length in bits.
     * @param[in]  aPrefix        A pointer to the prefix.
     */
    void Init(uint8_t aDomainId, uint8_t aPrefixLength, const uint8_t *aPrefix)
    {
        NetworkDataTlv::Init();
        SetType(kTypePrefix);
        mDomainId     = aDomainId;
        mPrefixLength = aPrefixLength;
        memcpy(GetPrefix(), aPrefix, Ip6::Prefix::SizeForLength(aPrefixLength));
        SetSubTlvsLength(0);
    }

    /**
     * Initializes the TLV.
     *
     * @param[in]  aDomainId      The Domain ID.
     * @param[in]  aPrefix        The Prefix.
     */
    void Init(uint8_t aDomainId, const Ip6::Prefix aPrefix)
    {
        Init(aDomainId, aPrefix.GetLength(), aPrefix.GetBytes());
    }

    /**
     * Indicates whether or not the TLV appears to be well-formed.
     *
     * @retval TRUE   If the TLV appears to be well-formed.
     * @retval FALSE  If the TLV does not appear to be well-formed.
     */
    bool IsValid(void) const
    {
        return ((GetLength() >= sizeof(*this) - sizeof(NetworkDataTlv)) &&
                (GetLength() >= Ip6::Prefix::SizeForLength(mPrefixLength) + sizeof(*this) - sizeof(NetworkDataTlv)) &&
                (Ip6::Prefix::SizeForLength(mPrefixLength) <= sizeof(Ip6::Address)));
    }

    /**
     * Returns the Domain ID value.
     *
     * @returns The Domain ID value.
     */
    uint8_t GetDomainId(void) const { return mDomainId; }

    /**
     * Returns the Prefix Length value.
     *
     * @returns The Prefix Length value (in bits).
     */
    uint8_t GetPrefixLength(void) const { return mPrefixLength; }

    /**
     * Returns a pointer to the Prefix.
     *
     * @returns A pointer to the Prefix.
     */
    uint8_t *GetPrefix(void) { return reinterpret_cast<uint8_t *>(this) + sizeof(*this); }

    /**
     * Returns a pointer to the Prefix.
     *
     * @returns A pointer to the Prefix.
     */
    const uint8_t *GetPrefix(void) const { return reinterpret_cast<const uint8_t *>(this) + sizeof(*this); }

    /**
     * Copies the Prefix from TLV into a given `Ip6::Prefix`.
     *
     * @param[out] aPrefix  An `Ip6::Prefix` to copy the Prefix from TLV into.
     */
    void CopyPrefixTo(Ip6::Prefix &aPrefix) const { aPrefix.Set(GetPrefix(), GetPrefixLength()); }

    /**
     * Indicates whether the Prefix from TLV is equal to a given `Ip6::Prefix`.
     *
     * @param[in] aPrefix  A Prefix to compare with.
     *
     * @retval TRUE   The TLV's Prefix is equal to @p aPrefix.
     * @retval FALSE  The TLV's Prefix is not equal to @p aPrefix.
     */
    bool IsEqual(Ip6::Prefix &aPrefix) const { return aPrefix.IsEqual(GetPrefix(), GetPrefixLength()); }

    /**
     * Indicates whether the Prefix from TLV is equal to a given Prefix.
     *
     * @param[in]  aPrefix        A pointer to an IPv6 prefix to compare with.
     * @param[in]  aPrefixLength  The prefix length pointed to by @p aPrefix (in bits).
     *
     * @retval TRUE   The TLV's Prefix is equal to @p aPrefix.
     * @retval FALSE  The TLV's Prefix is not equal @p aPrefix.
     */
    bool IsEqual(const uint8_t *aPrefix, uint8_t aPrefixLength) const
    {
        return (aPrefixLength == mPrefixLength) &&
               (Ip6::Prefix::MatchLength(GetPrefix(), aPrefix, Ip6::Prefix::SizeForLength(aPrefixLength)) >=
                mPrefixLength);
    }

    /**
     * Returns a pointer to the Sub-TLVs.
     *
     * @returns A pointer to the Sub-TLVs.
     */
    NetworkDataTlv *GetSubTlvs(void)
    {
        return reinterpret_cast<NetworkDataTlv *>(GetPrefix() + Ip6::Prefix::SizeForLength(mPrefixLength));
    }

    /**
     * Returns a pointer to the Sub-TLVs.
     *
     * @returns A pointer to the Sub-TLVs.
     */
    const NetworkDataTlv *GetSubTlvs(void) const
    {
        return reinterpret_cast<const NetworkDataTlv *>(GetPrefix() + Ip6::Prefix::SizeForLength(mPrefixLength));
    }

    /**
     * Returns the Sub-TLVs length in bytes.
     *
     * @returns The Sub-TLVs length in bytes.
     */
    uint8_t GetSubTlvsLength(void) const
    {
        return GetLength() - (sizeof(*this) - sizeof(NetworkDataTlv) + Ip6::Prefix::SizeForLength(mPrefixLength));
    }

    /**
     * Sets the Sub-TLVs length in bytes.
     *
     * @param[in]  aLength  The Sub-TLVs length in bytes.
     */
    void SetSubTlvsLength(uint8_t aLength)
    {
        SetLength(sizeof(*this) - sizeof(NetworkDataTlv) + Ip6::Prefix::SizeForLength(mPrefixLength) + aLength);
    }

    /**
     * Searches in the sub-TLVs to find the first one matching a given TLV type.
     *
     * @tparam     SubTlvType    The sub-TLV type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    template <typename SubTlvType> SubTlvType *FindSubTlv(void)
    {
        return As<SubTlvType>(FindSubTlv(SubTlvType::kType));
    }

    /**
     * Searches in the sub-TLVs to find the first one matching a given TLV Type.
     *
     * @tparam     SubTlvType   The sub-TLV type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    template <typename SubTlvType> const SubTlvType *FindSubTlv(void) const
    {
        return As<SubTlvType>(FindSubTlv(SubTlvType::kType));
    }

    /**
     * Searches in the sub-TLVs to find the first one matching a given TLV type and stable flag.
     *
     * @tparam     SubTlvType    The sub-TLV type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @param[in]  aStable       TRUE to find a stable TLV, FALSE to find a TLV not marked as stable.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    template <typename SubTlvType> SubTlvType *FindSubTlv(bool aStable)
    {
        return As<SubTlvType>(FindSubTlv(static_cast<Type>(SubTlvType::kType), aStable));
    }

    /**
     * Searches in the sub-TLVs to find the first one matching a given TLV type and stable flag.
     *
     * @tparam     SubTlvType   The sub-TLV type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @param[in]  aStable      TRUE to find a stable TLV, FALSE to find a TLV not marked as stable.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    template <typename SubTlvType> const SubTlvType *FindSubTlv(bool aStable) const
    {
        return As<SubTlvType>(FindSubTlv(static_cast<Type>(SubTlvType::kType), aStable));
    }

    /**
     * Searches in the sub-TLVs to find the first one matching a given TLV type.
     *
     * @param[in]  aType        The sub-TLV type to search for.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    NetworkDataTlv *FindSubTlv(Type aType) { return AsNonConst(AsConst(this)->FindSubTlv(aType)); }

    /**
     * Searches in the sub-TLVs to find the first one matching a given TLV type.
     *
     * @param[in]  aType        The sub-TLV type to search for.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    const NetworkDataTlv *FindSubTlv(Type aType) const;

    /**
     * Searches in the sub-TLVs to find the first one matching a given TLV type and stable flag.
     *
     * @param[in]  aType        The sub-TLV type to search for.
     * @param[in]  aStable      TRUE to find a stable TLV, FALSE to find a TLV not marked as stable.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    NetworkDataTlv *FindSubTlv(Type aType, bool aStable)
    {
        return AsNonConst(AsConst(this)->FindSubTlv(aType, aStable));
    }

    /**
     * Searches in the sub-TLVs to find the first one matching a given TLV type and stable flag.
     *
     * @param[in]  aType        The sub-TLV type to search for.
     * @param[in]  aStable      TRUE to find a stable TLV, FALSE to find a TLV not marked as stable.
     *
     * @returns A pointer to the TLV if found, or `nullptr` if not found.
     */
    const NetworkDataTlv *FindSubTlv(Type aType, bool aStable) const;

    /**
     * Calculates the total size (number of bytes) of a Prefix TLV with a given Prefix Length value.
     *
     * Note that the returned size does include the Type and Length fields in the TLV, but does not account for any
     * sub TLVs of the Prefix TLV.
     *
     * @param[in]  aPrefixLength     A Prefix Length in bits.

     * @returns    The size (number of bytes) of the Prefix TLV.
     */
    static uint16_t CalculateSize(uint8_t aPrefixLength)
    {
        return sizeof(PrefixTlv) + Ip6::Prefix::SizeForLength(aPrefixLength);
    }

private:
    uint8_t mDomainId;
    uint8_t mPrefixLength;
} OT_TOOL_PACKED_END;

/**
 * Implements Border Router Entry generation and parsing.
 */
OT_TOOL_PACKED_BEGIN
class BorderRouterEntry : public Equatable<BorderRouterEntry>
{
    friend class OnMeshPrefixConfig;

public:
    /**
     * Initializes the TLV.
     */
    void Init(void)
    {
        SetRloc(Mle::kInvalidRloc16);
        mFlags = 0;
    }

    /**
     * Returns the RLOC16 value.
     *
     * @returns The RLOC16 value.
     */
    uint16_t GetRloc(void) const { return BigEndian::HostSwap16(mRloc); }

    /**
     * Sets the RLOC16 value.
     *
     * @param[in]  aRloc16  The RLOC16 value.
     */
    void SetRloc(uint16_t aRloc16) { mRloc = BigEndian::HostSwap16(aRloc16); }

    /**
     * Returns the Flags value.
     *
     * @returns The Flags value.
     */
    uint16_t GetFlags(void) const { return BigEndian::HostSwap16(mFlags); }

    /**
     * Sets the Flags value.
     *
     * @param[in]  aFlags  The Flags value.
     */
    void SetFlags(uint16_t aFlags) { mFlags = BigEndian::HostSwap16(aFlags); }

    /**
     * Returns the Preference value.
     *
     * @returns the Preference value.
     */
    int8_t GetPreference(void) const { return PreferenceFromFlags(GetFlags()); }

    /**
     * Indicates whether or not the Preferred flag is set.
     *
     * @retval TRUE   If the Preferred flag is set.
     * @retval FALSE  If the Preferred flag is not set.
     */
    bool IsPreferred(void) const { return (BigEndian::HostSwap16(mFlags) & kPreferredFlag) != 0; }

    /**
     * Indicates whether or not the SLAAC flag is set.
     *
     * @retval TRUE   If the SLAAC flag is set.
     * @retval FALSE  If the SLAAC flag is not set.
     */
    bool IsSlaac(void) const { return (BigEndian::HostSwap16(mFlags) & kSlaacFlag) != 0; }

    /**
     * Indicates whether or not the DHCP flag is set.
     *
     * @retval TRUE   If the DHCP flag is set.
     * @retval FALSE  If the DHCP flag is not set.
     */
    bool IsDhcp(void) const { return (BigEndian::HostSwap16(mFlags) & kDhcpFlag) != 0; }

    /**
     * Indicates whether or not the Configure flag is set.
     *
     * @retval TRUE   If the Configure flag is set.
     * @retval FALSE  If the Configure flag is not set.
     */
    bool IsConfigure(void) const { return (BigEndian::HostSwap16(mFlags) & kConfigureFlag) != 0; }

    /**
     * Indicates whether or not the Default Route flag is set.
     *
     * @retval TRUE   If the Default Route flag is set.
     * @retval FALSE  If the Default Route flag is not set.
     */
    bool IsDefaultRoute(void) const { return (BigEndian::HostSwap16(mFlags) & kDefaultRouteFlag) != 0; }

    /**
     * Indicates whether or not the On-Mesh flag is set.
     *
     * @retval TRUE   If the On-Mesh flag is set.
     * @retval FALSE  If the On-Mesh flag is not set.
     */
    bool IsOnMesh(void) const { return (BigEndian::HostSwap16(mFlags) & kOnMeshFlag) != 0; }

    /**
     * Indicates whether or not the Nd-Dns flag is set.
     *
     * @retval TRUE   If the Nd-Dns flag is set.
     * @retval FALSE  If the Nd-Dns flag is not set.
     */
    bool IsNdDns(void) const { return (BigEndian::HostSwap16(mFlags) & kNdDnsFlag) != 0; }

    /**
     * Indicates whether or not the Domain Prefix flag is set.
     *
     * @retval TRUE   If the Domain Prefix flag is set.
     * @retval FALSE  If the Domain Prefix flag is not set.
     */
    bool IsDp(void) const { return (BigEndian::HostSwap16(mFlags) & kDpFlag) != 0; }

    /**
     * Returns a pointer to the next BorderRouterEntry
     *
     * @returns A pointer to the next BorderRouterEntry.
     */
    BorderRouterEntry *GetNext(void) { return (this + 1); }

    /**
     * Returns a pointer to the next BorderRouterEntry
     *
     * @returns A pointer to the next BorderRouterEntry.
     */
    const BorderRouterEntry *GetNext(void) const { return (this + 1); }

    /**
     * Returns an updated flags bitmask by removing the preference bits (sets them to zero) from a
     * given flags bitmask.
     *
     * @param[in] aFlags  The flags bitmask.
     *
     * @returns An updated version @p aFlags with preference bits cleared.
     */
    static uint16_t FlagsWithoutPreference(uint16_t aFlags) { return (aFlags & ~kPreferenceMask); }

    /**
     * Gets the preference field from a flags bitmask.
     *
     * @param[in] aFlags  The flags.
     *
     * @returns The preference field from the @p aFlags.
     */
    static int8_t PreferenceFromFlags(uint16_t aFlags)
    {
        return RoutePreferenceFromValue(static_cast<uint8_t>(aFlags >> kPreferenceOffset));
    }

private:
    static constexpr uint8_t  kPreferenceOffset = 14;
    static constexpr uint16_t kPreferenceMask   = 3 << kPreferenceOffset;
    static constexpr uint16_t kPreferredFlag    = 1 << 13;
    static constexpr uint16_t kSlaacFlag        = 1 << 12;
    static constexpr uint16_t kDhcpFlag         = 1 << 11;
    static constexpr uint16_t kConfigureFlag    = 1 << 10;
    static constexpr uint16_t kDefaultRouteFlag = 1 << 9;
    static constexpr uint16_t kOnMeshFlag       = 1 << 8;
    static constexpr uint16_t kNdDnsFlag        = 1 << 7;
    static constexpr uint16_t kDpFlag           = 1 << 6;

    uint16_t mRloc;
    uint16_t mFlags;
} OT_TOOL_PACKED_END;

/**
 * Implements Border Router TLV generation and parsing.
 */
OT_TOOL_PACKED_BEGIN
class BorderRouterTlv : public NetworkDataTlv
{
public:
    static constexpr Type kType = kTypeBorderRouter; ///< The TLV Type.

    /**
     * Initializes the TLV.
     */
    void Init(void)
    {
        NetworkDataTlv::Init();
        SetType(kTypeBorderRouter);
        SetLength(0);
    }

    /**
     * Returns the number of Border Router entries.
     *
     * @returns The number of Border Router entries.
     */
    uint8_t GetNumEntries(void) const { return GetLength() / sizeof(BorderRouterEntry); }

    /**
     * Returns a pointer to the Border Router entry at a given index
     *
     * @param[in]  aIndex  The index.
     *
     * @returns A pointer to the Border Router entry at @p aIndex.
     */
    BorderRouterEntry *GetEntry(uint8_t aIndex)
    {
        return reinterpret_cast<BorderRouterEntry *>(GetValue() + (aIndex * sizeof(BorderRouterEntry)));
    }

    /**
     * Returns a pointer to the Border Router entry at a given index.
     *
     * @param[in]  aIndex  The index.
     *
     * @returns A pointer to the Border Router entry at @p aIndex
     */
    const BorderRouterEntry *GetEntry(uint8_t aIndex) const
    {
        return reinterpret_cast<const BorderRouterEntry *>(GetValue() + (aIndex * sizeof(BorderRouterEntry)));
    }

    /**
     * Returns a pointer to the first BorderRouterEntry (at index 0'th).
     *
     * @returns A pointer to the first BorderRouterEntry.
     */
    BorderRouterEntry *GetFirstEntry(void) { return reinterpret_cast<BorderRouterEntry *>(GetValue()); }

    /**
     * Returns a pointer to the first BorderRouterEntry (at index 0'th).
     *
     * @returns A pointer to the first BorderRouterEntry.
     */
    const BorderRouterEntry *GetFirstEntry(void) const
    {
        return reinterpret_cast<const BorderRouterEntry *>(GetValue());
    }

    /**
     * Returns a pointer to the last BorderRouterEntry.
     *
     * If there are no entries the pointer will be invalid but guaranteed to be before the `GetFirstEntry()` pointer.
     *
     * @returns A pointer to the last BorderRouterEntry.
     */
    BorderRouterEntry *GetLastEntry(void)
    {
        return reinterpret_cast<BorderRouterEntry *>(GetValue() + GetLength() - sizeof(BorderRouterEntry));
    }

    /**
     * Returns a pointer to the last BorderRouterEntry.
     *
     * If there are no entries the pointer will be invalid but guaranteed to be before the `GetFirstEntry()` pointer.
     *
     * @returns A pointer to the last BorderRouterEntry.
     */
    const BorderRouterEntry *GetLastEntry(void) const
    {
        return reinterpret_cast<const BorderRouterEntry *>(GetValue() + GetLength() - sizeof(BorderRouterEntry));
    }

} OT_TOOL_PACKED_END;

/**
 * Implements Context TLV generation and processing.
 */
OT_TOOL_PACKED_BEGIN
class ContextTlv : public NetworkDataTlv
{
public:
    static constexpr Type kType = kTypeContext; ///< The TLV Type.

    /**
     * Initializes the Context TLV.
     *
     * @param[in]  aContextId      The Context ID value.
     * @param[in]  aContextLength  The Context Length value.
     */
    void Init(uint8_t aContextId, uint8_t aContextLength)
    {
        NetworkDataTlv::Init();
        SetType(kTypeContext);
        SetLength(sizeof(ContextTlv) - sizeof(NetworkDataTlv));
        mFlags         = ((aContextId << kContextIdOffset) & kContextIdMask);
        mContextLength = aContextLength;
    }

    /**
     * Indicates whether or not the Compress flag is set.
     *
     * @retval TRUE   The Compress flag is set.
     * @retval FALSE  The Compress flags is not set.
     */
    bool IsCompress(void) const { return (mFlags & kCompressFlag) != 0; }

    /**
     * Clears the Compress flag.
     */
    void ClearCompress(void) { mFlags &= ~kCompressFlag; }

    /**
     * Sets the Compress flag.
     */
    void SetCompress(void) { mFlags |= kCompressFlag; }

    /**
     * Returns the Context ID value.
     *
     * @returns The Context ID value.
     */
    uint8_t GetContextId(void) const { return mFlags & kContextIdMask; }

    /**
     * Returns the Context Length value.
     *
     * @returns The Context Length value.
     */
    uint8_t GetContextLength(void) const { return mContextLength; }

private:
    static constexpr uint8_t kCompressFlag    = 1 << 4;
    static constexpr uint8_t kContextIdOffset = 0;
    static constexpr uint8_t kContextIdMask   = 0xf << kContextIdOffset;

    uint8_t mFlags;
    uint8_t mContextLength;
} OT_TOOL_PACKED_END;

/**
 * Implements Commissioning Data TLV generation and parsing.
 */
OT_TOOL_PACKED_BEGIN
class CommissioningDataTlv : public NetworkDataTlv
{
public:
    static constexpr Type kType = kTypeCommissioningData; ///< The TLV Type.

    /**
     * Initializes the TLV.
     */
    void Init(void)
    {
        NetworkDataTlv::Init();
        SetType(kTypeCommissioningData);
        SetLength(0);
    }
} OT_TOOL_PACKED_END;

/**
 * Implements Service Data TLV generation and parsing.
 */
OT_TOOL_PACKED_BEGIN
class ServiceTlv : public NetworkDataTlv
{
public:
    static constexpr Type kType = kTypeService; ///< The TLV Type.

    static constexpr uint32_t kThreadEnterpriseNumber = 44970; ///< Thread enterprise number.

    /**
     * Initializes the TLV.
     *
     * @param[in]  aServiceId          The Service Id value.
     * @param[in]  aEnterpriseNumber   The Enterprise Number.
     * @param[in]  aServiceData        The Service Data.
     */
    void Init(uint8_t aServiceId, uint32_t aEnterpriseNumber, const ServiceData &aServiceData);

    /**
     * Indicates whether or not the TLV appears to be well-formed.
     *
     * @retval TRUE   If the TLV appears to be well-formed.
     * @retval FALSE  If the TLV does not appear to be well-formed.
     */
    bool IsValid(void) const
    {
        uint8_t length = GetLength();

        return (length >= sizeof(mFlagsServiceId)) &&
               (length >= kMinLength + (IsThreadEnterprise() ? 0 : sizeof(uint32_t))) &&
               (static_cast<uint16_t>(length) + sizeof(NetworkDataTlv) >=
                CalculateSize(GetEnterpriseNumber(), GetServiceDataLength()));
    }

    /**
     * Returns the Service ID. It is in range 0x00-0x0f.
     *
     * @returns the Service ID.
     */
    uint8_t GetServiceId(void) const { return (mFlagsServiceId & kServiceIdMask); }

    /**
     * Returns Enterprise Number field.
     *
     * @returns The Enterprise Number.
     */
    uint32_t GetEnterpriseNumber(void) const
    {
        return IsThreadEnterprise() ? static_cast<uint32_t>(kThreadEnterpriseNumber)
                                    : BigEndian::HostSwap32(mShared.mEnterpriseNumber);
    }

    /**
     * Gets the Service Data.
     *
     * @param[out] aServiceData   A reference to a`ServiceData` to return the data.
     */
    void GetServiceData(ServiceData &aServiceData) const
    {
        aServiceData.Init(GetServiceData(), GetServiceDataLength());
    }

    /**
     * Gets Service Data length.
     *
     * @returns length of the Service Data field in bytes.
     */
    uint8_t GetServiceDataLength(void) const
    {
        return IsThreadEnterprise() ? mShared.mServiceDataLengthThreadEnterprise : mServiceDataLength;
    }

    /**
     * Returns the Sub-TLVs length in bytes.
     *
     * @returns The Sub-TLVs length in bytes.
     */
    uint8_t GetSubTlvsLength(void) { return GetLength() - GetFieldsLength(); }

    /**
     * Sets the Sub-TLVs length in bytes.
     *
     * @param[in]  aLength  The Sub-TLVs length in bytes.
     */
    void SetSubTlvsLength(uint8_t aLength) { SetLength(GetFieldsLength() + aLength); }

    /**
     * Returns a pointer to the Sub-TLVs.
     *
     * @returns A pointer to the Sub-TLVs.
     */
    NetworkDataTlv *GetSubTlvs(void) { return reinterpret_cast<NetworkDataTlv *>(GetValue() + GetFieldsLength()); }

    /**
     * Returns a pointer to the Sub-TLVs.
     *
     * @returns A pointer to the Sub-TLVs.
     */
    const NetworkDataTlv *GetSubTlvs(void) const
    {
        return reinterpret_cast<const NetworkDataTlv *>(GetValue() + GetFieldsLength());
    }

    /**
     * Calculates the total size (number of bytes) of a Service TLV with a given Enterprise Number
     * and Service Data length.
     *
     * Note that the returned size does include the Type and Length fields in the TLV, but does not account for any
     * sub-TLVs of the Service TLV.
     *
     * @param[in]  aEnterpriseNumber   A Enterprise Number.
     * @param[in]  aServiceDataLength  A Service Data length.
     *
     * @returns    The size (number of bytes) of the Service TLV.
     */
    static uint16_t CalculateSize(uint32_t aEnterpriseNumber, uint8_t aServiceDataLength)
    {
        return sizeof(NetworkDataTlv) + kMinLength + aServiceDataLength +
               ((aEnterpriseNumber == kThreadEnterpriseNumber) ? 0 : sizeof(uint32_t) /* mEnterpriseNumber  */);
    }

private:
    bool IsThreadEnterprise(void) const { return (mFlagsServiceId & kThreadEnterpriseFlag) != 0; }

    const uint8_t *GetServiceData(void) const
    {
        return (IsThreadEnterprise() ? &mShared.mServiceDataLengthThreadEnterprise : &mServiceDataLength) +
               sizeof(uint8_t);
    }

    uint8_t GetFieldsLength(void) const
    {
        // Returns the length of TLV value's common fields (flags, enterprise
        // number and service data) excluding any sub-TLVs.

        return kMinLength + (IsThreadEnterprise() ? 0 : sizeof(uint32_t)) + GetServiceDataLength();
    }

    static constexpr uint8_t kThreadEnterpriseFlag = (1 << 7);
    static constexpr uint8_t kServiceIdMask        = 0xf;
    static constexpr uint8_t kMinLength            = sizeof(uint8_t) + sizeof(uint8_t); // Flags & Service Data length.

    // When `kThreadEnterpriseFlag is set in the `mFlagsServiceId`, the
    // `mEnterpriseNumber` field is elided and `mFlagsServiceId` is
    // immediately followed by the Service Data length field (which is
    // represented by `mServiceDataLengthThreadEnterprise`)

    uint8_t mFlagsServiceId;
    union OT_TOOL_PACKED_FIELD
    {
        uint32_t mEnterpriseNumber;
        uint8_t  mServiceDataLengthThreadEnterprise;
    } mShared;
    uint8_t mServiceDataLength;

} OT_TOOL_PACKED_END;

/**
 * Implements Server Data TLV generation and parsing.
 */
OT_TOOL_PACKED_BEGIN
class ServerTlv : public NetworkDataTlv
{
public:
    static constexpr Type kType = kTypeServer; ///< The TLV Type.

    /**
     * Initializes the Server TLV.
     *
     * @param[in] aServer16          The Server16 value.
     * @param[in] aServerData        The Server Data.
     */
    void Init(uint16_t aServer16, const ServerData &aServerData)
    {
        NetworkDataTlv::Init();
        SetType(kTypeServer);
        SetServer16(aServer16);
        aServerData.CopyBytesTo(GetServerData());
        SetLength(sizeof(*this) - sizeof(NetworkDataTlv) + aServerData.GetLength());
    }

    /**
     * Indicates whether or not the TLV appears to be well-formed.
     *
     * @retval TRUE   If the TLV appears to be well-formed.
     * @retval FALSE  If the TLV does not appear to be well-formed.
     */
    bool IsValid(void) const { return GetLength() >= (sizeof(*this) - sizeof(NetworkDataTlv)); }

    /**
     * Returns the Server16 value.
     *
     * @returns The Server16 value.
     */
    uint16_t GetServer16(void) const { return BigEndian::HostSwap16(mServer16); }

    /*
     * Sets the Server16 value.
     *
     * @param[in]  aServer16  The Server16 value.
     */
    void SetServer16(uint16_t aServer16) { mServer16 = BigEndian::HostSwap16(aServer16); }

    /**
     * Gets the Server Data.
     *
     * @param[out] aServerData   A reference to a`ServerData` to return the data.
     */
    void GetServerData(ServerData &aServerData) const { aServerData.Init(GetServerData(), GetServerDataLength()); }

    /**
     * Returns the Server Data length in bytes.
     *
     * @returns The Server Data length in bytes.
     */
    uint8_t GetServerDataLength(void) const { return GetLength() - (sizeof(*this) - sizeof(NetworkDataTlv)); }

    /**
     * Indicates whether two Server TLVs fully match.
     *
     * @param[in]  aOther  Another Server TLV to compare with it.
     *
     * @retval TRUE  The two TLVs are equal.
     * @retval FALSE The two TLVs are not equal.
     */
    bool operator==(const ServerTlv &aOther) const
    {
        return (GetLength() == aOther.GetLength()) && (memcmp(GetValue(), aOther.GetValue(), GetLength()) == 0);
    }

    /**
     * Calculates the total size (number of bytes) of a Service TLV with a given Server Data length.
     *
     * Note that the returned size does include the Type and Length fields in the TLV.
     *
     * @param[in]  aServerDataLength    Server Data length in bytes.
     *
     * @returns    The size (number of bytes) of the Server TLV.
     */
    static uint16_t CalculateSize(uint8_t aServerDataLength) { return sizeof(ServerTlv) + aServerDataLength; }

private:
    const uint8_t *GetServerData(void) const { return reinterpret_cast<const uint8_t *>(this) + sizeof(*this); }
    uint8_t       *GetServerData(void) { return AsNonConst(AsConst(this)->GetServerData()); }

    uint16_t mServer16;
} OT_TOOL_PACKED_END;

/**
 * Represents a Network Data TLV iterator.
 */
class TlvIterator
{
public:
    /**
     * Initializes the `TlvIterator` to iterate over a given sequence of TLVs.
     *
     * @param[in] aStart  A pointer to the start of the TLV sequence.
     * @param[in] aEnd    A pointer to the end of the TLV sequence.
     */
    TlvIterator(const NetworkDataTlv *aStart, const NetworkDataTlv *aEnd)
        : mStart(aStart)
        , mEnd(aEnd)
    {
    }

    /**
     * Initializes the `TlvIterator` to iterate over TLVs from a given buffer.
     *
     * @param[in] aBuffer   A pointer to a buffer containing the TLVs.
     * @param[in] aLength   The length (number of bytes) of @p aBuffer.
     */
    TlvIterator(const uint8_t *aBuffer, uint8_t aLength)
        : TlvIterator(reinterpret_cast<const NetworkDataTlv *>(aBuffer),
                      reinterpret_cast<const NetworkDataTlv *>(aBuffer + aLength))
    {
    }

    /**
     * Initializes the `TlvIterator` to iterate over sub-TLVs of a given Prefix TLV.
     *
     * @param[in] aPrefixTlv   A Prefix TLV to iterate over its sub-TLVs.
     */
    explicit TlvIterator(const PrefixTlv &aPrefixTlv)
        : TlvIterator(aPrefixTlv.GetSubTlvs(), aPrefixTlv.GetNext())
    {
    }

    /**
     * Initializes the `TlvIterator` to iterate over sub-TLVs of a given Service TLV.
     *
     * @param[in] aServiceTlv   A Service TLV to iterate over its sub-TLVs.
     */
    explicit TlvIterator(const ServiceTlv &aServiceTlv)
        : TlvIterator(aServiceTlv.GetSubTlvs(), aServiceTlv.GetNext())
    {
    }

    /**
     * Iterates to the next TLV with a given type.
     *
     * @tparam  TlvType The TLV Type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @returns A pointer to the next TLV, or `nullptr` if it can not be found.
     */
    template <typename TlvType> const TlvType *Iterate(void) { return As<TlvType>(Iterate(TlvType::kType)); }

    /**
     * Iterates to the next TLV with a given type and stable flag.
     *
     * @tparam  TlvType The TLV Type to search for (MUST be a sub-class of `NetworkDataTlv`).
     *
     * @param[in]  aStable      TRUE to find a stable TLV, FALSE to find a TLV not marked as stable.
     *
     * @returns A pointer to the next TLV, or `nullptr` if it can not be found.
     */
    template <typename TlvType> const TlvType *Iterate(bool aStable)
    {
        return As<TlvType>(Iterate(TlvType::kType, aStable));
    }

private:
    const NetworkDataTlv *Iterate(NetworkDataTlv::Type aType);
    const NetworkDataTlv *Iterate(NetworkDataTlv::Type aType, bool aStable);

    const NetworkDataTlv *mStart;
    const NetworkDataTlv *mEnd;
};

/**
 * @}
 */

} // namespace NetworkData
} // namespace ot

#endif // NETWORK_DATA_TLVS_HPP_
