Implement commit

The commit operation will serialize the binarystore protobuf and write
it to the designated sysfile location, with its size stored followed by
actual data.

Signed-off-by: Kun Yi <kunyi@google.com>
Change-Id: Idc16f410d3a1585daaddda58a3665d92a898f5c7
diff --git a/binarystore.cpp b/binarystore.cpp
index 98a6264..22e2e48 100644
--- a/binarystore.cpp
+++ b/binarystore.cpp
@@ -20,6 +20,42 @@
 namespace binstore
 {
 
+namespace internal
+{
+
+/* Helper methods to interconvert an uint64_t and bytes, LSB first.
+   Convert bytes to uint64. Input should be exactly 8 bytes. */
+uint64_t bytesToUint64(const std::string& bytes)
+{
+    if (bytes.size() != sizeof(uint64_t))
+    {
+        return 0;
+    }
+
+    return static_cast<uint64_t>(bytes[7]) << 56 |
+           static_cast<uint64_t>(bytes[6]) << 48 |
+           static_cast<uint64_t>(bytes[5]) << 40 |
+           static_cast<uint64_t>(bytes[4]) << 32 |
+           static_cast<uint64_t>(bytes[3]) << 24 |
+           static_cast<uint64_t>(bytes[2]) << 16 |
+           static_cast<uint64_t>(bytes[1]) << 8 |
+           static_cast<uint64_t>(bytes[0]);
+}
+
+/* Convert uint64 to bytes, LSB first. */
+std::string uint64ToBytes(uint64_t num)
+{
+    std::string result;
+    for (size_t i = 0; i < sizeof(uint64_t); ++i)
+    {
+        result += static_cast<char>(num & 0xff);
+        num >>= 8;
+    }
+    return result;
+}
+
+} // namespace internal
+
 std::unique_ptr<BinaryStoreInterface>
     BinaryStore::createFromConfig(const std::string& baseBlobId,
                                   std::unique_ptr<SysFile> file,
@@ -71,6 +107,31 @@
 
     writable_ = flags & blobs::OpenFlags::write;
 
+    /* Load blob from sysfile if we know it might not match what we have.
+     * Note it will overwrite existing unsaved data per design. */
+    if (commitState_ != CommitState::Clean)
+    {
+        try
+        {
+            /* Parse length-prefixed format to protobuf */
+            auto size =
+                internal::bytesToUint64(file_->readAsStr(0, sizeof(uint64_t)));
+            if (!blob_.ParseFromString(
+                    file_->readAsStr(sizeof(uint64_t), size)))
+            {
+                /* Fail to parse the data, which might mean no preexsiting data
+                 * and is a valid case to handle */
+                // TODO: logging
+            }
+        }
+        catch (const std::exception& e)
+        {
+            /* Read causes unexpected system-level failure */
+            // TODO: logging
+            return false;
+        }
+    }
+
     /* Iterate and find if there is an existing blob with this id.
      * blobsPtr points to a BinaryBlob container with STL-like semantics*/
     auto blobsPtr = blob_.mutable_blobs();
@@ -152,13 +213,31 @@
 
 bool BinaryStore::commit()
 {
-    return false;
+
+    auto blobData = blob_.SerializeAsString();
+    std::string commitData(internal::uint64ToBytes((uint64_t)blobData.size()));
+    commitData += blobData;
+
+    try
+    {
+        file_->writeStr(commitData, 0);
+    }
+    catch (const std::exception& e)
+    {
+        // TODO: logging
+        commitState_ = CommitState::CommitError;
+        return false;
+    };
+
+    commitState_ = CommitState::Clean;
+    return true;
 }
 
 bool BinaryStore::close()
 {
     currentBlob_ = nullptr;
     writable_ = false;
+    commitState_ = CommitState::Dirty;
     return true;
 }