OLD | NEW |
| (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::mojom::MOJOM_NULL_POINTER; | |
6 use bindings::util; | |
7 | |
8 use std::mem; | |
9 use std::ptr; | |
10 use std::ops::{Add, AddAssign, Sub, Mul, Div, Rem}; | |
11 use std::vec::Vec; | |
12 | |
13 use system::UntypedHandle; | |
14 | |
15 /// Represents some count of bits. | |
16 /// | |
17 /// Used to distinguish when we have a bit and a byte | |
18 /// count. The byte count will go in a usize, while we | |
19 /// can use this structure to safely count bits without | |
20 /// running into some subtle bugs or crazy errors. | |
21 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] | |
22 pub struct Bits(pub usize); | |
23 | |
24 impl Bits { | |
25 /// Convert bit representation to bytes, rounding up to the nearest byte. | |
26 pub fn as_bytes(self) -> usize { | |
27 util::bits_to_bytes(self.0) | |
28 } | |
29 | |
30 /// Convert to a number of bytes plus the number of bits leftover | |
31 /// that could not fit in a full byte. | |
32 pub fn as_bits_and_bytes(self) -> (Bits, usize) { | |
33 (Bits(self.0 & 7), self.0 >> 3) | |
34 } | |
35 | |
36 /// Return 1 left-shifted by the amount of bits stored here. | |
37 /// | |
38 /// Only guaranteed to work for up to 8 bits. | |
39 pub fn as_set_bit(self) -> u8 { | |
40 debug_assert!(self.0 < 8); | |
41 1 << (self.0 & 7) | |
42 } | |
43 | |
44 pub fn checked_mul(self, val: usize) -> Option<Bits> { | |
45 match val.checked_mul(self.0) { | |
46 Some(result) => Some(Bits(result)), | |
47 None => None, | |
48 } | |
49 } | |
50 | |
51 /// Align the bits to some number of bytes. | |
52 pub fn align_to_bytes(&mut self, bytes: usize) { | |
53 self.0 = util::align_bytes(self.0, 8 * bytes); | |
54 } | |
55 } | |
56 | |
57 impl Add for Bits { | |
58 type Output = Self; | |
59 fn add(self, rhs: Self) -> Self { | |
60 Bits(self.0 + rhs.0) | |
61 } | |
62 } | |
63 | |
64 impl AddAssign for Bits { | |
65 fn add_assign(&mut self, rhs: Self) { | |
66 self.0 += rhs.0 | |
67 } | |
68 } | |
69 | |
70 impl Mul<usize> for Bits { | |
71 type Output = Self; | |
72 fn mul(self, rhs: usize) -> Self { | |
73 Bits(self.0 * rhs) | |
74 } | |
75 } | |
76 | |
77 /// This trait is intended to be used by Mojom primitive values | |
78 /// in order to be identified in generic contexts. | |
79 pub trait MojomNumeric: Copy + Clone + Sized + Add<Self> + Sub<Self, Output=Self
> + Mul<Self> + | |
80 Div<Self, Output=Self> + Rem<Self, Output=Self> + PartialEq<Self> + Default
{ | |
81 | |
82 /// Converts the primitive to a little-endian representation (the mojom endianne
ss). | |
83 fn to_mojom_endian(self) -> Self; | |
84 } | |
85 | |
86 macro_rules! impl_mojom_numeric_for_prim { | |
87 ($($t:ty),*) => { | |
88 $( | |
89 impl MojomNumeric for $t { | |
90 fn to_mojom_endian(self) -> $t { self.to_le() } | |
91 } | |
92 )* | |
93 } | |
94 } | |
95 | |
96 impl_mojom_numeric_for_prim!(i8, i16, i32, i64, u8, u16, u32, u64); | |
97 | |
98 impl MojomNumeric for f32 { | |
99 fn to_mojom_endian(self) -> f32 { | |
100 unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self).to_
le()) } | |
101 } | |
102 } | |
103 | |
104 impl MojomNumeric for f64 { | |
105 fn to_mojom_endian(self) -> f64 { | |
106 unsafe { mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self).to_
le()) } | |
107 } | |
108 } | |
109 | |
110 /// Align to the Mojom default of 8 bytes. | |
111 pub fn align_default(bytes: usize) -> usize { | |
112 util::align_bytes(bytes, 8) | |
113 } | |
114 | |
115 /// The size in bytes of any data header. | |
116 pub const DATA_HEADER_SIZE: usize = 8; | |
117 | |
118 /// A value that goes in the second u32 of a | |
119 /// a data header. | |
120 /// | |
121 /// Since the data header can head many types, | |
122 /// this enum represents all the kinds of data | |
123 /// that can end up in a data header. | |
124 #[derive(Clone, Copy)] | |
125 pub enum DataHeaderValue { | |
126 Elements(u32), | |
127 Version(u32), | |
128 UnionTag(u32), | |
129 } | |
130 | |
131 impl DataHeaderValue { | |
132 /// Get the raw u32 value. | |
133 fn as_raw(self) -> u32 { | |
134 match self { | |
135 DataHeaderValue::Elements(v) => v, | |
136 DataHeaderValue::Version(v) => v, | |
137 DataHeaderValue::UnionTag(v) => v, | |
138 } | |
139 } | |
140 } | |
141 | |
142 /// A data header is placed at the beginning of every serialized | |
143 /// Mojom object, providing its size as well as some extra meta-data. | |
144 /// | |
145 /// The meta-data should always come from a DataHeaderValue. | |
146 pub struct DataHeader { | |
147 size: u32, | |
148 data: u32, | |
149 } | |
150 | |
151 impl DataHeader { | |
152 /// Create a new DataHeader. | |
153 pub fn new(size: usize, data: DataHeaderValue) -> DataHeader { | |
154 DataHeader { | |
155 size: size as u32, | |
156 data: data.as_raw(), | |
157 } | |
158 } | |
159 | |
160 /// Getter for size. | |
161 pub fn size(&self) -> u32 { | |
162 self.size | |
163 } | |
164 | |
165 /// Getter for extra meta-data. | |
166 pub fn data(&self) -> u32 { | |
167 self.data | |
168 } | |
169 } | |
170 | |
171 /// This context object represents an encoding/decoding context. | |
172 #[derive(Clone, Default)] | |
173 pub struct Context { | |
174 /// An index representing an encoding state. | |
175 id: usize, | |
176 | |
177 /// Whether or not our current context is directly inside of | |
178 /// a union. | |
179 is_union: bool, | |
180 } | |
181 | |
182 impl Context { | |
183 /// Create a new context with all data default. | |
184 pub fn new(id: usize) -> Context { | |
185 Context { | |
186 id: id, | |
187 is_union: false, | |
188 } | |
189 } | |
190 | |
191 /// Getter for the encoding state ID. | |
192 pub fn id(&self) -> usize { | |
193 self.id | |
194 } | |
195 | |
196 /// Getter for whether or not we are in a union. | |
197 pub fn is_union(&self) -> bool { | |
198 self.is_union | |
199 } | |
200 | |
201 /// Change whether or not we are inside of a union and create that | |
202 /// as a new context. | |
203 pub fn set_is_union(&self, value: bool) -> Context { | |
204 let mut new_context = self.clone(); | |
205 new_context.is_union = value; | |
206 new_context | |
207 } | |
208 } | |
209 | |
210 /// An encoding state represents the encoding logic for a single | |
211 /// Mojom object that is NOT inlined, such as a struct or an array. | |
212 pub struct EncodingState<'slice> { | |
213 /// The buffer the state may write to. | |
214 data: &'slice mut [u8], | |
215 | |
216 /// The offset of this serialized object into the overall buffer. | |
217 global_offset: usize, | |
218 | |
219 /// The current offset within 'data'. | |
220 offset: usize, | |
221 | |
222 /// The current bit offset within 'data'. | |
223 bit_offset: Bits, | |
224 } | |
225 | |
226 impl<'slice> EncodingState<'slice> { | |
227 /// Create a new encoding state. | |
228 /// | |
229 /// Note: the encoder will not allocate a buffer for you, rather | |
230 /// a pre-allocated buffer must be passed in. | |
231 pub fn new(buffer: &'slice mut [u8], | |
232 header: &DataHeader, | |
233 offset: usize) | |
234 -> EncodingState<'slice> { | |
235 let mut state = EncodingState { | |
236 data: buffer, | |
237 global_offset: offset, | |
238 offset: 0, | |
239 bit_offset: Bits(0), | |
240 }; | |
241 state.write(header.size()); | |
242 state.write(header.data()); | |
243 state | |
244 } | |
245 | |
246 /// Align the encoding state to the next byte. | |
247 pub fn align_to_byte(&mut self) { | |
248 if self.bit_offset > Bits(0) { | |
249 self.offset += 1; | |
250 self.bit_offset = Bits(0); | |
251 } | |
252 } | |
253 | |
254 /// Align the encoding state to the next 'bytes' boundary. | |
255 pub fn align_to_bytes(&mut self, bytes: usize) { | |
256 self.offset = util::align_bytes(self.offset, bytes); | |
257 } | |
258 | |
259 /// Write a primitive into the buffer. | |
260 fn write<T: MojomNumeric>(&mut self, data: T) { | |
261 let num_bytes = mem::size_of::<T>(); | |
262 let bytes = data.to_mojom_endian(); | |
263 debug_assert!(num_bytes + self.offset <= self.data.len()); | |
264 unsafe { | |
265 ptr::copy_nonoverlapping(mem::transmute::<&T, *const u8>(&bytes), | |
266 (&mut self.data[self.offset..]).as_mut_ptr(
), | |
267 num_bytes); | |
268 } | |
269 self.bit_offset = Bits(0); | |
270 self.offset += num_bytes; | |
271 } | |
272 | |
273 /// Encode a primitive into the buffer, naturally aligning it. | |
274 pub fn encode<T: MojomNumeric>(&mut self, data: T) { | |
275 self.align_to_byte(); | |
276 self.align_to_bytes(mem::size_of::<T>()); | |
277 self.write(data); | |
278 } | |
279 | |
280 /// Encode a boolean value into the buffer as one bit. | |
281 pub fn encode_bool(&mut self, data: bool) { | |
282 let offset = self.offset; | |
283 if data { | |
284 self.data[offset] |= self.bit_offset.as_set_bit(); | |
285 } | |
286 self.bit_offset += Bits(1); | |
287 let (bits, bytes) = self.bit_offset.as_bits_and_bytes(); | |
288 self.offset += bytes; | |
289 self.bit_offset = bits; | |
290 } | |
291 | |
292 /// Encode a null union into the buffer. | |
293 pub fn encode_null_union(&mut self) { | |
294 self.align_to_byte(); | |
295 self.align_to_bytes(8); | |
296 self.write(0 as u32); // Size | |
297 self.write(0 as u32); // Tag | |
298 self.write(0 as u64); // Data | |
299 } | |
300 | |
301 /// Encode a null pointer into the buffer. | |
302 pub fn encode_null_pointer(&mut self) { | |
303 self.align_to_byte(); | |
304 self.align_to_bytes(8); | |
305 self.encode(MOJOM_NULL_POINTER); | |
306 } | |
307 | |
308 /// Encode a null handle into the buffer. | |
309 pub fn encode_null_handle(&mut self) { | |
310 self.align_to_byte(); | |
311 self.align_to_bytes(4); | |
312 self.encode(-1 as i32); | |
313 } | |
314 | |
315 /// Encode a non-null pointer into the buffer. | |
316 /// | |
317 /// 'location' is an absolute location in the global buffer, but | |
318 /// Mojom pointers are offsets relative to the pointer, so we | |
319 /// perform that conversion here before writing. | |
320 pub fn encode_pointer(&mut self, location: u64) { | |
321 self.align_to_byte(); | |
322 self.align_to_bytes(8); | |
323 let current_location = (self.global_offset + self.offset) as u64; | |
324 debug_assert!(location >= current_location); | |
325 self.encode(location - current_location); | |
326 } | |
327 } | |
328 | |
329 /// A struct that will encode a given Mojom object and convert it into | |
330 /// bytes and a vector of handles. | |
331 pub struct Encoder<'slice> { | |
332 bytes: usize, | |
333 buffer: Option<&'slice mut [u8]>, | |
334 states: Vec<EncodingState<'slice>>, | |
335 handles: Vec<UntypedHandle>, | |
336 } | |
337 | |
338 impl<'slice> Encoder<'slice> { | |
339 /// Create a new Encoder. | |
340 pub fn new(buffer: &'slice mut [u8]) -> Encoder<'slice> { | |
341 Encoder { | |
342 bytes: 0, | |
343 buffer: Some(buffer), | |
344 states: Vec::new(), | |
345 handles: Vec::new(), | |
346 } | |
347 } | |
348 | |
349 /// Get the current encoded size (useful for writing pointers). | |
350 pub fn size(&self) -> usize { | |
351 self.bytes | |
352 } | |
353 | |
354 /// Start encoding a new object with its data header. | |
355 /// | |
356 /// Creates a new encoding state for the object. | |
357 pub fn add(&mut self, header: &DataHeader) -> Option<Context> { | |
358 let buf = self.buffer.take().unwrap(); | |
359 if buf.len() < (header.size() as usize) { | |
360 self.buffer = Some(buf); | |
361 return None; | |
362 } | |
363 let obj_bytes = header.size() as usize; | |
364 let (claimed, rest) = buf.split_at_mut(obj_bytes); | |
365 self.states.push(EncodingState::new(claimed, header, self.bytes)); | |
366 self.bytes += obj_bytes; | |
367 let padding_bytes = align_default(obj_bytes) - obj_bytes; | |
368 if padding_bytes <= rest.len() { | |
369 let (_, new_buffer) = rest.split_at_mut(padding_bytes); | |
370 self.bytes += padding_bytes; | |
371 self.buffer = Some(new_buffer); | |
372 } else { | |
373 self.buffer = Some(rest); | |
374 } | |
375 Some(Context::new(self.states.len() - 1)) | |
376 } | |
377 | |
378 /// Adds a handle and returns an offset to that handle in the | |
379 /// final handle vector. | |
380 pub fn add_handle(&mut self, handle: UntypedHandle) -> usize { | |
381 self.handles.push(handle); | |
382 self.handles.len() - 1 | |
383 } | |
384 | |
385 /// Immutably borrow an encoding state via Context. | |
386 pub fn get(&self, context: &Context) -> &EncodingState<'slice> { | |
387 &self.states[context.id()] | |
388 } | |
389 | |
390 /// Mutably borrow an encoding state via Context. | |
391 pub fn get_mut(&mut self, context: &Context) -> &mut EncodingState<'slice> { | |
392 &mut self.states[context.id()] | |
393 } | |
394 | |
395 /// Signal to finish encoding by destroying the Encoder and returning the fi
nal | |
396 /// handle vector. | |
397 /// | |
398 /// Note: No byte buffer is returned as that is pre-allocated. | |
399 pub fn unwrap(self) -> Vec<UntypedHandle> { | |
400 self.handles | |
401 } | |
402 } | |
OLD | NEW |