Flutter Engine
The Flutter Engine
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 }
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);
127 } else {
128 stream.write(255);
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);
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);
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);
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);
269 for (final double d : array) {
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);
292 for (final float f : array) {
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 @Nullable
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++) {
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}
SkIDChangeListener::List List
GLenum type
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
uint8_t value
GAsyncResult * result
size_t length
Win32Message message
return FALSE
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SI auto map(std::index_sequence< I... >, Fn &&fn, const Args &... args) -> skvx::Vec< sizeof...(I), decltype(fn(args[0]...))>
Definition: SkVx.h:680
#define TAG()
long LONG
Definition: windows_types.h:23
int INT
Definition: windows_types.h:31