build: Refactor the build structure of the project

Moved all header files to `include` and all cpp files to `src`.
Updated the meson.build accordingly.

Change-Id: I9e26197b9c73b5e284cfc9d0d78a234546c282ad
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/include/binarystore.hpp b/include/binarystore.hpp
new file mode 100644
index 0000000..792de4a
--- /dev/null
+++ b/include/binarystore.hpp
@@ -0,0 +1,128 @@
+#pragma once
+
+#include "binarystore_interface.hpp"
+#include "sys_file.hpp"
+
+#include <unistd.h>
+
+#include <blobs-ipmid/blobs.hpp>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "binaryblob.pb.h"
+
+using std::size_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::uint8_t;
+
+namespace binstore
+{
+
+/**
+ * @class BinaryStore instantiates a concrete implementation of
+ *     BinaryStoreInterface. The dependency on file is injected through its
+ *     constructor.
+ */
+class BinaryStore : public BinaryStoreInterface
+{
+  public:
+    /* |CommitState| differs slightly with |StateFlags| in blob.hpp,
+     * and thus is defined in the OEM space (bit 8 - 15). User can call stat()
+     * to query the |CommitState| of the blob path. */
+    enum CommitState
+    {
+        Dirty = (1 << 8), // In-memory data might not match persisted data
+        Clean = (1 << 9), // In-memory data matches persisted data
+        Uninitialized = (1 << 10), // Cannot find persisted data
+        CommitError = (1 << 11)    // Error happened during committing
+    };
+
+    BinaryStore() = delete;
+    BinaryStore(const std::string& baseBlobId, std::unique_ptr<SysFile> file,
+                std::optional<uint32_t> maxSize = std::nullopt) :
+        baseBlobId_(baseBlobId),
+        file_(std::move(file)), maxSize(maxSize)
+    {
+        blob_.set_blob_base_id(baseBlobId_);
+        if (maxSize)
+        {
+            blob_.set_max_size_bytes(*maxSize);
+        }
+    }
+
+    BinaryStore(std::unique_ptr<SysFile> file, bool readOnly = false,
+                std::optional<uint32_t> maxSize = std::nullopt) :
+        readOnly_{readOnly},
+        file_(std::move(file)), maxSize(maxSize)
+    {
+        if (maxSize)
+        {
+            blob_.set_max_size_bytes(*maxSize);
+        }
+    }
+
+    ~BinaryStore() = default;
+
+    BinaryStore(const BinaryStore&) = delete;
+    BinaryStore& operator=(const BinaryStore&) = delete;
+    BinaryStore(BinaryStore&&) = default;
+    BinaryStore& operator=(BinaryStore&&) = default;
+
+    std::string getBaseBlobId() const override;
+    std::vector<std::string> getBlobIds() const override;
+    bool openOrCreateBlob(const std::string& blobId, uint16_t flags) override;
+    bool deleteBlob(const std::string& blobId) override;
+    std::vector<uint8_t> read(uint32_t offset, uint32_t requestedSize) override;
+    std::vector<uint8_t> readBlob(const std::string& blobId) const override;
+    bool write(uint32_t offset, const std::vector<uint8_t>& data) override;
+    bool commit() override;
+    bool close() override;
+    bool stat(blobs::BlobMeta* meta) override;
+
+    /**
+     * Helper factory method to create a BinaryStore instance
+     * @param baseBlobId: base id for the created instance
+     * @param sysFile: system file object for storing binary
+     * @returns unique_ptr to constructed BinaryStore. Caller should take
+     *     ownership of the instance.
+     */
+    static std::unique_ptr<BinaryStoreInterface>
+        createFromConfig(const std::string& baseBlobId,
+                         std::unique_ptr<SysFile> file,
+                         std::optional<uint32_t> maxSize = std::nullopt);
+
+    /**
+     * Helper factory method to create a BinaryStore instance
+     * This function should be used with existing stores. It reads
+     * the baseBlobId name from the storage.
+     * @param sysFile: system file object for storing binary
+     * @param readOnly: if true, open the store in read only mode
+     * @returns unique_ptr to constructed BinaryStore.
+     */
+    static std::unique_ptr<BinaryStoreInterface>
+        createFromFile(std::unique_ptr<SysFile> file, bool readOnly = true,
+                       std::optional<uint32_t> maxSize = std::nullopt);
+
+  private:
+    /* Load the serialized data from sysfile if commit state is dirty.
+     * Returns False if encountered error when loading */
+    bool loadSerializedData();
+
+    std::string baseBlobId_;
+    binaryblobproto::BinaryBlobBase blob_;
+    binaryblobproto::BinaryBlob* currentBlob_ = nullptr;
+    /* True if current blob is writable */
+    bool writable_ = false;
+    /* True if the entire store (not just individual blobs) is read only */
+    bool readOnly_ = false;
+    std::unique_ptr<SysFile> file_ = nullptr;
+    CommitState commitState_ = CommitState::Dirty;
+    std::optional<uint32_t> maxSize;
+};
+
+} // namespace binstore
diff --git a/include/binarystore_interface.hpp b/include/binarystore_interface.hpp
new file mode 100644
index 0000000..ba6dca3
--- /dev/null
+++ b/include/binarystore_interface.hpp
@@ -0,0 +1,100 @@
+#pragma once
+
+#include <blobs-ipmid/blobs.hpp>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+using std::size_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::uint8_t;
+
+namespace binstore
+{
+
+/**
+ * @class BinaryStoreInterface is an abstraction for a storage location.
+ *     Each instance would be uniquely identified by a baseId string.
+ */
+class BinaryStoreInterface
+{
+  public:
+    virtual ~BinaryStoreInterface() = default;
+
+    /**
+     * @returns baseId string of the storage.
+     */
+    virtual std::string getBaseBlobId() const = 0;
+
+    /**
+     * @returns List of all open blob IDs, plus the base.
+     */
+    virtual std::vector<std::string> getBlobIds() const = 0;
+
+    /**
+     * Opens a blob given its name. If there is no one, create one.
+     * @param blobId: The blob id to operate on.
+     * @param flags: Either read flag or r/w flag has to be specified.
+     * @returns True if open/create successfully.
+     */
+    virtual bool openOrCreateBlob(const std::string& blobId,
+                                  uint16_t flags) = 0;
+
+    /**
+     * Deletes a blob given its name. If there is no one,
+     * @param blobId: The blob id to operate on.
+     * @returns True if deleted.
+     */
+    virtual bool deleteBlob(const std::string& blobId) = 0;
+
+    /**
+     * Reads data from the currently opened blob.
+     * @param offset: offset into the blob to read
+     * @param requestedSize: how many bytes to read
+     * @returns Bytes able to read. Returns empty if nothing can be read or
+     *          if there is no open blob.
+     */
+    virtual std::vector<uint8_t> read(uint32_t offset,
+                                      uint32_t requestedSize) = 0;
+
+    /**
+     * Reads all data from the blob
+     * @param blobId: The blob id to operate on.
+     * @returns Bytes able to read. Returns empty if nothing can be read or
+     *          if there is no such blob.
+     */
+    virtual std::vector<uint8_t> readBlob(const std::string& blobId) const = 0;
+
+    /**
+     * Writes data to the currently openend blob.
+     * @param offset: offset into the blob to write
+     * @param data: bytes to write
+     * @returns True if able to write the entire data successfully
+     */
+    virtual bool write(uint32_t offset, const std::vector<uint8_t>& data) = 0;
+
+    /**
+     * Commits data to the persistent storage specified during blob init.
+     * @returns True if able to write data to sysfile successfully
+     */
+    virtual bool commit() = 0;
+
+    /**
+     * Closes blob, which prevents further modifications. Uncommitted data will
+     * be lost.
+     * @returns True if able to close the blob successfully
+     */
+    virtual bool close() = 0;
+
+    /**
+     * Returns blob stat flags.
+     * @param meta: output stat flags.
+     * @returns True if able to get the stat flags and write to *meta
+     */
+    virtual bool stat(blobs::BlobMeta* meta) = 0;
+};
+
+} // namespace binstore
diff --git a/include/binarystore_mock.hpp b/include/binarystore_mock.hpp
new file mode 100644
index 0000000..9360dd3
--- /dev/null
+++ b/include/binarystore_mock.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "binarystore.hpp"
+#include "binarystore_interface.hpp"
+#include "sys_file.hpp"
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+using ::testing::Invoke;
+
+namespace binstore
+{
+
+class MockBinaryStore : public BinaryStoreInterface
+{
+  public:
+    MockBinaryStore(const std::string& baseBlobId,
+                    std::unique_ptr<SysFile> file,
+                    std::optional<uint32_t> maxSize = std::nullopt) :
+        real_store_(baseBlobId, std::move(file), maxSize)
+    {
+        // Implemented calls in BinaryStore will be directed to the real object.
+        ON_CALL(*this, getBaseBlobId)
+            .WillByDefault(Invoke(&real_store_, &BinaryStore::getBaseBlobId));
+        ON_CALL(*this, getBlobIds)
+            .WillByDefault(Invoke(&real_store_, &BinaryStore::getBlobIds));
+        ON_CALL(*this, openOrCreateBlob)
+            .WillByDefault(
+                Invoke(&real_store_, &BinaryStore::openOrCreateBlob));
+        ON_CALL(*this, close)
+            .WillByDefault(Invoke(&real_store_, &BinaryStore::close));
+        ON_CALL(*this, read)
+            .WillByDefault(Invoke(&real_store_, &BinaryStore::read));
+        ON_CALL(*this, write)
+            .WillByDefault(Invoke(&real_store_, &BinaryStore::write));
+        ON_CALL(*this, commit)
+            .WillByDefault(Invoke(&real_store_, &BinaryStore::commit));
+        ON_CALL(*this, stat)
+            .WillByDefault(Invoke(&real_store_, &BinaryStore::stat));
+    }
+    MOCK_CONST_METHOD0(getBaseBlobId, std::string());
+    MOCK_CONST_METHOD0(getBlobIds, std::vector<std::string>());
+    MOCK_METHOD2(openOrCreateBlob, bool(const std::string&, uint16_t));
+    MOCK_METHOD1(deleteBlob, bool(const std::string&));
+    MOCK_METHOD2(read, std::vector<uint8_t>(uint32_t, uint32_t));
+    MOCK_METHOD2(write, bool(uint32_t, const std::vector<uint8_t>&));
+    MOCK_METHOD0(commit, bool());
+    MOCK_METHOD0(close, bool());
+    MOCK_METHOD1(stat, bool(blobs::BlobMeta* meta));
+
+    std::vector<uint8_t> readBlob(const std::string& blobId) const override
+    {
+        return real_store_.readBlob(blobId);
+    }
+
+  private:
+    BinaryStore real_store_;
+};
+
+} // namespace binstore
diff --git a/include/handler.hpp b/include/handler.hpp
new file mode 100644
index 0000000..2a75a57
--- /dev/null
+++ b/include/handler.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "binarystore.hpp"
+
+#include <blobs-ipmid/blobs.hpp>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+using std::size_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::uint8_t;
+
+namespace blobs
+{
+
+class BinaryStoreBlobHandler : public GenericBlobInterface
+{
+  public:
+    BinaryStoreBlobHandler() = default;
+    ~BinaryStoreBlobHandler() = default;
+    BinaryStoreBlobHandler(const BinaryStoreBlobHandler&) = delete;
+    BinaryStoreBlobHandler& operator=(const BinaryStoreBlobHandler&) = delete;
+    BinaryStoreBlobHandler(BinaryStoreBlobHandler&&) = default;
+    BinaryStoreBlobHandler& operator=(BinaryStoreBlobHandler&&) = default;
+
+    bool canHandleBlob(const std::string& path) override;
+    std::vector<std::string> getBlobIds() override;
+    bool deleteBlob(const std::string& path) override;
+    bool stat(const std::string& path, struct BlobMeta* meta) override;
+    bool open(uint16_t session, uint16_t flags,
+              const std::string& path) override;
+    std::vector<uint8_t> read(uint16_t session, uint32_t offset,
+                              uint32_t requestedSize) override;
+    bool write(uint16_t session, uint32_t offset,
+               const std::vector<uint8_t>& data) override;
+    bool writeMeta(uint16_t session, uint32_t offset,
+                   const std::vector<uint8_t>& data) override;
+    bool commit(uint16_t session, const std::vector<uint8_t>& data) override;
+    bool close(uint16_t session) override;
+    bool stat(uint16_t session, struct BlobMeta* meta) override;
+    bool expire(uint16_t session) override;
+
+    /**
+     * Registers a binarystore in the main handler. Once called, handler will
+     * take over the ownership of of enclosed binary store.
+     *
+     * @param store: pointer to a valid BinaryStore.
+     * TODO: a minimal amount of error checking would be better
+     */
+    void addNewBinaryStore(
+        std::unique_ptr<binstore::BinaryStoreInterface> store);
+
+  private:
+    /* map of baseId: binaryStore, which has a 1:1 relationship. */
+    std::map<std::string, std::unique_ptr<binstore::BinaryStoreInterface>>
+        stores_;
+
+    /* map of sessionId: open binaryStore pointer. */
+    std::unordered_map<uint16_t, binstore::BinaryStoreInterface*> sessions_;
+};
+
+} // namespace blobs
diff --git a/include/meson.build b/include/meson.build
new file mode 100644
index 0000000..e4ada8f
--- /dev/null
+++ b/include/meson.build
@@ -0,0 +1 @@
+blobstore_includes = include_directories('.')
diff --git a/include/parse_config.hpp b/include/parse_config.hpp
new file mode 100644
index 0000000..b778923
--- /dev/null
+++ b/include/parse_config.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cstdint>
+#include <nlohmann/json.hpp>
+#include <string>
+
+using std::uint32_t;
+using json = nlohmann::json;
+
+namespace conf
+{
+
+struct BinaryBlobConfig
+{
+    std::string blobBaseId;  // Required
+    std::string sysFilePath; // Required
+    uint32_t offsetBytes;    // Optional
+    uint32_t maxSizeBytes;   // Optional
+};
+
+/**
+ * @brief Parse parameters from a config json
+ * @param j: input json object
+ * @param config: output BinaryBlobConfig
+ * @throws: exception if config doesn't have required fields
+ */
+static inline void parseFromConfigFile(const json& j, BinaryBlobConfig& config)
+{
+    j.at("blobBaseId").get_to(config.blobBaseId);
+    j.at("sysFilePath").get_to(config.sysFilePath);
+    config.offsetBytes = j.value("offsetBytes", 0);
+    config.maxSizeBytes = j.value("maxSizeBytes", 0);
+}
+
+} // namespace conf
diff --git a/include/sys.hpp b/include/sys.hpp
new file mode 100644
index 0000000..230665b
--- /dev/null
+++ b/include/sys.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <unistd.h>
+
+namespace binstore
+{
+
+namespace internal
+{
+
+/** @class Sys
+ *  @brief Overridable direct syscall interface
+ *
+ *  TODO: factor this out into a syscall class shared by all upstream projects
+ */
+class Sys
+{
+  public:
+    virtual ~Sys() = default;
+    virtual int open(const char* pathname, int flags) const = 0;
+    virtual int close(int fd) const = 0;
+    virtual off_t lseek(int fd, off_t offset, int whence) const = 0;
+    virtual ssize_t read(int fd, void* buf, size_t count) const = 0;
+    virtual ssize_t write(int fd, const void* buf, size_t count) const = 0;
+};
+
+/** @class SysImpl
+ *  @brief syscall concrete implementation
+ *  @details Passes through all calls to the normal linux syscalls
+ */
+class SysImpl : public Sys
+{
+  public:
+    int open(const char* pathname, int flags) const override;
+    int close(int fd) const override;
+    off_t lseek(int fd, off_t offset, int whence) const override;
+    ssize_t read(int fd, void* buf, size_t count) const override;
+    ssize_t write(int fd, const void* buf, size_t count) const override;
+};
+
+/** @brief Default instantiation of sys */
+extern SysImpl sys_impl;
+
+} // namespace internal
+
+} // namespace binstore
diff --git a/include/sys_file.hpp b/include/sys_file.hpp
new file mode 100644
index 0000000..a45f881
--- /dev/null
+++ b/include/sys_file.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace binstore
+{
+
+/**
+ * @brief Represents a file that supports read/write semantics
+ * TODO: leverage stdplus's support for smart file descriptors when it's ready.
+ */
+class SysFile
+{
+  public:
+    virtual ~SysFile() = default;
+
+    /**
+     * @brief Reads content at pos to char* buffer
+     * @param pos The byte pos into the file to read from
+     * @param count How many bytes to read
+     * @param buf Output data
+     * @returns The size of data read
+     * @throws std::system_error if read operation cannot be completed
+     */
+    virtual size_t readToBuf(size_t pos, size_t count, char* buf) const = 0;
+
+    /**
+     * @brief Reads content at pos
+     * @param pos The byte pos into the file to read from
+     * @param count How many bytes to read
+     * @returns The data read in a vector, whose size might be smaller than
+     *          count if there is not enough to read. Might be empty if the
+     *          count specified is too large to even fit in a std::string.
+     * @throws std::system_error if read operation cannot be completed
+     *         std::bad_alloc if cannot construct string with 'count' size
+     */
+    virtual std::string readAsStr(size_t pos, size_t count) const = 0;
+
+    /**
+     * @brief Reads all the content in file after pos
+     * @param pos The byte pos to read from
+     * @returns The data read in a vector, whose size might be smaller than
+     *          count if there is not enough to read.
+     * @throws std::system_error if read operation cannot be completed
+     */
+    virtual std::string readRemainingAsStr(size_t pos) const = 0;
+
+    /**
+     * @brief Writes all of data into file at pos
+     * @param pos The byte pos to write
+     * @returns void
+     * @throws std::system_error if write operation cannot be completed or
+     *         not all of the bytes can be written
+     */
+    virtual void writeStr(const std::string& data, size_t pos) = 0;
+};
+
+} // namespace binstore
diff --git a/include/sys_file_impl.hpp b/include/sys_file_impl.hpp
new file mode 100644
index 0000000..c74c965
--- /dev/null
+++ b/include/sys_file_impl.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "sys.hpp"
+#include "sys_file.hpp"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace binstore
+{
+
+class SysFileImpl : public SysFile
+{
+  public:
+    /**
+     * @brief Constructs sysFile specified by path and offset
+     * @param path The file path
+     * @param offset The byte offset relatively. Reading a sysfile at position 0
+     *     actually reads underlying file at 'offset'
+     * @param sys Syscall operation interface
+     */
+    explicit SysFileImpl(const std::string& path, size_t offset = 0,
+                         const internal::Sys* sys = &internal::sys_impl);
+    ~SysFileImpl();
+    SysFileImpl() = delete;
+    SysFileImpl(const SysFileImpl&) = delete;
+    SysFileImpl& operator=(SysFileImpl) = delete;
+
+    size_t readToBuf(size_t pos, size_t count, char* buf) const override;
+    std::string readAsStr(size_t pos, size_t count) const override;
+    std::string readRemainingAsStr(size_t pos) const override;
+    void writeStr(const std::string& data, size_t pos) override;
+
+  private:
+    int fd_;
+    size_t offset_;
+    void lseek(size_t pos) const;
+    const internal::Sys* sys;
+};
+
+} // namespace binstore