Switch intro example to blobstore client
diff --git a/demo/BUCK b/demo/BUCK
index 846c149..43b83aa 100644
--- a/demo/BUCK
+++ b/demo/BUCK
@@ -4,8 +4,8 @@
name = "demo",
srcs = glob(["src/**/*.rs"]),
deps = [
+ ":blobstore-sys",
":bridge",
- ":demo-sys",
"//:cxx",
],
)
@@ -13,21 +13,21 @@
rust_cxx_bridge(
name = "bridge",
src = "src/main.rs",
- deps = [":demo-include"],
+ deps = [":blobstore-include"],
)
cxx_library(
- name = "demo-sys",
- srcs = ["src/demo.cc"],
+ name = "blobstore-sys",
+ srcs = ["src/blobstore.cc"],
compiler_flags = ["-std=c++14"],
deps = [
+ ":blobstore-include",
":bridge/include",
- ":demo-include",
],
)
cxx_library(
- name = "demo-include",
- exported_headers = ["include/demo.h"],
+ name = "blobstore-include",
+ exported_headers = ["include/blobstore.h"],
deps = ["//:core"],
)
diff --git a/demo/BUILD b/demo/BUILD
index 5bc8c77..cce8119 100644
--- a/demo/BUILD
+++ b/demo/BUILD
@@ -6,8 +6,8 @@
name = "demo",
srcs = glob(["src/**/*.rs"]),
deps = [
+ ":blobstore-sys",
":bridge",
- ":demo-sys",
"//:cxx",
],
)
@@ -15,21 +15,21 @@
rust_cxx_bridge(
name = "bridge",
src = "src/main.rs",
- deps = [":demo-include"],
+ deps = [":blobstore-include"],
)
cc_library(
- name = "demo-sys",
- srcs = ["src/demo.cc"],
+ name = "blobstore-sys",
+ srcs = ["src/blobstore.cc"],
copts = ["-std=c++14"],
deps = [
+ ":blobstore-include",
":bridge/include",
- ":demo-include",
],
)
cc_library(
- name = "demo-include",
- hdrs = ["include/demo.h"],
+ name = "blobstore-include",
+ hdrs = ["include/blobstore.h"],
deps = ["//:core"],
)
diff --git a/demo/build.rs b/demo/build.rs
index a3c31b3..c1b55cc 100644
--- a/demo/build.rs
+++ b/demo/build.rs
@@ -1,10 +1,10 @@
fn main() {
cxx_build::bridge("src/main.rs")
- .file("src/demo.cc")
+ .file("src/blobstore.cc")
.flag_if_supported("-std=c++14")
.compile("cxxbridge-demo");
println!("cargo:rerun-if-changed=src/main.rs");
- println!("cargo:rerun-if-changed=src/demo.cc");
- println!("cargo:rerun-if-changed=include/demo.h");
+ println!("cargo:rerun-if-changed=src/blobstore.cc");
+ println!("cargo:rerun-if-changed=include/blobstore.h");
}
diff --git a/demo/include/blobstore.h b/demo/include/blobstore.h
new file mode 100644
index 0000000..d89583a
--- /dev/null
+++ b/demo/include/blobstore.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "rust/cxx.h"
+#include <memory>
+
+namespace org {
+namespace blobstore {
+
+struct MultiBuf;
+struct BlobMetadata;
+
+class BlobstoreClient {
+public:
+ BlobstoreClient();
+ uint64_t put(MultiBuf &buf) const;
+ void tag(uint64_t blobid, rust::Str tag) const;
+ BlobMetadata metadata(uint64_t blobid) const;
+
+private:
+ class impl;
+ std::shared_ptr<impl> impl;
+};
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client();
+
+} // namespace blobstore
+} // namespace org
diff --git a/demo/include/demo.h b/demo/include/demo.h
deleted file mode 100644
index fafc474..0000000
--- a/demo/include/demo.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-#include "rust/cxx.h"
-#include <memory>
-#include <string>
-
-namespace org {
-namespace example {
-
-class ThingC {
-public:
- ThingC(std::string appname);
- ~ThingC();
-
- std::string appname;
-};
-
-struct SharedThing;
-
-std::unique_ptr<ThingC> make_demo(rust::Str appname);
-const std::string &get_name(const ThingC &thing);
-void do_thing(SharedThing state);
-
-} // namespace example
-} // namespace org
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);
}