Skip to content

Commit

Permalink
[Rust] Add the Allocator trait for the builder API (#8106)
Browse files Browse the repository at this point in the history
* Add an Allocator trait for FlatBufferBuilder

* Update rust generated code
  • Loading branch information
adsnaider authored Oct 7, 2023
1 parent f4e23bf commit 205285c
Show file tree
Hide file tree
Showing 56 changed files with 658 additions and 449 deletions.
356 changes: 277 additions & 79 deletions rust/flatbuffers/src/builder.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion rust/flatbuffers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ mod vtable;
mod vtable_writer;

pub use crate::array::{array_init, emplace_scalar_array, Array};
pub use crate::builder::FlatBufferBuilder;
pub use crate::builder::{Allocator, DefaultAllocator, FlatBufferBuilder};
pub use crate::endian_scalar::{emplace_scalar, read_scalar, read_scalar_at, EndianScalar};
pub use crate::follow::{Follow, FollowStart};
pub use crate::primitives::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl EquipmentT {
Self::Weapon(_) => Equipment::Weapon,
}
}
pub fn pack(&self, fbb: &mut flatbuffers::FlatBufferBuilder) -> Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>> {
pub fn pack<'b, A: flatbuffers::Allocator + 'b>(&self, fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>) -> Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>> {
match self {
Self::NONE => None,
Self::Weapon(v) => Some(v.pack(fbb).as_union_value()),
Expand Down
22 changes: 11 additions & 11 deletions samples/rust_generated/my_game/sample/monster_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ impl<'a> Monster<'a> {
Monster { _tab: table }
}
#[allow(unused_mut)]
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>,
args: &'args MonsterArgs<'args>
) -> flatbuffers::WIPOffset<Monster<'bldr>> {
let mut builder = MonsterBuilder::new(_fbb);
Expand Down Expand Up @@ -246,11 +246,11 @@ impl<'a> Default for MonsterArgs<'a> {
}
}

pub struct MonsterBuilder<'a: 'b, 'b> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
pub struct MonsterBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,
start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
}
impl<'a: 'b, 'b> MonsterBuilder<'a, 'b> {
impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> MonsterBuilder<'a, 'b, A> {
#[inline]
pub fn add_pos(&mut self, pos: &Vec3) {
self.fbb_.push_slot_always::<&Vec3>(Monster::VT_POS, pos);
Expand Down Expand Up @@ -292,7 +292,7 @@ impl<'a: 'b, 'b> MonsterBuilder<'a, 'b> {
self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(Monster::VT_PATH, path);
}
#[inline]
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> MonsterBuilder<'a, 'b> {
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> MonsterBuilder<'a, 'b, A> {
let start = _fbb.start_table();
MonsterBuilder {
fbb_: _fbb,
Expand Down Expand Up @@ -363,9 +363,9 @@ impl Default for MonsterT {
}
}
impl MonsterT {
pub fn pack<'b>(
pub fn pack<'b, A: flatbuffers::Allocator + 'b>(
&self,
_fbb: &mut flatbuffers::FlatBufferBuilder<'b>
_fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>
) -> flatbuffers::WIPOffset<Monster<'b>> {
let pos_tmp = self.pos.as_ref().map(|x| x.pack());
let pos = pos_tmp.as_ref();
Expand Down Expand Up @@ -461,13 +461,13 @@ pub unsafe fn size_prefixed_root_as_monster_unchecked(buf: &[u8]) -> Monster {
flatbuffers::size_prefixed_root_unchecked::<Monster>(buf)
}
#[inline]
pub fn finish_monster_buffer<'a, 'b>(
fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,
pub fn finish_monster_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(
fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,
root: flatbuffers::WIPOffset<Monster<'a>>) {
fbb.finish(root, None);
}

#[inline]
pub fn finish_size_prefixed_monster_buffer<'a, 'b>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, root: flatbuffers::WIPOffset<Monster<'a>>) {
pub fn finish_size_prefixed_monster_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, root: flatbuffers::WIPOffset<Monster<'a>>) {
fbb.finish_size_prefixed(root, None);
}
16 changes: 8 additions & 8 deletions samples/rust_generated/my_game/sample/weapon_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ impl<'a> Weapon<'a> {
Weapon { _tab: table }
}
#[allow(unused_mut)]
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>,
args: &'args WeaponArgs<'args>
) -> flatbuffers::WIPOffset<Weapon<'bldr>> {
let mut builder = WeaponBuilder::new(_fbb);
Expand Down Expand Up @@ -101,11 +101,11 @@ impl<'a> Default for WeaponArgs<'a> {
}
}

pub struct WeaponBuilder<'a: 'b, 'b> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
pub struct WeaponBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,
start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
}
impl<'a: 'b, 'b> WeaponBuilder<'a, 'b> {
impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> WeaponBuilder<'a, 'b, A> {
#[inline]
pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) {
self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(Weapon::VT_NAME, name);
Expand All @@ -115,7 +115,7 @@ impl<'a: 'b, 'b> WeaponBuilder<'a, 'b> {
self.fbb_.push_slot::<i16>(Weapon::VT_DAMAGE, damage, 0);
}
#[inline]
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> WeaponBuilder<'a, 'b> {
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> WeaponBuilder<'a, 'b, A> {
let start = _fbb.start_table();
WeaponBuilder {
fbb_: _fbb,
Expand Down Expand Up @@ -152,9 +152,9 @@ impl Default for WeaponT {
}
}
impl WeaponT {
pub fn pack<'b>(
pub fn pack<'b, A: flatbuffers::Allocator + 'b>(
&self,
_fbb: &mut flatbuffers::FlatBufferBuilder<'b>
_fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>
) -> flatbuffers::WIPOffset<Weapon<'b>> {
let name = self.name.as_ref().map(|x|{
_fbb.create_string(x)
Expand Down
39 changes: 25 additions & 14 deletions src/idl_gen_rust.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,8 @@ class RustGenerator : public BaseGenerator {
code_ += " }";
// Pack flatbuffers union value
code_ +=
" pub fn pack(&self, fbb: &mut flatbuffers::FlatBufferBuilder)"
" pub fn pack<'b, A: flatbuffers::Allocator + 'b>(&self, fbb: &mut "
"flatbuffers::FlatBufferBuilder<'b, A>)"
" -> Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>>"
" {";
code_ += " match self {";
Expand Down Expand Up @@ -1717,8 +1718,11 @@ class RustGenerator : public BaseGenerator {
code_.SetValue("MAYBE_LT",
TableBuilderArgsNeedsLifetime(struct_def) ? "<'args>" : "");
code_ += " #[allow(unused_mut)]";
code_ += " pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(";
code_ += " _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,";
code_ +=
" pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: "
"flatbuffers::Allocator + 'bldr>(";
code_ +=
" _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>,";
code_ += " {{MAYBE_US}}args: &'args {{STRUCT_TY}}Args{{MAYBE_LT}}";
code_ += " ) -> flatbuffers::WIPOffset<{{STRUCT_TY}}<'bldr>> {";

Expand Down Expand Up @@ -2117,15 +2121,20 @@ class RustGenerator : public BaseGenerator {
}

// Generate a builder struct:
code_ += "{{ACCESS_TYPE}} struct {{STRUCT_TY}}Builder<'a: 'b, 'b> {";
code_ += " fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
code_ +=
"{{ACCESS_TYPE}} struct {{STRUCT_TY}}Builder<'a: 'b, 'b, A: "
"flatbuffers::Allocator + 'a> {";
code_ += " fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,";
code_ +=
" start_: flatbuffers::WIPOffset<"
"flatbuffers::TableUnfinishedWIPOffset>,";
code_ += "}";

// Generate builder functions:
code_ += "impl<'a: 'b, 'b> {{STRUCT_TY}}Builder<'a, 'b> {";
code_ +=
"impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> "
"{{STRUCT_TY}}Builder<'a, "
"'b, A> {";
ForAllTableFields(struct_def, [&](const FieldDef &field) {
const bool is_scalar = IsScalar(field.value.type.base_type);
std::string offset = namer_.LegacyRustFieldOffsetName(field);
Expand Down Expand Up @@ -2160,8 +2169,8 @@ class RustGenerator : public BaseGenerator {
// Struct initializer (all fields required);
code_ += " #[inline]";
code_ +=
" pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> "
"{{STRUCT_TY}}Builder<'a, 'b> {";
" pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> "
"{{STRUCT_TY}}Builder<'a, 'b, A> {";
code_.SetValue("NUM_FIELDS", NumToString(struct_def.fields.vec.size()));
code_ += " let start = _fbb.start_table();";
code_ += " {{STRUCT_TY}}Builder {";
Expand Down Expand Up @@ -2264,9 +2273,9 @@ class RustGenerator : public BaseGenerator {

// Generate pack function.
code_ += "impl {{STRUCT_OTY}} {";
code_ += " pub fn pack<'b>(";
code_ += " pub fn pack<'b, A: flatbuffers::Allocator + 'b>(";
code_ += " &self,";
code_ += " _fbb: &mut flatbuffers::FlatBufferBuilder<'b>";
code_ += " _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>";
code_ += " ) -> flatbuffers::WIPOffset<{{STRUCT_TY}}<'b>> {";
// First we generate variables for each field and then later assemble them
// using "StructArgs" to more easily manage ownership of the builder.
Expand Down Expand Up @@ -2551,8 +2560,10 @@ class RustGenerator : public BaseGenerator {

// Finish a buffer with a given root object:
code_ += "#[inline]";
code_ += "pub fn finish_{{STRUCT_FN}}_buffer<'a, 'b>(";
code_ += " fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
code_ +=
"pub fn finish_{{STRUCT_FN}}_buffer<'a, 'b, A: "
"flatbuffers::Allocator + 'a>(";
code_ += " fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,";
code_ += " root: flatbuffers::WIPOffset<{{STRUCT_TY}}<'a>>) {";
if (parser_.file_identifier_.length()) {
code_ += " fbb.finish(root, Some({{STRUCT_CONST}}_IDENTIFIER));";
Expand All @@ -2564,8 +2575,8 @@ class RustGenerator : public BaseGenerator {
code_ += "#[inline]";
code_ +=
"pub fn finish_size_prefixed_{{STRUCT_FN}}_buffer"
"<'a, 'b>("
"fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, "
"<'a, 'b, A: flatbuffers::Allocator + 'a>("
"fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, "
"root: flatbuffers::WIPOffset<{{STRUCT_TY}}<'a>>) {";
if (parser_.file_identifier_.length()) {
code_ +=
Expand Down
22 changes: 11 additions & 11 deletions tests/arrays_test/my_game/example/array_table_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ impl<'a> ArrayTable<'a> {
ArrayTable { _tab: table }
}
#[allow(unused_mut)]
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>,
args: &'args ArrayTableArgs<'args>
) -> flatbuffers::WIPOffset<ArrayTable<'bldr>> {
let mut builder = ArrayTableBuilder::new(_fbb);
Expand Down Expand Up @@ -87,17 +87,17 @@ impl<'a> Default for ArrayTableArgs<'a> {
}
}

pub struct ArrayTableBuilder<'a: 'b, 'b> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
pub struct ArrayTableBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,
start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
}
impl<'a: 'b, 'b> ArrayTableBuilder<'a, 'b> {
impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ArrayTableBuilder<'a, 'b, A> {
#[inline]
pub fn add_a(&mut self, a: &ArrayStruct) {
self.fbb_.push_slot_always::<&ArrayStruct>(ArrayTable::VT_A, a);
}
#[inline]
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ArrayTableBuilder<'a, 'b> {
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> ArrayTableBuilder<'a, 'b, A> {
let start = _fbb.start_table();
ArrayTableBuilder {
fbb_: _fbb,
Expand Down Expand Up @@ -131,9 +131,9 @@ impl Default for ArrayTableT {
}
}
impl ArrayTableT {
pub fn pack<'b>(
pub fn pack<'b, A: flatbuffers::Allocator + 'b>(
&self,
_fbb: &mut flatbuffers::FlatBufferBuilder<'b>
_fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>
) -> flatbuffers::WIPOffset<ArrayTable<'b>> {
let a_tmp = self.a.as_ref().map(|x| x.pack());
let a = a_tmp.as_ref();
Expand Down Expand Up @@ -217,13 +217,13 @@ pub fn array_table_size_prefixed_buffer_has_identifier(buf: &[u8]) -> bool {
pub const ARRAY_TABLE_EXTENSION: &str = "mon";

#[inline]
pub fn finish_array_table_buffer<'a, 'b>(
fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,
pub fn finish_array_table_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(
fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,
root: flatbuffers::WIPOffset<ArrayTable<'a>>) {
fbb.finish(root, Some(ARRAY_TABLE_IDENTIFIER));
}

#[inline]
pub fn finish_size_prefixed_array_table_buffer<'a, 'b>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, root: flatbuffers::WIPOffset<ArrayTable<'a>>) {
pub fn finish_size_prefixed_array_table_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, root: flatbuffers::WIPOffset<ArrayTable<'a>>) {
fbb.finish_size_prefixed(root, Some(ARRAY_TABLE_IDENTIFIER));
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ impl<'a> TableB<'a> {
TableB { _tab: table }
}
#[allow(unused_mut)]
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>,
args: &'args TableBArgs<'args>
) -> flatbuffers::WIPOffset<TableB<'bldr>> {
let mut builder = TableBBuilder::new(_fbb);
Expand Down Expand Up @@ -87,17 +87,17 @@ impl<'a> Default for TableBArgs<'a> {
}
}

pub struct TableBBuilder<'a: 'b, 'b> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
pub struct TableBBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,
start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
}
impl<'a: 'b, 'b> TableBBuilder<'a, 'b> {
impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> TableBBuilder<'a, 'b, A> {
#[inline]
pub fn add_a(&mut self, a: flatbuffers::WIPOffset<super::super::TableA<'b >>) {
self.fbb_.push_slot_always::<flatbuffers::WIPOffset<super::super::TableA>>(TableB::VT_A, a);
}
#[inline]
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> TableBBuilder<'a, 'b> {
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> TableBBuilder<'a, 'b, A> {
let start = _fbb.start_table();
TableBBuilder {
fbb_: _fbb,
Expand Down Expand Up @@ -131,9 +131,9 @@ impl Default for TableBT {
}
}
impl TableBT {
pub fn pack<'b>(
pub fn pack<'b, A: flatbuffers::Allocator + 'b>(
&self,
_fbb: &mut flatbuffers::FlatBufferBuilder<'b>
_fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>
) -> flatbuffers::WIPOffset<TableB<'b>> {
let a = self.a.as_ref().map(|x|{
x.pack(_fbb)
Expand Down
16 changes: 8 additions & 8 deletions tests/include_test1/table_a_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ impl<'a> TableA<'a> {
TableA { _tab: table }
}
#[allow(unused_mut)]
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>(
_fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>,
args: &'args TableAArgs<'args>
) -> flatbuffers::WIPOffset<TableA<'bldr>> {
let mut builder = TableABuilder::new(_fbb);
Expand Down Expand Up @@ -87,17 +87,17 @@ impl<'a> Default for TableAArgs<'a> {
}
}

pub struct TableABuilder<'a: 'b, 'b> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
pub struct TableABuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> {
fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>,
start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
}
impl<'a: 'b, 'b> TableABuilder<'a, 'b> {
impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> TableABuilder<'a, 'b, A> {
#[inline]
pub fn add_b(&mut self, b: flatbuffers::WIPOffset<my_game::other_name_space::TableB<'b >>) {
self.fbb_.push_slot_always::<flatbuffers::WIPOffset<my_game::other_name_space::TableB>>(TableA::VT_B, b);
}
#[inline]
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> TableABuilder<'a, 'b> {
pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> TableABuilder<'a, 'b, A> {
let start = _fbb.start_table();
TableABuilder {
fbb_: _fbb,
Expand Down Expand Up @@ -131,9 +131,9 @@ impl Default for TableAT {
}
}
impl TableAT {
pub fn pack<'b>(
pub fn pack<'b, A: flatbuffers::Allocator + 'b>(
&self,
_fbb: &mut flatbuffers::FlatBufferBuilder<'b>
_fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>
) -> flatbuffers::WIPOffset<TableA<'b>> {
let b = self.b.as_ref().map(|x|{
x.pack(_fbb)
Expand Down
Loading

0 comments on commit 205285c

Please sign in to comment.