Introduce interfaces for compressing and decompressing log files

Change-Id: Ia7da3f840a1780a04203f1c312447b50b142a5a3
diff --git a/aos/containers/resizeable_buffer.h b/aos/containers/resizeable_buffer.h
new file mode 100644
index 0000000..b94f8cc
--- /dev/null
+++ b/aos/containers/resizeable_buffer.h
@@ -0,0 +1,101 @@
+#ifndef AOS_CONTAINERS_RESIZEABLE_BUFFER_H_
+#define AOS_CONTAINERS_RESIZEABLE_BUFFER_H_
+
+#include <stdlib.h>
+
+#include <memory>
+
+#include "glog/logging.h"
+
+namespace aos {
+
+// Kind of like a subset of vector<uint8_t>, but with less destructor calls.
+// When building unoptimized, especially with sanitizers, the vector<uint8_t>
+// version ends up being really slow in tests.
+class ResizeableBuffer {
+ public:
+  ResizeableBuffer() = default;
+
+  ResizeableBuffer(const ResizeableBuffer &other) { *this = other; }
+  ResizeableBuffer(ResizeableBuffer &&other) { *this = std::move(other); }
+  ResizeableBuffer &operator=(const ResizeableBuffer &other) {
+    resize(other.size());
+    memcpy(storage_.get(), other.storage_.get(), size());
+    return *this;
+  }
+  ResizeableBuffer &operator=(ResizeableBuffer &&other) {
+    std::swap(storage_, other.storage_);
+    std::swap(size_, other.size_);
+    std::swap(capacity_, other.capacity_);
+    return *this;
+  }
+
+  uint8_t *data() { return static_cast<uint8_t *>(storage_.get()); }
+  const uint8_t *data() const {
+    return static_cast<const uint8_t *>(storage_.get());
+  }
+
+  uint8_t *begin() { return data(); }
+  const uint8_t *begin() const { return data(); }
+  uint8_t *end() { return data() + size(); }
+  const uint8_t *end() const { return data() + size(); }
+
+  size_t size() const { return size_; }
+  size_t capacity() const { return capacity_; }
+
+  void resize(size_t new_size) {
+    if (new_size > capacity_) {
+      Allocate(new_size);
+    }
+    size_ = new_size;
+  }
+
+  void erase_front(size_t count) {
+    if (count == 0) {
+      return;
+    }
+    CHECK_LE(count, size_);
+    memmove(static_cast<void *>(data()), static_cast<void *>(data() + count),
+            size_ - count);
+    size_ -= count;
+  }
+
+  void push_back(uint8_t x) {
+    if (size_ == capacity_) {
+      Allocate(std::max<size_t>(16, size_ * 2));
+    }
+    *end() = x;
+    ++size_;
+    CHECK_LE(size_, capacity_);
+  }
+
+ private:
+  // We need this silly function because C++ is bad, and extensions to it which
+  // we work with make it a true nightmare.
+  //
+  // (a) You can't easily write out the signature of free because it depends on
+  // whether exceptions are enabled or not. You could use decltype, but see (b).
+  //
+  // (b) You can't easily write &free because CUDA overloads it with a
+  // __device__ version. You could cast to the appropriate version, but see (a).
+  //
+  // There's probably some kind of SFINAE thing which could find the matching
+  // signature from a set of choices, and then we could just
+  // static_cast<TheSignature>(&free). However, that sounds like a nightmare,
+  // especially because it has to conditionally enable the part mentioning CUDA
+  // identifiers in the preprocessor. This little function is way simpler.
+  static void DoFree(void *p) { free(p); }
+
+  void Allocate(size_t new_capacity) {
+    void *const old = storage_.release();
+    storage_.reset(CHECK_NOTNULL(realloc(old, new_capacity)));
+    capacity_ = new_capacity;
+  }
+
+  std::unique_ptr<void, decltype(&DoFree)> storage_{nullptr, &DoFree};
+  size_t size_ = 0, capacity_ = 0;
+};
+
+}  // namespace aos
+
+#endif  // AOS_CONTAINERS_RESIZEABLE_BUFFER_H_