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