OSDN Git Service

test_vendor: Add PacketView for copyless parsing
authorMyles Watson <mylesgw@google.com>
Wed, 7 Nov 2018 20:10:46 +0000 (12:10 -0800)
committerMyles Watson <mylesgw@google.com>
Mon, 10 Dec 2018 23:47:44 +0000 (23:47 +0000)
A View is holds bounds and a shared pointer.
A PacketView is a forward list of View, templated for endianness.
A BaseIterator iterates over PacketViews.
An Iterator is a BaseIterator with extract, which is templated for endianness.

Test: rootcanal-packets_test_host
Change-Id: I68d6502861be61f411c0f5f2da33ead3d2392694

test/gen_coverage.py
vendor_libs/test_vendor_lib/Android.bp
vendor_libs/test_vendor_lib/packets/Android.bp [new file with mode: 0644]
vendor_libs/test_vendor_lib/packets/iterator.cc [new file with mode: 0644]
vendor_libs/test_vendor_lib/packets/iterator.h [new file with mode: 0644]
vendor_libs/test_vendor_lib/packets/packet_view.cc [new file with mode: 0644]
vendor_libs/test_vendor_lib/packets/packet_view.h [new file with mode: 0644]
vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc [new file with mode: 0644]
vendor_libs/test_vendor_lib/packets/view.cc [new file with mode: 0644]
vendor_libs/test_vendor_lib/packets/view.h [new file with mode: 0644]

index 8122cb6..b1c9a2f 100755 (executable)
@@ -68,6 +68,17 @@ COVERAGE_TESTS = [
         "covered_files": [
             "system/bt/profile/sdp",
         ],
+    }, {
+        "test_name": "test-vendor_test_host",
+        "covered_files": [
+            "system/bt/vendor_libs/test_vendor_lib/include",
+            "system/bt/vendor_libs/test_vendor_lib/src",
+        ],
+    }, {
+        "test_name": "rootcanal-packets_test_host",
+        "covered_files": [
+            "system/bt/vendor_libs/test_vendor_lib/packets",
+        ],
     },
 ]
 
index ef927f4..09aedcf 100644 (file)
@@ -35,7 +35,10 @@ cc_library_static {
     local_include_dirs: [
         "include",
     ],
-    export_include_dirs: ["include"],
+    export_include_dirs: [
+        "include",
+        ".",
+    ],
     header_libs: [
         "libbluetooth_headers",
     ],
@@ -52,14 +55,19 @@ cc_library_static {
     ],
     static_libs: [
         "libbluetooth-types",
-    ]
+        "libbt-rootcanal-packets",
+    ],
 }
 
 // test-vendor unit tests for host
 // ========================================================
 cc_test_host {
     name: "test-vendor_test_host",
-    defaults: ["libchrome_support_defaults"],
+    defaults: [
+        "libchrome_support_defaults",
+        "clang_file_coverage",
+        "clang_coverage_bin",
+    ],
     srcs: [
         "src/async_manager.cc",
         "src/bt_address.cc",
@@ -94,6 +102,7 @@ cc_test_host {
     ],
     static_libs: [
         "libbluetooth-types",
+        "libbt-rootcanal-packets",
     ],
     cflags: [
         "-fvisibility=hidden",
diff --git a/vendor_libs/test_vendor_lib/packets/Android.bp b/vendor_libs/test_vendor_lib/packets/Android.bp
new file mode 100644 (file)
index 0000000..81efe12
--- /dev/null
@@ -0,0 +1,65 @@
+// packet library for libbt-rootcanal
+// ========================================================
+cc_library_static {
+    name: "libbt-rootcanal-packets",
+    defaults: [
+        "libchrome_support_defaults",
+        "clang_file_coverage",
+    ],
+    host_supported: true,
+    proprietary: true,
+    srcs: [
+        "iterator.cc",
+        "packet_view.cc",
+        "view.cc",
+    ],
+    cflags: [
+        "-fvisibility=hidden",
+    ],
+    local_include_dirs: [
+        ".",
+    ],
+    export_include_dirs: ["."],
+    include_dirs: [
+        "system/bt/vendor_libs/test_vendor_lib/include",
+        "system/bt/vendor_libs/test_vendor_lib/",
+        "system/bt/",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+}
+
+// Unit tests for the host
+// ========================================================
+cc_test_host {
+    name: "rootcanal-packets_test_host",
+    defaults: [
+        "libchrome_support_defaults",
+        "clang_file_coverage",
+        "clang_coverage_bin",
+    ],
+    srcs: [
+        "test/packet_view_test.cc",
+    ],
+    header_libs: [
+        "libbluetooth_headers",
+    ],
+    local_include_dirs: [
+        ".",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/hci/include",
+        "system/bt/vendor_libs/test_vendor_lib",
+        "system/bt/vendor_libs/test_vendor_lib/include",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libbt-rootcanal-packets",
+    ],
+}
diff --git a/vendor_libs/test_vendor_lib/packets/iterator.cc b/vendor_libs/test_vendor_lib/packets/iterator.cc
new file mode 100644 (file)
index 0000000..b6ec4f7
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ *      http://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 "iterator.h"
+
+#include <base/logging.h>
+
+namespace test_vendor_lib {
+namespace packets {
+
+template <bool little_endian>
+Iterator<little_endian>::Iterator(std::forward_list<View> data, size_t offset) {
+  data_ = data;
+  index_ = offset;
+  length_ = 0;
+  for (auto& view : data) {
+    length_ += view.size();
+  }
+}
+
+template <bool little_endian>
+Iterator<little_endian> Iterator<little_endian>::operator+(int offset) {
+  auto itr(*this);
+
+  return itr += offset;
+}
+
+template <bool little_endian>
+Iterator<little_endian>& Iterator<little_endian>::operator+=(int offset) {
+  index_ += offset;
+  return *this;
+}
+
+template <bool little_endian>
+Iterator<little_endian> Iterator<little_endian>::operator++(int) {
+  auto itr(*this);
+  index_++;
+  return itr;
+}
+
+template <bool little_endian>
+Iterator<little_endian>& Iterator<little_endian>::operator++() {
+  index_++;
+  return *this;
+}
+
+template <bool little_endian>
+Iterator<little_endian> Iterator<little_endian>::operator-(int offset) {
+  auto itr(*this);
+
+  return itr -= offset;
+}
+
+template <bool little_endian>
+int Iterator<little_endian>::operator-(Iterator<little_endian>& itr) {
+  return index_ - itr.index_;
+}
+
+template <bool little_endian>
+Iterator<little_endian>& Iterator<little_endian>::operator-=(int offset) {
+  index_ -= offset;
+
+  return *this;
+}
+
+template <bool little_endian>
+Iterator<little_endian> Iterator<little_endian>::operator--(int) {
+  auto itr(*this);
+  if (index_ != 0) index_--;
+
+  return itr;
+}
+
+template <bool little_endian>
+Iterator<little_endian>& Iterator<little_endian>::operator--() {
+  if (index_ != 0) index_--;
+
+  return *this;
+}
+
+template <bool little_endian>
+Iterator<little_endian>& Iterator<little_endian>::operator=(
+    const Iterator<little_endian>& itr) {
+  data_ = itr.data_;
+  index_ = itr.index_;
+
+  return *this;
+}
+
+template <bool little_endian>
+bool Iterator<little_endian>::operator==(
+    const Iterator<little_endian>& itr) const {
+  return index_ == itr.index_;
+}
+
+template <bool little_endian>
+bool Iterator<little_endian>::operator!=(
+    const Iterator<little_endian>& itr) const {
+  return !(*this == itr);
+}
+
+template <bool little_endian>
+bool Iterator<little_endian>::operator<(
+    const Iterator<little_endian>& itr) const {
+  return index_ < itr.index_;
+}
+
+template <bool little_endian>
+bool Iterator<little_endian>::operator>(
+    const Iterator<little_endian>& itr) const {
+  return index_ > itr.index_;
+}
+
+template <bool little_endian>
+bool Iterator<little_endian>::operator<=(
+    const Iterator<little_endian>& itr) const {
+  return index_ <= itr.index_;
+}
+
+template <bool little_endian>
+bool Iterator<little_endian>::operator>=(
+    const Iterator<little_endian>& itr) const {
+  return index_ >= itr.index_;
+}
+
+template <bool little_endian>
+uint8_t Iterator<little_endian>::operator*() const {
+  CHECK(index_ < length_) << "Index " << index_
+                          << " out of bounds: " << length_;
+  size_t index = index_;
+
+  for (auto view : data_) {
+    if (index < view.size()) {
+      return view[index];
+    }
+    index -= view.size();
+  }
+  CHECK(false) << "Out of fragments searching for Index " << index_;
+  return 0;
+}
+
+template <bool little_endian>
+size_t Iterator<little_endian>::NumBytesRemaining() const {
+  if (length_ > index_) {
+    return length_ - index_;
+  } else {
+    return 0;
+  }
+}
+
+// Explicit instantiations for both types of Iterators.
+template class Iterator<true>;
+template class Iterator<false>;
+}  // namespace packets
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/iterator.h b/vendor_libs/test_vendor_lib/packets/iterator.h
new file mode 100644 (file)
index 0000000..d883c86
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ *      http://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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <forward_list>
+
+#include "types/raw_address.h"
+#include "view.h"
+
+namespace test_vendor_lib {
+namespace packets {
+
+// Templated Iterator for endianness
+template <bool little_endian>
+class Iterator
+    : public std::iterator<std::random_access_iterator_tag, uint8_t> {
+ public:
+  Iterator(std::forward_list<View> data, size_t offset);
+  Iterator(const Iterator& itr) = default;
+  virtual ~Iterator() = default;
+
+  // All addition and subtraction operators are unbounded.
+  Iterator operator+(int offset);
+  Iterator& operator+=(int offset);
+  Iterator operator++(int);
+  Iterator& operator++();
+
+  Iterator operator-(int offset);
+  int operator-(Iterator& itr);
+  Iterator& operator-=(int offset);
+  Iterator operator--(int);
+  Iterator& operator--();
+
+  Iterator& operator=(const Iterator& itr);
+
+  bool operator!=(const Iterator& itr) const;
+  bool operator==(const Iterator& itr) const;
+
+  bool operator<(const Iterator& itr) const;
+  bool operator>(const Iterator& itr) const;
+
+  bool operator<=(const Iterator& itr) const;
+  bool operator>=(const Iterator& itr) const;
+
+  uint8_t operator*() const;
+  uint8_t operator->() const;
+
+  size_t NumBytesRemaining() const;
+
+  // Get the next sizeof(FixedWidthPODType) bytes and return the filled type
+  template <typename FixedWidthPODType>
+  FixedWidthPODType extract() {
+    static_assert(std::is_pod<FixedWidthPODType>::value,
+                  "Iterator::extract requires an fixed type.");
+    FixedWidthPODType extracted_value;
+    uint8_t* value_ptr = (uint8_t*)&extracted_value;
+
+    for (size_t i = 0; i < sizeof(FixedWidthPODType); i++) {
+      size_t index = (little_endian ? i : sizeof(FixedWidthPODType) - i - 1);
+      value_ptr[index] = *((*this)++);
+    }
+    return extracted_value;
+  }
+
+ private:
+  std::forward_list<View> data_;
+  size_t index_;
+  size_t length_;
+};
+
+}  // namespace packets
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/packet_view.cc b/vendor_libs/test_vendor_lib/packets/packet_view.cc
new file mode 100644 (file)
index 0000000..fc82625
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ *      http://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 "packet_view.h"
+
+#include <algorithm>
+
+#include <base/logging.h>
+
+namespace test_vendor_lib {
+namespace packets {
+
+template <bool little_endian>
+PacketView<little_endian>::PacketView(
+    const std::forward_list<class View> fragments)
+    : fragments_(fragments), length_(0) {
+  for (auto fragment : fragments_) {
+    length_ += fragment.size();
+  }
+}
+
+template <bool little_endian>
+PacketView<little_endian>::PacketView(
+    std::shared_ptr<std::vector<uint8_t>> packet)
+    : fragments_({View(packet, 0, packet->size())}), length_(packet->size()) {}
+
+template <bool little_endian>
+Iterator<little_endian> PacketView<little_endian>::begin() const {
+  return Iterator<little_endian>(this->fragments_, 0);
+}
+
+template <bool little_endian>
+Iterator<little_endian> PacketView<little_endian>::end() const {
+  return Iterator<little_endian>(this->fragments_, size());
+}
+
+template <bool little_endian>
+uint8_t PacketView<little_endian>::operator[](size_t index) const {
+  return at(index);
+}
+
+template <bool little_endian>
+uint8_t PacketView<little_endian>::at(size_t index) const {
+  CHECK(index < length_) << "Index " << index << " out of bounds";
+  for (const auto& fragment : fragments_) {
+    if (index < fragment.size()) {
+      return fragment[index];
+    }
+    index -= fragment.size();
+  }
+  CHECK(false) << "Out of fragments searching for Index " << index;
+  return 0;
+}
+
+template <bool little_endian>
+size_t PacketView<little_endian>::size() const {
+  return length_;
+}
+
+template <bool little_endian>
+std::forward_list<View> PacketView<little_endian>::SubViewList(
+    size_t begin, size_t end) const {
+  CHECK(begin <= end) << "Begin " << begin << " is past end";
+  CHECK(end <= length_) << "End " << end << " is too large";
+  std::forward_list<View> view_list;
+  std::forward_list<View>::iterator it = view_list.before_begin();
+  size_t length = end - begin;
+  for (const auto& fragment : fragments_) {
+    if (begin >= fragment.size()) {
+      begin -= fragment.size();
+    } else {
+      View view(fragment, begin,
+                begin + std::min(length, fragment.size() - begin));
+      length -= view.size();
+      it = view_list.insert_after(it, view);
+      begin = 0;
+    }
+  }
+  return view_list;
+}
+
+template <bool little_endian>
+PacketView<true> PacketView<little_endian>::SubViewLittleEndian(
+    size_t begin, size_t end) const {
+  return PacketView<true>(SubViewList(begin, end));
+}
+
+template <bool little_endian>
+PacketView<false> PacketView<little_endian>::SubViewBigEndian(
+    size_t begin, size_t end) const {
+  return PacketView<false>(SubViewList(begin, end));
+}
+
+// Explicit instantiations for both types of PacketViews.
+template class PacketView<true>;
+template class PacketView<false>;
+
+}  // namespace packets
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/packet_view.h b/vendor_libs/test_vendor_lib/packets/packet_view.h
new file mode 100644 (file)
index 0000000..16255fa
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ *      http://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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <forward_list>
+
+#include "iterator.h"
+#include "view.h"
+
+namespace test_vendor_lib {
+namespace packets {
+
+// Abstract base class that is subclassed to provide type-specifc accessors.
+// Holds a shared pointer to the underlying data.
+// The template parameter little_endian controls the generation of extract().
+template <bool little_endian>
+class PacketView {
+ public:
+  PacketView(const std::forward_list<class View> fragments);
+  PacketView(const PacketView& PacketView) = default;
+  virtual ~PacketView() = default;
+
+  virtual Iterator<little_endian> begin() const;
+
+  virtual Iterator<little_endian> end() const;
+
+  uint8_t operator[](size_t i) const;
+
+  uint8_t at(size_t index) const;
+
+  size_t size() const;
+
+  PacketView<true> SubViewLittleEndian(size_t begin, size_t end) const;
+
+  PacketView<false> SubViewBigEndian(size_t begin, size_t end) const;
+
+ protected:
+  PacketView(std::shared_ptr<std::vector<uint8_t>> packet);
+
+ private:
+  std::forward_list<View> fragments_;
+  size_t length_;
+  PacketView<little_endian>() = delete;
+  std::forward_list<View> SubViewList(size_t begin, size_t end) const;
+};
+
+}  // namespace packets
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc b/vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc
new file mode 100644 (file)
index 0000000..0aa7b43
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ *      http://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 "packets/packet_view.h"
+
+#include <gtest/gtest.h>
+#include <forward_list>
+#include <memory>
+
+#include "types/raw_address.h"
+
+using std::vector;
+
+namespace {
+vector<uint8_t> count_all = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+    0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+};
+
+vector<uint8_t> count_1 = {
+    0x00,
+    0x01,
+    0x02,
+};
+
+vector<uint8_t> count_2 = {
+    0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+};
+
+vector<uint8_t> count_3 = {
+    0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+    0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+};
+}  // namespace
+
+namespace test_vendor_lib {
+namespace packets {
+
+template <typename T>
+class IteratorTest : public ::testing::Test {
+ public:
+  IteratorTest() = default;
+  ~IteratorTest() = default;
+
+  void SetUp() {
+    packet = std::shared_ptr<T>(
+        new T({View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+                    count_all.size())}));
+  }
+
+  void TearDown() { packet.reset(); }
+
+  std::shared_ptr<T> packet;
+};
+
+using PacketViewTypes = ::testing::Types<PacketView<true>, PacketView<false>>;
+TYPED_TEST_CASE(IteratorTest, PacketViewTypes);
+
+class IteratorExtractTest : public ::testing::Test {
+ public:
+  IteratorExtractTest() = default;
+  ~IteratorExtractTest() = default;
+};
+
+template <typename T>
+class PacketViewTest : public IteratorTest<T> {
+ public:
+  PacketViewTest() = default;
+  ~PacketViewTest() = default;
+};
+
+using PacketViewTypes = ::testing::Types<PacketView<true>, PacketView<false>>;
+TYPED_TEST_CASE(PacketViewTest, PacketViewTypes);
+
+class PacketViewMultiViewTest : public ::testing::Test {
+ public:
+  PacketViewMultiViewTest() = default;
+  ~PacketViewMultiViewTest() = default;
+};
+
+class ViewTest : public ::testing::Test {
+ public:
+  ViewTest() = default;
+  ~ViewTest() = default;
+};
+
+TEST(IteratorExtractTest, extractLeTest) {
+  PacketView<true> packet(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  auto general_case = packet.begin();
+
+  ASSERT_EQ(0x00, general_case.extract<uint8_t>());
+  ASSERT_EQ(0x0201, general_case.extract<uint16_t>());
+  ASSERT_EQ(0x06050403u, general_case.extract<uint32_t>());
+  ASSERT_EQ(0x0e0d0c0b0a090807u, general_case.extract<uint64_t>());
+  ASSERT_EQ(0x0f, general_case.extract<uint8_t>());
+  RawAddress raw({0x10, 0x11, 0x12, 0x13, 0x14, 0x15});
+  ASSERT_EQ(raw, general_case.extract<RawAddress>());
+  ASSERT_EQ(0x16, general_case.extract<uint8_t>());
+}
+
+TEST(IteratorExtractTest, extractBeTest) {
+  PacketView<false> packet(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  auto general_case = packet.begin();
+
+  ASSERT_EQ(0x00, general_case.extract<uint8_t>());
+  ASSERT_EQ(0x0102, general_case.extract<uint16_t>());
+  ASSERT_EQ(0x03040506u, general_case.extract<uint32_t>());
+  ASSERT_EQ(0x0708090a0b0c0d0eu, general_case.extract<uint64_t>());
+  ASSERT_EQ(0x0f, general_case.extract<uint8_t>());
+  RawAddress raw({0x15, 0x14, 0x13, 0x12, 0x11, 0x10});
+  ASSERT_EQ(raw, general_case.extract<RawAddress>());
+  ASSERT_EQ(0x16, general_case.extract<uint8_t>());
+}
+
+TYPED_TEST(IteratorTest, extractBoundsDeathTest) {
+  auto bounds_test = this->packet->end();
+
+  ASSERT_DEATH(bounds_test.template extract<uint8_t>(), "");
+  ASSERT_DEATH(bounds_test.template extract<uint16_t>(), "");
+  ASSERT_DEATH(bounds_test.template extract<uint32_t>(), "");
+  ASSERT_DEATH(bounds_test.template extract<uint64_t>(), "");
+}
+
+TYPED_TEST(IteratorTest, dereferenceDeathTest) {
+  auto dereference_test = this->packet->end();
+
+  ASSERT_DEATH(*dereference_test, "");
+  ASSERT_EQ(0x1f, *(dereference_test - 1));
+}
+
+TYPED_TEST(IteratorTest, plusEqTest) {
+  auto plus_eq = this->packet->begin();
+  for (size_t i = 0; i < count_all.size(); i += 2) {
+    ASSERT_EQ(count_all[i], *plus_eq)
+        << "+= test: Dereferenced iterator does not equal expected at index "
+        << i;
+    plus_eq += 2;
+  }
+}
+
+TYPED_TEST(IteratorTest, preIncrementTest) {
+  auto plus_plus = this->packet->begin();
+  for (size_t i = 0; i < count_all.size() - 1; i++) {
+    ASSERT_EQ(count_all[i + 1], *(++plus_plus))
+        << "Pre-increment test: Dereferenced iterator does not equal expected "
+        << "at index " << i;
+  }
+}
+
+TYPED_TEST(IteratorTest, postIncrementTest) {
+  auto plus_plus = this->packet->begin();
+  for (size_t i = 0; i < count_all.size(); i++) {
+    ASSERT_EQ(count_all[i], *(plus_plus++))
+        << "Post-increment test: Dereferenced iterator does not equal expected "
+        << "at index " << i;
+  }
+}
+
+TYPED_TEST(IteratorTest, additionTest) {
+  auto plus = this->packet->begin();
+  for (size_t i = 0; i < count_all.size(); i++) {
+    ASSERT_EQ(count_all[i], *plus)
+        << "+ test: Dereferenced iterator does not equal expected at index "
+        << i;
+    plus = plus + 1;
+  }
+}
+
+TYPED_TEST(IteratorTest, minusEqTest) {
+  auto minus_eq = this->packet->end();
+  minus_eq -= 1;
+  size_t index = count_all.size() - 1;
+  for (size_t i = 0; index > i; i++) {
+    ASSERT_EQ(count_all[index], *minus_eq)
+        << "-= test: Dereferenced iterator does not equal expected at index "
+        << index;
+    index -= i;
+    minus_eq -= i;
+  }
+}
+
+TYPED_TEST(IteratorTest, preDecrementTest) {
+  auto minus_minus = this->packet->end();
+  for (size_t i = count_all.size(); i > 0; i--) {
+    ASSERT_EQ(count_all[i - 1], *(--minus_minus))
+        << "Pre-decrement test: Dereferenced iterator does not equal expected "
+        << "at index " << i;
+  }
+}
+
+TYPED_TEST(IteratorTest, postDecrementTest) {
+  auto minus_minus = this->packet->end();
+  minus_minus--;
+  for (size_t i = count_all.size() - 1; i > 0; i--) {
+    ASSERT_EQ(count_all[i], *(minus_minus--))
+        << "Post-decrement test: Dereferenced iterator does not equal expected "
+        << "at index " << i;
+  }
+}
+
+TYPED_TEST(IteratorTest, subtractionTest) {
+  auto minus = this->packet->end();
+  minus = minus - 1;
+  for (size_t i = count_all.size() - 1; i > 0; i--) {
+    ASSERT_EQ(count_all[i], *minus)
+        << "- test: Dereferenced iterator does not equal expected at index "
+        << i;
+    minus = minus - 1;
+  }
+}
+
+TYPED_TEST(IteratorTest, differenceTest) {
+  auto begin = this->packet->begin();
+  auto end = this->packet->end();
+  int difference = end - begin;
+  ASSERT_EQ(difference, static_cast<int>(count_all.size()));
+  int neg_difference = begin - end;
+  ASSERT_EQ(neg_difference, -static_cast<int>(count_all.size()));
+}
+
+TYPED_TEST(IteratorTest, equalityTest) {
+  auto begin = this->packet->begin();
+  auto end = this->packet->end();
+  auto begin_copy = this->packet->begin();
+  auto end_copy = this->packet->end();
+  ASSERT_EQ(begin_copy, begin);
+  ASSERT_EQ(end_copy, end);
+}
+
+TYPED_TEST(IteratorTest, comparisonsTest) {
+  auto begin = this->packet->begin();
+  auto end = this->packet->end();
+  auto begin_copy = this->packet->begin();
+  auto end_copy = this->packet->end();
+  ASSERT_EQ(begin_copy, begin);
+  ASSERT_EQ(end_copy, end);
+  ASSERT_NE(begin, end);
+  ASSERT_TRUE(begin < end);
+  ASSERT_FALSE(end < end);
+  ASSERT_FALSE(end < begin);
+  ASSERT_FALSE(begin > end);
+  ASSERT_FALSE(end > end);
+  ASSERT_TRUE(end > begin);
+  ASSERT_TRUE(begin <= end);
+  ASSERT_TRUE(end <= end);
+  ASSERT_FALSE(end <= begin);
+  ASSERT_FALSE(begin >= end);
+  ASSERT_TRUE(end >= end);
+  ASSERT_TRUE(end >= begin);
+}
+
+TYPED_TEST(PacketViewTest, getLengthTest) {
+  size_t length = this->packet->size();
+  ASSERT_EQ(length, count_all.size());
+}
+
+TYPED_TEST(PacketViewTest, getAtIndexTest) {
+  size_t past_end = this->packet->size();
+  ASSERT_DEATH(this->packet->at(past_end), "");
+  size_t working_index = 0x1f;
+  ASSERT_EQ(0x1f, this->packet->at(working_index));
+}
+
+TYPED_TEST(PacketViewTest, arrayOperatorTest) {
+  size_t past_end = this->packet->size();
+  ASSERT_DEATH((*(this->packet))[past_end], "");
+  size_t working_index = 0x1f;
+  ASSERT_EQ(0x1f, (*(this->packet))[working_index]);
+}
+
+TYPED_TEST(PacketViewTest, numBytesRemainingTest) {
+  auto all = this->packet->begin();
+  size_t remaining = all.NumBytesRemaining();
+  for (size_t n = remaining; n > 0; n--) {
+    ASSERT_EQ(remaining, all.NumBytesRemaining());
+    all++;
+    remaining--;
+  }
+  ASSERT_EQ(static_cast<size_t>(0), all.NumBytesRemaining());
+  ASSERT_DEATH(*(all++), "");
+  all++;
+  ASSERT_EQ(static_cast<size_t>(0), all.NumBytesRemaining());
+  ASSERT_DEATH(*(all++), "");
+}
+
+using SubViewTestParam = std::pair<size_t, size_t>;
+class SubViewBaseTest : public ::testing::TestWithParam<SubViewTestParam> {
+ public:
+  class SubPacketView : public PacketView<true> {
+   public:
+    using PacketView<true>::PacketView;
+    PacketView<true> Slice(size_t header, size_t tail) {
+      return PacketView<true>::SubViewLittleEndian(header, tail);
+    }
+  };
+};
+
+class SubViewPassTest : public SubViewBaseTest {};
+
+TEST_P(SubViewPassTest, subViewTest) {
+  auto header = GetParam().first;
+  auto tail = GetParam().second;
+  SubPacketView single_view(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  SubPacketView multi_view({
+      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
+  });
+
+  auto single_slice = single_view.Slice(header, tail);
+  auto multi_slice = multi_view.Slice(header, tail);
+
+  ASSERT_EQ(single_slice.size(), tail - header);
+  ASSERT_EQ(single_slice.size(), multi_slice.size());
+  for (size_t i = 0; i < single_slice.size(); i++) {
+    ASSERT_EQ(single_slice[i], multi_slice[i]);
+  }
+}
+
+static const size_t boundary_1 = count_1.size();
+static const size_t boundary_2 = count_1.size() + count_2.size();
+
+INSTANTIATE_TEST_CASE_P(
+    chopomatic, SubViewPassTest,
+    ::testing::Values(
+        // {begin, end} pairs for subsets into the PacketView
+        SubViewTestParam{0, 0}, SubViewTestParam{0, boundary_1},
+        SubViewTestParam{0, boundary_1 + 1}, SubViewTestParam{0, boundary_2},
+        SubViewTestParam{0, boundary_2 + 1},
+        SubViewTestParam{0, count_all.size()},
+        SubViewTestParam{boundary_1 - 1, boundary_1},
+        SubViewTestParam{boundary_1 - 1, boundary_1 + 1},
+        SubViewTestParam{boundary_1 - 1, boundary_2},
+        SubViewTestParam{boundary_1 - 1, boundary_2 + 1},
+        SubViewTestParam{boundary_1 - 1, count_all.size()},
+        SubViewTestParam{boundary_1, boundary_1},
+        SubViewTestParam{boundary_1, boundary_2},
+        SubViewTestParam{boundary_1, boundary_2 + 1},
+        SubViewTestParam{boundary_1, count_all.size()},
+        SubViewTestParam{boundary_2 - 1, boundary_2},
+        SubViewTestParam{boundary_2 - 1, boundary_2 + 1},
+        SubViewTestParam{boundary_2 - 1, count_all.size()},
+        SubViewTestParam{boundary_2, boundary_2},
+        SubViewTestParam{boundary_2, boundary_2 + 1},
+        SubViewTestParam{boundary_2, count_all.size()},
+        SubViewTestParam{count_all.size() - 1, count_all.size()},
+        SubViewTestParam{count_all.size(), count_all.size()}));
+
+class SubViewDeathTest : public SubViewBaseTest {};
+
+TEST_P(SubViewDeathTest, subViewDeathTest) {
+  auto header = GetParam().first;
+  auto tail = GetParam().second;
+  SubPacketView single_view(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  SubPacketView multi_view({
+      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
+  });
+
+  ASSERT_DEATH(auto single_slice = single_view.Slice(header, tail), "");
+  ASSERT_DEATH(auto multi_slice = multi_view.Slice(header, tail), "");
+}
+
+INSTANTIATE_TEST_CASE_P(
+    chopomaticDeath, SubViewDeathTest,
+    ::testing::Values(
+        // {begin, end} pairs for subsets into the PacketView
+        SubViewTestParam{1, 0},
+        SubViewTestParam{count_all.size(), count_all.size() - 1},
+        SubViewTestParam{count_all.size(), count_all.size() + 1}));
+
+TEST(SubViewTest, simpleSubViewTest) {
+  PacketView<true> view(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  PacketView<true> sub_1_view = view.SubViewLittleEndian(0, view.size());
+  PacketView<true> sub_2_view =
+      sub_1_view.SubViewLittleEndian(0, sub_1_view.size());
+  PacketView<true> sub_3_view =
+      sub_2_view.SubViewLittleEndian(0, sub_2_view.size());
+  PacketView<true> sub_4_view =
+      sub_3_view.SubViewLittleEndian(0, sub_3_view.size());
+  ASSERT_EQ(sub_1_view.size(), view.size());
+  ASSERT_EQ(sub_2_view.size(), view.size());
+  ASSERT_EQ(sub_3_view.size(), view.size());
+  ASSERT_EQ(sub_4_view.size(), view.size());
+}
+
+TEST(SubViewTest, realSubViewTest) {
+  PacketView<true> view(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  std::vector<PacketView<true>> sub_views{view};
+  for (size_t i = 1; i < 6; i++) {
+    size_t parent_size = sub_views[i - 1].size();
+    sub_views.push_back(
+        sub_views[i - 1].SubViewLittleEndian(1, parent_size - 1));
+    ASSERT_EQ(sub_views[i][0], i);
+    ASSERT_EQ(sub_views[i].size(), parent_size - 2);
+  }
+}
+
+TEST(SubViewTest, subSubViewTest) {
+  PacketView<true> single_view(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  PacketView<true> multi_view({
+      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
+  });
+  ASSERT_EQ(single_view.size(), multi_view.size());
+  for (size_t i = 0; i < count_all.size() / 2; i++) {
+    PacketView<true> sub_single_view =
+        single_view.SubViewLittleEndian(i, count_all.size() - i);
+    PacketView<true> sub_multi_view =
+        multi_view.SubViewLittleEndian(i, count_all.size() - i);
+    ASSERT_EQ(count_all.size() - 2 * i, sub_single_view.size());
+    ASSERT_EQ(sub_single_view.size(), sub_multi_view.size());
+    for (size_t j = 0; j < sub_single_view.size() / 2; j++) {
+      PacketView<true> sub_sub_single_view =
+          sub_single_view.SubViewLittleEndian(j, sub_single_view.size() - j);
+      PacketView<true> sub_sub_multi_view =
+          sub_multi_view.SubViewLittleEndian(j, sub_multi_view.size() - j);
+      ASSERT_EQ(sub_single_view.size() - 2 * j, sub_sub_single_view.size());
+      ASSERT_EQ(sub_sub_single_view.size(), sub_sub_multi_view.size());
+    }
+  }
+}
+
+TEST(PacketViewMultiViewTest, sizeTest) {
+  PacketView<true> single_view(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  PacketView<true> multi_view({
+      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
+  });
+  ASSERT_EQ(single_view.size(), multi_view.size());
+}
+
+TEST(PacketViewMultiViewTest, dereferenceTestLittleEndian) {
+  PacketView<true> single_view(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  PacketView<true> multi_view({
+      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
+  });
+  auto single_itr = single_view.begin();
+  auto multi_itr = multi_view.begin();
+  for (size_t i = 0; i < single_view.size(); i++) {
+    ASSERT_EQ(*(single_itr++), *(multi_itr++));
+  }
+  ASSERT_DEATH(*multi_itr, "");
+}
+
+TEST(PacketViewMultiViewTest, dereferenceTestBigEndian) {
+  PacketView<false> single_view(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  PacketView<false> multi_view({
+      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
+  });
+  auto single_itr = single_view.begin();
+  auto multi_itr = multi_view.begin();
+  for (size_t i = 0; i < single_view.size(); i++) {
+    ASSERT_EQ(*(single_itr++), *(multi_itr++));
+  }
+  ASSERT_DEATH(*multi_itr, "");
+}
+
+TEST(PacketViewMultiViewTest, arrayOperatorTest) {
+  PacketView<true> single_view(
+      {View(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size())});
+  PacketView<true> multi_view({
+      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
+  });
+  for (size_t i = 0; i < single_view.size(); i++) {
+    ASSERT_EQ(single_view[i], multi_view[i]);
+  }
+  ASSERT_DEATH(multi_view[single_view.size()], "");
+}
+
+TEST(ViewTest, arrayOperatorTest) {
+  View view_all(std::make_shared<const vector<uint8_t>>(count_all), 0,
+                count_all.size());
+  size_t past_end = view_all.size();
+  for (size_t i = 0; i < past_end; i++) {
+    ASSERT_EQ(view_all[i], count_all[i]);
+  }
+  ASSERT_DEATH(view_all[past_end], "");
+
+  size_t header_size = 2;
+  size_t tail_size = 3;
+  View view_subset(std::make_shared<const vector<uint8_t>>(count_all),
+                   header_size, count_all.size() - tail_size);
+  View view_subset2(view_all, header_size, count_all.size() - tail_size);
+  size_t subset_length = view_subset.size();
+  for (size_t i = 0; i < subset_length; i++) {
+    ASSERT_EQ(view_subset[i], count_all[header_size + i]);
+    ASSERT_EQ(view_subset[i], view_subset2[i]);
+  }
+  ASSERT_DEATH(view_subset[subset_length + 1], "");
+  ASSERT_DEATH(view_subset2[subset_length + 1], "");
+}
+
+TEST(ViewTest, earlySubSubViewTest) {
+  View view(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size());
+  View sub_1_view(view, view.size() - 3, view.size() - 1);
+  View sub_2_view(sub_1_view, 1, 2);
+  ASSERT_EQ(sub_1_view.size(), 2u);
+  ASSERT_EQ(sub_2_view.size(), 1u);
+}
+
+TEST(ViewTest, subSubViewTest) {
+  View view(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size());
+  std::vector<View> sub_views{view};
+  for (size_t i = 1; i < 6; i++) {
+    size_t parent_size = sub_views[i - 1].size();
+    sub_views.push_back({View(sub_views[i - 1], 1, parent_size - 1)});
+    ASSERT_EQ(sub_views[i][0], i);
+    ASSERT_EQ(sub_views[i].size(), parent_size - 2);
+  }
+}
+
+TEST(ViewTest, zeroSubViewTest) {
+  View view(std::make_shared<const vector<uint8_t>>(count_all), 0,
+            count_all.size());
+  View subview(view, view.size(), view.size() + 1);
+  ASSERT_EQ(subview.size(), 0u);
+}
+}  // namespace packets
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/view.cc b/vendor_libs/test_vendor_lib/packets/view.cc
new file mode 100644 (file)
index 0000000..67df552
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ *      http://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 "view.h"
+
+#include <base/logging.h>
+
+namespace test_vendor_lib {
+namespace packets {
+
+View::View(std::shared_ptr<const std::vector<uint8_t>> data, size_t begin,
+           size_t end)
+    : data_(data),
+      begin_(begin < data_->size() ? begin : data_->size()),
+      end_(end < data_->size() ? end : data_->size()) {}
+
+View::View(const View& view, size_t begin, size_t end) : data_(view.data_) {
+  begin_ = (begin < view.size() ? begin : view.size());
+  begin_ += view.begin_;
+  end_ = (end < view.size() ? end : view.size());
+  end_ += view.begin_;
+}
+
+uint8_t View::operator[](size_t i) const {
+  CHECK(i + begin_ < end_) << "Out of bounds access at " << i;
+  return data_->operator[](i + begin_);
+}
+
+size_t View::size() const { return end_ - begin_; }
+
+}  // namespace packets
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/view.h b/vendor_libs/test_vendor_lib/packets/view.h
new file mode 100644 (file)
index 0000000..521cbc8
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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
+ *
+ *      http://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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+namespace test_vendor_lib {
+namespace packets {
+
+// Base class that holds a shared pointer to data with bounds.
+class View {
+ public:
+  View(std::shared_ptr<const std::vector<uint8_t>> data, size_t begin,
+       size_t end);
+  View(const View& view, size_t begin, size_t end);
+  View(const View& view) = default;
+  virtual ~View() = default;
+
+  uint8_t operator[](size_t i) const;
+
+  size_t size() const;
+
+ private:
+  std::shared_ptr<const std::vector<uint8_t>> data_;
+  size_t begin_;
+  size_t end_;
+};
+
+}  // namespace packets
+}  // namespace test_vendor_lib