serialization.dart 7.91 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'dart:math' as math;
6 7
import 'dart:typed_data';

8
export 'dart:typed_data' show ByteData, Endian, Float32List, Float64List, Int32List, Int64List, Uint8List;
9

10 11 12
/// Write-only buffer for incrementally building a [ByteData] instance.
///
/// A WriteBuffer instance can be used only once. Attempts to reuse will result
13
/// in [StateError]s being thrown.
14
///
15
/// The byte order used is [Endian.host] throughout.
16
class WriteBuffer {
17
  /// Creates an interface for incrementally building a [ByteData] instance.
18 19 20 21 22
  /// [startCapacity] determines the start size of the [WriteBuffer] in bytes.
  /// The closer that value is to the real size used, the better the
  /// performance.
  factory WriteBuffer({int startCapacity = 8}) {
    assert(startCapacity > 0);
23 24
    final ByteData eightBytes = ByteData(8);
    final Uint8List eightBytesAsList = eightBytes.buffer.asUint8List();
25
    return WriteBuffer._(Uint8List(startCapacity), eightBytes, eightBytesAsList);
26 27
  }

28 29
  WriteBuffer._(this._buffer, this._eightBytes, this._eightBytesAsList);

30 31
  Uint8List _buffer;
  int _currentSize = 0;
32
  bool _isDone = false;
33
  final ByteData _eightBytes;
34 35 36
  final Uint8List _eightBytesAsList;
  static final Uint8List _zeroBuffer = Uint8List(8);

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
  void _add(int byte) {
    if (_currentSize == _buffer.length) {
      _resize();
    }
    _buffer[_currentSize] = byte;
    _currentSize += 1;
  }

  void _append(Uint8List other) {
    final int newSize = _currentSize + other.length;
    if (newSize >= _buffer.length) {
      _resize(newSize);
    }
    _buffer.setRange(_currentSize, newSize, other);
    _currentSize += other.length;
  }

54
  void _addAll(Uint8List data, [int start = 0, int? end]) {
55 56 57 58
    final int newEnd = end ?? _eightBytesAsList.length;
    final int newSize = _currentSize + (newEnd - start);
    if (newSize >= _buffer.length) {
      _resize(newSize);
59
    }
60 61 62 63 64 65 66 67 68 69
    _buffer.setRange(_currentSize, newSize, data);
    _currentSize = newSize;
  }

  void _resize([int? requiredLength]) {
    final int doubleLength = _buffer.length * 2;
    final int newLength = math.max(requiredLength ?? 0, doubleLength);
    final Uint8List newBuffer = Uint8List(newLength);
    newBuffer.setRange(0, _buffer.length, _buffer);
    _buffer = newBuffer;
70
  }
71

72
  /// Write a Uint8 into the buffer.
73
  void putUint8(int byte) {
74
    assert(!_isDone);
75
    _add(byte);
76 77
  }

78
  /// Write a Uint16 into the buffer.
79
  void putUint16(int value, {Endian? endian}) {
80
    assert(!_isDone);
81
    _eightBytes.setUint16(0, value, endian ?? Endian.host);
82
    _addAll(_eightBytesAsList, 0, 2);
83 84
  }

85
  /// Write a Uint32 into the buffer.
86
  void putUint32(int value, {Endian? endian}) {
87
    assert(!_isDone);
88
    _eightBytes.setUint32(0, value, endian ?? Endian.host);
89
    _addAll(_eightBytesAsList, 0, 4);
90 91
  }

92
  /// Write an Int32 into the buffer.
93
  void putInt32(int value, {Endian? endian}) {
94
    assert(!_isDone);
95
    _eightBytes.setInt32(0, value, endian ?? Endian.host);
96
    _addAll(_eightBytesAsList, 0, 4);
97 98
  }

99
  /// Write an Int64 into the buffer.
100
  void putInt64(int value, {Endian? endian}) {
101
    assert(!_isDone);
102
    _eightBytes.setInt64(0, value, endian ?? Endian.host);
103
    _addAll(_eightBytesAsList, 0, 8);
104 105
  }

106
  /// Write an Float64 into the buffer.
107
  void putFloat64(double value, {Endian? endian}) {
108
    assert(!_isDone);
109
    _alignTo(8);
110
    _eightBytes.setFloat64(0, value, endian ?? Endian.host);
111
    _addAll(_eightBytesAsList);
112 113
  }

114
  /// Write all the values from a [Uint8List] into the buffer.
115
  void putUint8List(Uint8List list) {
116
    assert(!_isDone);
117
    _append(list);
118 119
  }

120
  /// Write all the values from an [Int32List] into the buffer.
121
  void putInt32List(Int32List list) {
122
    assert(!_isDone);
123
    _alignTo(4);
124
    _append(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length));
125 126
  }

127
  /// Write all the values from an [Int64List] into the buffer.
128
  void putInt64List(Int64List list) {
129
    assert(!_isDone);
130
    _alignTo(8);
131
    _append(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length));
132 133
  }

134 135 136 137
  /// Write all the values from a [Float32List] into the buffer.
  void putFloat32List(Float32List list) {
    assert(!_isDone);
    _alignTo(4);
138
    _append(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length));
139 140
  }

141
  /// Write all the values from a [Float64List] into the buffer.
142
  void putFloat64List(Float64List list) {
143
    assert(!_isDone);
144
    _alignTo(8);
145
    _append(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length));
146 147 148
  }

  void _alignTo(int alignment) {
149
    assert(!_isDone);
150
    final int mod = _currentSize % alignment;
151
    if (mod != 0) {
152
      _addAll(_zeroBuffer, 0, alignment - mod);
153 154 155
    }
  }

156
  /// Finalize and return the written [ByteData].
157
  ByteData done() {
158 159 160
    if (_isDone) {
      throw StateError('done() must not be called more than once on the same $runtimeType.');
    }
161 162
    final ByteData result = _buffer.buffer.asByteData(0, _currentSize);
    _buffer = Uint8List(0);
163
    _isDone = true;
164 165 166 167 168 169
    return result;
  }
}

/// Read-only buffer for reading sequentially from a [ByteData] instance.
///
170
/// The byte order used is [Endian.host] throughout.
171 172
class ReadBuffer {
  /// Creates a [ReadBuffer] for reading from the specified [data].
173
  ReadBuffer(this.data);
174

175 176 177 178 179 180 181 182 183 184
  /// The underlying data being read.
  final ByteData data;

  /// The position to read next.
  int _position = 0;

  /// Whether the buffer has data remaining to read.
  bool get hasRemaining => _position < data.lengthInBytes;

  /// Reads a Uint8 from the buffer.
185
  int getUint8() {
186
    return data.getUint8(_position++);
187 188
  }

189
  /// Reads a Uint16 from the buffer.
190
  int getUint16({Endian? endian}) {
191
    final int value = data.getUint16(_position, endian ?? Endian.host);
192
    _position += 2;
193 194 195
    return value;
  }

196
  /// Reads a Uint32 from the buffer.
197
  int getUint32({Endian? endian}) {
198
    final int value = data.getUint32(_position, endian ?? Endian.host);
199
    _position += 4;
200 201 202
    return value;
  }

203
  /// Reads an Int32 from the buffer.
204
  int getInt32({Endian? endian}) {
205
    final int value = data.getInt32(_position, endian ?? Endian.host);
206
    _position += 4;
207 208 209
    return value;
  }

210
  /// Reads an Int64 from the buffer.
211
  int getInt64({Endian? endian}) {
212
    final int value = data.getInt64(_position, endian ?? Endian.host);
213
    _position += 8;
214 215 216
    return value;
  }

217
  /// Reads a Float64 from the buffer.
218
  double getFloat64({Endian? endian}) {
219
    _alignTo(8);
220
    final double value = data.getFloat64(_position, endian ?? Endian.host);
221
    _position += 8;
222 223 224
    return value;
  }

225
  /// Reads the given number of Uint8s from the buffer.
226
  Uint8List getUint8List(int length) {
227 228
    final Uint8List list = data.buffer.asUint8List(data.offsetInBytes + _position, length);
    _position += length;
229 230 231
    return list;
  }

232
  /// Reads the given number of Int32s from the buffer.
233 234
  Int32List getInt32List(int length) {
    _alignTo(4);
235 236
    final Int32List list = data.buffer.asInt32List(data.offsetInBytes + _position, length);
    _position += 4 * length;
237 238 239
    return list;
  }

240
  /// Reads the given number of Int64s from the buffer.
241 242
  Int64List getInt64List(int length) {
    _alignTo(8);
243 244
    final Int64List list = data.buffer.asInt64List(data.offsetInBytes + _position, length);
    _position += 8 * length;
245 246 247
    return list;
  }

248 249 250 251 252 253 254 255
  /// Reads the given number of Float32s from the buffer
  Float32List getFloat32List(int length) {
    _alignTo(4);
    final Float32List list = data.buffer.asFloat32List(data.offsetInBytes + _position, length);
    _position += 4 * length;
    return list;
  }

256
  /// Reads the given number of Float64s from the buffer.
257 258
  Float64List getFloat64List(int length) {
    _alignTo(8);
259 260
    final Float64List list = data.buffer.asFloat64List(data.offsetInBytes + _position, length);
    _position += 8 * length;
261 262 263 264
    return list;
  }

  void _alignTo(int alignment) {
265
    final int mod = _position % alignment;
266
    if (mod != 0) {
267
      _position += alignment - mod;
268
    }
269 270
  }
}