| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:convert'; |
| import 'dart:math'; |
| import 'dart:typed_data'; |
| |
| import 'package:zircon/zircon.dart'; |
| |
| import 'bits.dart'; |
| import 'codec.dart'; |
| import 'enum.dart'; |
| import 'error.dart'; |
| import 'interface.dart'; |
| import 'struct.dart'; |
| import 'table.dart'; |
| import 'unknown_data.dart'; |
| import 'wire_format.dart'; |
| import 'xunion.dart'; |
| |
| // ignore_for_file: public_member_api_docs |
| // ignore_for_file: always_specify_types |
| |
| const _notNullable = FidlError('Found null for a non-nullable type', |
| FidlErrorCode.fidlNonNullableTypeWithNullValue); |
| |
| void _throwIfExceedsLimit(int count, int? limit) { |
| if (limit != null && count > limit) { |
| throw FidlError('Found an object wth $count elements. Limited to $limit.', |
| FidlErrorCode.fidlStringTooLong); |
| } |
| } |
| |
| void _throwIfCountMismatch(int count, int expectedCount) { |
| if (count != expectedCount) { |
| throw FidlError('Found an array of count $count. Expected $expectedCount.'); |
| } |
| } |
| |
| // Threshold at which _copy* functions switch from a for loop for copying to |
| // setRange(). |
| // For small loops, constructing a typed view and calling setRange can take |
| // twice as long as directly iterating over the list. |
| // This threshold is chosen somewhat arbitrarily based on partial data |
| // and may not be optimal. |
| const _copySetRangeThreshold = 64; |
| |
| void _copyInt8(ByteData data, Iterable<int> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| for (final element in value) { |
| data.setInt8(off, element); |
| off++; |
| } |
| } else { |
| data.buffer.asInt8List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| void _copyUint8(ByteData data, Iterable<int> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| for (final element in value) { |
| data.setUint8(off, element); |
| off++; |
| } |
| } else { |
| data.buffer.asUint8List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| void _copyInt16(ByteData data, Iterable<int> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| const int stride = 2; |
| for (final element in value) { |
| data.setInt16(off, element, Endian.little); |
| off += stride; |
| } |
| } else { |
| data.buffer.asInt16List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| void _copyUint16(ByteData data, Iterable<int> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| const int stride = 2; |
| for (final element in value) { |
| data.setUint16(off, element, Endian.little); |
| off += stride; |
| } |
| } else { |
| data.buffer.asUint16List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| void _copyInt32(ByteData data, Iterable<int> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| const int stride = 4; |
| for (final element in value) { |
| data.setInt32(off, element, Endian.little); |
| off += stride; |
| } |
| } else { |
| data.buffer.asInt32List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| void _copyUint32(ByteData data, Iterable<int> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| const int stride = 4; |
| for (final element in value) { |
| data.setUint32(off, element, Endian.little); |
| off += stride; |
| } |
| } else { |
| data.buffer.asUint32List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| void _copyInt64(ByteData data, Iterable<int> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| const int stride = 8; |
| for (final element in value) { |
| data.setInt64(off, element, Endian.little); |
| off += stride; |
| } |
| } else { |
| data.buffer.asInt64List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| void _copyUint64(ByteData data, Iterable<int> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| const int stride = 8; |
| for (final element in value) { |
| data.setUint64(off, element, Endian.little); |
| off += stride; |
| } |
| } else { |
| data.buffer.asUint64List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| void _copyFloat32(ByteData data, Iterable<double> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| const int stride = 4; |
| for (final element in value) { |
| data.setFloat32(off, element, Endian.little); |
| off += stride; |
| } |
| } else { |
| data.buffer.asFloat32List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| void _copyFloat64(ByteData data, Iterable<double> value, int offset) { |
| if (value.length < _copySetRangeThreshold) { |
| int off = offset; |
| const int stride = 8; |
| for (final element in value) { |
| data.setFloat64(off, element, Endian.little); |
| off += stride; |
| } |
| } else { |
| data.buffer.asFloat64List(offset).setRange(0, value.length, value); |
| } |
| } |
| |
| const int kAllocAbsent = 0; |
| const int kAllocPresent = 0xFFFFFFFFFFFFFFFF; |
| const int kHandleAbsent = 0; |
| const int kHandlePresent = 0xFFFFFFFF; |
| |
| abstract class FidlType<T, I extends Iterable<T>> { |
| const FidlType({required this.inlineSizeV1, required this.inlineSizeV2}); |
| |
| final int inlineSizeV1; |
| final int inlineSizeV2; |
| |
| int inlineSize(WireFormat wireFormat) { |
| switch (wireFormat) { |
| case WireFormat.v1: |
| return inlineSizeV1; |
| case WireFormat.v2: |
| return inlineSizeV2; |
| default: |
| throw FidlError('unknown wire format'); |
| } |
| } |
| |
| void encode(Encoder encoder, T value, int offset, int depth); |
| |
| void encodeArray(Encoder encoder, Iterable<T> value, int offset, int depth) { |
| int off = offset; |
| final int stride = inlineSize(encoder.wireFormat); |
| for (final element in value) { |
| encode(encoder, element, off, depth); |
| off += stride; |
| } |
| } |
| |
| T decode(Decoder decoder, int offset, int depth); |
| |
| I decodeArray(Decoder decoder, int count, int offset, int depth); |
| } |
| |
| abstract class SimpleFidlType<T> extends FidlType<T, List<T>> { |
| const SimpleFidlType({required int inlineSizeV1, required int inlineSizeV2}) |
| : super(inlineSizeV1: inlineSizeV1, inlineSizeV2: inlineSizeV2); |
| |
| @override |
| List<T> decodeArray(Decoder decoder, int count, int offset, int depth) => |
| List<T>.generate( |
| count, |
| (int i) => decode( |
| decoder, offset + i * inlineSize(decoder.wireFormat), depth)); |
| } |
| |
| /// This encodes/decodes the UnknowRawData assuming it is in an envelope, i.e. |
| /// payload bytes followed directly by handles. |
| class UnknownRawDataType extends SimpleFidlType<UnknownRawData> { |
| const UnknownRawDataType({required this.numBytes, required this.numHandles}) |
| : super(inlineSizeV1: numBytes, inlineSizeV2: numBytes); |
| |
| final int numBytes; |
| final int numHandles; |
| |
| @override |
| void encode(Encoder encoder, UnknownRawData value, int offset, int depth) { |
| _copyUint8(encoder.data, value.data, offset); |
| for (int i = 0; i < value.handles.length; i++) { |
| encoder.addHandleDisposition(HandleDisposition(ZX.HANDLE_OP_MOVE, |
| value.handles[i], ZX.OBJ_TYPE_NONE, ZX.RIGHT_SAME_RIGHTS)); |
| } |
| } |
| |
| @override |
| UnknownRawData decode(Decoder decoder, int offset, int depth) { |
| final Uint8List data = Uint8List(numBytes); |
| for (var i = 0; i < numBytes; i++) { |
| data[i] = decoder.decodeUint8(offset + i); |
| } |
| final handleInfos = |
| List<HandleInfo>.generate(numHandles, (int i) => decoder.claimHandle()); |
| return UnknownRawData( |
| data, handleInfos.map((handleInfo) => handleInfo.handle).toList()); |
| } |
| } |
| |
| class BoolType extends SimpleFidlType<bool> { |
| const BoolType() : super(inlineSizeV1: 1, inlineSizeV2: 1); |
| |
| @override |
| void encode(Encoder encoder, bool value, int offset, int depth) { |
| encoder.encodeBool(value, offset); |
| } |
| |
| @override |
| bool decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeBool(offset); |
| } |
| |
| class StatusType extends Int32Type { |
| const StatusType(); |
| } |
| |
| class Int8Type extends FidlType<int, Int8List> { |
| const Int8Type() : super(inlineSizeV1: 1, inlineSizeV2: 1); |
| |
| @override |
| void encode(Encoder encoder, int value, int offset, int depth) { |
| encoder.encodeInt8(value, offset); |
| } |
| |
| @override |
| int decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeInt8(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<int> value, int offset, int depth) { |
| _copyInt8(encoder.data, value, offset); |
| } |
| |
| @override |
| Int8List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asInt8List(offset, count); |
| } |
| } |
| |
| class Int16Type extends FidlType<int, Int16List> { |
| const Int16Type() : super(inlineSizeV1: 2, inlineSizeV2: 2); |
| |
| @override |
| void encode(Encoder encoder, int value, int offset, int depth) { |
| encoder.encodeInt16(value, offset); |
| } |
| |
| @override |
| int decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeInt16(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<int> value, int offset, int depth) { |
| _copyInt16(encoder.data, value, offset); |
| } |
| |
| @override |
| Int16List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asInt16List(offset, count); |
| } |
| } |
| |
| class Int32Type extends FidlType<int, Int32List> { |
| const Int32Type() : super(inlineSizeV1: 4, inlineSizeV2: 4); |
| |
| @override |
| void encode(Encoder encoder, int value, int offset, int depth) { |
| encoder.encodeInt32(value, offset); |
| } |
| |
| @override |
| int decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeInt32(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<int> value, int offset, int depth) { |
| _copyInt32(encoder.data, value, offset); |
| } |
| |
| @override |
| Int32List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asInt32List(offset, count); |
| } |
| } |
| |
| class Int64Type extends FidlType<int, Int64List> { |
| const Int64Type() : super(inlineSizeV1: 8, inlineSizeV2: 8); |
| |
| @override |
| void encode(Encoder encoder, int value, int offset, int depth) { |
| encoder.encodeInt64(value, offset); |
| } |
| |
| @override |
| int decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeInt64(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<int> value, int offset, int depth) { |
| _copyInt64(encoder.data, value, offset); |
| } |
| |
| @override |
| Int64List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asInt64List(offset, count); |
| } |
| } |
| |
| class Uint8Type extends FidlType<int, Uint8List> { |
| const Uint8Type() : super(inlineSizeV1: 1, inlineSizeV2: 1); |
| |
| @override |
| void encode(Encoder encoder, int value, int offset, int depth) { |
| encoder.encodeUint8(value, offset); |
| } |
| |
| @override |
| int decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeUint8(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<int> value, int offset, int depth) { |
| _copyUint8(encoder.data, value, offset); |
| } |
| |
| @override |
| Uint8List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asUint8List(offset, count); |
| } |
| } |
| |
| class Uint16Type extends FidlType<int, Uint16List> { |
| const Uint16Type() : super(inlineSizeV1: 2, inlineSizeV2: 2); |
| |
| @override |
| void encode(Encoder encoder, int value, int offset, int depth) { |
| encoder.encodeUint16(value, offset); |
| } |
| |
| @override |
| int decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeUint16(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<int> value, int offset, int depth) { |
| _copyUint16(encoder.data, value, offset); |
| } |
| |
| @override |
| Uint16List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asUint16List(offset, count); |
| } |
| } |
| |
| class Uint32Type extends FidlType<int, Uint32List> { |
| const Uint32Type() : super(inlineSizeV1: 4, inlineSizeV2: 4); |
| |
| @override |
| void encode(Encoder encoder, int value, int offset, int depth) { |
| encoder.encodeUint32(value, offset); |
| } |
| |
| @override |
| int decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeUint32(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<int> value, int offset, int depth) { |
| _copyUint32(encoder.data, value, offset); |
| } |
| |
| @override |
| Uint32List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asUint32List(offset, count); |
| } |
| } |
| |
| class Uint64Type extends FidlType<int, Uint64List> { |
| const Uint64Type() : super(inlineSizeV1: 8, inlineSizeV2: 8); |
| |
| @override |
| void encode(Encoder encoder, int value, int offset, int depth) { |
| encoder.encodeUint64(value, offset); |
| } |
| |
| @override |
| int decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeUint64(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<int> value, int offset, int depth) { |
| _copyUint64(encoder.data, value, offset); |
| } |
| |
| @override |
| Uint64List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asUint64List(offset, count); |
| } |
| } |
| |
| class Float32Type extends FidlType<double, Float32List> { |
| const Float32Type() : super(inlineSizeV1: 4, inlineSizeV2: 4); |
| |
| @override |
| void encode(Encoder encoder, double value, int offset, int depth) { |
| encoder.encodeFloat32(value, offset); |
| } |
| |
| @override |
| double decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeFloat32(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<double> value, int offset, int depth) { |
| _copyFloat32(encoder.data, value, offset); |
| } |
| |
| @override |
| Float32List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asFloat32List(offset, count); |
| } |
| } |
| |
| class Float64Type extends FidlType<double, Float64List> { |
| const Float64Type() : super(inlineSizeV1: 8, inlineSizeV2: 8); |
| |
| @override |
| void encode(Encoder encoder, double value, int offset, int depth) { |
| encoder.encodeFloat64(value, offset); |
| } |
| |
| @override |
| double decode(Decoder decoder, int offset, int depth) => |
| decoder.decodeFloat64(offset); |
| |
| @override |
| void encodeArray( |
| Encoder encoder, Iterable<double> value, int offset, int depth) { |
| _copyFloat64(encoder.data, value, offset); |
| } |
| |
| @override |
| Float64List decodeArray(Decoder decoder, int count, int offset, int depth) { |
| return decoder.data.buffer.asFloat64List(offset, count); |
| } |
| } |
| |
| void _encodeHandle( |
| Encoder encoder, HandleDisposition? value, int offset, bool nullable) { |
| final bool present = value != null && value.handle.isValid; |
| if (!nullable && !present) { |
| throw _notNullable; |
| } |
| encoder.encodeUint32(present ? kHandlePresent : kHandleAbsent, offset); |
| if (present) { |
| encoder.addHandleDisposition(value); |
| } |
| } |
| |
| Handle _checkHandleRights(HandleInfo handleInfo, int objectType, int rights) { |
| if (objectType != ZX.OBJ_TYPE_NONE && |
| handleInfo.type != ZX.OBJ_TYPE_NONE && |
| handleInfo.type != objectType) { |
| handleInfo.handle.close(); |
| throw FidlError( |
| 'Handle has object type ${handleInfo.type} but required $objectType.', |
| FidlErrorCode.fidlIncorrectHandleType); |
| } |
| if (rights != ZX.RIGHT_SAME_RIGHTS && |
| handleInfo.rights != ZX.RIGHT_SAME_RIGHTS) { |
| if ((rights & ~handleInfo.rights) != 0) { |
| handleInfo.handle.close(); |
| throw FidlError( |
| 'Required handle rights were missing. Got ${handleInfo.rights}, want $rights.', |
| FidlErrorCode.fidlMissingRequiredHandleRights); |
| } |
| if ((handleInfo.rights & ~rights) != 0) { |
| return handleInfo.handle.replace(rights); |
| } |
| } |
| return handleInfo.handle; |
| } |
| |
| Handle? _decodeNullableHandle( |
| Decoder decoder, int offset, int objectType, int rights) { |
| final int encoded = decoder.decodeUint32(offset); |
| if (encoded != kHandleAbsent && encoded != kHandlePresent) { |
| throw FidlError('Invalid handle encoding: $encoded.'); |
| } |
| if (encoded == kHandlePresent) { |
| final HandleInfo handleInfo = decoder.claimHandle(); |
| return _checkHandleRights(handleInfo, objectType, rights); |
| } |
| return null; |
| } |
| |
| Handle _decodeHandle(Decoder decoder, int offset, int objectType, int rights) { |
| final handle = _decodeNullableHandle(decoder, offset, objectType, rights); |
| if (handle == null) { |
| throw _notNullable; |
| } |
| return handle; |
| } |
| |
| // TODO(pascallouis): By having _HandleWrapper exported, we could DRY this code |
| // by simply having an AbstractHandleType<H extend HandleWrapper<H>> and having |
| // the encoding / decoding once, with the only specialization on a per-type |
| // basis being construction. |
| // Further, if each HandleWrapper were to offer a static ctor function to invoke |
| // their constrctors, could be called directly. |
| // We could also explore having a Handle be itself a subtype of HandleWrapper |
| // to further standardize handling of handles. |
| |
| abstract class _BaseHandleType<W> extends SimpleFidlType<W> { |
| const _BaseHandleType({required this.objectType, required this.rights}) |
| : super(inlineSizeV1: 4, inlineSizeV2: 4); |
| |
| final int objectType; |
| final int rights; |
| |
| W wrap(Handle handle); |
| Handle? unwrap(W wrapper); |
| |
| HandleDisposition? _asHandleDisposition(W value) { |
| Handle? handle = unwrap(value); |
| if (handle != null) { |
| return HandleDisposition(ZX.HANDLE_OP_MOVE, handle, objectType, rights); |
| } |
| return null; |
| } |
| |
| @override |
| void encode(Encoder encoder, W value, int offset, int depth) => |
| _encodeHandle(encoder, _asHandleDisposition(value), offset, false); |
| |
| @override |
| W decode(Decoder decoder, int offset, int depth) => |
| wrap(_decodeHandle(decoder, offset, objectType, rights)); |
| } |
| |
| class NullableHandleType<W> extends SimpleFidlType<W?> { |
| final _BaseHandleType<W> _base; |
| const NullableHandleType(this._base) |
| : super(inlineSizeV1: 4, inlineSizeV2: 4); |
| |
| @override |
| void encode(Encoder encoder, W? value, int offset, int depth) => |
| _encodeHandle( |
| encoder, |
| value == null ? null : _base._asHandleDisposition(value), |
| offset, |
| true); |
| |
| @override |
| W? decode(Decoder decoder, int offset, int depth) { |
| final Handle? handle = |
| _decodeNullableHandle(decoder, offset, _base.objectType, _base.rights); |
| return handle == null ? null : _base.wrap(handle); |
| } |
| } |
| |
| class HandleType extends _BaseHandleType<Handle> { |
| const HandleType({required objectType, required rights}) |
| : super(objectType: objectType, rights: rights); |
| |
| @override |
| Handle wrap(Handle handle) => handle; |
| |
| @override |
| Handle? unwrap(Handle handle) => handle; |
| } |
| |
| class ChannelType extends _BaseHandleType<Channel> { |
| const ChannelType({required objectType, required rights}) |
| : super(objectType: objectType, rights: rights); |
| |
| @override |
| Channel wrap(Handle handle) => Channel(handle); |
| @override |
| Handle? unwrap(Channel wrapper) => wrapper.handle; |
| } |
| |
| class EventPairType extends _BaseHandleType<EventPair> { |
| const EventPairType({required objectType, required rights}) |
| : super(objectType: objectType, rights: rights); |
| |
| @override |
| EventPair wrap(Handle handle) => EventPair(handle); |
| @override |
| Handle? unwrap(EventPair wrapper) => wrapper.handle; |
| } |
| |
| class SocketType extends _BaseHandleType<Socket> { |
| const SocketType({required objectType, required rights}) |
| : super(objectType: objectType, rights: rights); |
| |
| @override |
| Socket wrap(Handle handle) => Socket(handle); |
| @override |
| Handle? unwrap(Socket wrapper) => wrapper.handle; |
| } |
| |
| class VmoType extends _BaseHandleType<Vmo> { |
| const VmoType({required objectType, required rights}) |
| : super(objectType: objectType, rights: rights); |
| |
| @override |
| Vmo wrap(Handle handle) => Vmo(handle); |
| @override |
| Handle? unwrap(Vmo wrapper) => wrapper.handle; |
| } |
| |
| class InterfaceHandleType<T> extends SimpleFidlType<InterfaceHandle<T>> { |
| const InterfaceHandleType() : super(inlineSizeV1: 4, inlineSizeV2: 4); |
| |
| @override |
| void encode( |
| Encoder encoder, InterfaceHandle<T> value, int offset, int depth) { |
| final handle = value.channel?.handle; |
| _encodeHandle( |
| encoder, |
| handle == null |
| ? null |
| : HandleDisposition(ZX.HANDLE_OP_MOVE, handle, ZX.OBJ_TYPE_CHANNEL, |
| ZX.DEFAULT_CHANNEL_RIGHTS), |
| offset, |
| false); |
| } |
| |
| @override |
| InterfaceHandle<T> decode(Decoder decoder, int offset, int depth) => |
| InterfaceHandle<T>(Channel(_decodeHandle( |
| decoder, offset, ZX.OBJ_TYPE_CHANNEL, ZX.DEFAULT_CHANNEL_RIGHTS))); |
| } |
| |
| class NullableInterfaceHandleType<T> |
| extends SimpleFidlType<InterfaceHandle<T>?> { |
| const NullableInterfaceHandleType() : super(inlineSizeV1: 4, inlineSizeV2: 4); |
| |
| @override |
| void encode( |
| Encoder encoder, InterfaceHandle<T>? value, int offset, int depth) { |
| final handle = value?.channel?.handle; |
| _encodeHandle( |
| encoder, |
| handle == null |
| ? null |
| : HandleDisposition(ZX.HANDLE_OP_MOVE, handle, ZX.OBJ_TYPE_CHANNEL, |
| ZX.DEFAULT_CHANNEL_RIGHTS), |
| offset, |
| true); |
| } |
| |
| @override |
| InterfaceHandle<T>? decode(Decoder decoder, int offset, int depth) { |
| final Handle? handle = _decodeNullableHandle( |
| decoder, offset, ZX.OBJ_TYPE_CHANNEL, ZX.DEFAULT_CHANNEL_RIGHTS); |
| return handle == null ? null : InterfaceHandle<T>(Channel(handle)); |
| } |
| } |
| |
| class InterfaceRequestType<T> extends SimpleFidlType<InterfaceRequest<T>> { |
| const InterfaceRequestType() : super(inlineSizeV1: 4, inlineSizeV2: 4); |
| |
| @override |
| void encode( |
| Encoder encoder, InterfaceRequest<T> value, int offset, int depth) { |
| final handle = value.channel?.handle; |
| _encodeHandle( |
| encoder, |
| handle == null |
| ? null |
| : HandleDisposition(ZX.HANDLE_OP_MOVE, handle, ZX.OBJ_TYPE_CHANNEL, |
| ZX.DEFAULT_CHANNEL_RIGHTS), |
| offset, |
| false); |
| } |
| |
| @override |
| InterfaceRequest<T> decode(Decoder decoder, int offset, int depth) => |
| InterfaceRequest<T>(Channel(_decodeHandle( |
| decoder, offset, ZX.OBJ_TYPE_CHANNEL, ZX.DEFAULT_CHANNEL_RIGHTS))); |
| } |
| |
| class NullableInterfaceRequestType<T> |
| extends SimpleFidlType<InterfaceRequest<T>?> { |
| const NullableInterfaceRequestType() |
| : super(inlineSizeV1: 4, inlineSizeV2: 4); |
| |
| @override |
| void encode( |
| Encoder encoder, InterfaceRequest<T>? value, int offset, int depth) { |
| final handle = value?.channel?.handle; |
| _encodeHandle( |
| encoder, |
| handle == null |
| ? null |
| : HandleDisposition(ZX.HANDLE_OP_MOVE, handle, ZX.OBJ_TYPE_CHANNEL, |
| ZX.DEFAULT_CHANNEL_RIGHTS), |
| offset, |
| true); |
| } |
| |
| @override |
| InterfaceRequest<T>? decode(Decoder decoder, int offset, int depth) { |
| final Handle? handle = _decodeNullableHandle( |
| decoder, offset, ZX.OBJ_TYPE_CHANNEL, ZX.DEFAULT_CHANNEL_RIGHTS); |
| return handle == null ? null : InterfaceRequest<T>(Channel(handle)); |
| } |
| } |
| |
| void _encodeString(Encoder encoder, String value, int offset, int depth, |
| int? maybeElementCount) { |
| final bytes = Utf8Encoder().convert(value); |
| final int size = bytes.length; |
| _throwIfExceedsLimit(size, maybeElementCount); |
| encoder |
| ..encodeUint64(size, offset) // size |
| ..encodeUint64(kAllocPresent, offset + 8); // data |
| int childOffset = encoder.alloc(size, depth); |
| _copyUint8(encoder.data, bytes, childOffset); |
| } |
| |
| String? _decodeString(Decoder decoder, int offset, int depth) { |
| final int size = decoder.decodeUint64(offset); |
| final int data = decoder.decodeUint64(offset + 8); |
| if (data == kAllocAbsent) { |
| if (size > 0) { |
| throw FidlError('Expected string, received null', |
| FidlErrorCode.fidlNonEmptyStringWithNullBody); |
| } |
| return null; |
| } |
| final Uint8List bytes = |
| decoder.data.buffer.asUint8List(decoder.claimMemory(size, depth), size); |
| try { |
| return const Utf8Decoder().convert(bytes, 0, size); |
| } on FormatException { |
| throw FidlError('Received a string with invalid UTF8: $bytes'); |
| } |
| } |
| |
| void _encodeNullVector(Encoder encoder, int offset) { |
| encoder |
| ..encodeUint64(0, offset) // size |
| ..encodeUint64(kAllocAbsent, offset + 8); // data |
| } |
| |
| class StringType extends SimpleFidlType<String> { |
| const StringType({ |
| this.maybeElementCount, |
| }) : super(inlineSizeV1: 16, inlineSizeV2: 16); |
| |
| final int? maybeElementCount; |
| |
| // See fidl_string_t. |
| |
| @override |
| void encode(Encoder encoder, String value, int offset, int depth) { |
| _encodeString(encoder, value, offset, depth, maybeElementCount); |
| } |
| |
| @override |
| String decode(Decoder decoder, int offset, int depth) { |
| String? value = _decodeString(decoder, offset, depth); |
| if (value == null) { |
| throw _notNullable; |
| } |
| _throwIfExceedsLimit(value.length, maybeElementCount); |
| return value; |
| } |
| } |
| |
| class NullableStringType extends SimpleFidlType<String?> { |
| const NullableStringType({ |
| this.maybeElementCount, |
| }) : super(inlineSizeV1: 16, inlineSizeV2: 16); |
| |
| final int? maybeElementCount; |
| @override |
| void encode(Encoder encoder, String? value, int offset, int depth) { |
| if (value == null) { |
| _encodeNullVector(encoder, offset); |
| } else { |
| _encodeString(encoder, value, offset, depth, maybeElementCount); |
| } |
| } |
| |
| @override |
| String? decode(Decoder decoder, int offset, int depth) { |
| String? value = _decodeString(decoder, offset, depth); |
| if (value != null) { |
| _throwIfExceedsLimit(value.length, maybeElementCount); |
| } |
| return value; |
| } |
| } |
| |
| class PointerType<T> extends SimpleFidlType<T?> { |
| const PointerType({required this.element}) |
| : super(inlineSizeV1: 8, inlineSizeV2: 8); |
| |
| final FidlType element; |
| |
| @override |
| void encode(Encoder encoder, T? value, int offset, int depth) { |
| if (value == null) { |
| encoder.encodeUint64(kAllocAbsent, offset); |
| } else { |
| encoder.encodeUint64(kAllocPresent, offset); |
| int childOffset = |
| encoder.alloc(element.inlineSize(encoder.wireFormat), depth); |
| element.encode(encoder, value, childOffset, depth + 1); |
| } |
| } |
| |
| @override |
| T? decode(Decoder decoder, int offset, int depth) { |
| final int data = decoder.decodeUint64(offset); |
| validateEncoded(data); |
| if (data == kAllocAbsent) { |
| return null; |
| } |
| T? decoded = element.decode( |
| decoder, |
| decoder.claimMemory(element.inlineSize(decoder.wireFormat), depth), |
| depth + 1); |
| return decoded; |
| } |
| |
| void validateEncoded(int encoded) { |
| if (encoded != kAllocAbsent && encoded != kAllocPresent) { |
| throw FidlError('Invalid pointer encoding: $encoded.'); |
| } |
| } |
| } |
| |
| class MemberType<T> { |
| const MemberType({ |
| required this.type, |
| required this.offset, |
| }); |
| |
| final FidlType type; |
| final int offset; |
| |
| void encode(Encoder encoder, T value, int base, int depth) { |
| type.encode(encoder, value, base + offset, depth); |
| } |
| |
| T decode(Decoder decoder, int base, int depth) { |
| return type.decode(decoder, base + offset, depth); |
| } |
| } |
| |
| class StructType<T extends Struct> extends SimpleFidlType<T> { |
| const StructType({ |
| required int inlineSizeV1, |
| required int inlineSizeV2, |
| required this.structDecode, |
| }) : super(inlineSizeV1: inlineSizeV1, inlineSizeV2: inlineSizeV2); |
| |
| final StructDecode<T> structDecode; |
| |
| @override |
| void encode(Encoder encoder, T value, int offset, int depth) { |
| value.$encode(encoder, offset, depth); |
| } |
| |
| @override |
| T decode(Decoder decoder, int offset, int depth) { |
| return structDecode(decoder, offset, depth); |
| } |
| } |
| |
| int envelopeSize(WireFormat wireFormat) { |
| switch (wireFormat) { |
| case WireFormat.v1: |
| return 16; |
| case WireFormat.v2: |
| return 8; |
| default: |
| throw FidlError('unknown wire format'); |
| } |
| } |
| |
| void _encodeEnvelopePresent<T, I extends Iterable<T>>( |
| Encoder encoder, int offset, int depth, T field, FidlType<T, I> fieldType) { |
| int numHandles = encoder.countHandles(); |
| final fieldOffset = |
| encoder.alloc(fieldType.inlineSize(encoder.wireFormat), depth); |
| fieldType.encode(encoder, field, fieldOffset, depth + 1); |
| numHandles = encoder.countHandles() - numHandles; |
| final numBytes = encoder.nextOffset() - fieldOffset; |
| |
| encoder |
| ..encodeUint32(numBytes, offset) |
| ..encodeUint32(numHandles, offset + 4) |
| ..encodeUint64(kAllocPresent, offset + 8); |
| } |
| |
| void _encodeEnvelopeAbsent(Encoder encoder, int offset) { |
| encoder..encodeUint64(0, offset)..encodeUint64(kAllocAbsent, offset + 8); |
| } |
| |
| enum EnvelopeContentLocation { |
| inline, |
| outOfLine, |
| } |
| |
| enum EnvelopePresence { |
| present, |
| absent, |
| } |
| |
| class EnvelopeHeader { |
| int numBytes; |
| int numHandles; |
| EnvelopePresence presence; |
| EnvelopeContentLocation contentLocation; |
| EnvelopeHeader( |
| this.numBytes, this.numHandles, this.presence, this.contentLocation); |
| } |
| |
| T? _decodeEnvelope<T>( |
| Decoder decoder, int offset, int depth, SimpleFidlType<T>? fieldType, |
| {bool isEmpty = false}) { |
| final header = _decodeEnvelopeHeader(decoder, offset); |
| return _decodeEnvelopeContent( |
| decoder, header, offset, fieldType, depth, isEmpty); |
| } |
| |
| EnvelopeHeader _decodeV1EnvelopeHeader(Decoder decoder, int offset) { |
| int numBytes = decoder.decodeUint32(offset); |
| int numHandles = decoder.decodeUint32(offset + 4); |
| switch (decoder.decodeUint64(offset + 8)) { |
| case kAllocPresent: |
| if (numBytes % 8 != 0) { |
| throw FidlError('improperly aligned byte count', |
| FidlErrorCode.fidlInvalidNumBytesInEnvelope); |
| } |
| return EnvelopeHeader( |
| numBytes, |
| numHandles, |
| EnvelopePresence.present, |
| EnvelopeContentLocation.outOfLine, |
| ); |
| case kAllocAbsent: |
| if (numBytes != 0) |
| throw FidlError('absent envelope with non-zero bytes', |
| FidlErrorCode.fidlInvalidNumBytesInEnvelope); |
| if (numHandles != 0) |
| throw FidlError('absent envelope with non-zero handles', |
| FidlErrorCode.fidlInvalidNumHandlesInEnvelope); |
| return EnvelopeHeader( |
| numBytes, |
| numHandles, |
| EnvelopePresence.absent, |
| EnvelopeContentLocation.outOfLine, |
| ); |
| default: |
| throw FidlError( |
| 'Bad reference encoding', FidlErrorCode.fidlInvalidPresenceIndicator); |
| } |
| } |
| |
| EnvelopeHeader _decodeV2EnvelopeHeader(Decoder decoder, int offset) { |
| int numHandles = decoder.decodeUint16(offset + 4); |
| switch (decoder.decodeUint16(offset + 6)) { |
| case 0: // out of line content |
| int numBytes = decoder.decodeUint32(offset); |
| if (numBytes % 8 != 0) { |
| throw FidlError('improperly aligned byte count', |
| FidlErrorCode.fidlInvalidNumBytesInEnvelope); |
| } |
| return EnvelopeHeader( |
| numBytes, |
| numHandles, |
| (numBytes != 0 || numHandles != 0) |
| ? EnvelopePresence.present |
| : EnvelopePresence.absent, |
| EnvelopeContentLocation.outOfLine, |
| ); |
| case 1: // inlined content |
| return EnvelopeHeader( |
| 0, |
| numHandles, |
| EnvelopePresence.present, |
| EnvelopeContentLocation.inline, |
| ); |
| default: |
| throw FidlError('invalid inline marker in envelope', |
| FidlErrorCode.fidlInvalidInlineMarkerInEnvelope); |
| } |
| } |
| |
| EnvelopeHeader _decodeEnvelopeHeader(Decoder decoder, int offset) { |
| switch (decoder.wireFormat) { |
| case WireFormat.v1: |
| return _decodeV1EnvelopeHeader(decoder, offset); |
| case WireFormat.v2: |
| return _decodeV2EnvelopeHeader(decoder, offset); |
| default: |
| throw FidlError('unknown wire format'); |
| } |
| } |
| |
| T? _decodeEnvelopeContent<T, I extends Iterable<T>>( |
| Decoder decoder, |
| EnvelopeHeader header, |
| int headerOffset, |
| FidlType<T, I>? fieldType, |
| int depth, |
| bool isEmpty) { |
| switch (header.presence) { |
| case EnvelopePresence.present: |
| if (isEmpty) throw FidlError('expected empty envelope'); |
| |
| if (header.contentLocation == EnvelopeContentLocation.inline) { |
| if (fieldType != null) { |
| final claimedHandles = decoder.countClaimedHandles(); |
| final field = fieldType.decode(decoder, headerOffset, depth + 1); |
| final numHandlesConsumed = |
| decoder.countClaimedHandles() - claimedHandles; |
| if (header.numHandles != numHandlesConsumed) { |
| throw FidlError('envelope handles were mis-sized', |
| FidlErrorCode.fidlInvalidNumHandlesInEnvelope); |
| } |
| return field; |
| } |
| for (int i = 0; i < header.numHandles; i++) { |
| final handleInfo = decoder.claimHandle(); |
| try { |
| handleInfo.handle.close(); |
| // ignore: avoid_catches_without_on_clauses |
| } catch (e) { |
| // best effort |
| } |
| } |
| return null; |
| } |
| |
| if (fieldType != null) { |
| final fieldOffset = decoder.claimMemory( |
| fieldType.inlineSize(decoder.wireFormat), depth); |
| final claimedHandles = decoder.countClaimedHandles(); |
| final field = fieldType.decode(decoder, fieldOffset, depth + 1); |
| final numBytesConsumed = decoder.nextOffset() - fieldOffset; |
| final numHandlesConsumed = |
| decoder.countClaimedHandles() - claimedHandles; |
| |
| if (header.numHandles != numHandlesConsumed) { |
| throw FidlError('envelope handles were mis-sized', |
| FidlErrorCode.fidlInvalidNumHandlesInEnvelope); |
| } |
| if (header.numBytes != numBytesConsumed) { |
| throw FidlError('envelope was mis-sized', |
| FidlErrorCode.fidlInvalidNumBytesInEnvelope); |
| } |
| return field; |
| } |
| |
| decoder.claimMemory(header.numBytes, depth); |
| for (int i = 0; i < header.numHandles; i++) { |
| final handleInfo = decoder.claimHandle(); |
| try { |
| handleInfo.handle.close(); |
| // ignore: avoid_catches_without_on_clauses |
| } catch (e) { |
| // best effort |
| } |
| } |
| return null; |
| case EnvelopePresence.absent: |
| if (header.numBytes != 0) |
| throw FidlError('absent envelope with non-zero bytes', |
| FidlErrorCode.fidlInvalidNumBytesInEnvelope); |
| if (header.numHandles != 0) |
| throw FidlError('absent envelope with non-zero handles', |
| FidlErrorCode.fidlInvalidNumHandlesInEnvelope); |
| return null; |
| } |
| } |
| |
| void _maybeThrowOnUnknownHandles(bool resource, UnknownRawData data) { |
| if (!resource && data.handles.isNotEmpty) { |
| data.closeHandles(); |
| throw FidlError('Unknown data contained handles on encode', |
| FidlErrorCode.fidlNonResourceHandle); |
| } |
| } |
| |
| class TableType<T extends Table> extends SimpleFidlType<T> { |
| const TableType({ |
| required int inlineSize, |
| required this.members, |
| required this.ctor, |
| required this.resource, |
| }) : super(inlineSizeV1: inlineSize, inlineSizeV2: inlineSize); |
| |
| final List<FidlType?> members; |
| final TableFactory<T> ctor; |
| final bool resource; |
| |
| @override |
| void encode(Encoder encoder, T value, int offset, int depth) { |
| final unknownDataMap = value.$unknownData; |
| |
| // Determining max index |
| int maxIndex = -1; |
| for (int i = 0; i < members.length; i++) { |
| final field = value.$field(i); |
| if (field != null) { |
| maxIndex = i; |
| } |
| } |
| if (unknownDataMap != null) { |
| // Update the max index based on the table's unknown fields. The unknown |
| // data map is keyed by ordinal, not index, so subtract by one. |
| maxIndex = max(maxIndex, unknownDataMap.keys.fold(0, max) - 1); |
| } |
| int maxOrdinal = maxIndex + 1; |
| |
| // Header. |
| encoder |
| ..encodeUint64(maxOrdinal, offset) |
| ..encodeUint64(kAllocPresent, offset + 8); |
| |
| // Sizing |
| int envelopeOffset = |
| encoder.alloc(maxOrdinal * envelopeSize(encoder.wireFormat), depth); |
| |
| // Envelopes, and fields. |
| for (int i = 0; i <= maxIndex; i++) { |
| var field = value.$field(i); |
| FidlType? fieldType; |
| if (i < members.length) { |
| fieldType = members[i]; |
| } |
| if (fieldType == null && unknownDataMap != null) { |
| // .$field is accessed by index, whereas the unknown data map is |
| // accessed by ordinal |
| final unknownData = unknownDataMap[i + 1]; |
| if (unknownData != null) { |
| _maybeThrowOnUnknownHandles(resource, unknownData); |
| field = unknownData; |
| fieldType = UnknownRawDataType( |
| numBytes: unknownData.data.length, |
| numHandles: unknownData.handles.length); |
| } |
| } |
| |
| if (field != null && fieldType != null) { |
| _encodeEnvelopePresent( |
| encoder, envelopeOffset, depth + 1, field, fieldType); |
| } else { |
| _encodeEnvelopeAbsent(encoder, envelopeOffset); |
| } |
| envelopeOffset += envelopeSize(encoder.wireFormat); |
| } |
| } |
| |
| @override |
| T decode(Decoder decoder, int offset, int depth) { |
| // Header. |
| final int maxOrdinal = decoder.decodeUint64(offset); |
| final int data = decoder.decodeUint64(offset + 8); |
| switch (data) { |
| case kAllocPresent: |
| break; // good |
| case kAllocAbsent: |
| throw FidlError('Unexpected null reference', |
| FidlErrorCode.fidlNonNullableTypeWithNullValue); |
| default: |
| throw FidlError('Bad reference encoding', |
| FidlErrorCode.fidlInvalidPresenceIndicator); |
| } |
| |
| // Early exit on empty table. |
| if (maxOrdinal == 0) { |
| return ctor({}); |
| } |
| |
| // Offsets. |
| int envelopeOffset = decoder.claimMemory( |
| maxOrdinal * envelopeSize(decoder.wireFormat), depth); |
| |
| // Envelopes, and fields. |
| final Map<int, dynamic> argv = {}; |
| Map<int, UnknownRawData> unknownData = {}; |
| for (int ordinal = 1; ordinal <= maxOrdinal; ordinal++) { |
| FidlType? fieldType; |
| if (ordinal <= members.length) { |
| fieldType = members[ordinal - 1]; |
| } else { |
| fieldType = null; |
| } |
| |
| final header = _decodeEnvelopeHeader(decoder, envelopeOffset); |
| final field = _decodeEnvelopeContent( |
| decoder, |
| header, |
| envelopeOffset, |
| fieldType ?? |
| UnknownRawDataType( |
| numBytes: header.numBytes, numHandles: header.numHandles), |
| depth + 1, |
| false); |
| if (field != null) { |
| if (fieldType != null) { |
| argv[ordinal] = field; |
| } else { |
| _maybeThrowOnUnknownHandles(resource, field); |
| unknownData[ordinal] = field; |
| } |
| } |
| envelopeOffset += envelopeSize(decoder.wireFormat); |
| } |
| |
| return ctor(argv, unknownData); |
| } |
| } |
| |
| void _encodeUnion<T extends XUnion>(Encoder encoder, T value, int offset, |
| int depth, Map<int, FidlType> members, bool flexible, bool resource) { |
| final int envelopeOffset = offset + 8; |
| final int ordinal = value.$ordinal; |
| var fieldType = members[ordinal]; |
| final data = value.$data; |
| if (fieldType == null && flexible && data is UnknownRawData) { |
| _maybeThrowOnUnknownHandles(resource, data); |
| fieldType = UnknownRawDataType( |
| numBytes: data.data.length, numHandles: data.handles.length); |
| } |
| if (fieldType == null) |
| throw FidlError('Bad xunion ordinal: $ordinal', |
| FidlErrorCode.fidlStrictXUnionUnknownField); |
| |
| encoder.encodeUint64(ordinal, offset); |
| _encodeEnvelopePresent(encoder, envelopeOffset, depth, data, fieldType); |
| } |
| |
| T? _decodeUnion<T extends XUnion>( |
| Decoder decoder, |
| int offset, |
| int depth, |
| Map<int, FidlType> members, |
| XUnionFactory<T> ctor, |
| bool flexible, |
| bool resource) { |
| final int envelopeOffset = offset + 8; |
| final int ordinal = decoder.decodeUint64(offset); |
| if (ordinal == 0) { |
| _decodeEnvelope(decoder, envelopeOffset, depth, null, isEmpty: true); |
| return null; |
| } else { |
| final header = _decodeEnvelopeHeader(decoder, envelopeOffset); |
| var fieldType = members[ordinal]; |
| if (fieldType == null) { |
| final unknownData = _decodeEnvelopeContent( |
| decoder, |
| header, |
| envelopeOffset, |
| UnknownRawDataType( |
| numBytes: header.numBytes, numHandles: header.numHandles), |
| depth, |
| false); |
| if (unknownData == null) throw FidlError('Bad xunion: missing content'); |
| |
| if (!flexible) { |
| unknownData.closeHandles(); |
| throw FidlError('Bad xunion ordinal: $ordinal', |
| FidlErrorCode.fidlStrictXUnionUnknownField); |
| } |
| |
| _maybeThrowOnUnknownHandles(resource, unknownData); |
| return ctor(ordinal, unknownData); |
| } |
| final field = _decodeEnvelopeContent( |
| decoder, header, envelopeOffset, fieldType, depth, false); |
| if (field == null) throw FidlError('Bad xunion: missing content'); |
| return ctor(ordinal, field); |
| } |
| } |
| |
| class UnionType<T extends XUnion> extends SimpleFidlType<T> { |
| const UnionType( |
| {required this.members, |
| required this.ctor, |
| required this.flexible, |
| required this.resource}) |
| : super(inlineSizeV1: 24, inlineSizeV2: 16); |
| |
| final Map<int, FidlType> members; |
| final XUnionFactory<T> ctor; |
| final bool flexible; |
| final bool resource; |
| |
| @override |
| void encode(Encoder encoder, T value, int offset, int depth) => |
| _encodeUnion(encoder, value, offset, depth, members, flexible, resource); |
| |
| @override |
| T decode(Decoder decoder, int offset, int depth) { |
| T? value = |
| _decodeUnion(decoder, offset, depth, members, ctor, flexible, resource); |
| if (value == null) { |
| throw _notNullable; |
| } |
| return value; |
| } |
| } |
| |
| class NullableUnionType<T extends XUnion> extends SimpleFidlType<T?> { |
| const NullableUnionType( |
| {required this.members, |
| required this.ctor, |
| required this.flexible, |
| required this.resource}) |
| : super(inlineSizeV1: 24, inlineSizeV2: 16); |
| |
| final Map<int, FidlType> members; |
| final XUnionFactory<T> ctor; |
| final bool flexible; |
| final bool resource; |
| |
| @override |
| void encode(Encoder encoder, T? value, int offset, int depth) { |
| if (value == null) { |
| encoder.encodeUint64(0, offset); |
| _encodeEnvelopeAbsent(encoder, offset + 8); |
| } else { |
| _encodeUnion(encoder, value, offset, depth, members, flexible, resource); |
| } |
| } |
| |
| @override |
| T? decode(Decoder decoder, int offset, int depth) => |
| _decodeUnion(decoder, offset, depth, members, ctor, flexible, resource); |
| } |
| |
| class EnumType<T extends Enum> extends SimpleFidlType<T> { |
| const EnumType({ |
| required this.type, |
| required this.values, |
| required this.ctor, |
| }) : super( |
| inlineSizeV1: 0, |
| inlineSizeV2: 0); // Note: inlineSize is calculated below |
| |
| final FidlType<int, Iterable<int>> type; |
| // TODO(fxb/7644): Currently, types are generated as consts. This means that |
| // we cannot use a Set, since there is no const Set. What we want to do |
| // instead is to generate types as vars, avoid cycles which are problematic, |
| // and once that's done, we can switch this to a Set. |
| // TODO(fxb/8008): Use this set to determine whether this value is unknown. |
| // (Alternative design would be to have a boolean field isUnknown.) |
| final Map<int, Null> values; |
| final EnumFactory<T> ctor; |
| |
| @override |
| int get inlineSizeV1 => type.inlineSizeV1; |
| @override |
| int get inlineSizeV2 => type.inlineSizeV2; |
| |
| @override |
| void encode(Encoder encoder, T value, int offset, int depth) { |
| type.encode(encoder, value.$value, offset, depth); |
| } |
| |
| @override |
| T decode(Decoder decoder, int offset, int depth) { |
| return ctor(type.decode(decoder, offset, depth)); |
| } |
| } |
| |
| class BitsType<T extends Bits> extends SimpleFidlType<T> { |
| const BitsType({ |
| required this.type, |
| required this.ctor, |
| }) : super( |
| inlineSizeV1: 0, |
| inlineSizeV2: 0); // Note: inlineSize is calculated below |
| |
| final FidlType<int, dynamic> type; |
| final BitsFactory<T> ctor; |
| |
| @override |
| int get inlineSizeV1 => type.inlineSizeV1; |
| @override |
| int get inlineSizeV2 => type.inlineSizeV2; |
| |
| @override |
| void encode(Encoder encoder, T value, int offset, int depth) { |
| type.encode(encoder, value.$value, offset, depth); |
| } |
| |
| @override |
| T decode(Decoder decoder, int offset, int depth) { |
| return ctor(type.decode(decoder, offset, depth)); |
| } |
| } |
| |
| class MethodType { |
| const MethodType({ |
| required this.request, |
| required this.response, |
| required this.name, |
| required this.requestInlineSize, |
| required this.responseInlineSize, |
| }); |
| |
| final List<MemberType>? request; |
| final List<MemberType>? response; |
| final String name; |
| final int requestInlineSize; |
| final int responseInlineSize; |
| |
| int encodingRequestInlineSize() { |
| return requestInlineSize; |
| } |
| |
| int encodingResponseInlineSize() { |
| return responseInlineSize; |
| } |
| |
| int decodeRequestInlineSize() { |
| return requestInlineSize; |
| } |
| |
| int decodeResponseInlineSize() { |
| return responseInlineSize; |
| } |
| } |
| |
| void _encodeVector<T, I extends Iterable<T>>( |
| Encoder encoder, |
| FidlType<T, I> element, |
| Iterable<T> value, |
| int offset, |
| int depth, |
| int? maybeElementCount) { |
| final int count = value.length; |
| _throwIfExceedsLimit(count, maybeElementCount); |
| encoder |
| ..encodeUint64(count, offset) // count |
| ..encodeUint64(kAllocPresent, offset + 8); // data |
| int childOffset = |
| encoder.alloc(count * element.inlineSize(encoder.wireFormat), depth); |
| element.encodeArray(encoder, value, childOffset, depth + 1); |
| } |
| |
| I? _decodeVector<T, I extends Iterable<T>>(Decoder decoder, |
| FidlType<T, I> element, int offset, int depth, int? maybeElementCount) { |
| final int count = decoder.decodeUint64(offset); |
| final int data = decoder.decodeUint64(offset + 8); |
| _throwIfExceedsLimit(count, maybeElementCount); |
| if (data == kAllocAbsent) { |
| return null; |
| } |
| final int base = decoder.claimMemory( |
| count * element.inlineSize(decoder.wireFormat), depth); |
| I? out = element.decodeArray(decoder, count, base, depth + 1); |
| return out; |
| } |
| |
| class VectorType<T, I extends Iterable<T>> extends SimpleFidlType<I> { |
| const VectorType({ |
| required this.element, |
| required this.maybeElementCount, |
| }) : super(inlineSizeV1: 16, inlineSizeV2: 16); |
| |
| final FidlType<T, I> element; |
| final int? maybeElementCount; |
| |
| @override |
| void encode(Encoder encoder, Iterable<T> value, int offset, int depth) => |
| _encodeVector(encoder, element, value, offset, depth, maybeElementCount); |
| |
| @override |
| I decode(Decoder decoder, int offset, int depth) { |
| I? value = |
| _decodeVector<T, I>(decoder, element, offset, depth, maybeElementCount); |
| if (value == null) { |
| throw _notNullable; |
| } |
| return value; |
| } |
| } |
| |
| class NullableVectorType<T, I extends Iterable<T>> extends SimpleFidlType<I?> { |
| const NullableVectorType({ |
| required this.element, |
| required this.maybeElementCount, |
| }) : super(inlineSizeV1: 16, inlineSizeV2: 16); |
| |
| final FidlType<T, I> element; |
| final int? maybeElementCount; |
| |
| @override |
| void encode(Encoder encoder, Iterable<T>? value, int offset, int depth) { |
| if (value == null) { |
| _encodeNullVector(encoder, offset); |
| } else { |
| _encodeVector(encoder, element, value, offset, depth, maybeElementCount); |
| } |
| } |
| |
| @override |
| I? decode(Decoder decoder, int offset, int depth) => |
| _decodeVector(decoder, element, offset, depth, maybeElementCount); |
| } |
| |
| class ArrayType<T, I extends Iterable<T>> extends SimpleFidlType<I> { |
| const ArrayType({ |
| required this.element, |
| required this.elementCount, |
| }) : super( |
| inlineSizeV1: 0, |
| inlineSizeV2: 0); // Note: inlineSize is calculated below |
| |
| final FidlType<T, I> element; |
| final int elementCount; |
| |
| @override |
| int get inlineSizeV1 => elementCount * element.inlineSizeV1; |
| @override |
| int get inlineSizeV2 => elementCount * element.inlineSizeV2; |
| |
| @override |
| void encode(Encoder encoder, Iterable<T> value, int offset, int depth) { |
| _throwIfCountMismatch(value.length, elementCount); |
| element.encodeArray(encoder, value, offset, depth); |
| } |
| |
| @override |
| I decode(Decoder decoder, int offset, int depth) => |
| element.decodeArray(decoder, elementCount, offset, depth); |
| } |