Switch intro example to blobstore client
diff --git a/demo/src/blobstore.cc b/demo/src/blobstore.cc
new file mode 100644
index 0000000..a75affc
--- /dev/null
+++ b/demo/src/blobstore.cc
@@ -0,0 +1,71 @@
+#include "demo/include/blobstore.h"
+#include "demo/src/main.rs.h"
+#include <algorithm>
+#include <functional>
+#include <set>
+#include <string>
+#include <unordered_map>
+
+namespace org {
+namespace blobstore {
+
+// Toy implementation of an in-memory blobstore.
+//
+// In reality the implementation of BlobstoreClient could be a large complex C++
+// library.
+class BlobstoreClient::impl {
+ friend BlobstoreClient;
+ using Blob = struct {
+ std::string data;
+ std::set<std::string> tags;
+ };
+ std::unordered_map<uint64_t, Blob> blobs;
+};
+
+BlobstoreClient::BlobstoreClient() : impl(new typename BlobstoreClient::impl) {}
+
+// Upload a new blob and return a blobid that serves as a handle to the blob.
+uint64_t BlobstoreClient::put(MultiBuf &buf) const {
+ std::string contents;
+
+ // Traverse the caller's chunk iterator.
+ //
+ // In reality there might be sophisticated batching of chunks and/or parallel
+ // upload implemented by the blobstore's C++ client.
+ while (true) {
+ auto chunk = next_chunk(buf);
+ if (chunk.size() == 0) {
+ break;
+ }
+ contents.append(reinterpret_cast<const char *>(chunk.data()), chunk.size());
+ }
+
+ // Insert into map and provide caller the handle.
+ auto blobid = std::hash<std::string>{}(contents);
+ impl->blobs[blobid] = {std::move(contents), {}};
+ return blobid;
+}
+
+// Add tag to an existing blob.
+void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const {
+ impl->blobs[blobid].tags.emplace(tag);
+}
+
+// Retrieve metadata about a blob.
+BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const {
+ BlobMetadata metadata{};
+ auto blob = impl->blobs.find(blobid);
+ if (blob != impl->blobs.end()) {
+ metadata.size = blob->second.data.size();
+ std::for_each(blob->second.tags.begin(), blob->second.tags.end(),
+ [&](auto &t) { metadata.tags.emplace_back(t); });
+ }
+ return metadata;
+}
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+ return std::make_unique<BlobstoreClient>();
+}
+
+} // namespace blobstore
+} // namespace org
diff --git a/demo/src/demo.cc b/demo/src/demo.cc
deleted file mode 100644
index 79c693f..0000000
--- a/demo/src/demo.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "demo/include/demo.h"
-#include "demo/src/main.rs.h"
-#include <iostream>
-
-namespace org {
-namespace example {
-
-ThingC::ThingC(std::string appname) : appname(std::move(appname)) {}
-
-ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; }
-
-std::unique_ptr<ThingC> make_demo(rust::Str appname) {
- return std::make_unique<ThingC>(std::string(appname));
-}
-
-const std::string &get_name(const ThingC &thing) { return thing.appname; }
-
-void do_thing(SharedThing state) { print_r(*state.y); }
-
-} // namespace example
-} // namespace org
diff --git a/demo/src/main.rs b/demo/src/main.rs
index 8f62084..10f57e5 100644
--- a/demo/src/main.rs
+++ b/demo/src/main.rs
@@ -1,39 +1,59 @@
-#[cxx::bridge(namespace = "org::example")]
+#[cxx::bridge(namespace = "org::blobstore")]
mod ffi {
- struct SharedThing {
- z: i32,
- y: Box<ThingR>,
- x: UniquePtr<ThingC>,
+ // Shared structs with fields visible to both languages.
+ struct BlobMetadata {
+ size: usize,
+ tags: Vec<String>,
}
- extern "C" {
- include!("demo/include/demo.h");
-
- type ThingC;
- fn make_demo(appname: &str) -> UniquePtr<ThingC>;
- fn get_name(thing: &ThingC) -> &CxxString;
- fn do_thing(state: SharedThing);
- }
-
+ // Rust types and signatures exposed to C++.
extern "Rust" {
- type ThingR;
- fn print_r(r: &ThingR);
+ type MultiBuf;
+
+ fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+ }
+
+ // C++ types and signatures exposed to Rust.
+ extern "C++" {
+ include!("demo/include/blobstore.h");
+
+ type BlobstoreClient;
+
+ fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+ fn put(&self, parts: &mut MultiBuf) -> u64;
+ fn tag(&self, blobid: u64, tag: &str);
+ fn metadata(&self, blobid: u64) -> BlobMetadata;
}
}
-pub struct ThingR(usize);
-
-fn print_r(r: &ThingR) {
- println!("called back with r={}", r.0);
+// An iterator over contiguous chunks of a discontiguous file object.
+//
+// Toy implementation uses a Vec<Vec<u8>> but in reality this might be iterating
+// over some more complex Rust data structure like a rope, or maybe loading
+// chunks lazily from somewhere.
+pub struct MultiBuf {
+ chunks: Vec<Vec<u8>>,
+ pos: usize,
+}
+pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
+ let next = buf.chunks.get(buf.pos);
+ buf.pos += 1;
+ next.map(Vec::as_slice).unwrap_or(&[])
}
fn main() {
- let x = ffi::make_demo("demo of cxx::bridge");
- println!("this is a {}", ffi::get_name(x.as_ref().unwrap()));
+ let client = ffi::new_blobstore_client();
- ffi::do_thing(ffi::SharedThing {
- z: 222,
- y: Box::new(ThingR(333)),
- x,
- });
+ // Upload a blob.
+ let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()];
+ let mut buf = MultiBuf { chunks, pos: 0 };
+ let blobid = client.put(&mut buf);
+ println!("blobid = {}", blobid);
+
+ // Add a tag.
+ client.tag(blobid, "rust");
+
+ // Read back the tags.
+ let metadata = client.metadata(blobid);
+ println!("tags = {:?}", metadata.tags);
}