// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

#include "pw_bluetooth_sapphire/internal/host/common/advertising_data.h"

#include <cpp-string/utf_codecs.h>
#include <endian.h>

#include <type_traits>

#include "pw_bluetooth_sapphire/internal/host/common/assert.h"
#include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
#include "pw_bluetooth_sapphire/internal/host/common/log.h"
#include "pw_bluetooth_sapphire/internal/host/common/uuid.h"

#pragma clang diagnostic ignored "-Wswitch-enum"

namespace bt {

namespace {

DataType ServiceUuidTypeForUuidSize(UUIDElemSize size, bool complete) {
  switch (size) {
    case UUIDElemSize::k16Bit:
      return complete ? DataType::kComplete16BitServiceUuids
                      : DataType::kIncomplete16BitServiceUuids;
    case UUIDElemSize::k32Bit:
      return complete ? DataType::kComplete32BitServiceUuids
                      : DataType::kIncomplete32BitServiceUuids;
    case UUIDElemSize::k128Bit:
      return complete ? DataType::kComplete128BitServiceUuids
                      : DataType::kIncomplete128BitServiceUuids;
    default:
      BT_PANIC(
          "called ServiceUuidTypeForUuidSize with unknown UUIDElemSize %du",
          size);
  }
}

DataType ServiceDataTypeForUuidSize(UUIDElemSize size) {
  switch (size) {
    case UUIDElemSize::k16Bit:
      return DataType::kServiceData16Bit;
    case UUIDElemSize::k32Bit:
      return DataType::kServiceData32Bit;
    case UUIDElemSize::k128Bit:
      return DataType::kServiceData128Bit;
    default:
      BT_PANIC(
          "called ServiceDataTypeForUuidSize with unknown UUIDElemSize %du",
          size);
  };
}

size_t EncodedServiceDataSize(const UUID& uuid, const BufferView data) {
  return uuid.CompactSize() + data.size();
}

// clang-format off
// https://www.bluetooth.com/specifications/assigned-numbers/uri-scheme-name-string-mapping
const char* kUriSchemes[] = {"aaa:", "aaas:", "about:", "acap:", "acct:", "cap:", "cid:",
        "coap:", "coaps:", "crid:", "data:", "dav:", "dict:", "dns:", "file:", "ftp:", "geo:",
        "go:", "gopher:", "h323:", "http:", "https:", "iax:", "icap:", "im:", "imap:", "info:",
        "ipp:", "ipps:", "iris:", "iris.beep:", "iris.xpc:", "iris.xpcs:", "iris.lwz:", "jabber:",
        "ldap:", "mailto:", "mid:", "msrp:", "msrps:", "mtqp:", "mupdate:", "news:", "nfs:", "ni:",
        "nih:", "nntp:", "opaquelocktoken:", "pop:", "pres:", "reload:", "rtsp:", "rtsps:", "rtspu:",
        "service:", "session:", "shttp:", "sieve:", "sip:", "sips:", "sms:", "snmp:", "soap.beep:",
        "soap.beeps:", "stun:", "stuns:", "tag:", "tel:", "telnet:", "tftp:", "thismessage:",
        "tn3270:", "tip:", "turn:", "turns:", "tv:", "urn:", "vemmi:", "ws:", "wss:", "xcon:",
        "xcon-userid:", "xmlrpc.beep:", "xmlrpc.beeps:", "xmpp:", "z39.50r:", "z39.50s:", "acr:",
        "adiumxtra:", "afp:", "afs:", "aim:", "apt:", "attachment:", "aw:", "barion:", "beshare:",
        "bitcoin:", "bolo:", "callto:", "chrome:", "chrome-extension:", "com-eventbrite-attendee:",
        "content:", "cvs:", "dlna-playsingle:", "dlna-playcontainer:", "dtn:", "dvb:", "ed2k:",
        "facetime:", "feed:", "feedready:", "finger:", "fish:", "gg:", "git:", "gizmoproject:",
        "gtalk:", "ham:", "hcp:", "icon:", "ipn:", "irc:", "irc6:", "ircs:", "itms:", "jar:",
        "jms:", "keyparc:", "lastfm:", "ldaps:", "magnet:", "maps:", "market:", "message:", "mms:",
        "ms-help:", "ms-settings-power:", "msnim:", "mumble:", "mvn:", "notes:", "oid:", "palm:",
        "paparazzi:", "pkcs11:", "platform:", "proxy:", "psyc:", "query:", "res:", "resource:",
        "rmi:", "rsync:", "rtmfp:", "rtmp:", "secondlife:", "sftp:", "sgn:", "skype:", "smb:",
        "smtp:", "soldat:", "spotify:", "ssh:", "steam:", "submit:", "svn:", "teamspeak:",
        "teliaeid:", "things:", "udp:", "unreal:", "ut2004:", "ventrilo:", "view-source:",
        "webcal:", "wtai:", "wyciwyg:", "xfire:", "xri:", "ymsgr:", "example:",
        "ms-settings-cloudstorage:"};
// clang-format on

const size_t kUriSchemesSize = std::extent<decltype(kUriSchemes)>::value;

std::string EncodeUri(const std::string& uri) {
  std::string encoded_scheme;
  for (uint32_t i = 0; i < kUriSchemesSize; i++) {
    const char* scheme = kUriSchemes[i];
    size_t scheme_len = strlen(scheme);
    if (std::equal(scheme, scheme + scheme_len, uri.begin())) {
      bt_lib_cpp_string::WriteUnicodeCharacter(i + 2, &encoded_scheme);
      return encoded_scheme + uri.substr(scheme_len);
    }
  }
  // First codepoint (U+0001) is for uncompressed schemes.
  bt_lib_cpp_string::WriteUnicodeCharacter(1, &encoded_scheme);
  return encoded_scheme + uri;
}

const char kUndefinedScheme = 0x01;

std::string DecodeUri(const std::string& uri) {
  if (uri[0] == kUndefinedScheme) {
    return uri.substr(1);
  }
  uint32_t code_point = 0;
  size_t index = 0;

  // NOTE: as we are reading UTF-8 from `uri`, it is possible that `code_point`
  // corresponds to > 1 byte of `uri` (even for valid URI encoding schemes, as
  // U+00(>7F) encodes to 2 bytes).
  if (!bt_lib_cpp_string::ReadUnicodeCharacter(
          uri.c_str(), uri.size(), &index, &code_point)) {
    bt_log(INFO,
           "gap-le",
           "Attempted to decode malformed UTF-8 in AdvertisingData URI");
    return "";
  }
  // `uri` is not a c-string, so URIs that start with '\0' after c_str
  // conversion (i.e. both empty URIs and URIs with leading null bytes '\0') are
  // caught by the code_point < 2 check. We check
  // "< 2" instead of "== 0" for redundancy (extra safety!) with the
  // kUndefinedScheme check above.
  if (code_point >= kUriSchemesSize + 2 || code_point < 2) {
    bt_log(
        ERROR,
        "gap-le",
        "Failed to decode URI - supplied UTF-8 encoding scheme codepoint %u "
        "must be in the "
        "range 2-kUriSchemesSize + 1 (2-%lu) to correspond to a URI encoding",
        code_point,
        kUriSchemesSize + 1);
    return "";
  }
  return kUriSchemes[code_point - 2] + uri.substr(index + 1);
}

template <typename T>
inline size_t BufferWrite(MutableByteBuffer* buffer, size_t pos, const T& var) {
  buffer->Write((const uint8_t*)(uintptr_t)(&var), sizeof(T), pos);
  return sizeof(T);
}

}  // namespace

AdvertisingData::AdvertisingData(AdvertisingData&& other) noexcept {
  *this = std::move(other);
}

AdvertisingData& AdvertisingData::operator=(AdvertisingData&& other) noexcept {
  // Reset `other`'s state to that of a fresh, empty AdvertisingData
  local_name_ = std::exchange(other.local_name_, {});
  tx_power_ = std::exchange(other.tx_power_, {});
  appearance_ = std::exchange(other.appearance_, {});
  service_uuids_ = std::exchange(other.service_uuids_, kEmptyServiceUuidMap);
  manufacturer_data_ = std::exchange(other.manufacturer_data_, {});
  service_data_ = std::exchange(other.service_data_, {});
  uris_ = std::exchange(other.uris_, {});
  flags_ = std::exchange(other.flags_, {});
  return *this;
}

std::string AdvertisingData::ParseErrorToString(ParseError e) {
  switch (e) {
    case ParseError::kInvalidTlvFormat:
      return "provided bytes are not a valid type-length-value container";
    case ParseError::kTxPowerLevelMalformed:
      return "malformed tx power level";
    case ParseError::kLocalNameTooLong:
      return "local name exceeds max length (248)";
    case ParseError::kUuidsMalformed:
      return "malformed service UUIDs list";
    case ParseError::kManufacturerSpecificDataTooSmall:
      return "manufacturer specific data too small";
    case ParseError::kServiceDataTooSmall:
      return "service data too small to fit UUIDs";
    case ParseError::kServiceDataUuidMalformed:
      return "UUIDs associated with service data are malformed";
    case ParseError::kAppearanceMalformed:
      return "malformed appearance field";
    case ParseError::kMissing:
      return "data missing";
  }
}

AdvertisingData::ParseResult AdvertisingData::FromBytes(
    const ByteBuffer& data) {
  if (data.size() == 0) {
    return fit::error(ParseError::kMissing);
  }
  SupplementDataReader reader(data);
  if (!reader.is_valid()) {
    return fit::error(ParseError::kInvalidTlvFormat);
  }

  AdvertisingData out_ad;
  DataType type;
  BufferView field;
  while (reader.GetNextField(&type, &field)) {
    // While parsing through the advertising data fields, we do not need to
    // validate that per-field sizes do not overflow a uint8_t because they, by
    // construction, are obtained from a uint8_t.
    BT_DEBUG_ASSERT(field.size() <= std::numeric_limits<uint8_t>::max());
    switch (type) {
      case DataType::kTxPowerLevel: {
        if (field.size() != kTxPowerLevelSize) {
          return fit::error(ParseError::kTxPowerLevelMalformed);
        }

        out_ad.SetTxPower(static_cast<int8_t>(field[0]));
        break;
      }
      case DataType::kShortenedLocalName: {
        if (field.ToString().size() > kMaxNameLength) {
          return fit::error(ParseError::kLocalNameTooLong);
        }

        (void)out_ad.SetLocalName(field.ToString(), /*is_complete=*/false);
        break;
      }
      case DataType::kCompleteLocalName: {
        if (field.ToString().size() > kMaxNameLength) {
          return fit::error(ParseError::kLocalNameTooLong);
        }

        (void)out_ad.SetLocalName(field.ToString(), /*is_complete=*/true);
        break;
      }
      case DataType::kIncomplete16BitServiceUuids:
      case DataType::kComplete16BitServiceUuids:
      case DataType::kIncomplete32BitServiceUuids:
      case DataType::kComplete32BitServiceUuids:
      case DataType::kIncomplete128BitServiceUuids:
      case DataType::kComplete128BitServiceUuids: {
        // AddServiceUuid fails when the number of N bit UUIDs exceed the
        // kMaxNBitUuids bounds. These bounds are based on the number of UUIDs
        // that fit in the wire (byte) representation of an AdvertisingData, so
        // for valid AdvertisingData packets, the number of N bit service UUIDs
        // cannot exceed the bounds limits. However, because invalid packets may
        // provide multiple DataType fields for the same UUID (not allowed by
        // CSS v9 Part A 1.1.1), this limit may be exceeded, in which case we
        // reject the packet.
        if (!ParseUuids(
                field,
                SizeForType(type),
                fit::bind_member<&AdvertisingData::AddServiceUuid>(&out_ad))) {
          return fit::error(ParseError::kUuidsMalformed);
        }
        break;
      }
      case DataType::kManufacturerSpecificData: {
        if (field.size() < kManufacturerSpecificDataSizeMin) {
          return fit::error(ParseError::kManufacturerSpecificDataTooSmall);
        }

        uint16_t id = le16toh(*reinterpret_cast<const uint16_t*>(field.data()));
        const BufferView manuf_data(field.data() + kManufacturerIdSize,
                                    field.size() - kManufacturerIdSize);

        BT_ASSERT(out_ad.SetManufacturerData(id, manuf_data));
        break;
      }
      case DataType::kServiceData16Bit:
      case DataType::kServiceData32Bit:
      case DataType::kServiceData128Bit: {
        UUID uuid;
        size_t uuid_size = SizeForType(type);
        if (field.size() < uuid_size) {
          return fit::error(ParseError::kServiceDataTooSmall);
        }
        const BufferView uuid_bytes(field.data(), uuid_size);
        if (!UUID::FromBytes(uuid_bytes, &uuid)) {
          // This is impossible given that uuid_bytes.size() is guaranteed to be
          // a valid UUID size, and the current UUID::FromBytes implementation
          // only fails if given an invalid size. We leave it in anyway in case
          // this implementation changes in the future.
          return fit::error(ParseError::kServiceDataUuidMalformed);
        }
        const BufferView service_data(field.data() + uuid_size,
                                      field.size() - uuid_size);
        BT_ASSERT(out_ad.SetServiceData(uuid, service_data));
        break;
      }
      case DataType::kAppearance: {
        // TODO(armansito): Peer should have a function to return the
        // device appearance, as it can be obtained either from advertising data
        // or via GATT.
        if (field.size() != kAppearanceSize) {
          return fit::error(ParseError::kAppearanceMalformed);
        }

        out_ad.SetAppearance(le16toh(field.To<uint16_t>()));
        break;
      }
      case DataType::kURI: {
        // Assertion is safe as AddUri only fails when field size > uint8_t,
        // which is impossible.
        BT_ASSERT(out_ad.AddUri(DecodeUri(field.ToString())));
        break;
      }
      case DataType::kFlags: {
        // Flags field may be zero or more octets long but we only store the
        // first octet.
        if (field.size() > 0) {
          out_ad.SetFlags(field[0]);
        } else {
          out_ad.SetFlags(0);
        }
        break;
      }
      default:
        bt_log(DEBUG,
               "gap",
               "ignored advertising field (type %#.2x)",
               static_cast<unsigned int>(type));
        break;
    }
  }

  return fit::ok(std::move(out_ad));
}

void AdvertisingData::Copy(AdvertisingData* out) const {
  *out = AdvertisingData();

  if (local_name_) {
    BT_ASSERT(out->SetLocalName(*local_name_));
  }

  if (tx_power_) {
    out->SetTxPower(*tx_power_);
  }

  if (appearance_) {
    out->SetAppearance(*appearance_);
  }

  out->service_uuids_ = service_uuids_;

  for (const auto& it : manufacturer_data_) {
    BT_ASSERT(out->SetManufacturerData(it.first, it.second.view()));
  }

  for (const auto& it : service_data_) {
    BT_ASSERT(out->SetServiceData(it.first, it.second.view()));
  }

  for (const auto& it : uris_) {
    BT_ASSERT_MSG(out->AddUri(it), "Copying invalid AD with too-long URI");
  }
}

[[nodiscard]] bool AdvertisingData::AddServiceUuid(const UUID& uuid) {
  auto iter = service_uuids_.find(uuid.CompactSize());
  BT_ASSERT(iter != service_uuids_.end());
  BoundedUuids& uuids = iter->second;
  return uuids.AddUuid(uuid);
}

std::unordered_set<UUID> AdvertisingData::service_uuids() const {
  std::unordered_set<UUID> out;
  for (auto& [_elemsize, uuids] : service_uuids_) {
    out.insert(uuids.set().begin(), uuids.set().end());
  }
  return out;
}

[[nodiscard]] bool AdvertisingData::SetServiceData(const UUID& uuid,
                                                   const ByteBuffer& data) {
  size_t encoded_size = EncodedServiceDataSize(uuid, data.view());
  if (encoded_size > kMaxEncodedServiceDataLength) {
    bt_log(WARN,
           "gap-le",
           "SetServiceData for UUID %s failed: (UUID+data) size %zu > maximum "
           "allowed size %du",
           bt_str(uuid),
           encoded_size,
           kMaxEncodedServiceDataLength);
    return false;
  }
  service_data_[uuid] = DynamicByteBuffer(data);
  return true;
}

std::unordered_set<UUID> AdvertisingData::service_data_uuids() const {
  std::unordered_set<UUID> uuids;
  for (const auto& it : service_data_) {
    uuids.emplace(it.first);
  }
  return uuids;
}

BufferView AdvertisingData::service_data(const UUID& uuid) const {
  auto iter = service_data_.find(uuid);
  if (iter == service_data_.end())
    return BufferView();
  return BufferView(iter->second);
}

[[nodiscard]] bool AdvertisingData::SetManufacturerData(
    const uint16_t company_id, const BufferView& data) {
  size_t field_size = data.size();
  if (field_size > kMaxManufacturerDataLength) {
    bt_log(WARN,
           "gap-le",
           "SetManufacturerData for company id %#.4x failed: (UUID+data) size "
           "%zu > maximum allowed "
           "size %hhu",
           company_id,
           field_size,
           kMaxManufacturerDataLength);
    return false;
  }
  manufacturer_data_[company_id] = DynamicByteBuffer(data);
  return true;
}

std::unordered_set<uint16_t> AdvertisingData::manufacturer_data_ids() const {
  std::unordered_set<uint16_t> manuf_ids;
  for (const auto& it : manufacturer_data_) {
    manuf_ids.emplace(it.first);
  }
  return manuf_ids;
}

BufferView AdvertisingData::manufacturer_data(const uint16_t company_id) const {
  auto iter = manufacturer_data_.find(company_id);
  if (iter == manufacturer_data_.end())
    return BufferView();
  return BufferView(iter->second);
}

void AdvertisingData::SetTxPower(int8_t dbm) { tx_power_ = dbm; }

std::optional<int8_t> AdvertisingData::tx_power() const { return tx_power_; }

bool AdvertisingData::SetLocalName(const LocalName& local_name) {
  if (local_name.name.size() > kMaxNameLength) {
    return false;
  }
  if (local_name_.has_value() && local_name_->is_complete &&
      !local_name.is_complete) {
    return false;
  }
  local_name_ = local_name;
  return true;
}

std::optional<AdvertisingData::LocalName> AdvertisingData::local_name() const {
  return local_name_;
}

[[nodiscard]] bool AdvertisingData::AddUri(const std::string& uri) {
  if (EncodeUri(uri).size() > kMaxEncodedUriLength) {
    bt_log(WARN,
           "gap-le",
           "not inserting uri %s as it exceeds the max URI size for AD",
           uri.c_str());
    return false;
  }
  if (uri.empty()) {
    bt_log(WARN, "gap-le", "skipping insertion of empty uri to AD");
    return true;
  }
  uris_.insert(uri);
  return true;
}

const std::unordered_set<std::string>& AdvertisingData::uris() const {
  return uris_;
}

void AdvertisingData::SetAppearance(uint16_t appearance) {
  appearance_ = appearance;
}

std::optional<uint16_t> AdvertisingData::appearance() const {
  return appearance_;
}

void AdvertisingData::SetFlags(AdvFlags flags) { flags_ = flags; }

std::optional<AdvFlags> AdvertisingData::flags() const { return flags_; }

size_t AdvertisingData::CalculateBlockSize(bool include_flags) const {
  size_t len = 0;
  if (include_flags) {
    len += kTLVFlagsSize;
  }

  if (tx_power_) {
    len += kTLVTxPowerLevelSize;
  }

  if (appearance_) {
    len += kTLVAppearanceSize;
  }

  if (local_name_) {
    len += 2 + local_name_->name.size();
  }

  for (const auto& manuf_pair : manufacturer_data_) {
    len += 2 + 2 + manuf_pair.second.size();
  }

  for (const auto& service_data_pair : service_data_) {
    len += 2 + service_data_pair.first.CompactSize() +
           service_data_pair.second.size();
  }

  for (const auto& uri : uris_) {
    len += 2 + EncodeUri(uri).size();
  }

  for (const auto& [uuid_size, bounded_uuids] : service_uuids_) {
    if (bounded_uuids.set().empty()) {
      continue;
    }
    len += 2;  // 1 byte for # of UUIDs and 1 for UUID type
    len += uuid_size * bounded_uuids.set().size();
  }

  return len;
}

bool AdvertisingData::WriteBlock(MutableByteBuffer* buffer,
                                 std::optional<AdvFlags> flags) const {
  BT_DEBUG_ASSERT(buffer);

  size_t min_buf_size = CalculateBlockSize(flags.has_value());
  if (buffer->size() < min_buf_size) {
    return false;
  }

  size_t pos = 0;
  if (flags) {
    (*buffer)[pos++] =
        kTLVFlagsSize - 1;  // size variable includes current field, subtract 1
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kFlags);
    (*buffer)[pos++] = static_cast<uint8_t>(flags.value());
  }

  if (tx_power_) {
    (*buffer)[pos++] = kTLVTxPowerLevelSize -
                       1;  // size variable includes current field, subtract 1
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kTxPowerLevel);
    (*buffer)[pos++] = static_cast<uint8_t>(tx_power_.value());
  }

  if (appearance_) {
    (*buffer)[pos++] = kTLVAppearanceSize -
                       1;  // size variable includes current field, subtract 1
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kAppearance);
    pos += BufferWrite(buffer, pos, appearance_.value());
  }

  if (local_name_) {
    BT_ASSERT(local_name_->name.size() <= kMaxNameLength);
    (*buffer)[pos++] =
        static_cast<uint8_t>(local_name_->name.size()) + 1;  // 1 for null char
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kCompleteLocalName);
    buffer->Write(reinterpret_cast<const uint8_t*>(local_name_->name.c_str()),
                  local_name_->name.size(),
                  pos);
    pos += local_name_->name.size();
  }

  for (const auto& manuf_pair : manufacturer_data_) {
    size_t data_size = manuf_pair.second.size();
    BT_ASSERT(data_size <= kMaxManufacturerDataLength);
    (*buffer)[pos++] =
        1 + 2 +
        static_cast<uint8_t>(data_size);  // 1 for type, 2 for Manuf. Code
    (*buffer)[pos++] =
        static_cast<uint8_t>(DataType::kManufacturerSpecificData);
    pos += BufferWrite(buffer, pos, manuf_pair.first);
    buffer->Write(manuf_pair.second, pos);
    pos += data_size;
  }

  for (const auto& service_data_pair : service_data_) {
    UUID uuid = service_data_pair.first;
    size_t encoded_service_data_size =
        EncodedServiceDataSize(uuid, service_data_pair.second.view());
    BT_ASSERT(encoded_service_data_size <= kMaxEncodedServiceDataLength);
    (*buffer)[pos++] =
        1 + static_cast<uint8_t>(encoded_service_data_size);  // 1 for type
    (*buffer)[pos++] =
        static_cast<uint8_t>(ServiceDataTypeForUuidSize(uuid.CompactSize()));
    auto target = buffer->mutable_view(pos);
    pos += service_data_pair.first.ToBytes(&target);
    buffer->Write(service_data_pair.second, pos);
    pos += service_data_pair.second.size();
  }

  for (const auto& uri : uris_) {
    std::string s = EncodeUri(uri);
    BT_ASSERT(s.size() <= kMaxEncodedUriLength);
    (*buffer)[pos++] = 1 + static_cast<uint8_t>(s.size());  // 1 for type
    (*buffer)[pos++] = static_cast<uint8_t>(DataType::kURI);
    buffer->Write(reinterpret_cast<const uint8_t*>(s.c_str()), s.length(), pos);
    pos += s.size();
  }

  for (const auto& [uuid_width, bounded_uuids] : service_uuids_) {
    if (bounded_uuids.set().empty()) {
      continue;
    }

    // 1 for type
    BT_ASSERT(1 + uuid_width * bounded_uuids.set().size() <=
              std::numeric_limits<uint8_t>::max());
    (*buffer)[pos++] =
        1 + uuid_width * static_cast<uint8_t>(bounded_uuids.set().size());
    (*buffer)[pos++] = static_cast<uint8_t>(
        ServiceUuidTypeForUuidSize(uuid_width, /*complete=*/false));
    for (const auto& uuid : bounded_uuids.set()) {
      BT_ASSERT_MSG(uuid.CompactSize() == uuid_width,
                    "UUID: %s - Expected Width: %d",
                    bt_str(uuid),
                    uuid_width);
      auto target = buffer->mutable_view(pos);
      pos += uuid.ToBytes(&target);
    }
  }

  return true;
}

bool AdvertisingData::operator==(const AdvertisingData& other) const {
  if ((local_name_ != other.local_name_) || (tx_power_ != other.tx_power_) ||
      (appearance_ != other.appearance_) ||
      (service_uuids_ != other.service_uuids_) || (uris_ != other.uris_) ||
      (flags_ != other.flags_)) {
    return false;
  }

  if (manufacturer_data_.size() != other.manufacturer_data_.size()) {
    return false;
  }

  for (const auto& it : manufacturer_data_) {
    auto that = other.manufacturer_data_.find(it.first);
    if (that == other.manufacturer_data_.end()) {
      return false;
    }
    size_t bytes = it.second.size();
    if (bytes != that->second.size()) {
      return false;
    }
    if (std::memcmp(it.second.data(), that->second.data(), bytes) != 0) {
      return false;
    }
  }

  if (service_data_.size() != other.service_data_.size()) {
    return false;
  }

  for (const auto& it : service_data_) {
    auto that = other.service_data_.find(it.first);
    if (that == other.service_data_.end()) {
      return false;
    }
    size_t bytes = it.second.size();
    if (bytes != that->second.size()) {
      return false;
    }
    if (std::memcmp(it.second.data(), that->second.data(), bytes) != 0) {
      return false;
    }
  }

  return true;
}

bool AdvertisingData::operator!=(const AdvertisingData& other) const {
  return !(*this == other);
}

bool AdvertisingData::BoundedUuids::AddUuid(UUID uuid) {
  BT_ASSERT(set_.size() <= bound_);
  if (set_.size() < bound_) {
    if (!set_.insert(uuid).second) {
      bt_log(INFO,
             "gap-le",
             "Skipping addition of duplicate UUID %s to AD",
             bt_str(uuid));
    }
    return true;
  }
  if (set_.find(uuid) != set_.end()) {
    bt_log(INFO,
           "gap-le",
           "Skipping addition of duplicate UUID %s to AD",
           bt_str(uuid));
    return true;
  }
  bt_log(WARN,
         "gap-le",
         "Failed to add service UUID %s to AD - no space left",
         bt_str(uuid));
  return false;
}
}  // namespace bt
