Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
StandardMessageCodec.java
Go to the documentation of this file.
1// Copyright 2013 The Flutter 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 io.flutter.plugin.common;
6
7import androidx.annotation.NonNull;
8import androidx.annotation.Nullable;
9import io.flutter.BuildConfig;
10import io.flutter.Log;
11import java.io.ByteArrayOutputStream;
12import java.math.BigInteger;
13import java.nio.ByteBuffer;
14import java.nio.ByteOrder;
15import java.nio.charset.Charset;
16import java.util.ArrayList;
17import java.util.HashMap;
18import java.util.List;
19import java.util.Map;
20import java.util.Map.Entry;
21
22/**
23 * MessageCodec using the Flutter standard binary encoding.
24 *
25 * <p>This codec is guaranteed to be compatible with the corresponding <a
26 * href="https://api.flutter.dev/flutter/services/StandardMessageCodec-class.html">StandardMessageCodec</a>
27 * on the Dart side. These parts of the Flutter SDK are evolved synchronously.
28 *
29 * <p>Supported messages are acyclic values of these forms:
30 *
31 * <ul>
32 * <li>null
33 * <li>Booleans
34 * <li>Bytes, Shorts, Integers, Longs
35 * <li>BigIntegers (see below)
36 * <li>Floats, Doubles
37 * <li>Strings
38 * <li>byte[], int[], long[], float[], double[]
39 * <li>Lists of supported values
40 * <li>Maps with supported keys and values
41 * </ul>
42 *
43 * <p>On the Dart side, these values are represented as follows:
44 *
45 * <ul>
46 * <li>null: null
47 * <li>Boolean: bool
48 * <li>Byte, Short, Integer, Long: int
49 * <li>Float, Double: double
50 * <li>String: String
51 * <li>byte[]: Uint8List
52 * <li>int[]: Int32List
53 * <li>long[]: Int64List
54 * <li>float[]: Float32List
55 * <li>double[]: Float64List
56 * <li>List: List
57 * <li>Map: Map
58 * </ul>
59 *
60 * <p>BigIntegers are represented in Dart as strings with the hexadecimal representation of the
61 * integer's value.
62 *
63 * <p>To extend the codec, overwrite the writeValue and readValueOfType methods.
64 */
65public class StandardMessageCodec implements MessageCodec<Object> {
66 private static final String TAG = "StandardMessageCodec#";
68
69 @Override
70 @Nullable
71 public ByteBuffer encodeMessage(@Nullable Object message) {
72 if (message == null) {
73 return null;
74 }
76 writeValue(stream, message);
77 final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());
78 buffer.put(stream.buffer(), 0, stream.size());
79 return buffer;
80 }
81
82 @Override
83 @Nullable
84 public Object decodeMessage(@Nullable ByteBuffer message) {
85 if (message == null) {
86 return null;
87 }
88 message.order(ByteOrder.nativeOrder());
89 final Object value = readValue(message);
90 if (message.hasRemaining()) {
91 throw new IllegalArgumentException("Message corrupted");
92 }
93 return value;
94 }
95
96 private static final boolean LITTLE_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
97 private static final Charset UTF8 = Charset.forName("UTF8");
98 private static final byte NULL = 0;
99 private static final byte TRUE = 1;
100 private static final byte FALSE = 2;
101 private static final byte INT = 3;
102 private static final byte LONG = 4;
103 private static final byte BIGINT = 5;
104 private static final byte DOUBLE = 6;
105 private static final byte STRING = 7;
106 private static final byte BYTE_ARRAY = 8;
107 private static final byte INT_ARRAY = 9;
108 private static final byte LONG_ARRAY = 10;
109 private static final byte DOUBLE_ARRAY = 11;
110 private static final byte LIST = 12;
111 private static final byte MAP = 13;
112 private static final byte FLOAT_ARRAY = 14;
113
114 /**
115 * Writes an int representing a size to the specified stream. Uses an expanding code of 1 to 5
116 * bytes to optimize for small values.
117 */
118 protected static final void writeSize(@NonNull ByteArrayOutputStream stream, int value) {
119 if (BuildConfig.DEBUG && 0 > value) {
120 Log.e(TAG, "Attempted to write a negative size.");
121 }
122 if (value < 254) {
123 stream.write(value);
124 } else if (value <= 0xffff) {
125 stream.write(254);
126 writeChar(stream, value);
127 } else {
128 stream.write(255);
129 writeInt(stream, value);
130 }
131 }
132
133 /** Writes the least significant two bytes of the specified int to the specified stream. */
134 protected static final void writeChar(@NonNull ByteArrayOutputStream stream, int value) {
135 if (LITTLE_ENDIAN) {
136 stream.write(value);
137 stream.write(value >>> 8);
138 } else {
139 stream.write(value >>> 8);
140 stream.write(value);
141 }
142 }
143
144 /** Writes the specified int as 4 bytes to the specified stream. */
145 protected static final void writeInt(@NonNull ByteArrayOutputStream stream, int value) {
146 if (LITTLE_ENDIAN) {
147 stream.write(value);
148 stream.write(value >>> 8);
149 stream.write(value >>> 16);
150 stream.write(value >>> 24);
151 } else {
152 stream.write(value >>> 24);
153 stream.write(value >>> 16);
154 stream.write(value >>> 8);
155 stream.write(value);
156 }
157 }
158
159 /** Writes the specified long as 8 bytes to the specified stream. */
160 protected static final void writeLong(@NonNull ByteArrayOutputStream stream, long value) {
161 if (LITTLE_ENDIAN) {
162 stream.write((byte) value);
163 stream.write((byte) (value >>> 8));
164 stream.write((byte) (value >>> 16));
165 stream.write((byte) (value >>> 24));
166 stream.write((byte) (value >>> 32));
167 stream.write((byte) (value >>> 40));
168 stream.write((byte) (value >>> 48));
169 stream.write((byte) (value >>> 56));
170 } else {
171 stream.write((byte) (value >>> 56));
172 stream.write((byte) (value >>> 48));
173 stream.write((byte) (value >>> 40));
174 stream.write((byte) (value >>> 32));
175 stream.write((byte) (value >>> 24));
176 stream.write((byte) (value >>> 16));
177 stream.write((byte) (value >>> 8));
178 stream.write((byte) value);
179 }
180 }
181
182 /** Writes the specified double as 4 bytes to the specified stream */
183 protected static final void writeFloat(@NonNull ByteArrayOutputStream stream, float value) {
184 writeInt(stream, Float.floatToIntBits(value));
185 }
186
187 /** Writes the specified double as 8 bytes to the specified stream. */
188 protected static final void writeDouble(@NonNull ByteArrayOutputStream stream, double value) {
189 writeLong(stream, Double.doubleToLongBits(value));
190 }
191
192 /** Writes the length and then the actual bytes of the specified array to the specified stream. */
193 protected static final void writeBytes(
194 @NonNull ByteArrayOutputStream stream, @NonNull byte[] bytes) {
195 writeSize(stream, bytes.length);
196 stream.write(bytes, 0, bytes.length);
197 }
198
199 /**
200 * Writes a number of padding bytes to the specified stream to ensure that the next value is
201 * aligned to a whole multiple of the specified alignment. An example usage with alignment = 8 is
202 * to ensure doubles are word-aligned in the stream.
203 */
204 protected static final void writeAlignment(@NonNull ByteArrayOutputStream stream, int alignment) {
205 final int mod = stream.size() % alignment;
206 if (mod != 0) {
207 for (int i = 0; i < alignment - mod; i++) {
208 stream.write(0);
209 }
210 }
211 }
212
213 /**
214 * Writes a type discriminator byte and then a byte serialization of the specified value to the
215 * specified stream.
216 *
217 * <p>Subclasses can extend the codec by overriding this method, calling super for values that the
218 * extension does not handle.
219 */
220 protected void writeValue(@NonNull ByteArrayOutputStream stream, @Nullable Object value) {
221 if (value == null || value.equals(null)) {
222 stream.write(NULL);
223 } else if (value instanceof Boolean) {
224 stream.write(((Boolean) value).booleanValue() ? TRUE : FALSE);
225 } else if (value instanceof Number) {
226 if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
227 stream.write(INT);
228 writeInt(stream, ((Number) value).intValue());
229 } else if (value instanceof Long) {
230 stream.write(LONG);
231 writeLong(stream, (long) value);
232 } else if (value instanceof Float || value instanceof Double) {
233 stream.write(DOUBLE);
234 writeAlignment(stream, 8);
235 writeDouble(stream, ((Number) value).doubleValue());
236 } else if (value instanceof BigInteger) {
237 stream.write(BIGINT);
238 writeBytes(stream, ((BigInteger) value).toString(16).getBytes(UTF8));
239 } else {
240 throw new IllegalArgumentException("Unsupported Number type: " + value.getClass());
241 }
242 } else if (value instanceof CharSequence) {
243 stream.write(STRING);
244 writeBytes(stream, value.toString().getBytes(UTF8));
245 } else if (value instanceof byte[]) {
246 stream.write(BYTE_ARRAY);
247 writeBytes(stream, (byte[]) value);
248 } else if (value instanceof int[]) {
249 stream.write(INT_ARRAY);
250 final int[] array = (int[]) value;
251 writeSize(stream, array.length);
252 writeAlignment(stream, 4);
253 for (final int n : array) {
254 writeInt(stream, n);
255 }
256 } else if (value instanceof long[]) {
257 stream.write(LONG_ARRAY);
258 final long[] array = (long[]) value;
259 writeSize(stream, array.length);
260 writeAlignment(stream, 8);
261 for (final long n : array) {
262 writeLong(stream, n);
263 }
264 } else if (value instanceof double[]) {
265 stream.write(DOUBLE_ARRAY);
266 final double[] array = (double[]) value;
267 writeSize(stream, array.length);
268 writeAlignment(stream, 8);
269 for (final double d : array) {
270 writeDouble(stream, d);
271 }
272 } else if (value instanceof List) {
273 stream.write(LIST);
274 final List<?> list = (List) value;
275 writeSize(stream, list.size());
276 for (final Object o : list) {
277 writeValue(stream, o);
278 }
279 } else if (value instanceof Map) {
280 stream.write(MAP);
281 final Map<?, ?> map = (Map) value;
282 writeSize(stream, map.size());
283 for (final Entry<?, ?> entry : map.entrySet()) {
284 writeValue(stream, entry.getKey());
285 writeValue(stream, entry.getValue());
286 }
287 } else if (value instanceof float[]) {
288 stream.write(FLOAT_ARRAY);
289 final float[] array = (float[]) value;
290 writeSize(stream, array.length);
291 writeAlignment(stream, 4);
292 for (final float f : array) {
293 writeFloat(stream, f);
294 }
295 } else {
296 throw new IllegalArgumentException(
297 "Unsupported value: '" + value + "' of type '" + value.getClass() + "'");
298 }
299 }
300
301 /** Reads an int representing a size as written by writeSize. */
302 protected static final int readSize(@NonNull ByteBuffer buffer) {
303 if (!buffer.hasRemaining()) {
304 throw new IllegalArgumentException("Message corrupted");
305 }
306 final int value = buffer.get() & 0xff;
307 if (value < 254) {
308 return value;
309 } else if (value == 254) {
310 return buffer.getChar();
311 } else {
312 return buffer.getInt();
313 }
314 }
315
316 /** Reads a byte array as written by writeBytes. */
317 @NonNull
318 protected static final byte[] readBytes(@NonNull ByteBuffer buffer) {
319 final int length = readSize(buffer);
320 final byte[] bytes = new byte[length];
321 buffer.get(bytes);
322 return bytes;
323 }
324
325 /** Reads alignment padding bytes as written by writeAlignment. */
326 protected static final void readAlignment(@NonNull ByteBuffer buffer, int alignment) {
327 final int mod = buffer.position() % alignment;
328 if (mod != 0) {
329 buffer.position(buffer.position() + alignment - mod);
330 }
331 }
332
333 /** Reads a value as written by writeValue. */
334 @NonNull
335 protected final Object readValue(@NonNull ByteBuffer buffer) {
336 if (!buffer.hasRemaining()) {
337 throw new IllegalArgumentException("Message corrupted");
338 }
339 final byte type = buffer.get();
340 return readValueOfType(type, buffer);
341 }
342
343 /**
344 * Reads a value of the specified type.
345 *
346 * <p>Subclasses may extend the codec by overriding this method, calling super for types that the
347 * extension does not handle.
348 */
349 @Nullable
350 protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
351 final Object result;
352 switch (type) {
353 case NULL:
354 result = null;
355 break;
356 case TRUE:
357 result = true;
358 break;
359 case FALSE:
360 result = false;
361 break;
362 case INT:
363 result = buffer.getInt();
364 break;
365 case LONG:
366 result = buffer.getLong();
367 break;
368 case BIGINT:
369 {
370 final byte[] hex = readBytes(buffer);
371 result = new BigInteger(new String(hex, UTF8), 16);
372 break;
373 }
374 case DOUBLE:
376 result = buffer.getDouble();
377 break;
378 case STRING:
379 {
380 final byte[] bytes = readBytes(buffer);
381 result = new String(bytes, UTF8);
382 break;
383 }
384 case BYTE_ARRAY:
385 {
387 break;
388 }
389 case INT_ARRAY:
390 {
391 final int length = readSize(buffer);
392 final int[] array = new int[length];
394 buffer.asIntBuffer().get(array);
395 result = array;
396 buffer.position(buffer.position() + 4 * length);
397 break;
398 }
399 case LONG_ARRAY:
400 {
401 final int length = readSize(buffer);
402 final long[] array = new long[length];
404 buffer.asLongBuffer().get(array);
405 result = array;
406 buffer.position(buffer.position() + 8 * length);
407 break;
408 }
409 case DOUBLE_ARRAY:
410 {
411 final int length = readSize(buffer);
412 final double[] array = new double[length];
414 buffer.asDoubleBuffer().get(array);
415 result = array;
416 buffer.position(buffer.position() + 8 * length);
417 break;
418 }
419 case LIST:
420 {
421 final int size = readSize(buffer);
422 final List<Object> list = new ArrayList<>(size);
423 for (int i = 0; i < size; i++) {
424 list.add(readValue(buffer));
425 }
426 result = list;
427 break;
428 }
429 case MAP:
430 {
431 final int size = readSize(buffer);
432 final Map<Object, Object> map = new HashMap<>();
433 for (int i = 0; i < size; i++) {
434 map.put(readValue(buffer), readValue(buffer));
435 }
436 result = map;
437 break;
438 }
439 case FLOAT_ARRAY:
440 {
441 final int length = readSize(buffer);
442 final float[] array = new float[length];
444 buffer.asFloatBuffer().get(array);
445 result = array;
446 buffer.position(buffer.position() + 4 * length);
447 break;
448 }
449 default:
450 throw new IllegalArgumentException("Message corrupted");
451 }
452 return result;
453 }
454
455 static final class ExposedByteArrayOutputStream extends ByteArrayOutputStream {
456 byte[] buffer() {
457 return buf;
458 }
459 }
460}
void add(sk_sp< SkIDChangeListener > listener) SK_EXCLUDES(fMutex)
static final boolean DEBUG
static void e(@NonNull String tag, @NonNull String message)
Definition Log.java:84
ByteBuffer encodeMessage(@Nullable Object message)
Object readValueOfType(byte type, @NonNull ByteBuffer buffer)
final Object readValue(@NonNull ByteBuffer buffer)
static final void writeInt(@NonNull ByteArrayOutputStream stream, int value)
static final void writeSize(@NonNull ByteArrayOutputStream stream, int value)
static final void writeChar(@NonNull ByteArrayOutputStream stream, int value)
static final void writeFloat(@NonNull ByteArrayOutputStream stream, float value)
static final void writeAlignment(@NonNull ByteArrayOutputStream stream, int alignment)
void writeValue(@NonNull ByteArrayOutputStream stream, @Nullable Object value)
static final void readAlignment(@NonNull ByteBuffer buffer, int alignment)
static final int readSize(@NonNull ByteBuffer buffer)
static final void writeLong(@NonNull ByteArrayOutputStream stream, long value)
static final void writeBytes( @NonNull ByteArrayOutputStream stream, @NonNull byte[] bytes)
Object decodeMessage(@Nullable ByteBuffer message)
static final void writeDouble(@NonNull ByteArrayOutputStream stream, double value)
static final byte[] readBytes(@NonNull ByteBuffer buffer)
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
static const uint8_t buffer[]
uint8_t value
GAsyncResult * result
size_t length
Win32Message message
return FALSE
#define TAG()
long LONG
int INT