flatbuffers_reflection/
lib.rs

1/*
2 * Copyright 2018 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17mod reflection_generated;
18mod reflection_verifier;
19mod safe_buffer;
20mod r#struct;
21pub use crate::r#struct::Struct;
22pub use crate::reflection_generated::reflection;
23pub use crate::safe_buffer::SafeBuffer;
24
25use flatbuffers::{
26    emplace_scalar, read_scalar, EndianScalar, Follow, ForwardsUOffset, InvalidFlatbuffer,
27    SOffsetT, Table, UOffsetT, VOffsetT, Vector, SIZE_SOFFSET, SIZE_UOFFSET,
28};
29use reflection_generated::reflection::{BaseType, Field, Object, Schema};
30
31use core::mem::size_of;
32use num_traits::float::Float;
33use num_traits::int::PrimInt;
34use num_traits::FromPrimitive;
35use thiserror::Error;
36
37#[derive(Error, Debug, PartialEq)]
38pub enum FlatbufferError {
39    #[error(transparent)]
40    VerificationError(#[from] flatbuffers::InvalidFlatbuffer),
41    #[error("Failed to convert between data type {0} and field type {1}")]
42    FieldTypeMismatch(String, String),
43    #[error("Set field value not supported for non-populated or non-scalar fields")]
44    SetValueNotSupported,
45    #[error(transparent)]
46    ParseFloatError(#[from] std::num::ParseFloatError),
47    #[error(transparent)]
48    TryFromIntError(#[from] std::num::TryFromIntError),
49    #[error("Couldn't set string because cache vector is polluted")]
50    SetStringPolluted,
51    #[error("Invalid schema: Polluted buffer or the schema doesn't match the buffer.")]
52    InvalidSchema,
53    #[error("Type not supported: {0}")]
54    TypeNotSupported(String),
55    #[error("No type or invalid type found in union enum")]
56    InvalidUnionEnum,
57    #[error("Table or Struct doesn't belong to the buffer")]
58    InvalidTableOrStruct,
59    #[error("Field not found in the table schema")]
60    FieldNotFound,
61}
62
63pub type FlatbufferResult<T, E = FlatbufferError> = core::result::Result<T, E>;
64
65/// Gets the root table from a trusted Flatbuffer.
66///
67/// # Safety
68///
69/// Flatbuffers accessors do not perform validation checks before accessing. Users
70/// must trust [data] contains a valid flatbuffer. Reading unchecked buffers may cause panics or even UB.
71pub unsafe fn get_any_root(data: &[u8]) -> Table {
72    <ForwardsUOffset<Table>>::follow(data, 0)
73}
74
75/// Gets an integer table field given its exact type. Returns default integer value if the field is not set. Returns [None] if no default value is found. Returns error if the type size doesn't match.
76///
77/// # Safety
78///
79/// The value of the corresponding slot must have type T
80pub unsafe fn get_field_integer<T: for<'a> Follow<'a, Inner = T> + PrimInt + FromPrimitive>(
81    table: &Table,
82    field: &Field,
83) -> FlatbufferResult<Option<T>> {
84    if size_of::<T>() != get_type_size(field.type_().base_type()) {
85        return Err(FlatbufferError::FieldTypeMismatch(
86            std::any::type_name::<T>().to_string(),
87            field
88                .type_()
89                .base_type()
90                .variant_name()
91                .unwrap_or_default()
92                .to_string(),
93        ));
94    }
95
96    let default = T::from_i64(field.default_integer());
97    Ok(table.get::<T>(field.offset(), default))
98}
99
100/// Gets a floating point table field given its exact type. Returns default float value if the field is not set. Returns [None] if no default value is found. Returns error if the type doesn't match.
101///
102/// # Safety
103///
104/// The value of the corresponding slot must have type T
105pub unsafe fn get_field_float<T: for<'a> Follow<'a, Inner = T> + Float>(
106    table: &Table,
107    field: &Field,
108) -> FlatbufferResult<Option<T>> {
109    if size_of::<T>() != get_type_size(field.type_().base_type()) {
110        return Err(FlatbufferError::FieldTypeMismatch(
111            std::any::type_name::<T>().to_string(),
112            field
113                .type_()
114                .base_type()
115                .variant_name()
116                .unwrap_or_default()
117                .to_string(),
118        ));
119    }
120
121    let default = T::from(field.default_real());
122    Ok(table.get::<T>(field.offset(), default))
123}
124
125/// Gets a String table field given its exact type. Returns empty string if the field is not set. Returns [None] if no default value is found. Returns error if the type size doesn't match.
126///
127/// # Safety
128///
129/// The value of the corresponding slot must have type String
130pub unsafe fn get_field_string<'a>(
131    table: &Table<'a>,
132    field: &Field,
133) -> FlatbufferResult<Option<&'a str>> {
134    if field.type_().base_type() != BaseType::String {
135        return Err(FlatbufferError::FieldTypeMismatch(
136            String::from("String"),
137            field
138                .type_()
139                .base_type()
140                .variant_name()
141                .unwrap_or_default()
142                .to_string(),
143        ));
144    }
145
146    Ok(table.get::<ForwardsUOffset<&'a str>>(field.offset(), Some("")))
147}
148
149/// Gets a [Struct] table field given its exact type. Returns [None] if the field is not set. Returns error if the type doesn't match.
150///
151/// # Safety
152///
153/// The value of the corresponding slot must have type Struct
154pub unsafe fn get_field_struct<'a>(
155    table: &Table<'a>,
156    field: &Field,
157) -> FlatbufferResult<Option<Struct<'a>>> {
158    // TODO inherited from C++: This does NOT check if the field is a table or struct, but we'd need
159    // access to the schema to check the is_struct flag.
160    if field.type_().base_type() != BaseType::Obj {
161        return Err(FlatbufferError::FieldTypeMismatch(
162            String::from("Obj"),
163            field
164                .type_()
165                .base_type()
166                .variant_name()
167                .unwrap_or_default()
168                .to_string(),
169        ));
170    }
171
172    Ok(table.get::<Struct>(field.offset(), None))
173}
174
175/// Gets a Vector table field given its exact type. Returns empty vector if the field is not set. Returns error if the type doesn't match.
176///
177/// # Safety
178///
179/// The value of the corresponding slot must have type Vector
180pub unsafe fn get_field_vector<'a, T: Follow<'a, Inner = T>>(
181    table: &Table<'a>,
182    field: &Field,
183) -> FlatbufferResult<Option<Vector<'a, T>>> {
184    if field.type_().base_type() != BaseType::Vector {
185        return Err(FlatbufferError::FieldTypeMismatch(
186            std::any::type_name::<T>().to_string(),
187            field
188                .type_()
189                .base_type()
190                .variant_name()
191                .unwrap_or_default()
192                .to_string(),
193        ));
194    }
195
196    Ok(table.get::<ForwardsUOffset<Vector<'a, T>>>(field.offset(), Some(Vector::<T>::default())))
197}
198
199/// Gets a Table table field given its exact type. Returns [None] if the field is not set. Returns error if the type doesn't match.
200///
201/// # Safety
202///
203/// The value of the corresponding slot must have type Table
204pub unsafe fn get_field_table<'a>(
205    table: &Table<'a>,
206    field: &Field,
207) -> FlatbufferResult<Option<Table<'a>>> {
208    if field.type_().base_type() != BaseType::Obj {
209        return Err(FlatbufferError::FieldTypeMismatch(
210            String::from("Obj"),
211            field
212                .type_()
213                .base_type()
214                .variant_name()
215                .unwrap_or_default()
216                .to_string(),
217        ));
218    }
219
220    Ok(table.get::<ForwardsUOffset<Table<'a>>>(field.offset(), None))
221}
222
223/// Returns the value of any table field as a 64-bit int, regardless of what type it is. Returns default integer if the field is not set or error if the value cannot be parsed as integer.
224/// [num_traits](https://docs.rs/num-traits/latest/num_traits/cast/trait.NumCast.html) is used for number casting.
225///
226/// # Safety
227///
228/// [table] must contain recursively valid offsets that match the [field].
229pub unsafe fn get_any_field_integer(table: &Table, field: &Field) -> FlatbufferResult<i64> {
230    if let Some(field_loc) = get_field_loc(table, field) {
231        get_any_value_integer(field.type_().base_type(), table.buf(), field_loc)
232    } else {
233        Ok(field.default_integer())
234    }
235}
236
237/// Returns the value of any table field as a 64-bit floating point, regardless of what type it is. Returns default float if the field is not set or error if the value cannot be parsed as float.
238///
239/// # Safety
240///
241/// [table] must contain recursively valid offsets that match the [field].
242pub unsafe fn get_any_field_float(table: &Table, field: &Field) -> FlatbufferResult<f64> {
243    if let Some(field_loc) = get_field_loc(table, field) {
244        get_any_value_float(field.type_().base_type(), table.buf(), field_loc)
245    } else {
246        Ok(field.default_real())
247    }
248}
249
250/// Returns the value of any table field as a string, regardless of what type it is. Returns empty string if the field is not set.
251///
252/// # Safety
253///
254/// [table] must contain recursively valid offsets that match the [field].
255pub unsafe fn get_any_field_string(table: &Table, field: &Field, schema: &Schema) -> String {
256    if let Some(field_loc) = get_field_loc(table, field) {
257        get_any_value_string(
258            field.type_().base_type(),
259            table.buf(),
260            field_loc,
261            schema,
262            field.type_().index() as usize,
263        )
264    } else {
265        String::from("")
266    }
267}
268
269/// Gets a [Struct] struct field given its exact type. Returns error if the type doesn't match.
270///
271/// # Safety
272///
273/// The value of the corresponding slot must have type Struct.
274pub unsafe fn get_field_struct_in_struct<'a>(
275    st: &Struct<'a>,
276    field: &Field,
277) -> FlatbufferResult<Struct<'a>> {
278    // TODO inherited from C++: This does NOT check if the field is a table or struct, but we'd need
279    // access to the schema to check the is_struct flag.
280    if field.type_().base_type() != BaseType::Obj {
281        return Err(FlatbufferError::FieldTypeMismatch(
282            String::from("Obj"),
283            field
284                .type_()
285                .base_type()
286                .variant_name()
287                .unwrap_or_default()
288                .to_string(),
289        ));
290    }
291
292    Ok(st.get::<Struct>(field.offset() as usize))
293}
294
295/// Returns the value of any struct field as a 64-bit int, regardless of what type it is. Returns error if the value cannot be parsed as integer.
296///
297/// # Safety
298///
299/// [st] must contain valid offsets that match the [field].
300pub unsafe fn get_any_field_integer_in_struct(st: &Struct, field: &Field) -> FlatbufferResult<i64> {
301    let field_loc = st.loc() + field.offset() as usize;
302
303    get_any_value_integer(field.type_().base_type(), st.buf(), field_loc)
304}
305
306/// Returns the value of any struct field as a 64-bit floating point, regardless of what type it is. Returns error if the value cannot be parsed as float.
307///
308/// # Safety
309///
310/// [st] must contain valid offsets that match the [field].
311pub unsafe fn get_any_field_float_in_struct(st: &Struct, field: &Field) -> FlatbufferResult<f64> {
312    let field_loc = st.loc() + field.offset() as usize;
313
314    get_any_value_float(field.type_().base_type(), st.buf(), field_loc)
315}
316
317/// Returns the value of any struct field as a string, regardless of what type it is.
318///
319/// # Safety
320///
321/// [st] must contain valid offsets that match the [field].
322pub unsafe fn get_any_field_string_in_struct(
323    st: &Struct,
324    field: &Field,
325    schema: &Schema,
326) -> String {
327    let field_loc = st.loc() + field.offset() as usize;
328
329    get_any_value_string(
330        field.type_().base_type(),
331        st.buf(),
332        field_loc,
333        schema,
334        field.type_().index() as usize,
335    )
336}
337
338/// Sets any table field with the value of a 64-bit integer. Returns error if the field is not originally set or is with non-scalar value or the provided value cannot be cast into the field type.
339///
340/// # Safety
341///
342/// [buf] must contain a valid root table and valid offset to it.
343pub unsafe fn set_any_field_integer(
344    buf: &mut [u8],
345    table_loc: usize,
346    field: &Field,
347    v: i64,
348) -> FlatbufferResult<()> {
349    let field_type = field.type_().base_type();
350    let table = Table::follow(buf, table_loc);
351
352    let Some(field_loc) = get_field_loc(&table, field) else {
353        return Err(FlatbufferError::SetValueNotSupported);
354    };
355
356    if !is_scalar(field_type) {
357        return Err(FlatbufferError::SetValueNotSupported);
358    }
359
360    set_any_value_integer(field_type, buf, field_loc, v)
361}
362
363/// Sets any table field with the value of a 64-bit floating point. Returns error if the field is not originally set or is with non-scalar value or the provided value cannot be cast into the field type.
364///
365/// # Safety
366///
367/// [buf] must contain a valid root table and valid offset to it.
368pub unsafe fn set_any_field_float(
369    buf: &mut [u8],
370    table_loc: usize,
371    field: &Field,
372    v: f64,
373) -> FlatbufferResult<()> {
374    let field_type = field.type_().base_type();
375    let table = Table::follow(buf, table_loc);
376
377    let Some(field_loc) = get_field_loc(&table, field) else {
378        return Err(FlatbufferError::SetValueNotSupported);
379    };
380
381    if !is_scalar(field_type) {
382        return Err(FlatbufferError::SetValueNotSupported);
383    }
384
385    set_any_value_float(field_type, buf, field_loc, v)
386}
387
388/// Sets any table field with the value of a string. Returns error if the field is not originally set or is with non-scalar value or the provided value cannot be parsed as the field type.
389///
390/// # Safety
391///
392/// [buf] must contain a valid root table and valid offset to it.
393pub unsafe fn set_any_field_string(
394    buf: &mut [u8],
395    table_loc: usize,
396    field: &Field,
397    v: &str,
398) -> FlatbufferResult<()> {
399    let field_type = field.type_().base_type();
400    let table = Table::follow(buf, table_loc);
401
402    let Some(field_loc) = get_field_loc(&table, field) else {
403        return Err(FlatbufferError::SetValueNotSupported);
404    };
405
406    if !is_scalar(field_type) {
407        return Err(FlatbufferError::SetValueNotSupported);
408    }
409
410    set_any_value_float(field_type, buf, field_loc, v.parse::<f64>()?)
411}
412
413/// Sets any scalar field given its exact type. Returns error if the field is not originally set or is with non-scalar value.
414///
415/// # Safety
416///
417/// [buf] must contain a valid root table and valid offset to it.
418pub unsafe fn set_field<T: EndianScalar>(
419    buf: &mut [u8],
420    table_loc: usize,
421    field: &Field,
422    v: T,
423) -> FlatbufferResult<()> {
424    let field_type = field.type_().base_type();
425    let table = Table::follow(buf, table_loc);
426
427    if !is_scalar(field_type) {
428        return Err(FlatbufferError::SetValueNotSupported);
429    }
430
431    if core::mem::size_of::<T>() != get_type_size(field_type) {
432        return Err(FlatbufferError::FieldTypeMismatch(
433            std::any::type_name::<T>().to_string(),
434            field_type.variant_name().unwrap_or_default().to_string(),
435        ));
436    }
437
438    let Some(field_loc) = get_field_loc(&table, field) else {
439        return Err(FlatbufferError::SetValueNotSupported);
440    };
441
442    if buf.len() < field_loc.saturating_add(get_type_size(field_type)) {
443        return Err(FlatbufferError::VerificationError(
444            InvalidFlatbuffer::RangeOutOfBounds {
445                range: core::ops::Range {
446                    start: field_loc,
447                    end: field_loc.saturating_add(get_type_size(field_type)),
448                },
449                error_trace: Default::default(),
450            },
451        ));
452    }
453
454    // SAFETY: the buffer range was verified above.
455    unsafe { Ok(emplace_scalar::<T>(&mut buf[field_loc..], v)) }
456}
457
458/// Sets a string field to a new value. Returns error if the field is not originally set or is not of string type in which cases the [buf] stays intact. Returns error if the [buf] fails to be updated.
459///
460/// # Safety
461///
462/// [buf] must contain a valid root table and valid offset to it and conform to the [schema].
463pub unsafe fn set_string(
464    buf: &mut Vec<u8>,
465    table_loc: usize,
466    field: &Field,
467    v: &str,
468    schema: &Schema,
469) -> FlatbufferResult<()> {
470    if v.is_empty() {
471        return Ok(());
472    }
473
474    let field_type = field.type_().base_type();
475    if field_type != BaseType::String {
476        return Err(FlatbufferError::FieldTypeMismatch(
477            String::from("String"),
478            field_type.variant_name().unwrap_or_default().to_string(),
479        ));
480    }
481
482    let table = Table::follow(buf, table_loc);
483
484    let Some(field_loc) = get_field_loc(&table, field) else {
485        return Err(FlatbufferError::SetValueNotSupported);
486    };
487
488    if buf.len() < field_loc + get_type_size(field_type) {
489        return Err(FlatbufferError::VerificationError(
490            InvalidFlatbuffer::RangeOutOfBounds {
491                range: core::ops::Range {
492                    start: field_loc,
493                    end: field_loc.saturating_add(get_type_size(field_type)),
494                },
495                error_trace: Default::default(),
496            },
497        ));
498    }
499
500    // SAFETY: the buffer range was verified above.
501    let string_loc = unsafe { deref_uoffset(buf, field_loc)? };
502    if buf.len() < string_loc.saturating_add(SIZE_UOFFSET) {
503        return Err(FlatbufferError::VerificationError(
504            InvalidFlatbuffer::RangeOutOfBounds {
505                range: core::ops::Range {
506                    start: string_loc,
507                    end: string_loc.saturating_add(SIZE_UOFFSET),
508                },
509                error_trace: Default::default(),
510            },
511        ));
512    }
513
514    // SAFETY: the buffer range was verified above.
515    let len_old = unsafe { read_uoffset(buf, string_loc) };
516    if buf.len()
517        < string_loc
518            .saturating_add(SIZE_UOFFSET)
519            .saturating_add(len_old.try_into()?)
520    {
521        return Err(FlatbufferError::VerificationError(
522            InvalidFlatbuffer::RangeOutOfBounds {
523                range: core::ops::Range {
524                    start: string_loc,
525                    end: string_loc
526                        .saturating_add(SIZE_UOFFSET)
527                        .saturating_add(len_old.try_into()?),
528                },
529                error_trace: Default::default(),
530            },
531        ));
532    }
533
534    let len_new = v.len();
535    let delta = len_new as isize - len_old as isize;
536    let mut bytes_to_insert = v.as_bytes().to_vec();
537
538    if delta != 0 {
539        // Rounds the delta up to the nearest multiple of the maximum int size to keep the types after the insersion point aligned.
540        // stdint crate defines intmax_t as an alias for c_long; use it directly to avoid extra
541        // dependency.
542        let mask = (size_of::<core::ffi::c_long>() - 1) as isize;
543        let offset = (delta + mask) & !mask;
544        let mut visited_vec = vec![false; buf.len()];
545
546        if offset != 0 {
547            update_offset(
548                buf,
549                table_loc,
550                &mut visited_vec,
551                &schema.root_table().unwrap(),
552                schema,
553                string_loc,
554                offset,
555            )?;
556
557            // Sets the new length.
558            emplace_scalar::<SOffsetT>(
559                &mut buf[string_loc..string_loc + SIZE_UOFFSET],
560                len_new.try_into()?,
561            );
562        }
563
564        // Pads the bytes vector with 0 if `offset` doesn't equal `delta`.
565        bytes_to_insert.resize(bytes_to_insert.len() + (offset - delta) as usize, 0);
566    }
567
568    // Replaces the data.
569    buf.splice(
570        string_loc + SIZE_SOFFSET..string_loc + SIZE_UOFFSET + usize::try_from(len_old)?,
571        bytes_to_insert,
572    );
573    Ok(())
574}
575
576/// Returns the size of a scalar type in the `BaseType` enum. In the case of structs, returns the size of their offset (`UOffsetT`) in the buffer.
577fn get_type_size(base_type: BaseType) -> usize {
578    match base_type {
579        BaseType::UType | BaseType::Bool | BaseType::Byte | BaseType::UByte => 1,
580        BaseType::Short | BaseType::UShort => 2,
581        BaseType::Int
582        | BaseType::UInt
583        | BaseType::Float
584        | BaseType::String
585        | BaseType::Vector
586        | BaseType::Obj
587        | BaseType::Union => 4,
588        BaseType::Long | BaseType::ULong | BaseType::Double | BaseType::Vector64 => 8,
589        _ => 0,
590    }
591}
592
593/// Returns the absolute field location in the buffer and [None] if the field is not populated.
594///
595/// # Safety
596///
597/// [table] must contain a valid vtable.
598unsafe fn get_field_loc(table: &Table, field: &Field) -> Option<usize> {
599    let field_offset = table.vtable().get(field.offset()) as usize;
600    if field_offset == 0 {
601        return None;
602    }
603
604    Some(table.loc() + field_offset)
605}
606
607/// Reads value as a 64-bit int from the provided byte slice at the specified location. Returns error if the value cannot be parsed as integer.
608///
609/// # Safety
610///
611/// Caller must ensure `buf.len() >= loc + size_of::<T>()` at all the access layers.
612unsafe fn get_any_value_integer(
613    base_type: BaseType,
614    buf: &[u8],
615    loc: usize,
616) -> FlatbufferResult<i64> {
617    match base_type {
618        BaseType::UType | BaseType::UByte => i64::from_u8(u8::follow(buf, loc)),
619        BaseType::Bool => bool::follow(buf, loc).try_into().ok(),
620        BaseType::Byte => i64::from_i8(i8::follow(buf, loc)),
621        BaseType::Short => i64::from_i16(i16::follow(buf, loc)),
622        BaseType::UShort => i64::from_u16(u16::follow(buf, loc)),
623        BaseType::Int => i64::from_i32(i32::follow(buf, loc)),
624        BaseType::UInt => i64::from_u32(u32::follow(buf, loc)),
625        BaseType::Long => Some(i64::follow(buf, loc)),
626        BaseType::ULong => i64::from_u64(u64::follow(buf, loc)),
627        BaseType::Float => i64::from_f32(f32::follow(buf, loc)),
628        BaseType::Double => i64::from_f64(f64::follow(buf, loc)),
629        BaseType::String => ForwardsUOffset::<&str>::follow(buf, loc)
630            .parse::<i64>()
631            .ok(),
632        _ => None, // Tables & vectors do not make sense.
633    }
634    .ok_or(FlatbufferError::FieldTypeMismatch(
635        String::from("i64"),
636        base_type.variant_name().unwrap_or_default().to_string(),
637    ))
638}
639
640/// Reads value as a 64-bit floating point from the provided byte slice at the specified location. Returns error if the value cannot be parsed as float.
641///
642/// # Safety
643///
644/// Caller must ensure `buf.len() >= loc + size_of::<T>()` at all the access layers.
645unsafe fn get_any_value_float(
646    base_type: BaseType,
647    buf: &[u8],
648    loc: usize,
649) -> FlatbufferResult<f64> {
650    match base_type {
651        BaseType::UType | BaseType::UByte => f64::from_u8(u8::follow(buf, loc)),
652        BaseType::Bool => bool::follow(buf, loc).try_into().ok(),
653        BaseType::Byte => f64::from_i8(i8::follow(buf, loc)),
654        BaseType::Short => f64::from_i16(i16::follow(buf, loc)),
655        BaseType::UShort => f64::from_u16(u16::follow(buf, loc)),
656        BaseType::Int => f64::from_i32(i32::follow(buf, loc)),
657        BaseType::UInt => f64::from_u32(u32::follow(buf, loc)),
658        BaseType::Long => f64::from_i64(i64::follow(buf, loc)),
659        BaseType::ULong => f64::from_u64(u64::follow(buf, loc)),
660        BaseType::Float => f64::from_f32(f32::follow(buf, loc)),
661        BaseType::Double => Some(f64::follow(buf, loc)),
662        BaseType::String => ForwardsUOffset::<&str>::follow(buf, loc)
663            .parse::<f64>()
664            .ok(),
665        _ => None,
666    }
667    .ok_or(FlatbufferError::FieldTypeMismatch(
668        String::from("f64"),
669        base_type.variant_name().unwrap_or_default().to_string(),
670    ))
671}
672
673/// Reads value as a string from the provided byte slice at the specified location.
674///
675/// # Safety
676///
677/// Caller must ensure `buf.len() >= loc + size_of::<T>()` at all the access layers.
678unsafe fn get_any_value_string(
679    base_type: BaseType,
680    buf: &[u8],
681    loc: usize,
682    schema: &Schema,
683    type_index: usize,
684) -> String {
685    match base_type {
686        BaseType::Float | BaseType::Double => get_any_value_float(base_type, buf, loc)
687            .unwrap_or_default()
688            .to_string(),
689        BaseType::String => {
690            String::from_utf8_lossy(ForwardsUOffset::<&[u8]>::follow(buf, loc)).to_string()
691        }
692        BaseType::Obj => {
693            // Converts the table to a string. This is mostly for debugging purposes,
694            // and does NOT promise to be JSON compliant.
695            // Also prefixes the type.
696            let object: Object = schema.objects().get(type_index);
697            let mut s = object.name().to_string();
698            s += " { ";
699            if object.is_struct() {
700                let st: Struct<'_> = Struct::follow(buf, loc);
701                for field in object.fields() {
702                    let field_value = get_any_field_string_in_struct(&st, &field, schema);
703                    s += field.name();
704                    s += ": ";
705                    s += field_value.as_str();
706                    s += ", ";
707                }
708            } else {
709                let table = ForwardsUOffset::<Table>::follow(buf, loc);
710                for field in object.fields() {
711                    if table.vtable().get(field.offset()) == 0 {
712                        continue;
713                    }
714                    let mut field_value = get_any_field_string(&table, &field, schema);
715                    if field.type_().base_type() == BaseType::String {
716                        // Escape the string
717                        field_value = format!("{:?}", field_value.as_str());
718                    }
719                    s += field.name();
720                    s += ": ";
721                    s += field_value.as_str();
722                    s += ", ";
723                }
724            }
725            s + "}"
726        }
727        BaseType::Vector => String::from("[(elements)]"), // TODO inherited from C++: implement this as well.
728        BaseType::Union => String::from("(union)"), // TODO inherited from C++: implement this as well.
729        _ => get_any_value_integer(base_type, buf, loc)
730            .unwrap_or_default()
731            .to_string(),
732    }
733}
734
735/// Sets any scalar value with a 64-bit integer. Returns error if the value is not successfully replaced.
736fn set_any_value_integer(
737    base_type: BaseType,
738    buf: &mut [u8],
739    field_loc: usize,
740    v: i64,
741) -> FlatbufferResult<()> {
742    if buf.len() < get_type_size(base_type) {
743        return Err(FlatbufferError::VerificationError(
744            InvalidFlatbuffer::RangeOutOfBounds {
745                range: core::ops::Range {
746                    start: field_loc,
747                    end: field_loc.saturating_add(get_type_size(base_type)),
748                },
749                error_trace: Default::default(),
750            },
751        ));
752    }
753    let buf = &mut buf[field_loc..];
754    let type_name = base_type.variant_name().unwrap_or_default().to_string();
755
756    macro_rules! try_emplace {
757        ($ty:ty, $value:expr) => {
758            if let Ok(v) = TryInto::<$ty>::try_into($value) {
759                // SAFETY: buffer size is verified at the beginning of this function.
760                unsafe { Ok(emplace_scalar::<$ty>(buf, v)) }
761            } else {
762                Err(FlatbufferError::FieldTypeMismatch(
763                    String::from("i64"),
764                    type_name,
765                ))
766            }
767        };
768    }
769
770    match base_type {
771        BaseType::UType | BaseType::UByte => {
772            try_emplace!(u8, v)
773        }
774        BaseType::Bool => {
775            // SAFETY: buffer size is verified at the beginning of this function.
776            unsafe { Ok(emplace_scalar::<bool>(buf, v != 0)) }
777        }
778        BaseType::Byte => {
779            try_emplace!(i8, v)
780        }
781        BaseType::Short => {
782            try_emplace!(i16, v)
783        }
784        BaseType::UShort => {
785            try_emplace!(u16, v)
786        }
787        BaseType::Int => {
788            try_emplace!(i32, v)
789        }
790        BaseType::UInt => {
791            try_emplace!(u32, v)
792        }
793        BaseType::Long => {
794            // SAFETY: buffer size is verified at the beginning of this function.
795            unsafe { Ok(emplace_scalar::<i64>(buf, v)) }
796        }
797        BaseType::ULong => {
798            try_emplace!(u64, v)
799        }
800        BaseType::Float => {
801            if let Some(value) = f32::from_i64(v) {
802                // SAFETY: buffer size is verified at the beginning of this function.
803                unsafe { Ok(emplace_scalar::<f32>(buf, value)) }
804            } else {
805                Err(FlatbufferError::FieldTypeMismatch(
806                    String::from("i64"),
807                    type_name,
808                ))
809            }
810        }
811        BaseType::Double => {
812            if let Some(value) = f64::from_i64(v) {
813                // SAFETY: buffer size is verified at the beginning of this function.
814                unsafe { Ok(emplace_scalar::<f64>(buf, value)) }
815            } else {
816                Err(FlatbufferError::FieldTypeMismatch(
817                    String::from("i64"),
818                    type_name,
819                ))
820            }
821        }
822        _ => Err(FlatbufferError::SetValueNotSupported),
823    }
824}
825
826/// Sets any scalar value with a 64-bit floating point. Returns error if the value is not successfully replaced.
827fn set_any_value_float(
828    base_type: BaseType,
829    buf: &mut [u8],
830    field_loc: usize,
831    v: f64,
832) -> FlatbufferResult<()> {
833    if buf.len() < get_type_size(base_type) {
834        return Err(FlatbufferError::VerificationError(
835            InvalidFlatbuffer::RangeOutOfBounds {
836                range: core::ops::Range {
837                    start: field_loc,
838                    end: field_loc.saturating_add(get_type_size(base_type)),
839                },
840                error_trace: Default::default(),
841            },
842        ));
843    }
844    let buf = &mut buf[field_loc..];
845    let type_name = base_type.variant_name().unwrap_or_default().to_string();
846
847    match base_type {
848        BaseType::UType | BaseType::UByte => {
849            if let Some(value) = u8::from_f64(v) {
850                // SAFETY: buffer size is verified at the beginning of this function.
851                unsafe {
852                    return Ok(emplace_scalar::<u8>(buf, value));
853                }
854            }
855        }
856        BaseType::Bool => {
857            // SAFETY: buffer size is verified at the beginning of this function.
858            unsafe {
859                return Ok(emplace_scalar::<bool>(buf, v != 0f64));
860            }
861        }
862        BaseType::Byte => {
863            if let Some(value) = i8::from_f64(v) {
864                // SAFETY: buffer size is verified at the beginning of this function.
865                unsafe {
866                    return Ok(emplace_scalar::<i8>(buf, value));
867                }
868            }
869        }
870        BaseType::Short => {
871            if let Some(value) = i16::from_f64(v) {
872                // SAFETY: buffer size is verified at the beginning of this function.
873                unsafe {
874                    return Ok(emplace_scalar::<i16>(buf, value));
875                }
876            }
877        }
878        BaseType::UShort => {
879            if let Some(value) = u16::from_f64(v) {
880                // SAFETY: buffer size is verified at the beginning of this function.
881                unsafe {
882                    return Ok(emplace_scalar::<u16>(buf, value));
883                }
884            }
885        }
886        BaseType::Int => {
887            if let Some(value) = i32::from_f64(v) {
888                // SAFETY: buffer size is verified at the beginning of this function.
889                unsafe {
890                    return Ok(emplace_scalar::<i32>(buf, value));
891                }
892            }
893        }
894        BaseType::UInt => {
895            if let Some(value) = u32::from_f64(v) {
896                // SAFETY: buffer size is verified at the beginning of this function.
897                unsafe {
898                    return Ok(emplace_scalar::<u32>(buf, value));
899                }
900            }
901        }
902        BaseType::Long => {
903            if let Some(value) = i64::from_f64(v) {
904                // SAFETY: buffer size is verified at the beginning of this function.
905                unsafe {
906                    return Ok(emplace_scalar::<i64>(buf, value));
907                }
908            }
909        }
910        BaseType::ULong => {
911            if let Some(value) = u64::from_f64(v) {
912                // SAFETY: buffer size is verified at the beginning of this function.
913                unsafe {
914                    return Ok(emplace_scalar::<u64>(buf, value));
915                }
916            }
917        }
918        BaseType::Float => {
919            if let Some(value) = f32::from_f64(v) {
920                // Value converted to inf if overflow occurs
921                if value != f32::INFINITY {
922                    // SAFETY: buffer size is verified at the beginning of this function.
923                    unsafe {
924                        return Ok(emplace_scalar::<f32>(buf, value));
925                    }
926                }
927            }
928        }
929        BaseType::Double => {
930            // SAFETY: buffer size is verified at the beginning of this function.
931            unsafe {
932                return Ok(emplace_scalar::<f64>(buf, v));
933            }
934        }
935        _ => return Err(FlatbufferError::SetValueNotSupported),
936    }
937    return Err(FlatbufferError::FieldTypeMismatch(
938        String::from("f64"),
939        type_name,
940    ));
941}
942
943fn is_scalar(base_type: BaseType) -> bool {
944    return base_type <= BaseType::Double;
945}
946
947/// Iterates through the buffer and updates all the relative offsets affected by the insertion.
948///
949/// # Safety
950///
951/// Caller must ensure [buf] contains valid data that conforms to [schema].
952unsafe fn update_offset(
953    buf: &mut [u8],
954    table_loc: usize,
955    updated: &mut [bool],
956    object: &Object,
957    schema: &Schema,
958    insertion_loc: usize,
959    offset: isize,
960) -> FlatbufferResult<()> {
961    if updated.len() != buf.len() {
962        return Err(FlatbufferError::SetStringPolluted);
963    }
964
965    if updated[table_loc] {
966        return Ok(());
967    }
968
969    let slice = &mut buf[table_loc..table_loc + SIZE_SOFFSET];
970    let vtable_offset = isize::try_from(read_scalar::<SOffsetT>(slice))?;
971    let vtable_loc = (isize::try_from(table_loc)? - vtable_offset).try_into()?;
972
973    if insertion_loc <= table_loc {
974        // Checks if insertion point is between the table and a vtable that
975        // precedes it.
976        if (vtable_loc..table_loc).contains(&insertion_loc) {
977            emplace_scalar::<SOffsetT>(slice, (vtable_offset + offset).try_into()?);
978            updated[table_loc] = true;
979        }
980
981        // Early out: since all fields inside the table must point forwards in
982        // memory, if the insertion point is before the table we can stop here.
983        return Ok(());
984    }
985
986    for field in object.fields() {
987        let field_type = field.type_().base_type();
988        if is_scalar(field_type) {
989            continue;
990        }
991
992        let field_offset = VOffsetT::follow(buf, vtable_loc.saturating_add(field.offset().into()));
993        if field_offset == 0 {
994            continue;
995        }
996
997        let field_loc = table_loc + usize::from(field_offset);
998        if updated[field_loc] {
999            continue;
1000        }
1001
1002        if field_type == BaseType::Obj
1003            && schema
1004                .objects()
1005                .get(field.type_().index().try_into()?)
1006                .is_struct()
1007        {
1008            continue;
1009        }
1010
1011        // Updates the relative offset from table to actual data if needed
1012        let slice = &mut buf[field_loc..field_loc + SIZE_UOFFSET];
1013        let field_value_offset = read_scalar::<UOffsetT>(slice);
1014        let field_value_loc = field_loc.saturating_add(field_value_offset.try_into()?);
1015        if (field_loc..field_value_loc).contains(&insertion_loc) {
1016            emplace_scalar::<UOffsetT>(
1017                slice,
1018                (isize::try_from(field_value_offset)? + offset).try_into()?,
1019            );
1020            updated[field_loc] = true;
1021        }
1022
1023        match field_type {
1024            BaseType::Obj => {
1025                let field_obj = schema.objects().get(field.type_().index().try_into()?);
1026                update_offset(
1027                    buf,
1028                    field_value_loc,
1029                    updated,
1030                    &field_obj,
1031                    schema,
1032                    insertion_loc,
1033                    offset,
1034                )?;
1035            }
1036            BaseType::Vector => {
1037                let elem_type = field.type_().element();
1038                if elem_type != BaseType::Obj || elem_type != BaseType::String {
1039                    continue;
1040                }
1041                if elem_type == BaseType::Obj
1042                    && schema
1043                        .objects()
1044                        .get(field.type_().index().try_into()?)
1045                        .is_struct()
1046                {
1047                    continue;
1048                }
1049                let vec_size = usize::try_from(read_uoffset(buf, field_value_loc))?;
1050                for index in 0..vec_size {
1051                    let elem_loc = field_value_loc + SIZE_UOFFSET + index * SIZE_UOFFSET;
1052                    if updated[elem_loc] {
1053                        continue;
1054                    }
1055                    let slice = &mut buf[elem_loc..elem_loc + SIZE_UOFFSET];
1056                    let elem_value_offset = read_scalar::<UOffsetT>(slice);
1057                    let elem_value_loc = elem_loc.saturating_add(elem_value_offset.try_into()?);
1058                    if (elem_loc..elem_value_loc).contains(&insertion_loc) {
1059                        emplace_scalar::<UOffsetT>(
1060                            slice,
1061                            (isize::try_from(elem_value_offset)? + offset).try_into()?,
1062                        );
1063                        updated[elem_loc] = true;
1064                    }
1065
1066                    if elem_type == BaseType::Obj {
1067                        let elem_obj = schema.objects().get(field.type_().index().try_into()?);
1068                        update_offset(
1069                            buf,
1070                            elem_value_loc,
1071                            updated,
1072                            &elem_obj,
1073                            schema,
1074                            insertion_loc,
1075                            offset,
1076                        )?;
1077                    }
1078                }
1079            }
1080            BaseType::Union => {
1081                let union_enum = schema.enums().get(field.type_().index().try_into()?);
1082                let union_type = object
1083                    .fields()
1084                    .lookup_by_key(field.name().to_string() + "_type", |field, key| {
1085                        field.key_compare_with_value(key)
1086                    })
1087                    .unwrap();
1088                let union_type_loc = vtable_loc.saturating_add(union_type.offset().into());
1089                let union_type_offset = VOffsetT::follow(buf, union_type_loc);
1090                let union_type_value =
1091                    u8::follow(buf, table_loc.saturating_add(union_type_offset.into()));
1092                let union_enum_value = union_enum
1093                    .values()
1094                    .lookup_by_key(union_type_value.into(), |value, key| {
1095                        value.key_compare_with_value(*key)
1096                    })
1097                    .unwrap();
1098                let union_object = schema
1099                    .objects()
1100                    .get(union_enum_value.union_type().unwrap().index().try_into()?);
1101                update_offset(
1102                    buf,
1103                    field_value_loc,
1104                    updated,
1105                    &union_object,
1106                    schema,
1107                    insertion_loc,
1108                    offset,
1109                )?;
1110            }
1111            _ => (),
1112        }
1113    }
1114
1115    // Checks if the vtable offset points beyond the insertion point.
1116    if (table_loc..vtable_loc).contains(&insertion_loc) {
1117        let slice = &mut buf[table_loc..table_loc + SIZE_SOFFSET];
1118        emplace_scalar::<SOffsetT>(slice, (vtable_offset - offset).try_into()?);
1119        updated[table_loc] = true;
1120    }
1121    Ok(())
1122}
1123
1124/// Returns the absolute location of the data (e.g. string) in the buffer when the field contains relative offset (`UOffsetT`) to the data.
1125///
1126/// # Safety
1127///
1128/// The value of the corresponding slot must have type `UOffsetT`.
1129unsafe fn deref_uoffset(buf: &[u8], field_loc: usize) -> FlatbufferResult<usize> {
1130    Ok(field_loc.saturating_add(read_uoffset(buf, field_loc).try_into()?))
1131}
1132
1133/// Reads the value of `UOffsetT` at the give location.
1134///
1135/// # Safety
1136///
1137/// The value of the corresponding slot must have type `UOffsetT`.
1138unsafe fn read_uoffset(buf: &[u8], loc: usize) -> UOffsetT {
1139    let slice = &buf[loc..loc + SIZE_UOFFSET];
1140    read_scalar::<UOffsetT>(slice)
1141}