[Rust] Shared String (#6367)
authormustiikhalil <mustii@mmk.one>
Mon, 4 Jan 2021 14:18:35 +0000 (17:18 +0300)
committerGitHub <noreply@github.com>
Mon, 4 Jan 2021 14:18:35 +0000 (06:18 -0800)
* Adds shared strings and tests for shared strings

* Adds resets on string_map

* Moved shared strings to use vector instead of hashmap

* Addresses all the issues

* Resolves some comments

rust/flatbuffers/src/builder.rs
rust/flatbuffers/src/primitives.rs
tests/rust_usage_test/tests/integration_test.rs

index f3bfcf2..2db363b 100644 (file)
@@ -54,6 +54,7 @@ pub struct FlatBufferBuilder<'fbb> {
 
     min_align: usize,
     force_defaults: bool,
+    strings_pool: Vec<WIPOffset<&'fbb str>>,
 
     _phantom: PhantomData<&'fbb ()>,
 }
@@ -88,6 +89,7 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
 
             min_align: 0,
             force_defaults: false,
+            strings_pool: Vec::new(),
 
             _phantom: PhantomData,
         }
@@ -121,6 +123,7 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
         self.finished = false;
 
         self.min_align = 0;
+        self.strings_pool.clear();
     }
 
     /// Destroy the FlatBufferBuilder, returning its internal byte vector
@@ -235,6 +238,46 @@ impl<'fbb> FlatBufferBuilder<'fbb> {
         WIPOffset::new(o.value())
     }
 
+    #[inline]
+    pub fn create_shared_string<'a: 'b, 'b>(&'a mut self, s: &'b str) -> WIPOffset<&'fbb str> {
+        self.assert_not_nested(
+            "create_shared_string can not be called when a table or vector is under construction",
+        );
+
+        // Saves a ref to owned_buf since rust doesnt like us refrencing it
+        // in the binary_search_by code.
+        let buf = &self.owned_buf;
+
+        let found = self.strings_pool.binary_search_by(|offset| {
+            let ptr = offset.value() as usize;
+            // Gets The pointer to the size of the string
+            let str_memory = &buf[buf.len() - ptr..];
+            // Gets the size of the written string from buffer
+            let size = u32::from_le_bytes([
+                str_memory[0],
+                str_memory[1],
+                str_memory[2],
+                str_memory[3],
+            ]) as usize;
+            // Size of the string size
+            let string_size: usize = 4;
+            // Fetches actual string bytes from index of string after string size
+            // to the size of string plus string size
+            let iter = str_memory[string_size..size + string_size].iter();
+            // Compares bytes of fetched string and current writable string
+            iter.cloned().cmp(s.bytes())
+        });
+
+        match found {
+            Ok(index) => self.strings_pool[index],
+            Err(index) => {
+                let address = WIPOffset::new(self.create_byte_string(s.as_bytes()).value());
+                self.strings_pool.insert(index, address);
+                address
+            }
+        }
+    }
+
     /// Create a utf8 string.
     ///
     /// The wire format represents this as a zero-terminated byte vector.
index 350e984..3d9d4c8 100644 (file)
@@ -93,6 +93,8 @@ impl<T> Clone for WIPOffset<T> {
     }
 }
 
+impl<T> Eq for WIPOffset<T> {}
+
 impl<T> PartialEq for WIPOffset<T> {
     fn eq(&self, o: &WIPOffset<T>) -> bool {
         self.value() == o.value()
index 4f04760..e219624 100644 (file)
@@ -3051,4 +3051,51 @@ fn load_file(filename: &str) -> Result<Vec<u8>, std::io::Error> {
     f.read_to_end(&mut buf)?;
     Ok(buf)
 }
+
+#[test]
+fn test_shared_strings() {
+    let mut builder = flatbuffers::FlatBufferBuilder::new();
+    let offset1 = builder.create_shared_string("welcome to flatbuffers!!");
+    let offset2 = builder.create_shared_string("welcome");
+    let offset3 = builder.create_shared_string("welcome to flatbuffers!!");
+    assert_ne!(offset2.value(), offset3.value());
+    assert_eq!(offset1.value(), offset3.value());
+    builder.reset();
+    let offset4 = builder.create_shared_string("welcome");
+    let offset5 = builder.create_shared_string("welcome to flatbuffers!!");
+    assert_ne!(offset2.value(), offset4.value());
+    assert_ne!(offset5.value(), offset1.value());
+    builder.reset();
+
+    // Checks if the shared string function would always work with
+    // an object in between the writes
+    let name = builder.create_shared_string("foo");
+    let enemy = my_game::example::Monster::create(&mut builder, &my_game::example::MonsterArgs {
+        name: Some(name),
+        ..Default::default()
+    });
+    let secondary_name = builder.create_shared_string("foo");
+    assert_eq!(name.value(), secondary_name.value());
+
+    // Builds a new monster object and embeds enemy into it so we can verify
+    // that shared strings are working.
+    let args = my_game::example::MonsterArgs {
+        name: Some(secondary_name),
+        enemy: Some(enemy),
+        testarrayofstring: Some(builder.create_vector(&[name, secondary_name])),
+        ..Default::default()
+    };
+    // Building secondary monster
+    let main_monster = my_game::example::Monster::create(&mut builder, &args);
+    builder.finish(main_monster, None);
+    let monster = my_game::example::root_as_monster(builder.finished_data()).unwrap();
+
+    // Checks if the embedded object (Enemy) name is foo
+    assert_eq!(monster.enemy().unwrap().name(), "foo");
+    let string_vector = monster.testarrayofstring().unwrap();
+    // Check if the vector will have the same string
+    assert_eq!(string_vector.get(0), "foo");
+    assert_eq!(string_vector.get(1), "foo");
+}
+
 }