mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
handle data element
PiperOrigin-RevId: 728727545
This commit is contained in:
committed by
Copybara-Service
parent
eea3441da9
commit
46c0f153fe
@@ -406,6 +406,7 @@ let package = Package(
|
||||
// build files
|
||||
"connections/implementation/analytics/BUILD",
|
||||
"connections/implementation/flags/BUILD",
|
||||
"connections/implementation/mediums/advertisements/BUILD",
|
||||
"connections/implementation/mediums/ble_v2/BUILD",
|
||||
"connections/implementation/mediums/multiplex/BUILD",
|
||||
"connections/implementation/mediums/BUILD",
|
||||
@@ -454,6 +455,7 @@ let package = Package(
|
||||
"connections/implementation/wifi_hotspot_bwu_test.cc",
|
||||
"connections/implementation/analytics/analytics_recorder_test.cc",
|
||||
"connections/implementation/analytics/throughput_recorder_test.cc",
|
||||
"connections/implementation/mediums/advertisements/data_element_test.cc",
|
||||
"connections/implementation/mediums/ble_v2_test.cc",
|
||||
"connections/implementation/mediums/ble_v2/bloom_filter_test.cc",
|
||||
"connections/implementation/mediums/ble_v2/ble_packet_test.cc",
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# 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.
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
cc_library(
|
||||
name = "common",
|
||||
srcs = ["data_element.cc"],
|
||||
hdrs = ["data_element.h"],
|
||||
deps = [
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform:util",
|
||||
"@com_google_absl//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "data_element_test",
|
||||
srcs = ["data_element_test.cc"],
|
||||
deps = [
|
||||
":common",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:util",
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,159 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// 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 "connections/implementation/mediums/advertisements/data_element.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/logging.h"
|
||||
#include "internal/platform/stream_reader.h"
|
||||
#include "internal/platform/stream_writer.h"
|
||||
|
||||
namespace nearby::connections::advertisements {
|
||||
|
||||
std::optional<DataElement> DataElement::From(uint8_t type, std::string value) {
|
||||
if (type > kMaxTypeValue || type == 0 || value.size() > kMaxLengthValue) {
|
||||
LOG(WARNING) << "Invalid data element: type=" << type
|
||||
<< ", length=" << value.size();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
DataElement data_element;
|
||||
data_element.type_ = type;
|
||||
data_element.value_ = value;
|
||||
return data_element;
|
||||
}
|
||||
|
||||
std::optional<DataElement> DataElement::FromStreamReader(StreamReader& reader) {
|
||||
std::optional<uint8_t> is_two_bytes = reader.ReadBits(1);
|
||||
if (!is_two_bytes.has_value()) {
|
||||
LOG(WARNING) << "Failed to read type";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
std::string value;
|
||||
|
||||
if (*is_two_bytes) {
|
||||
// two bytes DE header
|
||||
std::optional<uint8_t> length_result = reader.ReadBits(7);
|
||||
if (!length_result.has_value()) {
|
||||
LOG(WARNING) << "Failed to read length";
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<uint8_t> type_result = reader.ReadUint8();
|
||||
if (!type_result.has_value() || *type_result == 0) {
|
||||
LOG(WARNING) << "Failed to read type";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
type = *type_result;
|
||||
length = *length_result;
|
||||
|
||||
} else {
|
||||
// one byte DE header
|
||||
std::optional<uint8_t> length_result = reader.ReadBits(3);
|
||||
if (!length_result.has_value()) {
|
||||
LOG(WARNING) << "Failed to read length";
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<uint8_t> type_result = reader.ReadBits(4);
|
||||
if (!type_result.has_value() || *type_result == 0) {
|
||||
LOG(WARNING) << "Failed to read type";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
type = *type_result;
|
||||
length = *length_result;
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
std::optional<ByteArray> bytes = reader.ReadBytes(length);
|
||||
if (!bytes.has_value()) {
|
||||
LOG(WARNING) << "Failed to read bytes";
|
||||
return std::nullopt;
|
||||
}
|
||||
value = bytes->string_data();
|
||||
}
|
||||
|
||||
DataElement data_element;
|
||||
data_element.type_ = type;
|
||||
data_element.value_ = value;
|
||||
return data_element;
|
||||
}
|
||||
|
||||
std::optional<DataElement> DataElement::FromData(const std::string& data) {
|
||||
ByteArray bytes(data);
|
||||
StreamReader reader(bytes);
|
||||
return FromStreamReader(reader);
|
||||
}
|
||||
|
||||
std::optional<std::string> DataElement::ToData() const {
|
||||
StreamWriter writer;
|
||||
|
||||
if (!ToStreamWriter(writer)) {
|
||||
LOG(WARNING) << "Failed to write data element";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return writer.GetData();
|
||||
}
|
||||
|
||||
bool DataElement::ToStreamWriter(StreamWriter& writer) const {
|
||||
bool is_one_byte = true;
|
||||
if (type_ > 15 || value_.size() > 7) {
|
||||
is_one_byte = false;
|
||||
}
|
||||
|
||||
if (is_one_byte) {
|
||||
if (!writer.WriteBits(0, 1).Ok()) {
|
||||
LOG(WARNING) << "Failed to write is_one_byte";
|
||||
return false;
|
||||
}
|
||||
if (!writer.WriteBits(value_.size(), 3).Ok()) {
|
||||
LOG(WARNING) << "Failed to write length";
|
||||
return false;
|
||||
}
|
||||
if (!writer.WriteBits(type_, 4).Ok()) {
|
||||
LOG(WARNING) << "Failed to write type";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!writer.WriteBits(1, 1).Ok()) {
|
||||
LOG(WARNING) << "Failed to write is_one_byte";
|
||||
return false;
|
||||
}
|
||||
if (!writer.WriteBits(value_.size(), 7).Ok()) {
|
||||
LOG(WARNING) << "Failed to write length";
|
||||
return false;
|
||||
}
|
||||
if (!writer.WriteUint8(type_).Ok()) {
|
||||
LOG(WARNING) << "Failed to write type";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!writer.WriteBytes(value_).Ok()) {
|
||||
LOG(WARNING) << "Failed to write value";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nearby::connections::advertisements
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_MEDIUMS_ADVERTISEMENTS_DATA_ELEMENT_H_
|
||||
#define THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_MEDIUMS_ADVERTISEMENTS_DATA_ELEMENT_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "internal/platform/stream_reader.h"
|
||||
#include "internal/platform/stream_writer.h"
|
||||
|
||||
namespace nearby::connections::advertisements {
|
||||
|
||||
class DataElement {
|
||||
public:
|
||||
constexpr static uint8_t kMaxLengthValue = 0x7f;
|
||||
constexpr static uint8_t kMaxTypeValue = 0xff;
|
||||
|
||||
~DataElement() = default;
|
||||
|
||||
static std::optional<DataElement> From(uint8_t type, std::string value);
|
||||
static std::optional<DataElement> FromStreamReader(StreamReader& reader);
|
||||
static std::optional<DataElement> FromData(const std::string& data);
|
||||
|
||||
uint8_t type() const { return type_; }
|
||||
uint8_t length() const { return value_.size(); }
|
||||
std::string value() const { return value_; }
|
||||
|
||||
std::optional<std::string> ToData() const;
|
||||
bool ToStreamWriter(StreamWriter& writer) const;
|
||||
|
||||
private:
|
||||
// Each DE has a defined meaning and follows a common TLV structure.
|
||||
uint8_t type_{0};
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
} // namespace nearby::connections::advertisements
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_MEDIUMS_ADVERTISEMENTS_DATA_ELEMENT_H_
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// 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 "connections/implementation/mediums/advertisements/data_element.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/stream_reader.h"
|
||||
#include "internal/platform/stream_writer.h"
|
||||
|
||||
namespace nearby::connections::advertisements {
|
||||
namespace {
|
||||
|
||||
TEST(DataElementTest, From) {
|
||||
std::optional<DataElement> data_element =
|
||||
DataElement::From(0x01, "test_value");
|
||||
ASSERT_TRUE(data_element.has_value());
|
||||
EXPECT_EQ(data_element->type(), 0x01);
|
||||
EXPECT_EQ(data_element->length(), 10);
|
||||
EXPECT_EQ(data_element->value(), "test_value");
|
||||
}
|
||||
|
||||
TEST(DataElementTest, FromInvalidType) {
|
||||
std::optional<DataElement> data_element =
|
||||
DataElement::From(0x00, "test_value");
|
||||
EXPECT_FALSE(data_element.has_value());
|
||||
}
|
||||
|
||||
TEST(DataElementTest, FromInvalidLength) {
|
||||
std::optional<DataElement> data_element =
|
||||
DataElement::From(0x01, std::string(128, 'a'));
|
||||
EXPECT_FALSE(data_element.has_value());
|
||||
}
|
||||
|
||||
TEST(DataElementTest, OneByteFromStreamReader) {
|
||||
ByteArray bytes("\x21\x01\x02");
|
||||
StreamReader reader(bytes);
|
||||
std::optional<DataElement> data_element =
|
||||
DataElement::FromStreamReader(reader);
|
||||
ASSERT_TRUE(data_element.has_value());
|
||||
EXPECT_EQ(data_element->type(), 1);
|
||||
EXPECT_EQ(data_element->length(), 2);
|
||||
EXPECT_EQ(data_element->value(), "\x01\x02");
|
||||
}
|
||||
|
||||
TEST(DataElementTest, TwoByteFromStreamReader) {
|
||||
ByteArray bytes("\x84\x01\x01\x02\x03\x04");
|
||||
StreamReader reader(bytes);
|
||||
std::optional<DataElement> data_element =
|
||||
DataElement::FromStreamReader(reader);
|
||||
ASSERT_TRUE(data_element.has_value());
|
||||
EXPECT_EQ(data_element->type(), 1);
|
||||
EXPECT_EQ(data_element->length(), 4);
|
||||
EXPECT_EQ(data_element->value(), "\x01\x02\x03\x04");
|
||||
}
|
||||
|
||||
TEST(DataElementTest, FromData) {
|
||||
std::string data = "\x21\x01\x02";
|
||||
std::optional<DataElement> data_element = DataElement::FromData(data);
|
||||
ASSERT_TRUE(data_element.has_value());
|
||||
EXPECT_EQ(data_element->type(), 1);
|
||||
EXPECT_EQ(data_element->length(), 2);
|
||||
EXPECT_EQ(data_element->value(), "\x01\x02");
|
||||
}
|
||||
|
||||
TEST(DataElementTest, InvalidData) {
|
||||
EXPECT_FALSE(DataElement::FromData("").has_value());
|
||||
EXPECT_FALSE(DataElement::FromData("\x20").has_value());
|
||||
EXPECT_FALSE(DataElement::FromData("\x21\x00").has_value());
|
||||
EXPECT_FALSE(DataElement::FromData("\x91\x00").has_value());
|
||||
EXPECT_FALSE(DataElement::FromData("\x91\x01").has_value());
|
||||
EXPECT_TRUE(DataElement::FromData("\x81\x01\x03").has_value());
|
||||
}
|
||||
|
||||
TEST(DataElementTest, ToData) {
|
||||
std::string data = "\x21\x01\x03";
|
||||
EXPECT_EQ(DataElement::FromData(data)->ToData(), data);
|
||||
data = "\x81\x23\x21";
|
||||
EXPECT_EQ(DataElement::FromData(data)->ToData(), data);
|
||||
}
|
||||
|
||||
TEST(DataElementTest, ToStreamWriter) {
|
||||
StreamWriter writer;
|
||||
std::string data = "\x21\x01\x03";
|
||||
DataElement::FromData(data)->ToStreamWriter(writer);
|
||||
EXPECT_EQ(writer.GetData(), data);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace nearby::connections::advertisements
|
||||
Reference in New Issue
Block a user