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.
5 package org.chromium.mojo.bindings;
7 import org.chromium.mojo.bindings.Struct.DataHeader;
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;
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;
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.
23 public class Encoder {
26 * Container class for all state that must be shared between the main encoder and any used sub
29 private static class EncoderState {
32 * The core used to encode interfaces.
34 public final Core core;
37 * The ByteBuffer to which the message will be encoded.
39 public ByteBuffer byteBuffer;
42 * The list of encountered handles.
44 public final List<Handle> handles = new ArrayList<Handle>();
47 * The current absolute position for the next data section.
52 * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
53 * 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
57 private EncoderState(Core core, int bufferSize) {
58 assert bufferSize % BindingsHelper.ALIGNMENT == 0;
60 byteBuffer = ByteBuffer.allocateDirect(
61 bufferSize > 0 ? bufferSize : INITIAL_BUFFER_SIZE);
62 byteBuffer.order(ByteOrder.nativeOrder());
67 * Claim the given amount of memory at the end of the buffer, resizing it if needed.
69 public void claimMemory(int size) {
75 * Grow the associated ByteBuffer if needed.
77 private void growIfNeeded() {
78 if (byteBuffer.capacity() >= dataEnd) {
81 int targetSize = byteBuffer.capacity() * 2;
82 while (targetSize < dataEnd) {
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;
95 * Default initial size of the data buffer. This must be a multiple of 8 bytes.
97 private static final int INITIAL_BUFFER_SIZE = 1024;
100 * Base offset in the byte buffer for writing.
102 private int mBaseOffset;
105 * The encoder state shared by the main encoder and all its sub-encoder.
107 private final EncoderState mEncoderState;
110 * Returns the result message.
112 public Message getMessage() {
113 mEncoderState.byteBuffer.position(0);
114 mEncoderState.byteBuffer.limit(mEncoderState.dataEnd);
115 return new Message(mEncoderState.byteBuffer, mEncoderState.handles);
121 * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
122 * 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.
125 public Encoder(Core core, int sizeHint) {
126 this(new EncoderState(core, sizeHint));
130 * Private constructor for sub-encoders.
132 private Encoder(EncoderState bufferInformation) {
133 mEncoderState = bufferInformation;
134 mBaseOffset = bufferInformation.dataEnd;
138 * Returns a new encoder that will append to the current buffer.
140 public Encoder getEncoderAtDataOffset(DataHeader dataHeader) {
141 Encoder result = new Encoder(mEncoderState);
142 result.encode(dataHeader);
147 * Encode a {@link DataHeader} and claim the amount of memory required for the data section
148 * (resizing the buffer if required).
150 public void encode(DataHeader s) {
151 mEncoderState.claimMemory(BindingsHelper.align(s.size));
152 encode(s.size, DataHeader.SIZE_OFFSET);
153 encode(s.numFields, DataHeader.NUM_FIELDS_OFFSET);
157 * Encode a byte at the given offset.
159 public void encode(byte v, int offset) {
160 mEncoderState.byteBuffer.put(mBaseOffset + offset, v);
164 * Encode a boolean at the given offset.
166 public void encode(boolean v, int offset, int bit) {
168 byte encodedValue = mEncoderState.byteBuffer.get(mBaseOffset + offset);
169 encodedValue |= 1 << bit;
170 mEncoderState.byteBuffer.put(mBaseOffset + offset, encodedValue);
175 * Encode a short at the given offset.
177 public void encode(short v, int offset) {
178 mEncoderState.byteBuffer.putShort(mBaseOffset + offset, v);
182 * Encode an int at the given offset.
184 public void encode(int v, int offset) {
185 mEncoderState.byteBuffer.putInt(mBaseOffset + offset, v);
189 * Encode a float at the given offset.
191 public void encode(float v, int offset) {
192 mEncoderState.byteBuffer.putFloat(mBaseOffset + offset, v);
196 * Encode a long at the given offset.
198 public void encode(long v, int offset) {
199 mEncoderState.byteBuffer.putLong(mBaseOffset + offset, v);
203 * Encode a double at the given offset.
205 public void encode(double v, int offset) {
206 mEncoderState.byteBuffer.putDouble(mBaseOffset + offset, v);
210 * Encode a {@link Struct} at the given offset.
212 public void encode(Struct v, int offset, boolean nullable) {
214 encodeNullPointer(offset, nullable);
217 encodePointerToNextUnclaimedData(offset);
224 public void encode(String v, int offset, boolean nullable) {
226 encodeNullPointer(offset, nullable);
229 final int arrayNullability = nullable ?
230 BindingsHelper.ARRAY_NULLABLE : BindingsHelper.NOTHING_NULLABLE;
231 encode(v.getBytes(Charset.forName("utf8")), offset, arrayNullability,
232 BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
236 * Encodes a {@link Handle}.
238 public void encode(Handle v, int offset, boolean nullable) {
239 if (v == null || !v.isValid()) {
240 encodeInvalidHandle(offset, nullable);
242 encode(mEncoderState.handles.size(), offset);
243 mEncoderState.handles.add(v);
248 * Encode an {@link Interface}.
250 public <T extends Interface> void encode(T v, int offset, boolean nullable,
251 Interface.Manager<T, ?> manager) {
253 encodeInvalidHandle(offset, nullable);
256 if (mEncoderState.core == null) {
257 throw new UnsupportedOperationException(
258 "The encoder has been created without a Core. It can't encode an interface.");
260 // If the instance is a proxy, pass the proxy's handle instead of creating a new stub.
261 if (v instanceof Interface.AbstractProxy) {
262 Interface.AbstractProxy proxy = (Interface.AbstractProxy) v;
263 if (proxy.getMessageReceiver() instanceof HandleOwner) {
264 encode(((HandleOwner<?>) proxy.getMessageReceiver()).passHandle(), offset,
268 // If the proxy is not over a message pipe, the default case applies.
270 Pair<MessagePipeHandle, MessagePipeHandle> handles =
271 mEncoderState.core.createMessagePipe(null);
272 manager.bind(v, handles.first);
273 encode(handles.second, offset, nullable);
277 * Encode an {@link InterfaceRequest}.
279 public <I extends Interface> void encode(InterfaceRequest<I> v, int offset, boolean nullable) {
281 encodeInvalidHandle(offset, nullable);
284 if (mEncoderState.core == null) {
285 throw new UnsupportedOperationException(
286 "The encoder has been created without a Core. It can't encode an interface.");
288 encode(v.passHandle(), offset, nullable);
292 * Returns an {@link Encoder} suitable for encoding an array of pointer of the given length.
294 public Encoder encodePointerArray(int length, int offset, int expectedLength) {
295 return encoderForArray(BindingsHelper.POINTER_SIZE, length, offset, expectedLength);
299 * Encodes an array of booleans.
301 public void encode(boolean[] v, int offset, int arrayNullability, int expectedLength) {
303 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
306 if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH &&
307 expectedLength != v.length) {
308 throw new SerializationException("Trying to encode a fixed array of incorrect length.");
310 byte[] bytes = new byte[(v.length + 7) / BindingsHelper.ALIGNMENT];
311 for (int i = 0; i < bytes.length; ++i) {
312 for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
313 int booleanIndex = BindingsHelper.ALIGNMENT * i + j;
314 if (booleanIndex < v.length && v[booleanIndex]) {
315 bytes[i] |= (1 << j);
319 encodeByteArray(bytes, v.length, offset);
323 * Encodes an array of bytes.
325 public void encode(byte[] v, int offset, int arrayNullability, int expectedLength) {
327 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
330 if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH &&
331 expectedLength != v.length) {
332 throw new SerializationException("Trying to encode a fixed array of incorrect length.");
334 encodeByteArray(v, v.length, offset);
338 * Encodes an array of shorts.
340 public void encode(short[] v, int offset, int arrayNullability, int expectedLength) {
342 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
345 encoderForArray(2, v.length, offset, expectedLength).append(v);
349 * Encodes an array of ints.
351 public void encode(int[] v, int offset, int arrayNullability, int expectedLength) {
353 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
356 encoderForArray(4, v.length, offset, expectedLength).append(v);
360 * Encodes an array of floats.
362 public void encode(float[] v, int offset, int arrayNullability, int expectedLength) {
364 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
367 encoderForArray(4, v.length, offset, expectedLength).append(v);
371 * Encodes an array of longs.
373 public void encode(long[] v, int offset, int arrayNullability, int expectedLength) {
375 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
378 encoderForArray(8, v.length, offset, expectedLength).append(v);
382 * Encodes an array of doubles.
384 public void encode(double[] v, int offset, int arrayNullability, int expectedLength) {
386 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
389 encoderForArray(8, v.length, offset, expectedLength).append(v);
393 * Encodes an array of {@link Handle}.
395 public void encode(Handle[] v, int offset, int arrayNullability, int expectedLength) {
397 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
400 Encoder e = encoderForArray(
401 BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
402 for (int i = 0; i < v.length; ++i) {
403 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
404 BindingsHelper.isElementNullable(arrayNullability));
409 * Encodes an array of {@link Interface}.
411 public <T extends Interface> void encode(T[] v, int offset, int arrayNullability,
412 int expectedLength, Interface.Manager<T, ?> manager) {
414 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
417 Encoder e = encoderForArray(
418 BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
419 for (int i = 0; i < v.length; ++i) {
420 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
421 BindingsHelper.isElementNullable(arrayNullability), manager);
425 public Encoder encoderForMap(int offset) {
426 encodePointerToNextUnclaimedData(offset);
427 return getEncoderAtDataOffset(BindingsHelper.MAP_STRUCT_HEADER);
431 * Encodes an array of {@link InterfaceRequest}.
433 public <I extends Interface> void encode(InterfaceRequest<I>[] v, int offset,
434 int arrayNullability, int expectedLength) {
436 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
439 Encoder e = encoderForArray(
440 BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
441 for (int i = 0; i < v.length; ++i) {
442 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
443 BindingsHelper.isElementNullable(arrayNullability));
448 * Encodes a <code>null</code> pointer iff the object is nullable, raises an exception
451 public void encodeNullPointer(int offset, boolean nullable) {
453 throw new SerializationException(
454 "Trying to encode a null pointer for a non-nullable type.");
456 mEncoderState.byteBuffer.putLong(mBaseOffset + offset, 0);
460 * Encodes an invalid handle iff the object is nullable, raises an exception otherwise.
462 public void encodeInvalidHandle(int offset, boolean nullable) {
464 throw new SerializationException(
465 "Trying to encode an invalid handle for a non-nullable type.");
467 mEncoderState.byteBuffer.putInt(mBaseOffset + offset, -1);
470 private void encodePointerToNextUnclaimedData(int offset) {
471 encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset);
474 private Encoder encoderForArray(
475 int elementSizeInByte, int length, int offset, int expectedLength) {
476 if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH &&
477 expectedLength != length) {
478 throw new SerializationException("Trying to encode a fixed array of incorrect length.");
480 return encoderForArrayByTotalSize(length * elementSizeInByte, length, offset);
483 private Encoder encoderForArrayByTotalSize(int byteSize, int length, int offset) {
484 encodePointerToNextUnclaimedData(offset);
485 return getEncoderAtDataOffset(
486 new DataHeader(DataHeader.HEADER_SIZE + byteSize, length));
489 private void encodeByteArray(byte[] bytes, int length, int offset) {
490 encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes);
493 private void append(byte[] v) {
494 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
495 mEncoderState.byteBuffer.put(v);
498 private void append(short[] v) {
499 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
500 mEncoderState.byteBuffer.asShortBuffer().put(v);
503 private void append(int[] v) {
504 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
505 mEncoderState.byteBuffer.asIntBuffer().put(v);
508 private void append(float[] v) {
509 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
510 mEncoderState.byteBuffer.asFloatBuffer().put(v);
513 private void append(double[] v) {
514 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
515 mEncoderState.byteBuffer.asDoubleBuffer().put(v);
518 private void append(long[] v) {
519 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
520 mEncoderState.byteBuffer.asLongBuffer().put(v);