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