Add a Vector type.

Vector is a generic dynamically-sized array container, essentially a
primitive version of std::vector (which we can't use in environments
that lack a standard library).

BUG: 25762536
Change-Id: I602378a815997eeb445f5456d10364dedd907f37
diff --git a/include/nvram/compiler.h b/include/nvram/compiler.h
index c312e8b..8d6ad05 100644
--- a/include/nvram/compiler.h
+++ b/include/nvram/compiler.h
@@ -17,12 +17,19 @@
 #ifndef NVRAM_COMPILER_H_
 #define NVRAM_COMPILER_H_
 
+extern "C" {
+#include <stdlib.h>
+}
+
 #if defined(__GNUC__) || defined(__clang__)
 #define NVRAM_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
 #else
 #define NVRAM_WARN_UNUSED_RESULT
 #endif
 
+// Check a condition, abort if it doesn't hold.
+#define NVRAM_CHECK(cond) if (!(cond)) abort()
+
 // The visibility attribute works both with clang and GCC.
 #define NVRAM_EXPORT __attribute__((visibility("default")))
 
diff --git a/include/nvram/vector.h b/include/nvram/vector.h
new file mode 100644
index 0000000..c96e129
--- /dev/null
+++ b/include/nvram/vector.h
@@ -0,0 +1,169 @@
+/*
+ * 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 NVRAM_VECTOR_H_
+#define NVRAM_VECTOR_H_
+
+extern "C" {
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+}
+
+#include <new>
+
+#include <nvram/compiler.h>
+
+namespace nvram {
+
+// A bare-bones dynamically-sized array container, similar to std::vector.
+//
+// This class is intended for use in restricted environments where the C++
+// standard library is not available. Prefer std::vector wherever possible.
+template <typename ElementType> class Vector {
+ public:
+  Vector() = default;
+  ~Vector() {
+    for (size_t i = 0; i < size_; ++i) {
+      data_[i].~ElementType();
+    }
+    free(data_);
+    data_ = nullptr;
+    size_ = 0;
+    capacity_ = 0;
+  }
+
+  // Vector is not copyable as this would require memory allocations that may
+  // fail. However, Vector supports move semantics.
+  Vector(const Vector<ElementType>& other) = delete;
+  Vector<ElementType>& operator=(const Vector<ElementType>& other) = delete;
+  Vector(Vector<ElementType>&& other) : Vector() {
+    swap(*this, other);
+  }
+  Vector<ElementType>& operator=(Vector<ElementType>&& other) {
+    swap(*this, other);
+    return *this;
+  }
+  friend void swap(Vector<ElementType>& first, Vector<ElementType>& second) {
+    // This does not use std::swap since it needs to work in environments that
+    // are lacking a standard library.
+    ElementType* tmp_data = first.data_;
+    size_t tmp_size = first.size_;
+    first.data_ = second.data_;
+    first.size_ = second.size_;
+    second.data_ = tmp_data;
+    second.size_ = tmp_size;
+  }
+
+  ElementType& operator[](size_t pos) {
+    NVRAM_CHECK(pos < size_);
+    return data_[pos];
+  }
+  const ElementType& operator[](size_t pos) const {
+    NVRAM_CHECK(pos < size_);
+    return data_[pos];
+  }
+
+  ElementType* begin() { return data_; }
+  ElementType* end() { return data_ + size_; }
+
+  const ElementType* begin() const { return data_; }
+  const ElementType* end() const { return data_ + size_; }
+
+  size_t size() const { return size_; }
+
+  // Resizes the Vector. Truncates if |size| decreases. Pads the Vector with
+  // value-constructed entries if |size| increases.
+  bool Resize(size_t size) NVRAM_WARN_UNUSED_RESULT {
+    // Check for capacity change.
+    size_t new_capacity = capacity_;
+    if (size < capacity_ / 2) {
+      new_capacity = size;
+    } else if (size > capacity_) {
+      new_capacity = capacity_ * 2 > size ? capacity_ * 2 : size;
+    }
+    NVRAM_CHECK(new_capacity >= size);
+
+    // Allocate new memory if necessary.
+    ElementType* new_data = nullptr;
+    if (new_capacity != capacity_) {
+      if (new_capacity == 0) {
+        new_data = nullptr;
+      } else {
+        new_data = static_cast<ElementType*>(
+            calloc(new_capacity, sizeof(ElementType)));
+        if (!new_data) {
+          return false;
+        }
+      }
+    } else {
+      new_data = data_;
+    }
+
+    size_t min_size = (size < size_) ? size : size_;
+    if (new_data != data_) {
+      // Move elements that remain valid.
+      for (size_t i = 0; i < min_size; ++i) {
+        new (&new_data[i]) ElementType(static_cast<ElementType&&>(data_[i]));
+      }
+    }
+
+    // Destroy elements that are no longer part of the list.
+    for (size_t i = min_size; i < size_; ++i) {
+      data_[i].~ElementType();
+    }
+
+    // Construct new elements that got appended.
+    for (size_t i = min_size; i < size; ++i) {
+      new (&new_data[i]) ElementType;
+    }
+
+    if (new_data != data_) {
+      free(data_);
+    }
+    data_ = new_data;
+    capacity_ = new_capacity;
+    size_ = size;
+    return true;
+  }
+
+  // Appends an element.
+  bool Append(const ElementType& element) NVRAM_WARN_UNUSED_RESULT {
+    if (!Resize(size_ + 1)) {
+      return false;
+    }
+    data_[size_ - 1] = element;
+    return true;
+  }
+
+  // Rvalue-reference version of Append.
+  bool Append(ElementType&& element) NVRAM_WARN_UNUSED_RESULT {
+    if (!Resize(size_ + 1)) {
+      return false;
+    }
+    data_[size_ - 1] = element;
+    return true;
+  }
+
+ private:
+  size_t size_ = 0;
+  size_t capacity_ = 0;
+  ElementType* data_ = nullptr;
+};
+
+}  // namespace nvram
+
+#endif  // NVRAM_VECTOR_H_