2
2
// for details. All rights reserved. Use of this source code is governed by a
3
3
// BSD-style license that can be found in the LICENSE file.
4
4
5
+ import 'dart:ffi' ;
5
6
import 'dart:io' as io;
7
+ import 'dart:math' ;
8
+ import 'dart:typed_data' ;
6
9
10
+ import 'package:ffi/ffi.dart' as ffi;
11
+ import 'package:meta/meta.dart' ;
7
12
import 'package:stdlibc/stdlibc.dart' as stdlibc;
8
13
9
14
import 'file_system.dart' ;
10
15
16
+ // The maximum number of bytes to read in a single call to `read`.
17
+ //
18
+ // On macOS, it is an error to call
19
+ // `read/_read(fildes, buf, nbyte)` with `nbyte >= INT_MAX`.
20
+ //
21
+ // The POSIX specification states that the behavior of `read` is
22
+ // implementation-defined if `nbyte > SSIZE_MAX`. On Linux, the `read` will
23
+ // transfer at most 0x7ffff000 bytes and return the number of bytes actually.
24
+ // transfered.
25
+ //
26
+ // A smaller value has the advantage of making vm-service clients
27
+ // (e.g. debugger) more responsive.
28
+ //
29
+ // A bigger value reduces the number of system calls.
30
+ @visibleForTesting
31
+ const int maxReadSize = 16 * 1024 * 1024 ; // 16MB.
32
+
33
+ // If the size of a file is unknown, read in blocks of this size.
34
+ @visibleForTesting
35
+ const int blockSize = 64 * 1024 ;
36
+
11
37
Exception _getError (int errno, String message, String path) {
12
38
//TODO(brianquinlan): In the long-term, do we need to avoid exceptions that
13
39
// are part of `dart:io`? Can we move those exceptions into a different
@@ -25,6 +51,21 @@ Exception _getError(int errno, String message, String path) {
25
51
}
26
52
}
27
53
54
+ /// Return the given function until the result is not `EINTR` .
55
+ int _tempFailureRetry (int Function () f) {
56
+ int result;
57
+ do {
58
+ result = f ();
59
+ } while (result == - 1 && stdlibc.errno == stdlibc.EINTR );
60
+ return result;
61
+ }
62
+
63
+ /// The POSIX `read` function.
64
+ ///
65
+ /// See https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html
66
+ @Native < Int Function (Int , Pointer <Uint8 >, Int )> (isLeaf: false )
67
+ external int read (int fd, Pointer <Uint8 > buf, int count);
68
+
28
69
/// A [FileSystem] implementation for POSIX systems (e.g. Android, iOS, Linux,
29
70
/// macOS).
30
71
base class PosixFileSystem extends FileSystem {
@@ -36,4 +77,77 @@ base class PosixFileSystem extends FileSystem {
36
77
throw _getError (errno, 'rename failed' , oldPath);
37
78
}
38
79
}
80
+
81
+ @override
82
+ Uint8List readAsBytes (String path) {
83
+ final fd = stdlibc.open (path, flags: stdlibc.O_RDONLY | stdlibc.O_CLOEXEC );
84
+ if (fd == - 1 ) {
85
+ final errno = stdlibc.errno;
86
+ throw _getError (errno, 'open failed' , path);
87
+ }
88
+ try {
89
+ final stat = stdlibc.fstat (fd);
90
+ if (stat != null &&
91
+ stat.st_size == 0 &&
92
+ stat.st_mode & stdlibc.S_IFREG != 0 ) {
93
+ return Uint8List (0 );
94
+ }
95
+ final length = stat? .st_size ?? 0 ;
96
+ if (length == 0 ) {
97
+ return _readUnknownLength (path, fd);
98
+ } else {
99
+ return _readKnownLength (path, fd, length);
100
+ }
101
+ } finally {
102
+ stdlibc.close (fd);
103
+ }
104
+ }
105
+
106
+ Uint8List _readUnknownLength (String path, int fd) => ffi.using ((arena) {
107
+ final buf = ffi.malloc <Uint8 >(blockSize);
108
+ final builder = BytesBuilder (copy: true );
109
+
110
+ while (true ) {
111
+ final r = _tempFailureRetry (() => read (fd, buf, blockSize));
112
+ switch (r) {
113
+ case - 1 :
114
+ final errno = stdlibc.errno;
115
+ throw _getError (errno, 'read failed' , path);
116
+ case 0 :
117
+ return builder.takeBytes ();
118
+ default :
119
+ final typed = buf.asTypedList (r);
120
+ builder.add (typed);
121
+ }
122
+ }
123
+ });
124
+
125
+ Uint8List _readKnownLength (String path, int fd, int length) {
126
+ final buffer = ffi.malloc <Uint8 >(length);
127
+ var bufferOffset = 0 ;
128
+
129
+ while (bufferOffset < length) {
130
+ final r = _tempFailureRetry (
131
+ () => read (
132
+ fd,
133
+ (buffer + bufferOffset).cast (),
134
+ min (length - bufferOffset, maxReadSize),
135
+ ),
136
+ );
137
+ switch (r) {
138
+ case - 1 :
139
+ final errno = stdlibc.errno;
140
+ ffi.calloc.free (buffer);
141
+ throw _getError (errno, 'read failed' , path);
142
+ case 0 :
143
+ return buffer.asTypedList (
144
+ bufferOffset,
145
+ finalizer: ffi.calloc.nativeFree,
146
+ );
147
+ default :
148
+ bufferOffset += r;
149
+ }
150
+ }
151
+ return buffer.asTypedList (length, finalizer: ffi.calloc.nativeFree);
152
+ }
39
153
}
0 commit comments