writeValue method Null safety

void writeValue(
  1. WriteBuffer buffer,
  2. Object? value
)

Writes value to buffer by first writing a type discriminator byte, then the value itself.

This method may be called recursively to serialize container values.

Type discriminators 0 through 127 inclusive are reserved for use by the base class, as follows:

  • null = 0
  • true = 1
  • false = 2
  • 32 bit integer = 3
  • 64 bit integer = 4
  • larger integers = 5 (see below)
  • 64 bit floating-point number = 6
  • String = 7
  • Uint8List = 8
  • Int32List = 9
  • Int64List = 10
  • Float64List = 11
  • List = 12
  • Map = 13
  • Float32List = 14
  • Reserved for future expansion: 15..127

The codec can be extended by overriding this method, calling super for values that the extension does not handle. Type discriminators used by extensions must be greater than or equal to 128 in order to avoid clashes with any later extensions to the base class.

The "larger integers" type, 5, is never used by writeValue. A subclass could represent big integers from another package using that type. The format is first the type byte (0x05), then the actual number as an ASCII string giving the hexadecimal representation of the integer, with the string's length as encoded by writeSize followed by the string bytes. On Android, that would get converted to a java.math.BigInteger object. On iOS, the string representation is returned.

Implementation

void writeValue(WriteBuffer buffer, Object? value) {
  if (value == null) {
    buffer.putUint8(_valueNull);
  } else if (value is bool) {
    buffer.putUint8(value ? _valueTrue : _valueFalse);
  } else if (value is double) {  // Double precedes int because in JS everything is a double.
                                 // Therefore in JS, both `is int` and `is double` always
                                 // return `true`. If we check int first, we'll end up treating
                                 // all numbers as ints and attempt the int32/int64 conversion,
                                 // which is wrong. This precedence rule is irrelevant when
                                 // decoding because we use tags to detect the type of value.
    buffer.putUint8(_valueFloat64);
    buffer.putFloat64(value);
  } else if (value is int) { // ignore: avoid_double_and_int_checks, JS code always goes through the `double` path above
    if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) {
      buffer.putUint8(_valueInt32);
      buffer.putInt32(value);
    } else {
      buffer.putUint8(_valueInt64);
      buffer.putInt64(value);
    }
  } else if (value is String) {
    buffer.putUint8(_valueString);
    final Uint8List asciiBytes = Uint8List(value.length);
    Uint8List? utf8Bytes;
    int utf8Offset = 0;
    // Only do utf8 encoding if we encounter non-ascii characters.
    for (int i = 0; i < value.length; i += 1) {
      final int char = value.codeUnitAt(i);
      if (char <= 0x7f) {
        asciiBytes[i] = char;
      } else {
        utf8Bytes = utf8.encoder.convert(value.substring(i));
        utf8Offset = i;
        break;
      }
    }
    if (utf8Bytes != null) {
      writeSize(buffer, utf8Offset + utf8Bytes.length);
      buffer.putUint8List(Uint8List.sublistView(asciiBytes, 0, utf8Offset));
      buffer.putUint8List(utf8Bytes);
    } else {
      writeSize(buffer, asciiBytes.length);
      buffer.putUint8List(asciiBytes);
    }
  } else if (value is Uint8List) {
    buffer.putUint8(_valueUint8List);
    writeSize(buffer, value.length);
    buffer.putUint8List(value);
  } else if (value is Int32List) {
    buffer.putUint8(_valueInt32List);
    writeSize(buffer, value.length);
    buffer.putInt32List(value);
  } else if (value is Int64List) {
    buffer.putUint8(_valueInt64List);
    writeSize(buffer, value.length);
    buffer.putInt64List(value);
  } else if (value is Float32List) {
    buffer.putUint8(_valueFloat32List);
    writeSize(buffer, value.length);
    buffer.putFloat32List(value);
  } else if (value is Float64List) {
    buffer.putUint8(_valueFloat64List);
    writeSize(buffer, value.length);
    buffer.putFloat64List(value);
  } else if (value is List) {
    buffer.putUint8(_valueList);
    writeSize(buffer, value.length);
    for (final Object? item in value) {
      writeValue(buffer, item);
    }
  } else if (value is Map) {
    buffer.putUint8(_valueMap);
    writeSize(buffer, value.length);
    value.forEach((Object? key, Object? value) {
      writeValue(buffer, key);
      writeValue(buffer, value);
    });
  } else {
    throw ArgumentError.value(value);
  }
}