diff options
author | qsr@chromium.org <qsr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-03 12:42:52 +0000 |
---|---|---|
committer | qsr@chromium.org <qsr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-03 12:42:52 +0000 |
commit | fe856e352e51a2b83a16d48576a3999524eedb7a (patch) | |
tree | 1f950124ae80ee40d13b97b6164f139648378652 /mojo/bindings/java/src/org | |
parent | 94d285158732c0685f2f42e85b87d29634c812ee (diff) | |
download | chromium_src-fe856e352e51a2b83a16d48576a3999524eedb7a.zip chromium_src-fe856e352e51a2b83a16d48576a3999524eedb7a.tar.gz chromium_src-fe856e352e51a2b83a16d48576a3999524eedb7a.tar.bz2 |
Add serialization/deserialization of structs for mojo java bindings.
R=viettrungluu@chromium.org,rmcilroy@chromium.org
Review URL: https://codereview.chromium.org/317273006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281255 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo/bindings/java/src/org')
5 files changed, 1047 insertions, 1 deletions
diff --git a/mojo/bindings/java/src/org/chromium/mojo/bindings/BindingsHelper.java b/mojo/bindings/java/src/org/chromium/mojo/bindings/BindingsHelper.java index f2aa046..0288678 100644 --- a/mojo/bindings/java/src/org/chromium/mojo/bindings/BindingsHelper.java +++ b/mojo/bindings/java/src/org/chromium/mojo/bindings/BindingsHelper.java @@ -9,11 +9,23 @@ package org.chromium.mojo.bindings; */ public class BindingsHelper { /** - * Alignment in byte for mojo serialization. + * Alignment in bytes for mojo serialization. */ public static final int ALIGNMENT = 8; /** + * The size, in bytes, of a serialized handle. A handle is serialized as an int representing the + * offset of the handle in the list of handles. + */ + public static final int SERIALIZED_HANDLE_SIZE = 4; + + /** + * The size, in bytes, of a serialized pointer. A pointer is serializaed as an unsigned long + * representing the offset from its position to the pointed elemnt. + */ + public static final int POINTER_SIZE = 8; + + /** * Align |size| on {@link BindingsHelper#ALIGNMENT}. */ public static int align(int size) { diff --git a/mojo/bindings/java/src/org/chromium/mojo/bindings/Decoder.java b/mojo/bindings/java/src/org/chromium/mojo/bindings/Decoder.java new file mode 100644 index 0000000..86a038f --- /dev/null +++ b/mojo/bindings/java/src/org/chromium/mojo/bindings/Decoder.java @@ -0,0 +1,486 @@ +// 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.DataPipe; +import org.chromium.mojo.system.Handle; +import org.chromium.mojo.system.InvalidHandle; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.SharedBufferHandle; + +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +/** + * A Decoder is a helper class for deserializing a mojo struct. It enables deserialization of basic + * types from a {@link Message} object at a given offset into it's byte buffer. + */ +public class Decoder { + + /** + * Helper class to validate the decoded message. + */ + static final class Validator { + + /** + * Minimal value for the next handle to deserialize. + */ + private int mMinNextClaimedHandle = 0; + /** + * Minimal value of the start of the next memory to claim. + */ + private long mMinNextMemory = 0; + + /** + * The maximal memory accessible. + */ + private final long mMaxMemory; + + /** + * Constructor. + */ + Validator(long maxMemory) { + mMaxMemory = maxMemory; + } + + public void claimHandle(int handle) { + if (handle < mMinNextClaimedHandle) { + throw new DeserializationException( + "Trying to access handle out of order."); + } + mMinNextClaimedHandle = handle + 1; + } + + public void claimMemory(long start, long end) { + if (start < mMinNextMemory) { + throw new DeserializationException("Trying to access memory out of order."); + } + if (end < start) { + throw new DeserializationException("Incorrect memory range."); + } + if (end > mMaxMemory) { + throw new DeserializationException("Trying to access out of range memory."); + } + if (start % BindingsHelper.ALIGNMENT != 0 || end % BindingsHelper.ALIGNMENT != 0) { + throw new DeserializationException("Incorrect alignment."); + } + mMinNextMemory = end; + } + } + + /** + * The message to deserialize from. + */ + private final Message mMessage; + + /** + * The base offset in the byte buffer. + */ + private final int mBaseOffset; + + /** + * Validator for the decoded message. + */ + private final Validator mValidator; + + /** + * Constructor. + * + * @param message The message to decode. + */ + public Decoder(Message message) { + this(message, new Validator(message.buffer.limit()), 0); + } + + private Decoder(Message message, Validator validator, int baseOffset) { + mMessage = message; + mMessage.buffer.order(ByteOrder.nativeOrder()); + mBaseOffset = baseOffset; + mValidator = validator; + // Claim the memory for the header. + mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE); + } + + /** + * Deserializes a {@link DataHeader} at the given offset. + */ + public DataHeader readDataHeader() { + int size = readInt(DataHeader.SIZE_OFFSET); + int numFields = readInt(DataHeader.NUM_FIELDS_OFFSET); + // The memory for the header has already been claimed. + mValidator.claimMemory(mBaseOffset + DataHeader.HEADER_SIZE, mBaseOffset + size); + DataHeader res = new DataHeader(size, numFields); + return res; + } + + /** + * Deserializes a byte at the given offset. + */ + public byte readByte(int offset) { + return mMessage.buffer.get(mBaseOffset + offset); + } + + /** + * Deserializes a boolean at the given offset, re-using any partially read byte. + */ + public boolean readBoolean(int offset, int bit) { + return (readByte(offset) & (1 << bit)) != 0; + } + + /** + * Deserializes a short at the given offset. + */ + public short readShort(int offset) { + return mMessage.buffer.getShort(mBaseOffset + offset); + } + + /** + * Deserializes an int at the given offset. + */ + public int readInt(int offset) { + return mMessage.buffer.getInt(mBaseOffset + offset); + } + + /** + * Deserializes a float at the given offset. + */ + public float readFloat(int offset) { + return mMessage.buffer.getFloat(mBaseOffset + offset); + } + + /** + * Deserializes a long at the given offset. + */ + public long readLong(int offset) { + return mMessage.buffer.getLong(mBaseOffset + offset); + } + + /** + * Deserializes a double at the given offset. + */ + public double readDouble(int offset) { + return mMessage.buffer.getDouble(mBaseOffset + offset); + } + + /** + * Deserializes a pointer at the given offset. Returns a Decoder suitable to decode the content + * of the pointer. + */ + public Decoder readPointer(int offset) { + int basePosition = mBaseOffset + offset; + long pointerOffset = readLong(offset); + if (pointerOffset == 0) { + return null; + } + int newPosition = (int) (basePosition + pointerOffset); + // The method |getDecoderAtPosition| will validate that the pointer address is valid. + return getDecoderAtPosition(newPosition); + + } + + /** + * Deserializes an array of boolean at the given offset. + */ + public boolean[] readBooleans(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + byte[] bytes = new byte[si.numFields + 7 / BindingsHelper.ALIGNMENT]; + d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.buffer.get(bytes); + boolean[] result = new boolean[si.numFields]; + for (int i = 0; i < bytes.length; ++i) { + for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) { + int booleanIndex = i * BindingsHelper.ALIGNMENT + j; + if (booleanIndex < result.length) { + result[booleanIndex] = (bytes[i] & (1 << j)) != 0; + } + } + } + return result; + } + + /** + * Deserializes an array of bytes at the given offset. + */ + public byte[] readBytes(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + byte[] result = new byte[si.numFields]; + d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.buffer.get(result); + return result; + } + + /** + * Deserializes an array of shorts at the given offset. + */ + public short[] readShorts(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + short[] result = new short[si.numFields]; + d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.buffer.asShortBuffer().get(result); + return result; + } + + /** + * Deserializes an array of ints at the given offset. + */ + public int[] readInts(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + int[] result = new int[si.numFields]; + d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.buffer.asIntBuffer().get(result); + return result; + } + + /** + * Deserializes an array of floats at the given offset. + */ + public float[] readFloats(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + float[] result = new float[si.numFields]; + d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.buffer.asFloatBuffer().get(result); + return result; + } + + /** + * Deserializes an array of longs at the given offset. + */ + public long[] readLongs(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + long[] result = new long[si.numFields]; + d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.buffer.asLongBuffer().get(result); + return result; + } + + /** + * Deserializes an array of doubles at the given offset. + */ + public double[] readDoubles(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + double[] result = new double[si.numFields]; + d.mMessage.buffer.position(d.mBaseOffset + DataHeader.HEADER_SIZE); + d.mMessage.buffer.asDoubleBuffer().get(result); + return result; + } + + /** + * Deserializes an |Handle| at the given offset. + */ + public Handle readHandle(int offset) { + int index = readInt(offset); + if (index == -1) { + return InvalidHandle.INSTANCE; + } + mValidator.claimHandle(index); + return mMessage.handles.get(index); + } + + /** + * Deserializes a |ConsumerHandle| at the given offset. + */ + public DataPipe.ConsumerHandle readConsumerHandle(int offset) { + return readHandle(offset).toUntypedHandle().toDataPipeConsumerHandle(); + } + + /** + * Deserializes a |ProducerHandle| at the given offset. + */ + public DataPipe.ProducerHandle readProducerHandle(int offset) { + return readHandle(offset).toUntypedHandle().toDataPipeProducerHandle(); + } + + /** + * Deserializes a |MessagePipeHandle| at the given offset. + */ + public MessagePipeHandle readMessagePipeHandle(int offset) { + return readHandle(offset).toUntypedHandle().toMessagePipeHandle(); + } + + /** + * Deserializes a |SharedBufferHandle| at the given offset. + */ + public SharedBufferHandle readSharedBufferHandle(int offset) { + return readHandle(offset).toUntypedHandle().toSharedBufferHandle(); + } + + /** + * Deserializes a |ServiceHandle| at the given offset. + */ + public <S extends Interface> S readServiceInterface(int offset, Object builder) { + // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented. + throw new UnsupportedOperationException("Unimplemented operation"); + } + + /** + * Deserializes a |InterfaceRequest| at the given offset. + */ + public <S extends Interface> InterfaceRequest<S> readInterfaceRequest(int offset) { + // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented. + throw new UnsupportedOperationException("Unimplemented operation"); + } + + /** + * Deserializes a string at the given offset. + */ + public String readString(int offset) { + byte[] bytes = readBytes(offset); + if (bytes == null) { + return null; + } + return new String(bytes, Charset.forName("utf8")); + } + + /** + * Deserializes an array of |Handle| at the given offset. + */ + public Handle[] readHandles(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + Handle[] result = new Handle[si.numFields]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i); + } + return result; + } + + /** + * Deserializes an array of |ConsumerHandle| at the given offset. + */ + public DataPipe.ConsumerHandle[] readConsumerHandles(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + DataPipe.ConsumerHandle[] result = new DataPipe.ConsumerHandle[si.numFields]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readConsumerHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i); + } + return result; + } + + /** + * Deserializes an array of |ProducerHandle| at the given offset. + */ + public DataPipe.ProducerHandle[] readProducerHandles(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + DataPipe.ProducerHandle[] result = new DataPipe.ProducerHandle[si.numFields]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readProducerHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i); + } + return result; + + } + + /** + * Deserializes an array of |MessagePipeHandle| at the given offset. + */ + public MessagePipeHandle[] readMessagePipeHandles(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + MessagePipeHandle[] result = new MessagePipeHandle[si.numFields]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readMessagePipeHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i); + } + return result; + + } + + /** + * Deserializes an array of |SharedBufferHandle| at the given offset. + */ + public SharedBufferHandle[] readSharedBufferHandles(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + SharedBufferHandle[] result = new SharedBufferHandle[si.numFields]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readSharedBufferHandle( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i); + } + return result; + + } + + /** + * Deserializes an array of |ServiceHandle| at the given offset. + */ + public <S extends Interface> S[] readServiceInterfaces(int offset, Object builder) { + // TODO(qsr): To be implemented when interfaces proxy and stubs are implemented. + throw new UnsupportedOperationException("Unimplemented operation"); + } + + /** + * Deserializes an array of |InterfaceRequest| at the given offset. + */ + public <S extends Interface> InterfaceRequest<S>[] readInterfaceRequests(int offset) { + Decoder d = readPointer(offset); + if (d == null) { + return null; + } + DataHeader si = d.readDataHeader(); + @SuppressWarnings("unchecked") + InterfaceRequest<S>[] result = new InterfaceRequest[si.numFields]; + for (int i = 0; i < result.length; ++i) { + result[i] = d.readInterfaceRequest( + DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i); + } + return result; + } + + /** + * Returns a view of this decoder at the offset |offset|. + */ + private Decoder getDecoderAtPosition(int offset) { + return new Decoder(mMessage, mValidator, offset); + } + +} diff --git a/mojo/bindings/java/src/org/chromium/mojo/bindings/DeserializationException.java b/mojo/bindings/java/src/org/chromium/mojo/bindings/DeserializationException.java new file mode 100644 index 0000000..eeb511c --- /dev/null +++ b/mojo/bindings/java/src/org/chromium/mojo/bindings/DeserializationException.java @@ -0,0 +1,26 @@ +// 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; + +/** + * Error when deserializing a mojo message. + */ +public class DeserializationException extends RuntimeException { + + /** + * Constructs a new deserialization exception with the specified detail message. + */ + public DeserializationException(String message) { + super(message); + } + + /** + * Constructs a new deserialization exception with the specified cause. + */ + public DeserializationException(Exception cause) { + super(cause); + } + +} 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 0000000..9ec2cb2 --- /dev/null +++ b/mojo/bindings/java/src/org/chromium/mojo/bindings/Encoder.java @@ -0,0 +1,458 @@ +// 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 { + + /** + * Container class for all state that must be shared between the main encoder and any used sub + * encoder. + */ + private static class EncoderState { + + /** + * The core used to encode interfaces. + */ + public final Core core; + + /** + * The ByteBuffer to which the message will be encoded. + */ + public ByteBuffer byteBuffer; + + /** + * The list of encountered handles. + */ + public final List<Handle> handles = new ArrayList<Handle>(); + + /** + * The current absolute position for the next data section. + */ + public int dataEnd; + + /** + * @param core the |Core| implementation used to generate handles. Only used if the |Struct| + * being encoded contains interfaces, can be |null| otherwise. + * @param bufferSize A hint on the size of the message. Used to build the initial byte + * buffer. + */ + private EncoderState(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; + } + + /** + * Claim the given amount of memory at the end of the buffer, resizing it if needed. + */ + public void claimMemory(int size) { + dataEnd += size; + growIfNeeded(); + } + + /** + * Grow the associated ByteBuffer if needed. + */ + private 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; + } + } + + /** + * 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; + + /** + * The encoder state shared by the main encoder and all its sub-encoder. + */ + private final EncoderState mEncoderState; + + /** + * Returns the result message. + */ + public Message getMessage() { + mEncoderState.byteBuffer.position(0); + mEncoderState.byteBuffer.limit(mEncoderState.dataEnd); + return new Message(mEncoderState.byteBuffer, mEncoderState.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 EncoderState(core, sizeHint)); + } + + /** + * Private constructor for sub-encoders. + */ + private Encoder(EncoderState bufferInformation) { + mEncoderState = bufferInformation; + mBaseOffset = bufferInformation.dataEnd; + } + + /** + * Returns a new encoder that will append to the current buffer. + */ + public Encoder getEncoderAtDataOffset(DataHeader dataHeader) { + Encoder result = new Encoder(mEncoderState); + result.encode(dataHeader); + return result; + + } + + /** + * Encode a {@link DataHeader} and claim the amount of memory required for the data section + * (resizing the buffer if required). + */ + public void encode(DataHeader s) { + mEncoderState.claimMemory(s.size); + encode(s.size, DataHeader.SIZE_OFFSET); + encode(s.numFields, DataHeader.NUM_FIELDS_OFFSET); + } + + /** + * Encode a byte at the given offset. + */ + public void encode(byte v, int offset) { + mEncoderState.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 = mEncoderState.byteBuffer.get(mBaseOffset + offset); + encodedValue |= 1 << bit; + mEncoderState.byteBuffer.put(mBaseOffset + offset, encodedValue); + } + } + + /** + * Encode a short at the given offset. + */ + public void encode(short v, int offset) { + mEncoderState.byteBuffer.putShort(mBaseOffset + offset, v); + } + + /** + * Encode an int at the given offset. + */ + public void encode(int v, int offset) { + mEncoderState.byteBuffer.putInt(mBaseOffset + offset, v); + } + + /** + * Encode a float at the given offset. + */ + public void encode(float v, int offset) { + mEncoderState.byteBuffer.putFloat(mBaseOffset + offset, v); + } + + /** + * Encode a long at the given offset. + */ + public void encode(long v, int offset) { + mEncoderState.byteBuffer.putLong(mBaseOffset + offset, v); + } + + /** + * Encode a double at the given offset. + */ + public void encode(double v, int offset) { + mEncoderState.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; + } + encodePointerToNextUnclaimedData(offset); + v.encode(this); + } + + /** + * 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()) { + encode(-1, offset); + } else { + encode(mEncoderState.handles.size(), offset); + mEncoderState.handles.add(v); + } + } + + /** + * Encode an {@link Interface}. + */ + public <T extends Interface> void encode(T v, int offset, Object builder) { + if (mEncoderState.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 (mEncoderState.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(BindingsHelper.POINTER_SIZE, length, offset); + } + + /** + * 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) { + mEncoderState.byteBuffer.putLong(mBaseOffset + offset, 0); + } + + private void encodePointerToNextUnclaimedData(int offset) { + encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset); + } + + private Encoder encoderForArray(int elementSizeInByte, int length, int offset) { + return encoderForArrayByTotalSize(length * elementSizeInByte, length, offset); + } + + private Encoder encoderForArrayByTotalSize(int byteSize, int length, int offset) { + encodePointerToNextUnclaimedData(offset); + return getEncoderAtDataOffset( + new DataHeader(DataHeader.HEADER_SIZE + BindingsHelper.align(byteSize), length)); + } + + private void encodeByteArray(byte[] bytes, int length, int offset) { + encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes); + } + + private void append(byte[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.put(v); + } + + private void append(short[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asShortBuffer().put(v); + } + + private void append(int[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asIntBuffer().put(v); + } + + private void append(float[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asFloatBuffer().put(v); + } + + private void append(double[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asDoubleBuffer().put(v); + } + + private void append(long[] v) { + mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); + mEncoderState.byteBuffer.asLongBuffer().put(v); + } + +} diff --git a/mojo/bindings/java/src/org/chromium/mojo/bindings/Struct.java b/mojo/bindings/java/src/org/chromium/mojo/bindings/Struct.java index 54bf355..f7c16a2 100644 --- a/mojo/bindings/java/src/org/chromium/mojo/bindings/Struct.java +++ b/mojo/bindings/java/src/org/chromium/mojo/bindings/Struct.java @@ -4,9 +4,73 @@ package org.chromium.mojo.bindings; +import org.chromium.mojo.system.Core; + /** * Base class for all mojo structs. */ public abstract class Struct { + /** + * The header for a mojo complex element. + */ + public static final class DataHeader { + + /** + * The size of a serialized header, in bytes. + */ + public static final int HEADER_SIZE = 8; + + /** + * The offset of the size field. + */ + public static final int SIZE_OFFSET = 0; + + /** + * The offset of the number of fields field. + */ + public static final int NUM_FIELDS_OFFSET = 4; + + public final int size; + public final int numFields; + + /** + * Constructor. + */ + public DataHeader(int size, int numFields) { + super(); + this.size = size; + this.numFields = numFields; + } + } + + /** + * The base size of the struct. + */ + protected final int mEncodedBaseSize; + + /** + * Constructor. + */ + protected Struct(int encodedBaseSize) { + this.mEncodedBaseSize = encodedBaseSize; + } + + /** + * Use the given encoder to serialized this struct. + */ + protected abstract void encode(Encoder encoder); + + /** + * Returns the serialization of the struct. This method can close Handles. + * + * @param core the |Core| implementation used to generate handles. Only used if the |Struct| + * being encoded contains interfaces, can be |null| otherwise. + */ + public Message serialize(Core core) { + Encoder encoder = new Encoder(core, mEncodedBaseSize); + encode(encoder); + return encoder.getMessage(); + } + } |