| 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 |