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;
11 import java.nio.ByteBuffer;
12 import java.nio.ByteOrder;
13 import java.nio.charset.Charset;
14 import java.util.ArrayList;
15 import java.util.List;
18 * Helper class to encode a mojo struct. It keeps track of the output buffer, resizing it as needed.
19 * It also keeps track of the associated handles, and the offset of the current data section.
21 public class Encoder {
24 * Container class for all state that must be shared between the main encoder and any used sub
27 private static class EncoderState {
30 * The core used to encode interfaces.
32 public final Core core;
35 * The ByteBuffer to which the message will be encoded.
37 public ByteBuffer byteBuffer;
40 * The list of encountered handles.
42 public final List<Handle> handles = new ArrayList<Handle>();
45 * The current absolute position for the next data section.
50 * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
51 * being encoded contains interfaces, can be |null| otherwise.
52 * @param bufferSize A hint on the size of the message. Used to build the initial byte
55 private EncoderState(Core core, int bufferSize) {
56 assert bufferSize % BindingsHelper.ALIGNMENT == 0;
58 byteBuffer = ByteBuffer.allocateDirect(
59 bufferSize > 0 ? bufferSize : INITIAL_BUFFER_SIZE);
60 byteBuffer.order(ByteOrder.nativeOrder());
65 * Claim the given amount of memory at the end of the buffer, resizing it if needed.
67 public void claimMemory(int size) {
73 * Grow the associated ByteBuffer if needed.
75 private void growIfNeeded() {
76 if (byteBuffer.capacity() >= dataEnd) {
79 int targetSize = byteBuffer.capacity() * 2;
80 while (targetSize < dataEnd) {
83 ByteBuffer newBuffer = ByteBuffer.allocateDirect(targetSize);
84 newBuffer.order(ByteOrder.nativeOrder());
85 byteBuffer.position(0);
86 byteBuffer.limit(byteBuffer.capacity());
87 newBuffer.put(byteBuffer);
88 byteBuffer = newBuffer;
93 * Default initial size of the data buffer. This must be a multiple of 8 bytes.
95 private static final int INITIAL_BUFFER_SIZE = 1024;
98 * Base offset in the byte buffer for writing.
100 private int mBaseOffset;
103 * The encoder state shared by the main encoder and all its sub-encoder.
105 private final EncoderState mEncoderState;
108 * Returns the result message.
110 public Message getMessage() {
111 mEncoderState.byteBuffer.position(0);
112 mEncoderState.byteBuffer.limit(mEncoderState.dataEnd);
113 return new Message(mEncoderState.byteBuffer, mEncoderState.handles);
119 * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
120 * being encoded contains interfaces, can be |null| otherwise.
121 * @param sizeHint A hint on the size of the message. Used to build the initial byte buffer.
123 public Encoder(Core core, int sizeHint) {
124 this(new EncoderState(core, sizeHint));
128 * Private constructor for sub-encoders.
130 private Encoder(EncoderState bufferInformation) {
131 mEncoderState = bufferInformation;
132 mBaseOffset = bufferInformation.dataEnd;
136 * Returns a new encoder that will append to the current buffer.
138 public Encoder getEncoderAtDataOffset(DataHeader dataHeader) {
139 Encoder result = new Encoder(mEncoderState);
140 result.encode(dataHeader);
146 * Encode a {@link DataHeader} and claim the amount of memory required for the data section
147 * (resizing the buffer if required).
149 public void encode(DataHeader s) {
150 mEncoderState.claimMemory(s.size);
151 encode(s.size, DataHeader.SIZE_OFFSET);
152 encode(s.numFields, DataHeader.NUM_FIELDS_OFFSET);
156 * Encode a byte at the given offset.
158 public void encode(byte v, int offset) {
159 mEncoderState.byteBuffer.put(mBaseOffset + offset, v);
163 * Encode a boolean at the given offset.
165 public void encode(boolean v, int offset, int bit) {
167 byte encodedValue = mEncoderState.byteBuffer.get(mBaseOffset + offset);
168 encodedValue |= 1 << bit;
169 mEncoderState.byteBuffer.put(mBaseOffset + offset, encodedValue);
174 * Encode a short at the given offset.
176 public void encode(short v, int offset) {
177 mEncoderState.byteBuffer.putShort(mBaseOffset + offset, v);
181 * Encode an int at the given offset.
183 public void encode(int v, int offset) {
184 mEncoderState.byteBuffer.putInt(mBaseOffset + offset, v);
188 * Encode a float at the given offset.
190 public void encode(float v, int offset) {
191 mEncoderState.byteBuffer.putFloat(mBaseOffset + offset, v);
195 * Encode a long at the given offset.
197 public void encode(long v, int offset) {
198 mEncoderState.byteBuffer.putLong(mBaseOffset + offset, v);
202 * Encode a double at the given offset.
204 public void encode(double v, int offset) {
205 mEncoderState.byteBuffer.putDouble(mBaseOffset + offset, v);
209 * Encode a {@link Struct} at the given offset.
211 public void encode(Struct v, int offset) {
213 encodeNullPointer(offset);
216 encodePointerToNextUnclaimedData(offset);
223 public void encode(String v, int offset) {
225 encodeNullPointer(offset);
228 encode(v.getBytes(Charset.forName("utf8")), offset);
232 * Encodes a {@link Handle}.
234 public void encode(Handle v, int offset) {
235 if (v == null || !v.isValid()) {
238 encode(mEncoderState.handles.size(), offset);
239 mEncoderState.handles.add(v);
244 * Encode an {@link Interface}.
246 public <T extends Interface> void encode(T v, int offset, Object builder) {
247 if (mEncoderState.core == null) {
248 throw new UnsupportedOperationException(
249 "The encoder has been created without a Core. It can't encode an interface.");
251 // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented.
252 throw new UnsupportedOperationException("Unimplemented operation");
256 * Encode an {@link InterfaceRequest}.
258 public <T extends Interface> void encode(InterfaceRequest<T> v, int offset) {
259 if (mEncoderState.core == null) {
260 throw new UnsupportedOperationException(
261 "The encoder has been created without a Core. It can't encode an interface.");
263 // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented.
264 throw new UnsupportedOperationException("Unimplemented operation");
268 * Returns an {@link Encoder} suitable for encoding an array of pointer of the given length.
270 public Encoder encodePointerArray(int length, int offset) {
271 return encoderForArray(BindingsHelper.POINTER_SIZE, length, offset);
275 * Encodes an array of booleans.
277 public void encode(boolean[] v, int offset) {
279 encodeNullPointer(offset);
282 byte[] bytes = new byte[(v.length + 7) / BindingsHelper.ALIGNMENT];
283 for (int i = 0; i < bytes.length; ++i) {
284 for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
285 int booleanIndex = BindingsHelper.ALIGNMENT * i + j;
286 if (booleanIndex < v.length && v[booleanIndex]) {
287 bytes[i] |= (1 << j);
291 encodeByteArray(bytes, v.length, offset);
295 * Encodes an array of bytes.
297 public void encode(byte[] v, int offset) {
299 encodeNullPointer(offset);
302 encodeByteArray(v, v.length, offset);
306 * Encodes an array of shorts.
308 public void encode(short[] v, int offset) {
310 encodeNullPointer(offset);
313 encoderForArray(2, v.length, offset).append(v);
317 * Encodes an array of ints.
319 public void encode(int[] v, int offset) {
321 encodeNullPointer(offset);
324 encoderForArray(4, v.length, offset).append(v);
328 * Encodes an array of floats.
330 public void encode(float[] v, int offset) {
332 encodeNullPointer(offset);
335 encoderForArray(4, v.length, offset).append(v);
339 * Encodes an array of longs.
341 public void encode(long[] v, int offset) {
343 encodeNullPointer(offset);
346 encoderForArray(8, v.length, offset).append(v);
350 * Encodes an array of doubles.
352 public void encode(double[] v, int offset) {
354 encodeNullPointer(offset);
357 encoderForArray(8, v.length, offset).append(v);
361 * Encodes an array of {@link Handle}.
363 public void encode(Handle[] v, int offset) {
365 encodeNullPointer(offset);
368 Encoder e = encoderForArray(BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset);
369 for (int i = 0; i < v.length; ++i) {
370 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i);
375 * Encodes an array of {@link Interface}.
377 public <T extends Interface> void encode(T[] v, int offset, Object builder) {
379 encodeNullPointer(offset);
382 Encoder e = encoderForArray(BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset);
383 for (int i = 0; i < v.length; ++i) {
384 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
390 * Encodes an array of {@link Interface}.
392 public <T extends Interface> void encode(InterfaceRequest<T>[] v, int offset) {
394 encodeNullPointer(offset);
397 Encoder e = encoderForArray(BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset);
398 for (int i = 0; i < v.length; ++i) {
399 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i);
404 * Encode a <code>null</code> pointer.
406 public void encodeNullPointer(int offset) {
407 mEncoderState.byteBuffer.putLong(mBaseOffset + offset, 0);
410 private void encodePointerToNextUnclaimedData(int offset) {
411 encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset);
414 private Encoder encoderForArray(int elementSizeInByte, int length, int offset) {
415 return encoderForArrayByTotalSize(length * elementSizeInByte, length, offset);
418 private Encoder encoderForArrayByTotalSize(int byteSize, int length, int offset) {
419 encodePointerToNextUnclaimedData(offset);
420 return getEncoderAtDataOffset(
421 new DataHeader(DataHeader.HEADER_SIZE + BindingsHelper.align(byteSize), length));
424 private void encodeByteArray(byte[] bytes, int length, int offset) {
425 encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes);
428 private void append(byte[] v) {
429 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
430 mEncoderState.byteBuffer.put(v);
433 private void append(short[] v) {
434 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
435 mEncoderState.byteBuffer.asShortBuffer().put(v);
438 private void append(int[] v) {
439 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
440 mEncoderState.byteBuffer.asIntBuffer().put(v);
443 private void append(float[] v) {
444 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
445 mEncoderState.byteBuffer.asFloatBuffer().put(v);
448 private void append(double[] v) {
449 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
450 mEncoderState.byteBuffer.asDoubleBuffer().put(v);
453 private void append(long[] v) {
454 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
455 mEncoderState.byteBuffer.asLongBuffer().put(v);