• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
5 package org.chromium.mojo.bindings;
6 
7 import org.chromium.mojo.bindings.Interface.Proxy.Handler;
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;
12 
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;
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  */
23 public 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          */
EncoderState(Core core, int bufferSize)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          */
claimMemory(int size)69         public void claimMemory(int size) {
70             dataEnd += size;
71             growIfNeeded();
72         }
73 
74         /**
75          * Grow the associated ByteBuffer if needed.
76          */
growIfNeeded()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      */
getMessage()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      */
Encoder(Core core, int sizeHint)125     public Encoder(Core core, int sizeHint) {
126         this(new EncoderState(core, sizeHint));
127     }
128 
129     /**
130      * Private constructor for sub-encoders.
131      */
Encoder(EncoderState bufferInformation)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      */
getEncoderAtDataOffset(DataHeader dataHeader)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      */
encode(DataHeader s)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      */
encode(byte v, int offset)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      */
encode(boolean v, int offset, int bit)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      */
encode(short v, int offset)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      */
encode(int v, int offset)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      */
encode(float v, int offset)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      */
encode(long v, int offset)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      */
encode(double v, int offset)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      */
encode(Struct v, int offset, boolean nullable)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      */
encode(Union v, int offset, boolean nullable)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      */
encode(String v, int offset, boolean nullable)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      */
encode(Handle v, int offset, boolean nullable)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      */
encode(T v, int offset, boolean nullable, Interface.Manager<T, ?> manager)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      */
encode(InterfaceRequest<I> v, int offset, boolean nullable)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      */
encode(AssociatedInterfaceNotSupported v, int offset, boolean nullable)309     public void encode(AssociatedInterfaceNotSupported v, int offset, boolean nullable) {
310     }
311 
312     /**
313      * Encode an associated interface request. Not yet supported.
314      */
encode(AssociatedInterfaceRequestNotSupported v, int offset, boolean nullable)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      */
encodePointerArray(int length, int offset, int expectedLength)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      */
encodeUnionArray(int length, int offset, int expectedLength)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      */
encode(boolean[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(byte[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(short[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(int[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(float[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(long[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(double[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(Handle[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(T[] v, int offset, int arrayNullability, int expectedLength, Interface.Manager<T, ?> manager)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 
encoderForMap(int offset)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      */
encoderForUnionPointer(int offset)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      */
encode(InterfaceRequest<I>[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(AssociatedInterfaceNotSupported[] v, int offset, int arrayNullability, int expectedLength)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      */
encode(AssociatedInterfaceRequestNotSupported[] v, int offset, int arrayNullability, int expectedLength)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      */
encodeNullPointer(int offset, boolean nullable)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      */
encodeInvalidHandle(int offset, boolean nullable)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      */
claimMemory(int size)530     void claimMemory(int size) {
531         mEncoderState.claimMemory(BindingsHelper.align(size));
532     }
533 
encodePointerToNextUnclaimedData(int offset)534     private void encodePointerToNextUnclaimedData(int offset) {
535         encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset);
536     }
537 
encoderForArray( int elementSizeInByte, int length, int offset, int expectedLength)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 
encoderForArrayByTotalSize(int byteSize, int length, int offset)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 
encodeByteArray(byte[] bytes, int length, int offset)553     private void encodeByteArray(byte[] bytes, int length, int offset) {
554         encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes);
555     }
556 
append(byte[] v)557     private void append(byte[] v) {
558         mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
559         mEncoderState.byteBuffer.put(v);
560     }
561 
append(short[] v)562     private void append(short[] v) {
563         mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
564         mEncoderState.byteBuffer.asShortBuffer().put(v);
565     }
566 
append(int[] v)567     private void append(int[] v) {
568         mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
569         mEncoderState.byteBuffer.asIntBuffer().put(v);
570     }
571 
append(float[] v)572     private void append(float[] v) {
573         mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
574         mEncoderState.byteBuffer.asFloatBuffer().put(v);
575     }
576 
append(double[] v)577     private void append(double[] v) {
578         mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
579         mEncoderState.byteBuffer.asDoubleBuffer().put(v);
580     }
581 
append(long[] v)582     private void append(long[] v) {
583         mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
584         mEncoderState.byteBuffer.asLongBuffer().put(v);
585     }
586 
587 }
588