add ByteBuffer

ByteBuffer provides a simple way of concatenating data, such
as we might want to do when generating a protocol packet.

ByteBuffer is implemented as a class template, rather than a
simple class, because we want to be able to allocate a ByteBuffer
on the stack. For stack allocation, the object size must be
known at compile-time. Hence, ByteBuffer is a class template,
parameterized on the buffer size.

Bug: 31861967
Test: ./runtests.sh (on bullhead)
Change-Id: I1fe89a9cdcf34cf380365b3aad2537edca87a424
diff --git a/Android.mk b/Android.mk
index 87a37ef..8915a61 100644
--- a/Android.mk
+++ b/Android.mk
@@ -46,6 +46,7 @@
 LOCAL_CPPFLAGS := $(wifilogd_cpp_flags) $(wifilogd_gtest_cpp_flags)
 LOCAL_C_INCLUDES := $(wifilogd_includes)
 LOCAL_SRC_FILES := \
+    tests/byte_buffer_unittest.cpp \
     tests/local_utils_unittest.cpp \
     tests/main.cpp \
     tests/message_buffer_unittest.cpp \
diff --git a/byte_buffer.h b/byte_buffer.h
new file mode 100644
index 0000000..18c74b7
--- /dev/null
+++ b/byte_buffer.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef BYTE_BUFFER_H_
+#define BYTE_BUFFER_H_
+
+#include <array>
+#include <cstring>
+
+#include "android-base/logging.h"
+
+#include "wifilogd/local_utils.h"
+
+namespace android {
+namespace wifilogd {
+
+// A fixed-size buffer, which provides the ability to accumulate bytes.
+// The buffer tracks its (populated) size, and does not require dynamic
+// memory allocation.
+//
+// Typical usage would be as follows:
+//     ByteBuffer buffer;
+//     buffer.AppendOrDie(header.data(), header.size());
+//     buffer.AppendOrDie(body.data(), body.size());
+//     write(fd, buffer.data(), buffer.size());
+template <size_t SizeBytes>
+class ByteBuffer {
+ public:
+  ByteBuffer() : write_pos_(0) {}
+
+  // Appends data to the end of this buffer. Aborts if the available
+  // space in the buffer is less than |data_len|.
+  void AppendOrDie(NONNULL const void* data, size_t data_len) {
+    CHECK(data_len <= raw_buffer_.size() - write_pos_);
+    std::memcpy(raw_buffer_.data() + write_pos_, data, data_len);
+    write_pos_ += data_len;
+  }
+
+  // Returns a pointer to the head of this buffer.
+  RETURNS_NONNULL const uint8_t* data() const { return raw_buffer_.data(); }
+
+  // Returns the number of bytes written to this buffer.
+  size_t size() const { return write_pos_; }
+
+ private:
+  std::array<uint8_t, SizeBytes> raw_buffer_;
+  size_t write_pos_;
+};
+
+}  // namespace wifilogd
+}  // namespace android
+#endif  // BYTE_BUFFER_H_
diff --git a/local_utils.h b/local_utils.h
index b9f76ee..403328a 100644
--- a/local_utils.h
+++ b/local_utils.h
@@ -34,13 +34,15 @@
   local_utils::internal::SafelyClamp<decltype(SRC), DST_TYPE, MIN, MAX, MIN, \
                                      MAX>(SRC)
 
-// While attributes are standard in C++11, the nonnull attribute is not part of
-// the standard. We use a macro to abstract the nonnull attribute, to allow
-// the code to compile with compilers that don't recognize nonnull.
+// While attributes are standard in C++11, these attributes are not part of
+// the standard. We use macros to abstract these attributes, to allow
+// the code to compile with compilers that don't recognize these attributes.
 #if defined(__clang__)
-#define NONNULL [[gnu::nonnull]] /* NOLINT(whitespace/braces) */
+#define NONNULL [[gnu::nonnull]]                 /* NOLINT(whitespace/braces) */
+#define RETURNS_NONNULL [[gnu::returns_nonnull]] /* NOLINT ... */
 #else
 #define NONNULL
+#define RETURNS_NONNULL
 #endif
 
 namespace android {
diff --git a/tests/byte_buffer_unittest.cpp b/tests/byte_buffer_unittest.cpp
new file mode 100644
index 0000000..71830f6
--- /dev/null
+++ b/tests/byte_buffer_unittest.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016, 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 <array>
+#include <cstring>
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "wifilogd/byte_buffer.h"
+
+namespace android {
+namespace wifilogd {
+namespace {
+
+constexpr size_t kBufferSizeBytes = 1024;
+constexpr std::array<uint8_t, 1> kSmallestMessage{};
+constexpr std::array<uint8_t, kBufferSizeBytes> kLargestMessage{};
+
+class ByteBufferTest : public ::testing::Test {
+ public:
+  ByteBufferTest() {}
+
+ protected:
+  ByteBuffer<kBufferSizeBytes> buffer_;
+};
+
+}  // namespace
+
+TEST_F(ByteBufferTest, AppendMinimalOnEmptyBufferSucceeds) {
+  buffer_.AppendOrDie(kSmallestMessage.data(), kSmallestMessage.size());
+}
+
+TEST_F(ByteBufferTest, AppendMaximalOnEmptyBufferSucceeds) {
+  buffer_.AppendOrDie(kLargestMessage.data(), kLargestMessage.size());
+}
+
+TEST_F(ByteBufferTest, AppendStoresOurData) {
+  const std::string message1{"hello"};
+  const std::string message2{"world"};
+  buffer_.AppendOrDie(message1.data(), message1.size());
+  buffer_.AppendOrDie(message2.data(), message2.size());
+
+  const std::string expected{"helloworld"};
+  EXPECT_EQ(0, std::memcmp(buffer_.data(), expected.data(), expected.size()));
+}
+
+TEST_F(ByteBufferTest, AssigningWorks) {
+  const std::string message1{"hello"};
+  buffer_.AppendOrDie(message1.data(), message1.size());
+
+  ByteBuffer<kBufferSizeBytes> copy;
+  ASSERT_NE(buffer_.size(), copy.size());
+
+  copy = buffer_;
+  ASSERT_EQ(buffer_.size(), copy.size());
+  EXPECT_EQ(0, std::memcmp(copy.data(), buffer_.data(), buffer_.size()));
+}
+
+TEST_F(ByteBufferTest, CopyingWorks) {
+  const std::string message1{"hello"};
+  buffer_.AppendOrDie(message1.data(), message1.size());
+
+  const ByteBuffer<kBufferSizeBytes> copy{buffer_};
+  ASSERT_EQ(buffer_.size(), copy.size());
+  EXPECT_EQ(0, std::memcmp(copy.data(), buffer_.data(), buffer_.size()));
+}
+
+TEST_F(ByteBufferTest, DataDoesNotReturnNullOnFreshBuffer) {
+  EXPECT_NE(nullptr, buffer_.data());
+}
+
+TEST_F(ByteBufferTest, DataDoesNotReturnNullAfterLargeWrite) {
+  buffer_.AppendOrDie(kLargestMessage.data(), kLargestMessage.size());
+  EXPECT_NE(nullptr, buffer_.data());
+}
+
+TEST_F(ByteBufferTest, SizeReturnsZeroOnFreshBuffer) {
+  EXPECT_EQ(0U, buffer_.size());
+}
+
+TEST_F(ByteBufferTest, SizeIsCorrectAfterSmallWrite) {
+  buffer_.AppendOrDie(kSmallestMessage.data(), kSmallestMessage.size());
+  EXPECT_EQ(kSmallestMessage.size(), buffer_.size());
+}
+
+TEST_F(ByteBufferTest, SizeIsCorrectAfterLargeWrite) {
+  buffer_.AppendOrDie(kLargestMessage.data(), kLargestMessage.size());
+  EXPECT_EQ(kLargestMessage.size(), buffer_.size());
+}
+
+TEST_F(ByteBufferTest, SizeIsCorrectAfterMultipleWrites) {
+  buffer_.AppendOrDie(kSmallestMessage.data(), kSmallestMessage.size());
+  buffer_.AppendOrDie(kSmallestMessage.data(), kSmallestMessage.size());
+  buffer_.AppendOrDie(kSmallestMessage.data(), kSmallestMessage.size());
+  EXPECT_EQ(3 * kSmallestMessage.size(), buffer_.size());
+}
+
+// Per
+// github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-tests),
+// death tests should be specially named.
+using ByteBufferDeathTest = ByteBufferTest;
+
+TEST_F(ByteBufferDeathTest, AppendLargerThanBufferCausesDeath) {
+  constexpr std::array<uint8_t, kBufferSizeBytes + 1> oversized_message{};
+  EXPECT_DEATH(
+      buffer_.AppendOrDie(oversized_message.data(), oversized_message.size()),
+      "Check failed");
+}
+
+TEST_F(ByteBufferDeathTest, AppendLargerThanFreeSpaceCausesDeath) {
+  buffer_.AppendOrDie(kLargestMessage.data(), kLargestMessage.size());
+  EXPECT_DEATH(
+      buffer_.AppendOrDie(kSmallestMessage.data(), kSmallestMessage.size()),
+      "Check failed");
+}
+
+}  // namespace wifilogd
+}  // namespace android