Skip to content

Commit 733e432

Browse files
Rust full reflection (#8102)
* #Rust Create a crate for reflection * #Rust Add a crate for reflection tests and helper to access schema * #Rust Get root table of a buffer and access field with schema * #Rust Add 'Struct' struct and corresponding getter * #Rust Add functions of getting any table/struct field value as integer/float/string * #Rust Add setters for scalar fields * #Rust Add setter for string fields * #Rust Add getter for Table/Vector fields * #Rust Add buffer verification * Add a 'SafeBuffer' struct which provides safe methods for reflection It verifies buffer against schema during construction and provides all the unsafe getters in lib.rs in a safe way --------- Co-authored-by: Derek Bailey <[email protected]>
1 parent 5414e04 commit 733e432

File tree

13 files changed

+7181
-29
lines changed

13 files changed

+7181
-29
lines changed

rust/flatbuffers/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub use crate::push::{Push, PushAlignment};
5656
pub use crate::table::{buffer_has_identifier, Table};
5757
pub use crate::vector::{follow_cast_ref, Vector, VectorIter};
5858
pub use crate::verifier::{
59-
ErrorTraceDetail, InvalidFlatbuffer, SimpleToVerifyInSlice, Verifiable, Verifier,
59+
ErrorTraceDetail, InvalidFlatbuffer, SimpleToVerifyInSlice, TableVerifier, Verifiable, Verifier,
6060
VerifierOptions,
6161
};
6262
pub use crate::vtable::field_index_to_field_offset;

rust/flatbuffers/src/vector.rs

+32
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
use core::cmp::Ordering;
1718
use core::fmt::{Debug, Formatter, Result};
1819
use core::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator};
1920
use core::marker::PhantomData;
@@ -102,6 +103,37 @@ impl<'a, T: Follow<'a> + 'a> Vector<'a, T> {
102103
unsafe { T::follow(self.0, self.1 as usize + SIZE_UOFFSET + sz * idx) }
103104
}
104105

106+
#[inline(always)]
107+
pub fn lookup_by_key<K: Ord>(
108+
&self,
109+
key: K,
110+
f: fn(&<T as Follow<'a>>::Inner, &K) -> Ordering,
111+
) -> Option<T::Inner> {
112+
if self.is_empty() {
113+
return None;
114+
}
115+
116+
let mut left: usize = 0;
117+
let mut right = self.len() - 1;
118+
119+
while left <= right {
120+
let mid = (left + right) / 2;
121+
let value = self.get(mid);
122+
match f(&value, &key) {
123+
Ordering::Equal => return Some(value),
124+
Ordering::Less => left = mid + 1,
125+
Ordering::Greater => {
126+
if mid == 0 {
127+
return None;
128+
}
129+
right = mid - 1;
130+
},
131+
}
132+
}
133+
134+
None
135+
}
136+
105137
#[inline(always)]
106138
pub fn iter(&self) -> VectorIter<'a, T> {
107139
VectorIter::from_vector(*self)

rust/flatbuffers/src/verifier.rs

+56-28
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ use alloc::vec::Vec;
55
use core::ops::Range;
66
use core::option::Option;
77

8+
#[cfg(not(feature = "std"))]
9+
use alloc::borrow::Cow;
10+
#[cfg(feature = "std")]
11+
use std::borrow::Cow;
12+
813
#[cfg(all(nightly, not(feature = "std")))]
914
use core::error::Error;
1015
#[cfg(feature = "std")]
@@ -20,11 +25,11 @@ pub enum ErrorTraceDetail {
2025
position: usize,
2126
},
2227
TableField {
23-
field_name: &'static str,
28+
field_name: Cow<'static, str>,
2429
position: usize,
2530
},
2631
UnionVariant {
27-
variant: &'static str,
32+
variant: Cow<'static, str>,
2833
position: usize,
2934
},
3035
}
@@ -44,12 +49,12 @@ impl core::convert::AsRef<[ErrorTraceDetail]> for ErrorTrace {
4449
#[derive(Clone, Debug, PartialEq, Eq)]
4550
pub enum InvalidFlatbuffer {
4651
MissingRequiredField {
47-
required: &'static str,
52+
required: Cow<'static, str>,
4853
error_trace: ErrorTrace,
4954
},
5055
InconsistentUnion {
51-
field: &'static str,
52-
field_type: &'static str,
56+
field: Cow<'static, str>,
57+
field_type: Cow<'static, str>,
5358
error_trace: ErrorTrace,
5459
},
5560
Utf8Error {
@@ -63,7 +68,7 @@ pub enum InvalidFlatbuffer {
6368
},
6469
Unaligned {
6570
position: usize,
66-
unaligned_type: &'static str,
71+
unaligned_type: Cow<'static, str>,
6772
error_trace: ErrorTrace,
6873
},
6974
RangeOutOfBounds {
@@ -217,16 +222,19 @@ impl InvalidFlatbuffer {
217222
error_trace: Default::default(),
218223
})
219224
}
220-
fn new_inconsistent_union<T>(field: &'static str, field_type: &'static str) -> Result<T> {
225+
pub fn new_inconsistent_union<T>(
226+
field: impl Into<Cow<'static, str>>,
227+
field_type: impl Into<Cow<'static, str>>,
228+
) -> Result<T> {
221229
Err(Self::InconsistentUnion {
222-
field,
223-
field_type,
230+
field: field.into(),
231+
field_type: field_type.into(),
224232
error_trace: Default::default(),
225233
})
226234
}
227-
fn new_missing_required<T>(required: &'static str) -> Result<T> {
235+
pub fn new_missing_required<T>(required: impl Into<Cow<'static, str>>) -> Result<T> {
228236
Err(Self::MissingRequiredField {
229-
required,
237+
required: required.into(),
230238
error_trace: Default::default(),
231239
})
232240
}
@@ -251,7 +259,7 @@ fn append_trace<T>(mut res: Result<T>, d: ErrorTraceDetail) -> Result<T> {
251259
}
252260

253261
/// Adds a TableField trace detail if `res` is a data error.
254-
fn trace_field<T>(res: Result<T>, field_name: &'static str, position: usize) -> Result<T> {
262+
fn trace_field<T>(res: Result<T>, field_name: Cow<'static, str>, position: usize) -> Result<T> {
255263
append_trace(
256264
res,
257265
ErrorTraceDetail::TableField {
@@ -333,19 +341,19 @@ impl<'opts, 'buf> Verifier<'opts, 'buf> {
333341
///
334342
/// Note this does not impact soundness as this crate does not assume alignment of structs
335343
#[inline]
336-
fn is_aligned<T>(&self, pos: usize) -> Result<()> {
344+
pub fn is_aligned<T>(&self, pos: usize) -> Result<()> {
337345
if pos % core::mem::align_of::<T>() == 0 {
338346
Ok(())
339347
} else {
340348
Err(InvalidFlatbuffer::Unaligned {
341-
unaligned_type: core::any::type_name::<T>(),
349+
unaligned_type: Cow::Borrowed(core::any::type_name::<T>()),
342350
position: pos,
343351
error_trace: Default::default(),
344352
})
345353
}
346354
}
347355
#[inline]
348-
fn range_in_buffer(&mut self, pos: usize, size: usize) -> Result<()> {
356+
pub fn range_in_buffer(&mut self, pos: usize, size: usize) -> Result<()> {
349357
let end = pos.saturating_add(size);
350358
if end > self.buffer.len() {
351359
return InvalidFlatbuffer::new_range_oob(pos, end);
@@ -363,12 +371,17 @@ impl<'opts, 'buf> Verifier<'opts, 'buf> {
363371
self.range_in_buffer(pos, core::mem::size_of::<T>())
364372
}
365373
#[inline]
374+
pub fn get_u8(&mut self, pos: usize) -> Result<u8> {
375+
self.in_buffer::<u8>(pos)?;
376+
Ok(u8::from_le_bytes([self.buffer[pos]]))
377+
}
378+
#[inline]
366379
fn get_u16(&mut self, pos: usize) -> Result<u16> {
367380
self.in_buffer::<u16>(pos)?;
368381
Ok(u16::from_le_bytes([self.buffer[pos], self.buffer[pos + 1]]))
369382
}
370383
#[inline]
371-
fn get_uoffset(&mut self, pos: usize) -> Result<UOffsetT> {
384+
pub fn get_uoffset(&mut self, pos: usize) -> Result<UOffsetT> {
372385
self.in_buffer::<u32>(pos)?;
373386
Ok(u32::from_le_bytes([
374387
self.buffer[pos],
@@ -434,11 +447,17 @@ impl<'opts, 'buf> Verifier<'opts, 'buf> {
434447
/// tracing the error.
435448
pub fn verify_union_variant<T: Verifiable>(
436449
&mut self,
437-
variant: &'static str,
450+
variant: impl Into<Cow<'static, str>>,
438451
position: usize,
439452
) -> Result<()> {
440453
let res = T::run_verifier(self, position);
441-
append_trace(res, ErrorTraceDetail::UnionVariant { variant, position })
454+
append_trace(
455+
res,
456+
ErrorTraceDetail::UnionVariant {
457+
variant: variant.into(),
458+
position,
459+
},
460+
)
442461
}
443462
}
444463

@@ -456,7 +475,7 @@ pub struct TableVerifier<'ver, 'opts, 'buf> {
456475
}
457476

458477
impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
459-
fn deref(&mut self, field: VOffsetT) -> Result<Option<usize>> {
478+
pub fn deref(&mut self, field: VOffsetT) -> Result<Option<usize>> {
460479
let field = field as usize;
461480
if field < self.vtable_len {
462481
let field_offset = self.verifier.get_u16(self.vtable.saturating_add(field))?;
@@ -469,23 +488,28 @@ impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
469488
Ok(None)
470489
}
471490

491+
#[inline]
492+
pub fn verifier(&mut self) -> &mut Verifier<'opts, 'buf> {
493+
self.verifier
494+
}
495+
472496
#[inline]
473497
pub fn visit_field<T: Verifiable>(
474498
mut self,
475-
field_name: &'static str,
499+
field_name: impl Into<Cow<'static, str>>,
476500
field: VOffsetT,
477501
required: bool,
478502
) -> Result<Self> {
479503
if let Some(field_pos) = self.deref(field)? {
480504
trace_field(
481505
T::run_verifier(self.verifier, field_pos),
482-
field_name,
506+
field_name.into(),
483507
field_pos,
484508
)?;
485509
return Ok(self);
486510
}
487511
if required {
488-
InvalidFlatbuffer::new_missing_required(field_name)
512+
InvalidFlatbuffer::new_missing_required(field_name.into())
489513
} else {
490514
Ok(self)
491515
}
@@ -496,9 +520,9 @@ impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
496520
/// reads the key, then invokes the callback to perform data-dependent verification.
497521
pub fn visit_union<Key, UnionVerifier>(
498522
mut self,
499-
key_field_name: &'static str,
523+
key_field_name: impl Into<Cow<'static, str>>,
500524
key_field_voff: VOffsetT,
501-
val_field_name: &'static str,
525+
val_field_name: impl Into<Cow<'static, str>>,
502526
val_field_voff: VOffsetT,
503527
required: bool,
504528
verify_union: UnionVerifier,
@@ -515,24 +539,28 @@ impl<'ver, 'opts, 'buf> TableVerifier<'ver, 'opts, 'buf> {
515539
match (key_pos, val_pos) {
516540
(None, None) => {
517541
if required {
518-
InvalidFlatbuffer::new_missing_required(val_field_name)
542+
InvalidFlatbuffer::new_missing_required(val_field_name.into())
519543
} else {
520544
Ok(self)
521545
}
522546
}
523547
(Some(k), Some(v)) => {
524-
trace_field(Key::run_verifier(self.verifier, k), key_field_name, k)?;
548+
trace_field(
549+
Key::run_verifier(self.verifier, k),
550+
key_field_name.into(),
551+
k,
552+
)?;
525553
// Safety:
526554
// Run verifier on `k` above
527555
let discriminant = unsafe { Key::follow(self.verifier.buffer, k) };
528556
trace_field(
529557
verify_union(discriminant, self.verifier, v),
530-
val_field_name,
558+
val_field_name.into(),
531559
v,
532560
)?;
533561
Ok(self)
534562
}
535-
_ => InvalidFlatbuffer::new_inconsistent_union(key_field_name, val_field_name),
563+
_ => InvalidFlatbuffer::new_inconsistent_union(key_field_name.into(), val_field_name.into()),
536564
}
537565
}
538566
pub fn finish(self) -> &'ver mut Verifier<'opts, 'buf> {

rust/reflection/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
Cargo.lock

rust/reflection/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "flatbuffers-reflection"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
flatbuffers = { path = "../flatbuffers"}
8+
escape_string = "0.1.2"
9+
stdint = "0.2.0"
10+
num = "0.4.1"
11+
anyhow = "1.0.75"
12+
thiserror = "1.0"

0 commit comments

Comments
 (0)