Index: mojo/bindings/java/src/org/chromium/mojo/bindings/Encoder.java |
diff --git a/mojo/bindings/java/src/org/chromium/mojo/bindings/Encoder.java b/mojo/bindings/java/src/org/chromium/mojo/bindings/Encoder.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ee6539e9ddcadc490033edc4c4cc45de5e1fd592 |
--- /dev/null |
+++ b/mojo/bindings/java/src/org/chromium/mojo/bindings/Encoder.java |
@@ -0,0 +1,447 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.mojo.bindings; |
+ |
+import org.chromium.mojo.bindings.Struct.DataHeader; |
+import org.chromium.mojo.system.Core; |
+import org.chromium.mojo.system.Handle; |
+ |
+import java.nio.ByteBuffer; |
+import java.nio.ByteOrder; |
+import java.nio.charset.Charset; |
+import java.util.ArrayList; |
+import java.util.List; |
+ |
+/** |
+ * Helper class to encode a mojo struct. It keeps track of the output buffer, resizing it as needed. |
+ * It also keeps track of the associated handles, and the offset of the current data section. |
+ */ |
+public class Encoder { |
+ |
+ /** |
+ * Default initial size of the data buffer. This must be a multiple of 8 bytes. |
+ */ |
+ private static final int INITIAL_BUFFER_SIZE = 1024; |
+ |
+ /** |
+ * Base offset in the byte buffer for writing. |
+ */ |
+ private int mBaseOffset; |
+ |
+ /** |
+ * Container class for all information that must be shared between the main encoder and any used |
+ * sub encoder. |
+ */ |
+ private static class EncoderInformation { |
rmcilroy
2014/06/25 13:14:39
Maybe EncoderState (and update javadoc) is more un
qsr
2014/06/25 14:43:05
Done.
|
+ |
+ /** |
+ * The core to encode interfaces. |
rmcilroy
2014/06/25 13:14:39
The core used to encode interfaces.
qsr
2014/06/25 14:43:05
Done.
|
+ */ |
+ public final Core core; |
+ |
+ /** |
+ * The ByteBuffer to write to. |
rmcilroy
2014/06/25 13:14:39
The ByteBuffer to which the message will be encode
qsr
2014/06/25 14:43:05
Done.
|
+ */ |
+ public ByteBuffer byteBuffer; |
+ |
+ /** |
+ * The current absolute position for the next data section. |
+ */ |
+ public int dataEnd; |
+ |
+ /** |
+ * The list of encountered handles. |
+ */ |
+ public final List<Handle> handles = new ArrayList<Handle>(); |
+ |
+ /** |
+ * Constructor. |
rmcilroy
2014/06/25 13:14:39
javadoc parameters
qsr
2014/06/25 14:43:04
Done.
|
+ */ |
+ private EncoderInformation(Core core, int bufferSize) { |
+ assert bufferSize % BindingsHelper.ALIGNMENT == 0; |
+ this.core = core; |
+ byteBuffer = ByteBuffer.allocateDirect( |
+ bufferSize > 0 ? bufferSize : INITIAL_BUFFER_SIZE); |
+ byteBuffer.order(ByteOrder.nativeOrder()); |
+ dataEnd = 0; |
+ } |
+ |
+ /** |
+ * Grow the associated ByteBuffer if needed. |
+ */ |
+ public void growIfNeeded() { |
+ if (byteBuffer.capacity() >= dataEnd) { |
+ return; |
+ } |
+ int targetSize = byteBuffer.capacity() * 2; |
+ while (targetSize < dataEnd) { |
+ targetSize *= 2; |
+ } |
+ ByteBuffer newBuffer = ByteBuffer.allocateDirect(targetSize); |
+ newBuffer.order(ByteOrder.nativeOrder()); |
+ byteBuffer.position(0); |
+ byteBuffer.limit(byteBuffer.capacity()); |
+ newBuffer.put(byteBuffer); |
+ byteBuffer = newBuffer; |
+ } |
+ } |
+ |
+ /** |
+ * The encoder information shared by the main encoder and all its sub-encoder. |
+ */ |
+ private final EncoderInformation mEncoderInformation; |
+ |
+ /** |
+ * Returns the result message. |
+ */ |
+ public Message getMessage() { |
+ mEncoderInformation.byteBuffer.position(0); |
+ mEncoderInformation.byteBuffer.limit(mEncoderInformation.dataEnd); |
+ return new Message(mEncoderInformation.byteBuffer, mEncoderInformation.handles); |
+ } |
+ |
+ /** |
+ * Constructor. |
+ * |
+ * @param core the |Core| implementation used to generate handles. Only used if the |Struct| |
+ * being encoded contains interfaces, can be |null| otherwise. |
+ * @param sizeHint A hint on the size of the message. Used to build the initial byte buffer. |
+ */ |
+ public Encoder(Core core, int sizeHint) { |
+ this(new EncoderInformation(core, sizeHint)); |
+ } |
+ |
+ /** |
+ * Private constructor for sub-encoders. |
+ */ |
+ private Encoder(EncoderInformation bufferInformation) { |
+ mEncoderInformation = bufferInformation; |
+ mBaseOffset = bufferInformation.dataEnd; |
+ } |
+ |
+ /** |
+ * Returns a new encoder that will append to the current buffer. |
+ */ |
+ public Encoder getEncoderAtDataOffset(DataHeader dataHeader) { |
+ Encoder result = new Encoder(mEncoderInformation); |
+ result.encode(dataHeader); |
+ return result; |
+ |
+ } |
+ |
+ /** |
+ * Encoder a {@link DataHeader}. Also resize the buffer to allow writing the data section if |
rmcilroy
2014/06/25 13:14:39
Encoder a {@link DataHeader} and claim the amount
qsr
2014/06/25 14:43:05
Done.
|
+ * needed. |
+ */ |
+ public void encode(DataHeader s) { |
+ mEncoderInformation.dataEnd = mEncoderInformation.dataEnd + s.size; |
rmcilroy
2014/06/25 13:14:39
I think it makes more sense to update dataEnd and
qsr
2014/06/25 14:43:05
Added claim memory. But kept dataEnd as is, as thi
|
+ mEncoderInformation.growIfNeeded(); |
+ encode(s.size, 0); |
+ encode(s.numFields, 4); |
+ } |
+ |
+ /** |
+ * Encode a byte at the given offset. |
+ */ |
+ public void encode(byte v, int offset) { |
+ mEncoderInformation.byteBuffer.put(mBaseOffset + offset, v); |
+ } |
+ |
+ /** |
+ * Encode a boolean at the given offset. |
+ */ |
+ public void encode(boolean v, int offset, int bit) { |
+ if (v) { |
+ byte encodedValue = mEncoderInformation.byteBuffer.get(mBaseOffset + offset); |
+ encodedValue |= 1 << bit; |
+ mEncoderInformation.byteBuffer.put(mBaseOffset + offset, encodedValue); |
+ } |
+ } |
+ |
+ /** |
+ * Encode a short at the given offset. |
+ */ |
+ public void encode(short v, int offset) { |
+ mEncoderInformation.byteBuffer.putShort(mBaseOffset + offset, v); |
+ } |
+ |
+ /** |
+ * Encode an int at the given offset. |
+ */ |
+ public void encode(int v, int offset) { |
+ mEncoderInformation.byteBuffer.putInt(mBaseOffset + offset, v); |
+ } |
+ |
+ /** |
+ * Encode a float at the given offset. |
+ */ |
+ public void encode(float v, int offset) { |
+ mEncoderInformation.byteBuffer.putFloat(mBaseOffset + offset, v); |
+ } |
+ |
+ /** |
+ * Encode a long at the given offset. |
+ */ |
+ public void encode(long v, int offset) { |
+ mEncoderInformation.byteBuffer.putLong(mBaseOffset + offset, v); |
+ } |
+ |
+ /** |
+ * Encode a double at the given offset. |
+ */ |
+ public void encode(double v, int offset) { |
+ mEncoderInformation.byteBuffer.putDouble(mBaseOffset + offset, v); |
+ } |
+ |
+ /** |
+ * Encode a {@link Struct} at the given offset. |
+ */ |
+ public void encode(Struct v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ encode((long) mEncoderInformation.dataEnd - (mBaseOffset + offset), offset); |
rmcilroy
2014/06/25 13:14:39
maybe pull this out into a private method "encoded
qsr
2014/06/25 14:43:04
Done.
|
+ Encoder e = new Encoder(mEncoderInformation); |
rmcilroy
2014/06/25 13:14:39
This is a bit confusing that we end up creating tw
qsr
2014/06/25 14:43:04
Yes, this was a bug. Thanks.
|
+ v.encode(e); |
+ } |
+ |
+ /** |
+ * Encodes a String. |
+ */ |
+ public void encode(String v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ encode(v.getBytes(Charset.forName("utf8")), offset); |
+ } |
+ |
+ /** |
+ * Encodes a {@link Handle}. |
+ */ |
+ public void encode(Handle v, int offset) { |
+ if (v != null && v.isValid()) { |
rmcilroy
2014/06/25 13:14:39
nit - do this check the other way around to be con
qsr
2014/06/25 14:43:05
Done.
|
+ encode(mEncoderInformation.handles.size(), offset); |
+ mEncoderInformation.handles.add(v); |
+ } else { |
+ encode(-1, offset); |
+ } |
+ } |
+ |
+ /** |
+ * Encode an {@link Interface}. |
+ */ |
+ public <T extends Interface> void encode(T v, int offset, Object builder) { |
+ if (mEncoderInformation.core == null) { |
+ throw new UnsupportedOperationException( |
+ "The encoder has been created without a Core. It can't encode an interface."); |
+ } |
+ // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented. |
+ throw new UnsupportedOperationException("Unimplemented operation"); |
+ } |
+ |
+ /** |
+ * Encode an {@link InterfaceRequest}. |
+ */ |
+ public <T extends Interface> void encode(InterfaceRequest<T> v, int offset) { |
+ if (mEncoderInformation.core == null) { |
+ throw new UnsupportedOperationException( |
+ "The encoder has been created without a Core. It can't encode an interface."); |
+ } |
+ // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented. |
+ throw new UnsupportedOperationException("Unimplemented operation"); |
+ } |
+ |
+ /** |
+ * Returns an {@link Encoder} suitable for encoding an array of pointer of the given length. |
+ */ |
+ public Encoder encodePointerArray(int length, int offset) { |
+ return encoderForArray(8, length, offset); |
rmcilroy
2014/06/25 13:14:39
nit - use constants for element sizes
qsr
2014/06/25 14:43:05
Done.
|
+ } |
+ |
+ /** |
+ * Encodes an array of booleans. |
+ */ |
+ public void encode(boolean[] v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ byte[] bytes = new byte[(v.length + 7) / BindingsHelper.ALIGNMENT]; |
+ for (int i = 0; i < bytes.length; ++i) { |
+ for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) { |
+ int booleanIndex = BindingsHelper.ALIGNMENT * i + j; |
+ if (booleanIndex < v.length && v[booleanIndex]) { |
+ bytes[i] |= (1 << j); |
+ } |
+ } |
+ } |
+ encodeByteArray(bytes, v.length, offset); |
+ } |
+ |
+ /** |
+ * Encodes an array of bytes. |
+ */ |
+ public void encode(byte[] v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ encodeByteArray(v, v.length, offset); |
+ } |
+ |
+ /** |
+ * Encodes an array of shorts. |
+ */ |
+ public void encode(short[] v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ encoderForArray(2, v.length, offset).append(v); |
+ } |
+ |
+ /** |
+ * Encodes an array of ints. |
+ */ |
+ public void encode(int[] v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ encoderForArray(4, v.length, offset).append(v); |
+ } |
+ |
+ /** |
+ * Encodes an array of floats. |
+ */ |
+ public void encode(float[] v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ encoderForArray(4, v.length, offset).append(v); |
+ } |
+ |
+ /** |
+ * Encodes an array of longs. |
+ */ |
+ public void encode(long[] v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ encoderForArray(8, v.length, offset).append(v); |
+ } |
+ |
+ /** |
+ * Encodes an array of doubles. |
+ */ |
+ public void encode(double[] v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ encoderForArray(8, v.length, offset).append(v); |
+ } |
+ |
+ /** |
+ * Encodes an array of {@link Handle}. |
+ */ |
+ public void encode(Handle[] v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ Encoder e = encoderForArray(BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset); |
+ for (int i = 0; i < v.length; ++i) { |
+ e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i); |
+ } |
+ } |
+ |
+ /** |
+ * Encodes an array of {@link Interface}. |
+ */ |
+ public <T extends Interface> void encode(T[] v, int offset, Object builder) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ Encoder e = encoderForArray(BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset); |
+ for (int i = 0; i < v.length; ++i) { |
+ e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i, |
+ builder); |
+ } |
+ } |
+ |
+ /** |
+ * Encodes an array of {@link Interface}. |
+ */ |
+ public <T extends Interface> void encode(InterfaceRequest<T>[] v, int offset) { |
+ if (v == null) { |
+ encodeNullPointer(offset); |
+ return; |
+ } |
+ Encoder e = encoderForArray(BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset); |
+ for (int i = 0; i < v.length; ++i) { |
+ e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i); |
+ } |
+ } |
+ |
+ /** |
+ * Encode a <code>null</code> pointer. |
+ */ |
+ public void encodeNullPointer(int offset) { |
+ mEncoderInformation.byteBuffer.putLong(mBaseOffset + offset, 0); |
+ } |
+ |
+ private Encoder encoderForArray(int elementSizeInByte, int length, int offset) { |
+ return encoderForArrayByTotalSize(length * elementSizeInByte, length, offset); |
+ } |
+ |
+ private Encoder encoderForArrayByTotalSize(int byteSize, int length, int offset) { |
+ assert mEncoderInformation.dataEnd != -1; |
+ encode((long) mEncoderInformation.dataEnd - (mBaseOffset + offset), offset); |
+ Encoder e = new Encoder(mEncoderInformation); |
+ e.encode(new DataHeader(DataHeader.HEADER_SIZE + BindingsHelper.align(byteSize), length)); |
rmcilroy
2014/06/25 13:14:39
nit - instead of the above two lines, can you just
qsr
2014/06/25 14:43:05
Done.
|
+ return e; |
+ } |
+ |
+ private void encodeByteArray(byte[] bytes, int length, int offset) { |
+ encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes); |
+ } |
+ |
+ private void append(byte[] v) { |
+ mEncoderInformation.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); |
+ mEncoderInformation.byteBuffer.put(v); |
+ } |
+ |
+ private void append(short[] v) { |
+ mEncoderInformation.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); |
+ mEncoderInformation.byteBuffer.asShortBuffer().put(v); |
+ } |
+ |
+ private void append(int[] v) { |
+ mEncoderInformation.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); |
+ mEncoderInformation.byteBuffer.asIntBuffer().put(v); |
+ } |
+ |
+ private void append(float[] v) { |
+ mEncoderInformation.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); |
+ mEncoderInformation.byteBuffer.asFloatBuffer().put(v); |
+ } |
+ |
+ private void append(double[] v) { |
+ mEncoderInformation.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); |
+ mEncoderInformation.byteBuffer.asDoubleBuffer().put(v); |
+ } |
+ |
+ private void append(long[] v) { |
+ mEncoderInformation.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); |
+ mEncoderInformation.byteBuffer.asLongBuffer().put(v); |
+ } |
+ |
+} |