1 // Copyright 2014 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 package org.chromium.mojo.bindings; 6 7 import org.chromium.mojo.bindings.Interface.Proxy.Handler; 8 import org.chromium.mojo.system.Core; 9 import org.chromium.mojo.system.Handle; 10 import org.chromium.mojo.system.MessagePipeHandle; 11 import org.chromium.mojo.system.Pair; 12 13 import java.nio.ByteBuffer; 14 import java.nio.ByteOrder; 15 import java.nio.charset.Charset; 16 import java.util.ArrayList; 17 import java.util.List; 18 19 /** 20 * Helper class to encode a mojo struct. It keeps track of the output buffer, resizing it as needed. 21 * It also keeps track of the associated handles, and the offset of the current data section. 22 */ 23 public class Encoder { 24 25 /** 26 * Container class for all state that must be shared between the main encoder and any used sub 27 * encoder. 28 */ 29 private static class EncoderState { 30 31 /** 32 * The core used to encode interfaces. 33 */ 34 public final Core core; 35 36 /** 37 * The ByteBuffer to which the message will be encoded. 38 */ 39 public ByteBuffer byteBuffer; 40 41 /** 42 * The list of encountered handles. 43 */ 44 public final List<Handle> handles = new ArrayList<Handle>(); 45 46 /** 47 * The current absolute position for the next data section. 48 */ 49 public int dataEnd; 50 51 /** 52 * @param core the |Core| implementation used to generate handles. Only used if the data 53 * structure being encoded contains interfaces, can be |null| otherwise. 54 * @param bufferSize A hint on the size of the message. Used to build the initial byte 55 * buffer. 56 */ EncoderState(Core core, int bufferSize)57 private EncoderState(Core core, int bufferSize) { 58 assert bufferSize % BindingsHelper.ALIGNMENT == 0; 59 this.core = core; 60 byteBuffer = ByteBuffer.allocateDirect( 61 bufferSize > 0 ? bufferSize : INITIAL_BUFFER_SIZE); 62 byteBuffer.order(ByteOrder.LITTLE_ENDIAN); 63 dataEnd = 0; 64 } 65 66 /** 67 * Claim the given amount of memory at the end of the buffer, resizing it if needed. 68 */ claimMemory(int size)69 public void claimMemory(int size) { 70 dataEnd += size; 71 growIfNeeded(); 72 } 73 74 /** 75 * Grow the associated ByteBuffer if needed. 76 */ growIfNeeded()77 private void growIfNeeded() { 78 if (byteBuffer.capacity() >= dataEnd) { 79 return; 80 } 81 int targetSize = byteBuffer.capacity() * 2; 82 while (targetSize < dataEnd) { 83 targetSize *= 2; 84 } 85 ByteBuffer newBuffer = ByteBuffer.allocateDirect(targetSize); 86 newBuffer.order(ByteOrder.nativeOrder()); 87 byteBuffer.position(0); 88 byteBuffer.limit(byteBuffer.capacity()); 89 newBuffer.put(byteBuffer); 90 byteBuffer = newBuffer; 91 } 92 } 93 94 /** 95 * Default initial size of the data buffer. This must be a multiple of 8 bytes. 96 */ 97 private static final int INITIAL_BUFFER_SIZE = 1024; 98 99 /** 100 * Base offset in the byte buffer for writing. 101 */ 102 private int mBaseOffset; 103 104 /** 105 * The encoder state shared by the main encoder and all its sub-encoder. 106 */ 107 private final EncoderState mEncoderState; 108 109 /** 110 * Returns the result message. 111 */ getMessage()112 public Message getMessage() { 113 mEncoderState.byteBuffer.position(0); 114 mEncoderState.byteBuffer.limit(mEncoderState.dataEnd); 115 return new Message(mEncoderState.byteBuffer, mEncoderState.handles); 116 } 117 118 /** 119 * Constructor. 120 * 121 * @param core the |Core| implementation used to generate handles. Only used if the data 122 * structure being encoded contains interfaces, can be |null| otherwise. 123 * @param sizeHint A hint on the size of the message. Used to build the initial byte buffer. 124 */ Encoder(Core core, int sizeHint)125 public Encoder(Core core, int sizeHint) { 126 this(new EncoderState(core, sizeHint)); 127 } 128 129 /** 130 * Private constructor for sub-encoders. 131 */ Encoder(EncoderState bufferInformation)132 private Encoder(EncoderState bufferInformation) { 133 mEncoderState = bufferInformation; 134 mBaseOffset = bufferInformation.dataEnd; 135 } 136 137 /** 138 * Returns a new encoder that will append to the current buffer. 139 */ getEncoderAtDataOffset(DataHeader dataHeader)140 public Encoder getEncoderAtDataOffset(DataHeader dataHeader) { 141 Encoder result = new Encoder(mEncoderState); 142 result.encode(dataHeader); 143 return result; 144 } 145 146 /** 147 * Encode a {@link DataHeader} and claim the amount of memory required for the data section 148 * (resizing the buffer if required). 149 */ encode(DataHeader s)150 public void encode(DataHeader s) { 151 mEncoderState.claimMemory(BindingsHelper.align(s.size)); 152 encode(s.size, DataHeader.SIZE_OFFSET); 153 encode(s.elementsOrVersion, DataHeader.ELEMENTS_OR_VERSION_OFFSET); 154 } 155 156 /** 157 * Encode a byte at the given offset. 158 */ encode(byte v, int offset)159 public void encode(byte v, int offset) { 160 mEncoderState.byteBuffer.put(mBaseOffset + offset, v); 161 } 162 163 /** 164 * Encode a boolean at the given offset. 165 */ encode(boolean v, int offset, int bit)166 public void encode(boolean v, int offset, int bit) { 167 if (v) { 168 byte encodedValue = mEncoderState.byteBuffer.get(mBaseOffset + offset); 169 encodedValue |= (byte) (1 << bit); 170 mEncoderState.byteBuffer.put(mBaseOffset + offset, encodedValue); 171 } 172 } 173 174 /** 175 * Encode a short at the given offset. 176 */ encode(short v, int offset)177 public void encode(short v, int offset) { 178 mEncoderState.byteBuffer.putShort(mBaseOffset + offset, v); 179 } 180 181 /** 182 * Encode an int at the given offset. 183 */ encode(int v, int offset)184 public void encode(int v, int offset) { 185 mEncoderState.byteBuffer.putInt(mBaseOffset + offset, v); 186 } 187 188 /** 189 * Encode a float at the given offset. 190 */ encode(float v, int offset)191 public void encode(float v, int offset) { 192 mEncoderState.byteBuffer.putFloat(mBaseOffset + offset, v); 193 } 194 195 /** 196 * Encode a long at the given offset. 197 */ encode(long v, int offset)198 public void encode(long v, int offset) { 199 mEncoderState.byteBuffer.putLong(mBaseOffset + offset, v); 200 } 201 202 /** 203 * Encode a double at the given offset. 204 */ encode(double v, int offset)205 public void encode(double v, int offset) { 206 mEncoderState.byteBuffer.putDouble(mBaseOffset + offset, v); 207 } 208 209 /** 210 * Encode a {@link Struct} at the given offset. 211 */ encode(Struct v, int offset, boolean nullable)212 public void encode(Struct v, int offset, boolean nullable) { 213 if (v == null) { 214 encodeNullPointer(offset, nullable); 215 return; 216 } 217 encodePointerToNextUnclaimedData(offset); 218 v.encode(this); 219 } 220 221 /** 222 * Encode a {@link Union} at the given offset. 223 */ encode(Union v, int offset, boolean nullable)224 public void encode(Union v, int offset, boolean nullable) { 225 if (v == null && !nullable) { 226 throw new SerializationException( 227 "Trying to encode a null pointer for a non-nullable type."); 228 } 229 if (v == null) { 230 encode(0L, offset); 231 encode(0L, offset + DataHeader.HEADER_SIZE); 232 return; 233 } 234 v.encode(this, offset); 235 } 236 237 /** 238 * Encodes a String. 239 */ encode(String v, int offset, boolean nullable)240 public void encode(String v, int offset, boolean nullable) { 241 if (v == null) { 242 encodeNullPointer(offset, nullable); 243 return; 244 } 245 final int arrayNullability = nullable 246 ? BindingsHelper.ARRAY_NULLABLE : BindingsHelper.NOTHING_NULLABLE; 247 encode(v.getBytes(Charset.forName("utf8")), offset, arrayNullability, 248 BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); 249 } 250 251 /** 252 * Encodes a {@link Handle}. 253 */ encode(Handle v, int offset, boolean nullable)254 public void encode(Handle v, int offset, boolean nullable) { 255 if (v == null || !v.isValid()) { 256 encodeInvalidHandle(offset, nullable); 257 } else { 258 encode(mEncoderState.handles.size(), offset); 259 mEncoderState.handles.add(v); 260 } 261 } 262 263 /** 264 * Encode an {@link Interface}. 265 */ encode(T v, int offset, boolean nullable, Interface.Manager<T, ?> manager)266 public <T extends Interface> void encode(T v, int offset, boolean nullable, 267 Interface.Manager<T, ?> manager) { 268 if (v == null) { 269 encodeInvalidHandle(offset, nullable); 270 encode(0, offset + BindingsHelper.SERIALIZED_HANDLE_SIZE); 271 return; 272 } 273 if (mEncoderState.core == null) { 274 throw new UnsupportedOperationException( 275 "The encoder has been created without a Core. It can't encode an interface."); 276 } 277 // If the instance is a proxy, pass the proxy's handle instead of creating a new stub. 278 if (v instanceof Interface.Proxy) { 279 Handler handler = ((Interface.Proxy) v).getProxyHandler(); 280 encode(handler.passHandle(), offset, nullable); 281 encode(handler.getVersion(), offset + BindingsHelper.SERIALIZED_HANDLE_SIZE); 282 return; 283 } 284 Pair<MessagePipeHandle, MessagePipeHandle> handles = 285 mEncoderState.core.createMessagePipe(null); 286 manager.bind(v, handles.first); 287 encode(handles.second, offset, nullable); 288 encode(manager.getVersion(), offset + BindingsHelper.SERIALIZED_HANDLE_SIZE); 289 } 290 291 /** 292 * Encode an {@link InterfaceRequest}. 293 */ encode(InterfaceRequest<I> v, int offset, boolean nullable)294 public <I extends Interface> void encode(InterfaceRequest<I> v, int offset, boolean nullable) { 295 if (v == null) { 296 encodeInvalidHandle(offset, nullable); 297 return; 298 } 299 if (mEncoderState.core == null) { 300 throw new UnsupportedOperationException( 301 "The encoder has been created without a Core. It can't encode an interface."); 302 } 303 encode(v.passHandle(), offset, nullable); 304 } 305 306 /** 307 * Encode an associated interface. Not yet supported. 308 */ encode(AssociatedInterfaceNotSupported v, int offset, boolean nullable)309 public void encode(AssociatedInterfaceNotSupported v, int offset, boolean nullable) { 310 } 311 312 /** 313 * Encode an associated interface request. Not yet supported. 314 */ encode(AssociatedInterfaceRequestNotSupported v, int offset, boolean nullable)315 public void encode(AssociatedInterfaceRequestNotSupported v, int offset, boolean nullable) { 316 } 317 318 /** 319 * Returns an {@link Encoder} suitable for encoding an array of pointer of the given length. 320 */ encodePointerArray(int length, int offset, int expectedLength)321 public Encoder encodePointerArray(int length, int offset, int expectedLength) { 322 return encoderForArray(BindingsHelper.POINTER_SIZE, length, offset, expectedLength); 323 } 324 325 /** 326 * Returns an {@link Encoder} suitable for encoding an array of union of the given length. 327 */ encodeUnionArray(int length, int offset, int expectedLength)328 public Encoder encodeUnionArray(int length, int offset, int expectedLength) { 329 return encoderForArray(BindingsHelper.UNION_SIZE, length, offset, expectedLength); 330 } 331 332 /** 333 * Encodes an array of booleans. 334 */ encode(boolean[] v, int offset, int arrayNullability, int expectedLength)335 public void encode(boolean[] v, int offset, int arrayNullability, int expectedLength) { 336 if (v == null) { 337 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 338 return; 339 } 340 if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH 341 && expectedLength != v.length) { 342 throw new SerializationException("Trying to encode a fixed array of incorrect length."); 343 } 344 byte[] bytes = new byte[(v.length + 7) / BindingsHelper.ALIGNMENT]; 345 for (int i = 0; i < bytes.length; ++i) { 346 for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) { 347 int booleanIndex = BindingsHelper.ALIGNMENT * i + j; 348 if (booleanIndex < v.length && v[booleanIndex]) { 349 bytes[i] |= (byte) (1 << j); 350 } 351 } 352 } 353 encodeByteArray(bytes, v.length, offset); 354 } 355 356 /** 357 * Encodes an array of bytes. 358 */ encode(byte[] v, int offset, int arrayNullability, int expectedLength)359 public void encode(byte[] v, int offset, int arrayNullability, int expectedLength) { 360 if (v == null) { 361 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 362 return; 363 } 364 if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH 365 && expectedLength != v.length) { 366 throw new SerializationException("Trying to encode a fixed array of incorrect length."); 367 } 368 encodeByteArray(v, v.length, offset); 369 } 370 371 /** 372 * Encodes an array of shorts. 373 */ encode(short[] v, int offset, int arrayNullability, int expectedLength)374 public void encode(short[] v, int offset, int arrayNullability, int expectedLength) { 375 if (v == null) { 376 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 377 return; 378 } 379 encoderForArray(2, v.length, offset, expectedLength).append(v); 380 } 381 382 /** 383 * Encodes an array of ints. 384 */ encode(int[] v, int offset, int arrayNullability, int expectedLength)385 public void encode(int[] v, int offset, int arrayNullability, int expectedLength) { 386 if (v == null) { 387 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 388 return; 389 } 390 encoderForArray(4, v.length, offset, expectedLength).append(v); 391 } 392 393 /** 394 * Encodes an array of floats. 395 */ encode(float[] v, int offset, int arrayNullability, int expectedLength)396 public void encode(float[] v, int offset, int arrayNullability, int expectedLength) { 397 if (v == null) { 398 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 399 return; 400 } 401 encoderForArray(4, v.length, offset, expectedLength).append(v); 402 } 403 404 /** 405 * Encodes an array of longs. 406 */ encode(long[] v, int offset, int arrayNullability, int expectedLength)407 public void encode(long[] v, int offset, int arrayNullability, int expectedLength) { 408 if (v == null) { 409 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 410 return; 411 } 412 encoderForArray(8, v.length, offset, expectedLength).append(v); 413 } 414 415 /** 416 * Encodes an array of doubles. 417 */ encode(double[] v, int offset, int arrayNullability, int expectedLength)418 public void encode(double[] v, int offset, int arrayNullability, int expectedLength) { 419 if (v == null) { 420 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 421 return; 422 } 423 encoderForArray(8, v.length, offset, expectedLength).append(v); 424 } 425 426 /** 427 * Encodes an array of {@link Handle}. 428 */ encode(Handle[] v, int offset, int arrayNullability, int expectedLength)429 public void encode(Handle[] v, int offset, int arrayNullability, int expectedLength) { 430 if (v == null) { 431 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 432 return; 433 } 434 Encoder e = encoderForArray( 435 BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength); 436 for (int i = 0; i < v.length; ++i) { 437 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, 438 BindingsHelper.isElementNullable(arrayNullability)); 439 } 440 } 441 442 /** 443 * Encodes an array of {@link Interface}. 444 */ encode(T[] v, int offset, int arrayNullability, int expectedLength, Interface.Manager<T, ?> manager)445 public <T extends Interface> void encode(T[] v, int offset, int arrayNullability, 446 int expectedLength, Interface.Manager<T, ?> manager) { 447 if (v == null) { 448 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 449 return; 450 } 451 Encoder e = encoderForArray( 452 BindingsHelper.SERIALIZED_INTERFACE_SIZE, v.length, offset, expectedLength); 453 for (int i = 0; i < v.length; ++i) { 454 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_INTERFACE_SIZE * i, 455 BindingsHelper.isElementNullable(arrayNullability), manager); 456 } 457 } 458 encoderForMap(int offset)459 public Encoder encoderForMap(int offset) { 460 encodePointerToNextUnclaimedData(offset); 461 return getEncoderAtDataOffset(BindingsHelper.MAP_STRUCT_HEADER); 462 } 463 464 /** 465 * Encodes a pointer to the next unclaimed memory and returns an encoder suitable to encode an 466 * union at this location. 467 */ encoderForUnionPointer(int offset)468 public Encoder encoderForUnionPointer(int offset) { 469 encodePointerToNextUnclaimedData(offset); 470 Encoder result = new Encoder(mEncoderState); 471 result.mEncoderState.claimMemory(16); 472 return result; 473 } 474 475 /** 476 * Encodes an array of {@link InterfaceRequest}. 477 */ encode(InterfaceRequest<I>[] v, int offset, int arrayNullability, int expectedLength)478 public <I extends Interface> void encode(InterfaceRequest<I>[] v, int offset, 479 int arrayNullability, int expectedLength) { 480 if (v == null) { 481 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability)); 482 return; 483 } 484 Encoder e = encoderForArray( 485 BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength); 486 for (int i = 0; i < v.length; ++i) { 487 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, 488 BindingsHelper.isElementNullable(arrayNullability)); 489 } 490 } 491 492 /** 493 * Encodes an array of associated interfaces. Not yet supported. 494 */ encode(AssociatedInterfaceNotSupported[] v, int offset, int arrayNullability, int expectedLength)495 public void encode(AssociatedInterfaceNotSupported[] v, int offset, int arrayNullability, 496 int expectedLength) {} 497 498 /** 499 * Encodes an array of associated interface requests. Not yet supported. 500 */ encode(AssociatedInterfaceRequestNotSupported[] v, int offset, int arrayNullability, int expectedLength)501 public void encode(AssociatedInterfaceRequestNotSupported[] v, int offset, int arrayNullability, 502 int expectedLength) {} 503 504 /** 505 * Encodes a <code>null</code> pointer iff the object is nullable, raises an exception 506 * otherwise. 507 */ encodeNullPointer(int offset, boolean nullable)508 public void encodeNullPointer(int offset, boolean nullable) { 509 if (!nullable) { 510 throw new SerializationException( 511 "Trying to encode a null pointer for a non-nullable type."); 512 } 513 mEncoderState.byteBuffer.putLong(mBaseOffset + offset, 0); 514 } 515 516 /** 517 * Encodes an invalid handle iff the object is nullable, raises an exception otherwise. 518 */ encodeInvalidHandle(int offset, boolean nullable)519 public void encodeInvalidHandle(int offset, boolean nullable) { 520 if (!nullable) { 521 throw new SerializationException( 522 "Trying to encode an invalid handle for a non-nullable type."); 523 } 524 mEncoderState.byteBuffer.putInt(mBaseOffset + offset, -1); 525 } 526 527 /** 528 * Claim the given amount of memory at the end of the buffer, resizing it if needed. 529 */ claimMemory(int size)530 void claimMemory(int size) { 531 mEncoderState.claimMemory(BindingsHelper.align(size)); 532 } 533 encodePointerToNextUnclaimedData(int offset)534 private void encodePointerToNextUnclaimedData(int offset) { 535 encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset); 536 } 537 encoderForArray( int elementSizeInByte, int length, int offset, int expectedLength)538 private Encoder encoderForArray( 539 int elementSizeInByte, int length, int offset, int expectedLength) { 540 if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH 541 && expectedLength != length) { 542 throw new SerializationException("Trying to encode a fixed array of incorrect length."); 543 } 544 return encoderForArrayByTotalSize(length * elementSizeInByte, length, offset); 545 } 546 encoderForArrayByTotalSize(int byteSize, int length, int offset)547 private Encoder encoderForArrayByTotalSize(int byteSize, int length, int offset) { 548 encodePointerToNextUnclaimedData(offset); 549 return getEncoderAtDataOffset( 550 new DataHeader(DataHeader.HEADER_SIZE + byteSize, length)); 551 } 552 encodeByteArray(byte[] bytes, int length, int offset)553 private void encodeByteArray(byte[] bytes, int length, int offset) { 554 encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes); 555 } 556 append(byte[] v)557 private void append(byte[] v) { 558 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); 559 mEncoderState.byteBuffer.put(v); 560 } 561 append(short[] v)562 private void append(short[] v) { 563 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); 564 mEncoderState.byteBuffer.asShortBuffer().put(v); 565 } 566 append(int[] v)567 private void append(int[] v) { 568 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); 569 mEncoderState.byteBuffer.asIntBuffer().put(v); 570 } 571 append(float[] v)572 private void append(float[] v) { 573 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); 574 mEncoderState.byteBuffer.asFloatBuffer().put(v); 575 } 576 append(double[] v)577 private void append(double[] v) { 578 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); 579 mEncoderState.byteBuffer.asDoubleBuffer().put(v); 580 } 581 append(long[] v)582 private void append(long[] v) { 583 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); 584 mEncoderState.byteBuffer.asLongBuffer().put(v); 585 } 586 587 } 588