mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-02 04:04:19 +00:00
[Rust] Shared String (#6367)
* 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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user