Change implementations to return Uint8List rather than List<int> (#123)
This is in preparation for https://github.com/dart-lang/sdk/issues/36900
diff --git a/packages/file/CHANGELOG.md b/packages/file/CHANGELOG.md
index 740dfdf..5c28d0d 100644
--- a/packages/file/CHANGELOG.md
+++ b/packages/file/CHANGELOG.md
@@ -1,3 +1,7 @@
+#### 5.0.8
+
+* Return Uint8List rather than List<int>.
+
#### 5.0.7
* Dart 2 fixes for `RecordingProxyMixin` and `ReplayProxyMixin`.
diff --git a/packages/file/lib/src/backends/chroot.dart b/packages/file/lib/src/backends/chroot.dart
index e4355be..72ce3ca 100644
--- a/packages/file/lib/src/backends/chroot.dart
+++ b/packages/file/lib/src/backends/chroot.dart
@@ -6,6 +6,7 @@
import 'dart:async';
import 'dart:convert';
+import 'dart:typed_data';
import 'package:file/file.dart';
import 'package:file/src/common.dart' as common;
diff --git a/packages/file/lib/src/backends/chroot/chroot_file.dart b/packages/file/lib/src/backends/chroot/chroot_file.dart
index 79b361e..ea65c38 100644
--- a/packages/file/lib/src/backends/chroot/chroot_file.dart
+++ b/packages/file/lib/src/backends/chroot/chroot_file.dart
@@ -251,7 +251,7 @@
getDelegate(followLinks: true).openSync(mode: mode);
@override
- Stream<List<int>> openRead([int start, int end]) =>
+ Stream<Uint8List> openRead([int start, int end]) =>
getDelegate(followLinks: true).openRead(start, end);
@override
@@ -262,11 +262,11 @@
getDelegate(followLinks: true).openWrite(mode: mode, encoding: encoding);
@override
- Future<List<int>> readAsBytes() =>
+ Future<Uint8List> readAsBytes() =>
getDelegate(followLinks: true).readAsBytes();
@override
- List<int> readAsBytesSync() =>
+ Uint8List readAsBytesSync() =>
getDelegate(followLinks: true).readAsBytesSync();
@override
diff --git a/packages/file/lib/src/backends/memory/memory_file.dart b/packages/file/lib/src/backends/memory/memory_file.dart
index 73899dd..019c4be 100644
--- a/packages/file/lib/src/backends/memory/memory_file.dart
+++ b/packages/file/lib/src/backends/memory/memory_file.dart
@@ -5,6 +5,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:math' show min;
+import 'dart:typed_data';
import 'package:file/file.dart';
import 'package:file/src/common.dart' as common;
@@ -176,18 +177,18 @@
throw new UnimplementedError('TODO');
@override
- Stream<List<int>> openRead([int start, int end]) {
+ Stream<Uint8List> openRead([int start, int end]) {
try {
FileNode node = resolvedBacking;
- List<int> content = node.content;
+ Uint8List content = node.content;
if (start != null) {
content = end == null
? content.sublist(start)
: content.sublist(start, min(end, content.length));
}
- return new Stream<List<int>>.fromIterable(<List<int>>[content]);
+ return new Stream<Uint8List>.fromIterable(<Uint8List>[content]);
} catch (e) {
- return new Stream<List<int>>.fromFuture(new Future<List<int>>.error(e));
+ return new Stream<Uint8List>.fromFuture(new Future<Uint8List>.error(e));
}
}
@@ -204,10 +205,11 @@
}
@override
- Future<List<int>> readAsBytes() async => readAsBytesSync();
+ Future<Uint8List> readAsBytes() async => readAsBytesSync();
@override
- List<int> readAsBytesSync() => (resolvedBacking as FileNode).content;
+ Uint8List readAsBytesSync() =>
+ Uint8List.fromList((resolvedBacking as FileNode).content);
@override
Future<String> readAsString({Encoding encoding: utf8}) async =>
@@ -248,7 +250,7 @@
}
FileNode node = _resolvedBackingOrCreate;
_truncateIfNecessary(node, mode);
- node.content.addAll(bytes);
+ node.write(bytes);
node.touch();
}
@@ -278,7 +280,7 @@
void _truncateIfNecessary(FileNode node, io.FileMode mode) {
if (mode == io.FileMode.write || mode == io.FileMode.writeOnly) {
- node.content.clear();
+ node.clear();
}
}
@@ -402,7 +404,7 @@
void _addData(List<int> data) {
_pendingWrites = _pendingWrites.then((FileNode node) {
- node.content.addAll(data);
+ node.write(data);
return node;
});
}
diff --git a/packages/file/lib/src/backends/memory/node.dart b/packages/file/lib/src/backends/memory/node.dart
index b4073a5..65a2539 100644
--- a/packages/file/lib/src/backends/memory/node.dart
+++ b/packages/file/lib/src/backends/memory/node.dart
@@ -2,6 +2,8 @@
// for details. 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:typed_data';
+
import 'package:file/file.dart';
import 'package:file/src/io.dart' as io;
@@ -222,7 +224,8 @@
/// Class that represents the backing for an in-memory regular file.
class FileNode extends RealNode {
/// File contents in bytes.
- List<int> content = <int>[];
+ Uint8List get content => _content;
+ Uint8List _content = Uint8List(0);
/// Constructs a new [FileNode] as a child of the specified [parent].
FileNode(DirectoryNode parent) : super(parent);
@@ -231,7 +234,20 @@
io.FileSystemEntityType get type => io.FileSystemEntityType.file;
@override
- int get size => content.length;
+ int get size => _content.length;
+
+ /// Appends the specified bytes to the end of this node's [content].
+ void write(List<int> bytes) {
+ Uint8List existing = _content;
+ _content = Uint8List(existing.length + bytes.length);
+ _content.setRange(0, existing.length, existing);
+ _content.setRange(existing.length, _content.length, bytes);
+ }
+
+ /// Clears the [content] of the node.
+ void clear() {
+ _content = Uint8List(0);
+ }
/// Copies data from [source] into this node. The [modified] and [changed]
/// fields will be reset as opposed to copied to indicate that this file
@@ -240,7 +256,7 @@
modified = changed = new DateTime.now().millisecondsSinceEpoch;
accessed = source.accessed;
mode = source.mode;
- content = new List<int>.from(source.content);
+ _content = Uint8List.fromList(source.content);
}
}
diff --git a/packages/file/lib/src/backends/record_replay/codecs.dart b/packages/file/lib/src/backends/record_replay/codecs.dart
index 67236e7..bc1b30a 100644
--- a/packages/file/lib/src/backends/record_replay/codecs.dart
+++ b/packages/file/lib/src/backends/record_replay/codecs.dart
@@ -5,6 +5,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io' show systemEncoding;
+import 'dart:typed_data';
import 'package:file/file.dart';
import 'package:path/path.dart' as path;
@@ -455,6 +456,14 @@
List<T> convert(T input) => <T>[input];
}
+class Uint8ListToPlainList extends Converter<Uint8List, List<int>> {
+ /// Creates a new [Uint8ListToPlainList]
+ const Uint8ListToPlainList();
+
+ @override
+ List<int> convert(Uint8List list) => List<int>.from(list);
+}
+
/// Revives a [Directory] entity reference into a [ReplayDirectory].
class ReviveDirectory extends Converter<String, Directory> {
final ReplayFileSystemImpl _fileSystem;
@@ -571,7 +580,7 @@
/// Converts a blob reference (serialized as a [String] of the form
/// `!<filename>`) into a byte list.
-class BlobToBytes extends Converter<String, List<int>> {
+class BlobToBytes extends Converter<String, Uint8List> {
final ReplayFileSystemImpl _fileSystem;
/// Creates a new [BlobToBytes] that will use the specified file system's
@@ -579,7 +588,7 @@
const BlobToBytes(this._fileSystem);
@override
- List<int> convert(String input) {
+ Uint8List convert(String input) {
assert(input.startsWith('!'));
String basename = input.substring(1);
String dirname = _fileSystem.recording.path;
diff --git a/packages/file/lib/src/backends/record_replay/recording_file.dart b/packages/file/lib/src/backends/record_replay/recording_file.dart
index 4400a96..03ccd56 100644
--- a/packages/file/lib/src/backends/record_replay/recording_file.dart
+++ b/packages/file/lib/src/backends/record_replay/recording_file.dart
@@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:convert';
+import 'dart:typed_data';
import 'package:meta/meta.dart';
import 'package:file/file.dart';
@@ -89,11 +90,11 @@
RandomAccessFile _openSync({FileMode mode: FileMode.read}) =>
_wrapRandomAccessFile(delegate.openSync(mode: mode));
- StreamReference<List<int>> _openRead([int start, int end]) {
- return new _BlobStreamReference<List<int>>(
+ StreamReference<Uint8List> _openRead([int start, int end]) {
+ return new _BlobStreamReference<Uint8List>(
file: _newRecordingFile(),
stream: delegate.openRead(start, end),
- writer: (File file, List<int> bytes) {
+ writer: (File file, Uint8List bytes) {
file.writeAsBytesSync(bytes, mode: FileMode.append, flush: true);
},
);
@@ -106,21 +107,21 @@
);
}
- FutureReference<List<int>> _readAsBytes() {
- return new _BlobFutureReference<List<int>>(
+ FutureReference<Uint8List> _readAsBytes() {
+ return new _BlobFutureReference<Uint8List>(
file: _newRecordingFile(),
future: delegate.readAsBytes(),
- writer: (File file, List<int> bytes) async {
+ writer: (File file, Uint8List bytes) async {
await file.writeAsBytes(bytes, flush: true);
},
);
}
- ResultReference<List<int>> _readAsBytesSync() {
- return new _BlobReference<List<int>>(
+ ResultReference<Uint8List> _readAsBytesSync() {
+ return new _BlobReference<Uint8List>(
file: _newRecordingFile(),
value: delegate.readAsBytesSync(),
- writer: (File file, List<int> bytes) {
+ writer: (File file, Uint8List bytes) {
file.writeAsBytesSync(bytes, flush: true);
},
);
diff --git a/packages/file/lib/src/backends/record_replay/replay_file.dart b/packages/file/lib/src/backends/record_replay/replay_file.dart
index 9a21416..d40e046 100644
--- a/packages/file/lib/src/backends/record_replay/replay_file.dart
+++ b/packages/file/lib/src/backends/record_replay/replay_file.dart
@@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:convert';
+import 'dart:typed_data';
import 'package:file/file.dart';
@@ -20,10 +21,11 @@
Converter<String, File> reviveFile = new ReviveFile(fileSystem);
Converter<String, Future<File>> reviveFileAsFuture =
reviveFile.fuse(const ToFuture<File>());
- Converter<String, List<int>> blobToBytes = new BlobToBytes(fileSystem);
- Converter<String, Future<List<int>>> blobToBytesFuture =
- blobToBytes.fuse(const ToFuture<List<int>>());
- Converter<String, String> blobToString = blobToBytes.fuse(utf8.decoder);
+ Converter<String, Uint8List> blobToBytes = new BlobToBytes(fileSystem);
+ Converter<String, Future<Uint8List>> blobToBytesFuture =
+ blobToBytes.fuse(const ToFuture<Uint8List>());
+ Converter<String, String> blobToString =
+ blobToBytes.fuse(const Uint8ListToPlainList()).fuse(utf8.decoder);
Converter<String, Future<String>> blobToStringFuture =
blobToString.fuse(const ToFuture<String>());
Converter<String, RandomAccessFile> reviveRandomAccessFile =
@@ -36,9 +38,9 @@
blobToString.fuse(lineSplitter);
Converter<String, Future<List<String>>> blobToLinesFuture =
blobToLines.fuse(const ToFuture<List<String>>());
- Converter<String, Stream<List<int>>> blobToByteStream = blobToBytes
- .fuse(const Listify<List<int>>())
- .fuse(const ToStream<List<int>>());
+ Converter<String, Stream<Uint8List>> blobToByteStream = blobToBytes
+ .fuse(const Listify<Uint8List>())
+ .fuse(const ToStream<Uint8List>());
Converter<int, Future<DateTime>> reviveDateTime =
DateTimeCodec.deserialize.fuse(const ToFuture<DateTime>());
diff --git a/packages/file/lib/src/backends/record_replay/replay_random_access_file.dart b/packages/file/lib/src/backends/record_replay/replay_random_access_file.dart
index c1219b4..9bfb4b3 100644
--- a/packages/file/lib/src/backends/record_replay/replay_random_access_file.dart
+++ b/packages/file/lib/src/backends/record_replay/replay_random_access_file.dart
@@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:convert';
+import 'dart:typed_data';
import 'package:file/file.dart';
@@ -29,8 +30,8 @@
#closeSync: const Passthrough<Null>(),
#readByte: const ToFuture<int>(),
#readByteSync: const Passthrough<int>(),
- #read: const ToFuture<List<int>>(),
- #readSync: const Passthrough<List<int>>(),
+ #read: const ToFuture<Uint8List>(),
+ #readSync: const Passthrough<Uint8List>(),
#readInto: const ToFuture<int>(),
#readIntoSync: const Passthrough<int>(),
#writeByte: reviveRandomAccessFileAsFuture,
diff --git a/packages/file/lib/src/forwarding/forwarding_file.dart b/packages/file/lib/src/forwarding/forwarding_file.dart
index ef99d92..3d76af0 100644
--- a/packages/file/lib/src/forwarding/forwarding_file.dart
+++ b/packages/file/lib/src/forwarding/forwarding_file.dart
@@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:convert';
+import 'dart:typed_data';
import 'package:file/src/io.dart' as io;
import 'package:file/file.dart';
@@ -71,8 +72,8 @@
delegate.openSync(mode: mode);
@override
- Stream<List<int>> openRead([int start, int end]) =>
- delegate.openRead(start, end);
+ Stream<Uint8List> openRead([int start, int end]) =>
+ delegate.openRead(start, end).transform(const _ToUint8List());
@override
IOSink openWrite({
@@ -82,10 +83,14 @@
delegate.openWrite(mode: mode, encoding: encoding);
@override
- Future<List<int>> readAsBytes() => delegate.readAsBytes();
+ Future<Uint8List> readAsBytes() {
+ return delegate.readAsBytes().then<Uint8List>((List<int> bytes) {
+ return Uint8List.fromList(bytes);
+ });
+ }
@override
- List<int> readAsBytesSync() => delegate.readAsBytesSync();
+ Uint8List readAsBytesSync() => Uint8List.fromList(delegate.readAsBytesSync());
@override
Future<String> readAsString({Encoding encoding: utf8}) =>
@@ -151,3 +156,31 @@
flush: flush,
);
}
+
+class _ToUint8List extends Converter<List<int>, Uint8List> {
+ const _ToUint8List();
+
+ @override
+ Uint8List convert(List<int> input) => Uint8List.fromList(input);
+
+ @override
+ Sink<List<int>> startChunkedConversion(Sink<Uint8List> sink) {
+ return _Uint8ListConversionSink(sink);
+ }
+}
+
+class _Uint8ListConversionSink implements Sink<List<int>> {
+ const _Uint8ListConversionSink(this._target);
+
+ final Sink<Uint8List> _target;
+
+ @override
+ void add(List<int> data) {
+ _target.add(Uint8List.fromList(data));
+ }
+
+ @override
+ void close() {
+ _target.close();
+ }
+}
diff --git a/packages/file/pubspec.yaml b/packages/file/pubspec.yaml
index 2402f8c..dc7ccdd 100644
--- a/packages/file/pubspec.yaml
+++ b/packages/file/pubspec.yaml
@@ -1,5 +1,5 @@
name: file
-version: 5.0.7
+version: 5.0.8
authors:
- Matan Lurey <matanl@google.com>
- Yegor Jbanov <yjbanov@google.com>
diff --git a/packages/file/test/common_tests.dart b/packages/file/test/common_tests.dart
index 23923eb..281ac13 100644
--- a/packages/file/test/common_tests.dart
+++ b/packages/file/test/common_tests.dart
@@ -6,6 +6,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
+import 'dart:typed_data';
import 'package:file/file.dart';
import 'package:file_testing/file_testing.dart';
@@ -2394,6 +2395,15 @@
File f = fs.file(ns('/foo'))..createSync();
expect(f.readAsBytesSync(), isEmpty);
});
+
+ test('returns a copy, not a view, of the file content', () {
+ File f = fs.file(ns('/foo'))..createSync();
+ f.writeAsBytesSync(<int>[1, 2, 3, 4]);
+ List<int> result = f.readAsBytesSync();
+ expect(result, <int>[1, 2, 3, 4]);
+ result[0] = 10;
+ expect(f.readAsBytesSync(), <int>[1, 2, 3, 4]);
+ });
});
group('readAsString', () {
diff --git a/packages/file/test/record_replay_matchers.dart b/packages/file/test/record_replay_matchers.dart
index 34c5387..952e1e8 100644
--- a/packages/file/test/record_replay_matchers.dart
+++ b/packages/file/test/record_replay_matchers.dart
@@ -14,9 +14,9 @@
};
const Map<Type, Matcher> _kTypeMatchers = const <Type, Matcher>{
- MethodEvent: const isInstanceOf<MethodEvent<dynamic>>(),
- PropertyGetEvent: const isInstanceOf<PropertyGetEvent<dynamic>>(),
- PropertySetEvent: const isInstanceOf<PropertySetEvent<dynamic>>(),
+ MethodEvent: const TypeMatcher<MethodEvent<dynamic>>(),
+ PropertyGetEvent: const TypeMatcher<PropertyGetEvent<dynamic>>(),
+ PropertySetEvent: const TypeMatcher<PropertySetEvent<dynamic>>(),
};
/// Returns a matcher that will match against a [MethodEvent].
@@ -49,13 +49,10 @@
/// scope of the match (e.g. by property value, target object, etc).
PropertySet setsProperty([dynamic name]) => new PropertySet._(name);
-/// A matcher that successfully matches against an instance of
-/// [NoMatchingInvocationError].
-const Matcher isNoMatchingInvocationError = const _NoMatchingInvocationError();
-
/// A matcher that successfully matches against a future or function
/// that throws a [NoMatchingInvocationError].
-Matcher throwsNoMatchingInvocationError = throwsA(isNoMatchingInvocationError);
+Matcher throwsNoMatchingInvocationError =
+ throwsA(const TypeMatcher<NoMatchingInvocationError>());
/// Base class for matchers that match against generic [InvocationEvent]
/// instances.
@@ -583,11 +580,3 @@
return _matcher.describe(description);
}
}
-
-class _NoMatchingInvocationError extends TypeMatcher<dynamic> {
- const _NoMatchingInvocationError() : super("NoMatchingInvocationError");
-
- @override
- bool matches(dynamic item, Map<dynamic, dynamic> matchState) =>
- item is NoMatchingInvocationError;
-}
diff --git a/packages/file/test/recording_test.dart b/packages/file/test/recording_test.dart
index 03286fe..cb9dc46 100644
--- a/packages/file/test/recording_test.dart
+++ b/packages/file/test/recording_test.dart
@@ -9,7 +9,7 @@
import 'package:file/memory.dart';
import 'package:file/record_replay.dart';
import 'package:file/src/backends/record_replay/codecs.dart';
-import 'package:file/src/backends/record_replay/common.dart';
+import 'package:file/src/backends/record_replay/common.dart' hide TypeMatcher;
import 'package:file/src/backends/record_replay/events.dart';
import 'package:file/src/backends/record_replay/mutable_recording.dart';
import 'package:file/src/backends/record_replay/recording_proxy_mixin.dart';
@@ -157,7 +157,7 @@
test('awaitsPendingResultsIndefinitelyByDefault', () async {
rc.veryLongFutureMethod(); // ignore: unawaited_futures
expect(recording.flush().timeout(const Duration(milliseconds: 50)),
- throwsTimeoutException);
+ throwsA(const TypeMatcher<TimeoutException>()));
});
test('succeedsIfAwaitPendingResultsThatComplete', () async {
@@ -398,7 +398,7 @@
events[0],
getsProperty('path')
.on(fs)
- .withResult(const isInstanceOf<p.Context>()),
+ .withResult(const TypeMatcher<p.Context>()),
);
});
@@ -613,7 +613,7 @@
recording.events,
contains(invokesMethod('openRead')
.on(isFile)
- .withPositionalArguments([null, null])
+ .withPositionalArguments(<int>[null, null])
.withNoNamedArguments()
.withResult(isList)));
await recording.flush();
@@ -625,7 +625,7 @@
containsPair('type', 'invoke'),
containsPair('method', 'openRead'),
containsPair('object', matches(r'^RecordingFile@[0-9]+$')),
- containsPair('positionalArguments', [null, null]),
+ containsPair('positionalArguments', <int>[null, null]),
containsPair('namedArguments', isEmpty),
containsPair('result', matches(r'^![0-9]+.foo$')),
));
@@ -783,7 +783,8 @@
containsPair('object', matches(r'^RecordingFile@[0-9]+$')),
containsPair('positionalArguments', isEmpty),
containsPair('result', matches(r'^![0-9]+.foo$')),
- containsPair('namedArguments', {'encoding': 'utf-8'}),
+ containsPair(
+ 'namedArguments', <String, String>{'encoding': 'utf-8'}),
));
File file = _getRecordingFile(recording, manifest[1]['result']);
expect(file, exists);
@@ -932,7 +933,3 @@
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
-
-/// Successfully matches against a function that throws a [TimeoutException].
-Matcher throwsTimeoutException =
- throwsA(const isInstanceOf<TimeoutException>());
diff --git a/packages/file/test/replay_test.dart b/packages/file/test/replay_test.dart
index b059356..37cb261 100644
--- a/packages/file/test/replay_test.dart
+++ b/packages/file/test/replay_test.dart
@@ -204,4 +204,4 @@
}
/// Successfully matches against an instance of [Future].
-const Matcher isFuture = const isInstanceOf<Future<dynamic>>();
+const Matcher isFuture = const TypeMatcher<Future<dynamic>>();