Encoder.java revision 645501c2ab19a559ce82a1d5a29ced159a4c30fb
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
5package org.chromium.mojo.bindings;
6
7import org.chromium.mojo.bindings.Interface.Proxy.Handler;
8import org.chromium.mojo.system.Core;
9import org.chromium.mojo.system.Handle;
10import org.chromium.mojo.system.MessagePipeHandle;
11import org.chromium.mojo.system.Pair;
12
13import java.nio.ByteBuffer;
14import java.nio.ByteOrder;
15import java.nio.charset.Charset;
16import java.util.ArrayList;
17import 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 */
23public 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         */
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         */
69        public void claimMemory(int size) {
70            dataEnd += size;
71            growIfNeeded();
72        }
73
74        /**
75         * Grow the associated ByteBuffer if needed.
76         */
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     */
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     */
125    public Encoder(Core core, int sizeHint) {
126        this(new EncoderState(core, sizeHint));
127    }
128
129    /**
130     * Private constructor for sub-encoders.
131     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
309    public void encode(AssociatedInterfaceNotSupported v, int offset, boolean nullable) {
310    }
311
312    /**
313     * Encode an associated interface request. Not yet supported.
314     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
530    void claimMemory(int size) {
531        mEncoderState.claimMemory(BindingsHelper.align(size));
532    }
533
534    private void encodePointerToNextUnclaimedData(int offset) {
535        encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset);
536    }
537
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
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
553    private void encodeByteArray(byte[] bytes, int length, int offset) {
554        encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes);
555    }
556
557    private void append(byte[] v) {
558        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
559        mEncoderState.byteBuffer.put(v);
560    }
561
562    private void append(short[] v) {
563        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
564        mEncoderState.byteBuffer.asShortBuffer().put(v);
565    }
566
567    private void append(int[] v) {
568        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
569        mEncoderState.byteBuffer.asIntBuffer().put(v);
570    }
571
572    private void append(float[] v) {
573        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
574        mEncoderState.byteBuffer.asFloatBuffer().put(v);
575    }
576
577    private void append(double[] v) {
578        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
579        mEncoderState.byteBuffer.asDoubleBuffer().put(v);
580    }
581
582    private void append(long[] v) {
583        mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
584        mEncoderState.byteBuffer.asLongBuffer().put(v);
585    }
586
587}
588