1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
//! A Rust crate that enables a more flexible usage of FlatBuffers.
//!
//! Using the `flatbuffers_owned!` macro, you can generate wrapper structs for your flatc generated Rust FlatBuffers. \
//! The generated wrapper structs utilize more flexible lifetimes to access the actual underlying FlatBuffer structure. \
//! As the lifetimes are more relaxed, the raw FlatBuffer bytes can be owned and moved along, or be referenced with any lifetime available.
//!
//! ## Usage
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! flatbuffers-owned = "0.2"
//! ```
//!
//! ## Quickstart
//! Use the `flatbuffers_owned!` macro on your FlatBuffers to generate the wrapper structs.
//!
//! This generates a `RelaxedMessage` wrapper-struct and a `OwnedMessage` type alias for the `Message` FlatBuffer:
//! ```rust
//! use flatbuffers_owned::*;
//!
//! flatbuffers_owned!(Message);
//! ```
//!
//! Receive a byte slice, create a boxed slice, and initialize the owned flatbuffer:
//! ```rust
//! let message_bytes: &[u8] = receive_message_bytes();
//! let message_bytes: Box<[u8]> = Box::from(message_bytes);
//!
//! let owned_message = OwnedMessage::new(message_bytes).unwrap();
//! ```
//!
//! Access the actual FlatBuffer:
//! ```rust
//! let message: Message = owned_message.as_actual();
//!
//! assert_eq!(message.get_text().unwrap(), "Hello, world!");
//! ```
//!
//! ## Error-Handling
//! The `new()` constructor always verifies the raw FlatBuffer bytes using the FlatBuffer's built-in `run_verifier()` method.</br>
//! Since there can always be a faulty byte-slice passed, you need to check the returned Result of the constructor:
//! ```rust
//! for id in message_ids {
//!     let message_bytes = Box::from(receive_message_bytes());
//!
//!     let owned_message = OwnedMessage::new(message_bytes);
//!
//!     match owned_message {
//!         Ok(message) => {
//!             // ... process message
//!         },
//!         Err(e) => {
//!             println!("Failed to parse Message: {}", e);
//!             // ... handling logic
//!         }
//!     }
//! }
//! ```
//!
//! ## Approach
//! ### The wrapper struct
//! The `Relaxed{FLATBUFFER_NAME}` wrapper struct is a Newtype which can wrap any struct that can convert to a byte slice reference. (```where TBuffer: AsRef<[u8]>```) \
//! This struct can be used with buffers that fully own its memory, or only hold a shared-reference.
//!
//! The `Owned{FLATBUFFER_NAME}` type alias generated along the wrapper struct just predefines the `TBuffer` generic. \
//! For our `Message` example FlatBuffer, the generated type alias code would be the following:
//! ```rust
//! pub type OwnedMessage = RelaxedMessage<Box<[u8]>>;
//! ```
//!
//! ### Deref to &[u8]
//! The `RelaxedFlatBufferTrait` enforces a de-reference to the underlying [u8] byte slice. \
//! A de-reference to the actual FlatBuffer struct is sadly not possible, since the associated type of the `Deref` trait can not carry a lifetime.
//!
//! ## Open to Feedback
//! If you have any ideas for improvements or would like to contribute to this project, please feel free to open an issue or pull request.
//!
//! I will also be happy for any general tips or suggestions given that this is my first (published) library ever. :)

use std::ops::Deref;
use flatbuffers::{Follow, ForwardsUOffset, InvalidFlatbuffer, Verifiable, Verifier, VerifierOptions};

#[doc(hidden)]
pub use paste::paste;

/// This trait allows a `.follow()` method that returns a FlatBuffer with the lifetime of the provided byte slice.
///
/// # Example trait implementation
/// ```
/// use flatbuffers_owned::RelaxedFollowTrait;
///
/// impl RelaxedFollowTrait for MyFlatBuffer<'_> {
///    type Inner<'a> = MyFlatBuffer<'a>;
/// }
pub trait RelaxedFollowTrait {
    type Inner<'a>: Follow<'a>;

    /// # Safety
    ///
    /// This function lacks verification. This method could yield a FlatBuffer with undefined behavior on field reads when corrupted FlatBuffer bytes are passed.
    #[inline(always)]
    unsafe fn follow(buf: &[u8], loc: usize) -> <<Self as RelaxedFollowTrait>::Inner<'_> as Follow<'_>>::Inner {
        <ForwardsUOffset<Self::Inner<'_>>>::follow(buf, loc)
    }
}

/// This trait serves as the foundation for this crate. \
/// It allows access to the underlying FlatBuffer using the [as_actual()](RelaxedFlatBufferTrait::as_actual) method. \
/// The lifetimes used here are more relaxed than those in the flatc generated code. \
/// For example, it could be implemented for a fully owned `Box<\[u8\]>` new-type or even a reference to memory that has been temporarily pinned following a DB query.
///
/// This trait requires the [RelaxedFollowTrait] trait bound on the FlatBuffer type. \
/// It can be implemented either manually or using the [flatbuffers_owned!](flatbuffers_owned) macro.
///
/// # Safety
/// The default implementation of the [as_actual()](RelaxedFlatBufferTrait::as_actual) method uses the unsafe [.follow()](RelaxedFollowTrait::follow) method do return the actual FlatBuffer. \
/// [as_actual()](RelaxedFlatBufferTrait::as_actual) does not verify the FlatBuffer. \
/// If you choose to implement this trait manually, you must ensure that the underlying byte slice is verified and the buffer remains immutable.
///
/// # Example trait usage
/// ```
/// # use flatbuffers_owned::RelaxedFlatBufferTrait;
///
/// fn store_fbs<TBuffer>(flatbuffers: &[impl RelaxedFlatBufferTrait<TBuffer>]) {
///    for item in flatbuffers {
///         let bytes: &[u8] = item.deref();
///         // ... store the raw bytes somewhere.
///    }
/// }
/// ```
pub unsafe trait RelaxedFlatBufferTrait<TBuffer>
    where Self: Deref<Target = [u8]> + Sized
{
    type FlatBuffer: RelaxedFollowTrait + Verifiable;

    /// Initializes a actual FlatBuffer struct from the byte slice returned by the Self::deref() method.
    #[inline(always)]
    fn as_actual(&self) -> <<<Self as RelaxedFlatBufferTrait<TBuffer>>::FlatBuffer as RelaxedFollowTrait>::Inner<'_> as Follow<'_>>::Inner {
        unsafe { Self::FlatBuffer::follow(self, 0) }
    }

    /// Verifies the FlatBuffer data.
    fn verify(data: &[u8]) -> Result<(), InvalidFlatbuffer> {
        let opts = VerifierOptions::default();
        let mut v = Verifier::new(&opts, data);

        <ForwardsUOffset<Self::FlatBuffer>>::run_verifier(&mut v, 0)
    }

    fn new(data: TBuffer) -> Result<Self, InvalidFlatbuffer>;
}

/// Use this macro on your FlatBuffers to generate the required code to start using this crate.
///
/// After invoking the macro, you have two generated types for each of your passed FlatBuffers: \
/// 1. A generic new-type struct named `Relaxed{FLATBUFFER_NAME}`, which implements [RelaxedFlatBufferTrait] and takes the generic `TBuffer: AsRef<[u8]>`. \
/// 2. A type alias named `Owned{FLATBUFFER_NAME}, which aliases the `Relaxed{FLATBUFFER_NAME}` struct and sets `TBuffer` to `Box<[u8]>`.
///
/// # Usage
/// ```
/// use flatbuffers_owned::flatbuffers_owned;
///
/// flatbuffers_owned!(MyFirstFlatBuffer, MySecondFlatBuffer);
/// ```
#[macro_export]
macro_rules! flatbuffers_owned {
    ($struct_name:ident) => {
        $crate::paste! {
            impl $crate::RelaxedFollowTrait for $struct_name<'_> {
                type Inner<'a> = $struct_name<'a>;
            }

            #[derive(Clone, Debug, PartialEq, Eq, Hash)]
            pub struct [<Relaxed $struct_name>]<TBuffer: AsRef<[u8]>>(TBuffer);

            unsafe impl <TBuffer: AsRef<[u8]>> RelaxedFlatBufferTrait<TBuffer> for [<Relaxed $struct_name>]<TBuffer> {
                type FlatBuffer = $struct_name<'static>;

                fn new(data: TBuffer) -> Result<Self, flatbuffers::InvalidFlatbuffer> {
                    Self::verify(data.as_ref())?;

                    Ok(Self(data))
                }
            }
            
            impl <TBuffer: AsRef<[u8]>> std::ops::Deref for [<Relaxed $struct_name>]<TBuffer> {
                type Target = [u8];

                fn deref(&self) -> &Self::Target {
                    self.0.as_ref()
                }
            }

            pub type [<Owned $struct_name>] = [<Relaxed $struct_name>]<Box<[u8]>>;
        }
    };

    ($($struct_name:ident),*) => {
        $(
            $crate::flatbuffers_owned!($struct_name);
        )*
    };
}