OLD | NEW |
| (Empty) |
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, re
sizing 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 encode
r 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 th
e 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 i
t 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 byt
es. | |
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| oth
erwise. | |
123 * @param sizeHint A hint on the size of the message. Used to build the init
ial 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 t
he 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 + offse
t); | |
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_NULLABL
E; | |
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 encod
e an interface."); | |
276 } | |
277 // If the instance is a proxy, pass the proxy's handle instead of creati
ng 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_HAND
LE_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_S
IZE); | |
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 encod
e an interface."); | |
302 } | |
303 encode(v.passHandle(), offset, nullable); | |
304 } | |
305 | |
306 /** | |
307 * Returns an {@link Encoder} suitable for encoding an array of pointer of t
he given length. | |
308 */ | |
309 public Encoder encodePointerArray(int length, int offset, int expectedLength
) { | |
310 return encoderForArray(BindingsHelper.POINTER_SIZE, length, offset, expe
ctedLength); | |
311 } | |
312 | |
313 /** | |
314 * Returns an {@link Encoder} suitable for encoding an array of union of the
given length. | |
315 */ | |
316 public Encoder encodeUnionArray(int length, int offset, int expectedLength)
{ | |
317 return encoderForArray(BindingsHelper.UNION_SIZE, length, offset, expect
edLength); | |
318 } | |
319 | |
320 /** | |
321 * Encodes an array of booleans. | |
322 */ | |
323 public void encode(boolean[] v, int offset, int arrayNullability, int expect
edLength) { | |
324 if (v == null) { | |
325 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
326 return; | |
327 } | |
328 if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH | |
329 && expectedLength != v.length) { | |
330 throw new SerializationException("Trying to encode a fixed array of
incorrect length."); | |
331 } | |
332 byte[] bytes = new byte[(v.length + 7) / BindingsHelper.ALIGNMENT]; | |
333 for (int i = 0; i < bytes.length; ++i) { | |
334 for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) { | |
335 int booleanIndex = BindingsHelper.ALIGNMENT * i + j; | |
336 if (booleanIndex < v.length && v[booleanIndex]) { | |
337 bytes[i] |= (byte) (1 << j); | |
338 } | |
339 } | |
340 } | |
341 encodeByteArray(bytes, v.length, offset); | |
342 } | |
343 | |
344 /** | |
345 * Encodes an array of bytes. | |
346 */ | |
347 public void encode(byte[] v, int offset, int arrayNullability, int expectedL
ength) { | |
348 if (v == null) { | |
349 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
350 return; | |
351 } | |
352 if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH | |
353 && expectedLength != v.length) { | |
354 throw new SerializationException("Trying to encode a fixed array of
incorrect length."); | |
355 } | |
356 encodeByteArray(v, v.length, offset); | |
357 } | |
358 | |
359 /** | |
360 * Encodes an array of shorts. | |
361 */ | |
362 public void encode(short[] v, int offset, int arrayNullability, int expected
Length) { | |
363 if (v == null) { | |
364 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
365 return; | |
366 } | |
367 encoderForArray(2, v.length, offset, expectedLength).append(v); | |
368 } | |
369 | |
370 /** | |
371 * Encodes an array of ints. | |
372 */ | |
373 public void encode(int[] v, int offset, int arrayNullability, int expectedLe
ngth) { | |
374 if (v == null) { | |
375 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
376 return; | |
377 } | |
378 encoderForArray(4, v.length, offset, expectedLength).append(v); | |
379 } | |
380 | |
381 /** | |
382 * Encodes an array of floats. | |
383 */ | |
384 public void encode(float[] v, int offset, int arrayNullability, int expected
Length) { | |
385 if (v == null) { | |
386 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
387 return; | |
388 } | |
389 encoderForArray(4, v.length, offset, expectedLength).append(v); | |
390 } | |
391 | |
392 /** | |
393 * Encodes an array of longs. | |
394 */ | |
395 public void encode(long[] v, int offset, int arrayNullability, int expectedL
ength) { | |
396 if (v == null) { | |
397 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
398 return; | |
399 } | |
400 encoderForArray(8, v.length, offset, expectedLength).append(v); | |
401 } | |
402 | |
403 /** | |
404 * Encodes an array of doubles. | |
405 */ | |
406 public void encode(double[] v, int offset, int arrayNullability, int expecte
dLength) { | |
407 if (v == null) { | |
408 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
409 return; | |
410 } | |
411 encoderForArray(8, v.length, offset, expectedLength).append(v); | |
412 } | |
413 | |
414 /** | |
415 * Encodes an array of {@link Handle}. | |
416 */ | |
417 public void encode(Handle[] v, int offset, int arrayNullability, int expecte
dLength) { | |
418 if (v == null) { | |
419 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
420 return; | |
421 } | |
422 Encoder e = encoderForArray( | |
423 BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expecte
dLength); | |
424 for (int i = 0; i < v.length; ++i) { | |
425 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HA
NDLE_SIZE * i, | |
426 BindingsHelper.isElementNullable(arrayNullability)); | |
427 } | |
428 } | |
429 | |
430 /** | |
431 * Encodes an array of {@link Interface}. | |
432 */ | |
433 public <T extends Interface> void encode(T[] v, int offset, int arrayNullabi
lity, | |
434 int expectedLength, Interface.Manager<T, ?> manager) { | |
435 if (v == null) { | |
436 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
437 return; | |
438 } | |
439 Encoder e = encoderForArray( | |
440 BindingsHelper.SERIALIZED_INTERFACE_SIZE, v.length, offset, expe
ctedLength); | |
441 for (int i = 0; i < v.length; ++i) { | |
442 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_IN
TERFACE_SIZE * i, | |
443 BindingsHelper.isElementNullable(arrayNullability), manager)
; | |
444 } | |
445 } | |
446 | |
447 public Encoder encoderForMap(int offset) { | |
448 encodePointerToNextUnclaimedData(offset); | |
449 return getEncoderAtDataOffset(BindingsHelper.MAP_STRUCT_HEADER); | |
450 } | |
451 | |
452 /** | |
453 * Encodes a pointer to the next unclaimed memory and returns an encoder sui
table to encode an | |
454 * union at this location. | |
455 */ | |
456 public Encoder encoderForUnionPointer(int offset) { | |
457 encodePointerToNextUnclaimedData(offset); | |
458 Encoder result = new Encoder(mEncoderState); | |
459 result.mEncoderState.claimMemory(16); | |
460 return result; | |
461 } | |
462 | |
463 /** | |
464 * Encodes an array of {@link InterfaceRequest}. | |
465 */ | |
466 public <I extends Interface> void encode(InterfaceRequest<I>[] v, int offset
, | |
467 int arrayNullability, int expectedLength) { | |
468 if (v == null) { | |
469 encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullab
ility)); | |
470 return; | |
471 } | |
472 Encoder e = encoderForArray( | |
473 BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expecte
dLength); | |
474 for (int i = 0; i < v.length; ++i) { | |
475 e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HA
NDLE_SIZE * i, | |
476 BindingsHelper.isElementNullable(arrayNullability)); | |
477 } | |
478 } | |
479 | |
480 /** | |
481 * Encodes a <code>null</code> pointer iff the object is nullable, raises an
exception | |
482 * otherwise. | |
483 */ | |
484 public void encodeNullPointer(int offset, boolean nullable) { | |
485 if (!nullable) { | |
486 throw new SerializationException( | |
487 "Trying to encode a null pointer for a non-nullable type."); | |
488 } | |
489 mEncoderState.byteBuffer.putLong(mBaseOffset + offset, 0); | |
490 } | |
491 | |
492 /** | |
493 * Encodes an invalid handle iff the object is nullable, raises an exception
otherwise. | |
494 */ | |
495 public void encodeInvalidHandle(int offset, boolean nullable) { | |
496 if (!nullable) { | |
497 throw new SerializationException( | |
498 "Trying to encode an invalid handle for a non-nullable type.
"); | |
499 } | |
500 mEncoderState.byteBuffer.putInt(mBaseOffset + offset, -1); | |
501 } | |
502 | |
503 /** | |
504 * Claim the given amount of memory at the end of the buffer, resizing it if
needed. | |
505 */ | |
506 void claimMemory(int size) { | |
507 mEncoderState.claimMemory(BindingsHelper.align(size)); | |
508 } | |
509 | |
510 private void encodePointerToNextUnclaimedData(int offset) { | |
511 encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset); | |
512 } | |
513 | |
514 private Encoder encoderForArray( | |
515 int elementSizeInByte, int length, int offset, int expectedLength) { | |
516 if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH | |
517 && expectedLength != length) { | |
518 throw new SerializationException("Trying to encode a fixed array of
incorrect length."); | |
519 } | |
520 return encoderForArrayByTotalSize(length * elementSizeInByte, length, of
fset); | |
521 } | |
522 | |
523 private Encoder encoderForArrayByTotalSize(int byteSize, int length, int off
set) { | |
524 encodePointerToNextUnclaimedData(offset); | |
525 return getEncoderAtDataOffset( | |
526 new DataHeader(DataHeader.HEADER_SIZE + byteSize, length)); | |
527 } | |
528 | |
529 private void encodeByteArray(byte[] bytes, int length, int offset) { | |
530 encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes); | |
531 } | |
532 | |
533 private void append(byte[] v) { | |
534 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); | |
535 mEncoderState.byteBuffer.put(v); | |
536 } | |
537 | |
538 private void append(short[] v) { | |
539 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); | |
540 mEncoderState.byteBuffer.asShortBuffer().put(v); | |
541 } | |
542 | |
543 private void append(int[] v) { | |
544 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); | |
545 mEncoderState.byteBuffer.asIntBuffer().put(v); | |
546 } | |
547 | |
548 private void append(float[] v) { | |
549 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); | |
550 mEncoderState.byteBuffer.asFloatBuffer().put(v); | |
551 } | |
552 | |
553 private void append(double[] v) { | |
554 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); | |
555 mEncoderState.byteBuffer.asDoubleBuffer().put(v); | |
556 } | |
557 | |
558 private void append(long[] v) { | |
559 mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE); | |
560 mEncoderState.byteBuffer.asLongBuffer().put(v); | |
561 } | |
562 | |
563 } | |
OLD | NEW |