Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: mojo/public/rust/src/bindings/decoding.rs

Issue 2250183003: Make the fuchsia mojo/public repo the source of truth. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « mojo/public/rust/build.rs ('k') | mojo/public/rust/src/bindings/encoding.rs » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use bindings::encoding::{Bits, Context, DataHeader, DataHeaderValue, DATA_HEADER _SIZE,
6 MojomNumeric};
7 use bindings::mojom::{MojomEncodable, MOJOM_NULL_POINTER, UNION_SIZE};
8 use bindings::util;
9
10 use std::mem;
11 use std::ptr;
12 use std::vec::Vec;
13
14 use system;
15 use system::{Handle, CastHandle, UntypedHandle};
16
17 #[derive(Debug, Eq, PartialEq)]
18 pub enum ValidationError {
19 DifferentSizedArraysInMap,
20 IllegalHandle,
21 IllegalMemoryRange,
22 IllegalPointer,
23 MessageHeaderInvalidFlags,
24 MessageHeaderMissingRequestId,
25 MessageHeaderUnknownMethod,
26 MisalignedObject,
27 UnexpectedArrayHeader,
28 UnexpectedInvalidHandle,
29 UnexpectedNullPointer,
30 UnexpectedNullUnion,
31 UnexpectedStructHeader,
32 }
33
34 impl ValidationError {
35 pub fn as_str(self) -> &'static str {
36 match self {
37 ValidationError::DifferentSizedArraysInMap => "VALIDATION_ERROR_DIFF ERENT_SIZED_ARRAYS_IN_MAP",
38 ValidationError::IllegalHandle => "VALIDATION_ERROR_ILLEGAL_HANDLE",
39 ValidationError::IllegalMemoryRange => "VALIDATION_ERROR_ILLEGAL_MEM ORY_RANGE",
40 ValidationError::IllegalPointer => "VALIDATION_ERROR_ILLEGAL_POINTER ",
41 ValidationError::MessageHeaderInvalidFlags => "VALIDATION_ERROR_MESS AGE_HEADER_INVALID_FLAGS",
42 ValidationError::MessageHeaderMissingRequestId => "VALIDATION_ERROR_ MESSAGE_HEADER_MISSING_REQUEST_ID",
43 ValidationError::MessageHeaderUnknownMethod => "VALIDATION_ERROR_MES SAGE_HEADER_UNKNOWN_METHOD",
44 ValidationError::MisalignedObject => "VALIDATION_ERROR_MISALIGNED_OB JECT",
45 ValidationError::UnexpectedArrayHeader => "VALIDATION_ERROR_UNEXPECT ED_ARRAY_HEADER",
46 ValidationError::UnexpectedInvalidHandle => "VALIDATION_ERROR_UNEXPE CTED_INVALID_HANDLE",
47 ValidationError::UnexpectedNullPointer => "VALIDATION_ERROR_UNEXPECT ED_NULL_POINTER",
48 ValidationError::UnexpectedNullUnion => "VALIDATION_ERROR_UNEXPECTED _NULL_UNION",
49 ValidationError::UnexpectedStructHeader => "VALIDATION_ERROR_UNEXPEC TED_STRUCT_HEADER",
50 }
51 }
52 }
53
54 /// An decoding state represents the decoding logic for a single
55 /// Mojom object that is NOT inlined, such as a struct or an array.
56 pub struct DecodingState<'slice> {
57 /// The buffer the state may write to.
58 data: &'slice [u8],
59
60 /// The offset of this serialized object into the overall buffer.
61 global_offset: usize,
62
63 /// The current offset within 'data'.
64 offset: usize,
65
66 /// The current bit offset within 'data'.
67 bit_offset: Bits,
68 }
69
70 impl<'slice> DecodingState<'slice> {
71 /// Create a new decoding state.
72 pub fn new(buffer: &'slice [u8], offset: usize) -> DecodingState<'slice> {
73 DecodingState {
74 data: buffer,
75 global_offset: offset,
76 offset: 0,
77 bit_offset: Bits(0),
78 }
79 }
80
81 /// Align the decoding state to the next byte.
82 pub fn align_to_byte(&mut self) {
83 if self.bit_offset > Bits(0) {
84 self.offset += 1;
85 self.bit_offset = Bits(0);
86 }
87 }
88
89 /// Align the decoding state to the next 'bytes' boundary.
90 pub fn align_to_bytes(&mut self, bytes: usize) {
91 if self.offset != 0 {
92 self.offset = util::align_bytes(self.offset, bytes);
93 }
94 }
95
96 /// Read a primitive from the buffer without incrementing the offset.
97 fn read_in_place<T: MojomNumeric>(&mut self) -> T {
98 let mut value: T = Default::default();
99 debug_assert!(mem::size_of::<T>() + self.offset <= self.data.len());
100 let ptr = (&self.data[self.offset..]).as_ptr();
101 unsafe {
102 ptr::copy_nonoverlapping(mem::transmute::<*const u8, *const T>(ptr),
103 &mut value as *mut T,
104 1);
105 }
106 value
107 }
108
109 /// Read a primitive from the buffer and increment the offset.
110 fn read<T: MojomNumeric>(&mut self) -> T {
111 let value = self.read_in_place::<T>();
112 self.bit_offset = Bits(0);
113 self.offset += mem::size_of::<T>();
114 value
115 }
116
117 /// Decode a primitive from the buffer, naturally aligning before we read.
118 pub fn decode<T: MojomNumeric>(&mut self) -> T {
119 self.align_to_byte();
120 self.align_to_bytes(mem::size_of::<T>());
121 self.read::<T>()
122 }
123
124 /// Decode a boolean value from the buffer as one bit.
125 pub fn decode_bool(&mut self) -> bool {
126 let offset = self.offset;
127 // Check the bit by getting the set bit and checking if its non-zero
128 let value = (self.data[offset] & self.bit_offset.as_set_bit()) > 0;
129 self.bit_offset += Bits(1);
130 let (bits, bytes) = self.bit_offset.as_bits_and_bytes();
131 self.offset += bytes;
132 self.bit_offset = bits;
133 value
134 }
135
136 /// If we encounter a null pointer, increment past it.
137 ///
138 /// Returns if we skipped or not.
139 pub fn skip_if_null_pointer(&mut self) -> bool {
140 self.align_to_byte();
141 self.align_to_bytes(8);
142 let ptr = self.read_in_place::<u64>();
143 if ptr == MOJOM_NULL_POINTER {
144 self.offset += 8;
145 }
146 (ptr == MOJOM_NULL_POINTER)
147 }
148
149 /// If we encounter a null union, increment past it.
150 ///
151 /// Returns if we skipped or not.
152 pub fn skip_if_null_union(&mut self) -> bool {
153 self.align_to_byte();
154 self.align_to_bytes(8);
155 let size = self.read_in_place::<u32>();
156 if size == 0 {
157 self.offset += UNION_SIZE;
158 }
159 (size == 0)
160 }
161
162 /// If we encounter a null handle, increment past it.
163 ///
164 /// Returns if we skipped or not.
165 pub fn skip_if_null_handle(&mut self) -> bool {
166 self.align_to_byte();
167 self.align_to_bytes(4);
168 let index = self.read_in_place::<i32>();
169 if index < 0 {
170 self.offset += 4;
171 }
172 (index < 0)
173 }
174
175 /// If we encounter a null interface, increment past it.
176 ///
177 /// Returns if we skipped or not.
178 pub fn skip_if_null_interface(&mut self) -> bool {
179 self.align_to_byte();
180 self.align_to_bytes(4);
181 let index = self.read_in_place::<i32>();
182 if index < 0 {
183 self.offset += 8;
184 }
185 (index < 0)
186 }
187
188 /// Decode a pointer from the buffer as a global offset into the buffer.
189 ///
190 /// The pointer in the buffer is an offset relative to the pointer to anothe r
191 /// location in the buffer. We convert that to an absolute offset with respe ct
192 /// to the buffer before returning. This is our defintion of a pointer.
193 pub fn decode_pointer(&mut self) -> Option<u64> {
194 self.align_to_byte();
195 self.align_to_bytes(8);
196 let current_location = (self.global_offset + self.offset) as u64;
197 let offset = self.read::<u64>();
198 if offset == MOJOM_NULL_POINTER {
199 Some(MOJOM_NULL_POINTER)
200 } else {
201 offset.checked_add(current_location)
202 }
203 }
204
205 /// A routine for decoding an array header.
206 ///
207 /// Must be called with offset zero (that is, it must be the first thing
208 /// decoded). Performs numerous validation checks.
209 pub fn decode_array_header<T>(&mut self) -> Result<DataHeader, ValidationErr or>
210 where T: MojomEncodable
211 {
212 debug_assert_eq!(self.offset, 0);
213 // Make sure we can read the size first...
214 if self.data.len() < mem::size_of::<u32>() {
215 return Err(ValidationError::UnexpectedArrayHeader);
216 }
217 let bytes = self.decode::<u32>();
218 if (bytes as usize) < DATA_HEADER_SIZE {
219 return Err(ValidationError::UnexpectedArrayHeader);
220 }
221 let elems = self.decode::<u32>();
222 match T::embed_size(&Default::default()).checked_mul(elems as usize) {
223 Some(value) => {
224 if (bytes as usize) < value.as_bytes() + DATA_HEADER_SIZE {
225 return Err(ValidationError::UnexpectedArrayHeader);
226 }
227 }
228 None => return Err(ValidationError::UnexpectedArrayHeader),
229 }
230 Ok(DataHeader::new(bytes as usize, DataHeaderValue::Elements(elems)))
231 }
232
233 /// A routine for decoding an struct header.
234 ///
235 /// Must be called with offset zero (that is, it must be the first thing
236 /// decoded). Performs numerous validation checks.
237 pub fn decode_struct_header(&mut self,
238 versions: &[(u32, u32)])
239 -> Result<DataHeader, ValidationError> {
240 debug_assert_eq!(self.offset, 0);
241 // Make sure we can read the size first...
242 if self.data.len() < mem::size_of::<u32>() {
243 return Err(ValidationError::UnexpectedStructHeader);
244 }
245 let bytes = self.decode::<u32>();
246 if (bytes as usize) < DATA_HEADER_SIZE {
247 return Err(ValidationError::UnexpectedStructHeader);
248 }
249 let version = self.decode::<u32>();
250 // Versioning validation: versions are generated as a sorted array of tu ples, so
251 // to find the version we are given by the header we use a binary search .
252 match versions.binary_search_by(|val| val.0.cmp(&version)) {
253 Ok(idx) => {
254 let (_, size) = versions[idx];
255 if bytes != size {
256 return Err(ValidationError::UnexpectedStructHeader);
257 }
258 }
259 Err(idx) => {
260 if idx == 0 {
261 panic!("Should be earliest version? \
262 Versions: {:?}, \
263 Version: {}, \
264 Size: {}", versions, version, bytes);
265 }
266 let len = versions.len();
267 let (latest_version, _) = versions[len - 1];
268 let (_, size) = versions[idx - 1];
269 // If this is higher than any version we know, its okay for the size to be bigger,
270 // but if its a version we know about, it must match the size.
271 if (version > latest_version && bytes < size) ||
272 (version <= latest_version && bytes != size) {
273 return Err(ValidationError::UnexpectedStructHeader);
274 }
275 }
276 }
277 Ok(DataHeader::new(bytes as usize, DataHeaderValue::Version(version)))
278 }
279 }
280
281 /// A struct that will encode a given Mojom object and convert it into
282 /// bytes and a vector of handles.
283 pub struct Decoder<'slice> {
284 bytes: usize,
285 buffer: Option<&'slice [u8]>,
286 states: Vec<DecodingState<'slice>>,
287 handles: Vec<UntypedHandle>,
288 handles_claimed: usize, // A length that claims all handles were claimed up to this index
289 max_offset: usize, // Represents the maximum value an offset may have
290 }
291
292 impl<'slice> Decoder<'slice> {
293 /// Create a new Decoder.
294 pub fn new(buffer: &'slice [u8], handles: Vec<UntypedHandle>) -> Decoder<'sl ice> {
295 let max_offset = buffer.len();
296 Decoder {
297 bytes: 0,
298 buffer: Some(buffer),
299 states: Vec::new(),
300 handles: handles,
301 handles_claimed: 0,
302 max_offset: max_offset,
303 }
304 }
305
306 /// Claim space in the buffer to start decoding some object.
307 ///
308 /// Creates a new decoding state for the object and returns a context.
309 pub fn claim(&mut self, offset: usize) -> Result<Context, ValidationError> {
310 // Check if the layout order is sane
311 if offset < self.bytes {
312 return Err(ValidationError::IllegalMemoryRange);
313 }
314 // Check for 8-byte alignment
315 if offset & 7 != 0 {
316 return Err(ValidationError::MisalignedObject);
317 }
318 // Bounds check on offset
319 if offset > self.max_offset {
320 return Err(ValidationError::IllegalPointer);
321 }
322 let mut buffer = self.buffer.take().expect("No buffer?");
323 let space = offset - self.bytes;
324 buffer = &buffer[space..];
325 // Make sure we can even read the bytes in the header
326 if buffer.len() < mem::size_of::<u32>() {
327 return Err(ValidationError::IllegalMemoryRange);
328 }
329 // Read the number of bytes in the memory region according to the data h eader
330 let mut read_size: u32 = 0;
331 unsafe {
332 ptr::copy_nonoverlapping(mem::transmute::<*const u8, *const u32>(buf fer.as_ptr()),
333 &mut read_size as *mut u32,
334 mem::size_of::<u32>());
335 }
336 let size = u32::from_le(read_size) as usize;
337 // Make sure the size we read is sane...
338 if size > buffer.len() {
339 return Err(ValidationError::IllegalMemoryRange);
340 }
341 // TODO(mknyszek): Check size for validation
342 let (claimed, unclaimed) = buffer.split_at(size);
343 self.states.push(DecodingState::new(claimed, offset));
344 self.buffer = Some(unclaimed);
345 self.bytes += space + size;
346 Ok(Context::new(self.states.len() - 1))
347 }
348
349 /// Claims a handle at some particular index in the given handles array.
350 ///
351 /// Returns the handle with all type information in-tact.
352 pub fn claim_handle<T: Handle + CastHandle>(&mut self,
353 index: i32)
354 -> Result<T, ValidationError> {
355 let real_index = if index >= 0 {
356 index as usize
357 } else {
358 return Err(ValidationError::UnexpectedInvalidHandle);
359 };
360 // If the index exceeds our number of handles or if we have already clai med that handle
361 if real_index >= self.handles.len() || real_index < self.handles_claimed {
362 return Err(ValidationError::IllegalHandle);
363 }
364 self.handles_claimed = real_index + 1;
365 let raw_handle = self.handles[real_index].get_native_handle();
366 unsafe {
367 self.handles[real_index].invalidate();
368 Ok(T::from_untyped(system::acquire(raw_handle)))
369 }
370 }
371
372 /// Immutably borrow a decoding state via Context.
373 pub fn get(&self, context: &Context) -> &DecodingState<'slice> {
374 &self.states[context.id()]
375 }
376
377 /// Mutably borrow a decoding state via Context.
378 pub fn get_mut(&mut self, context: &Context) -> &mut DecodingState<'slice> {
379 &mut self.states[context.id()]
380 }
381 }
OLDNEW
« no previous file with comments | « mojo/public/rust/build.rs ('k') | mojo/public/rust/src/bindings/encoding.rs » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698