OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 part of dart.io; |
| 6 |
| 7 // Read the file in blocks of size 64k. |
| 8 const int _BLOCK_SIZE = 64 * 1024; |
| 9 |
| 10 |
| 11 class _FileStream extends Stream<List<int>> { |
| 12 // Stream controller. |
| 13 StreamController<List<int>> _controller; |
| 14 |
| 15 // Information about the underlying file. |
| 16 String _path; |
| 17 RandomAccessFile _openedFile; |
| 18 int _position; |
| 19 int _end; |
| 20 final Completer _closeCompleter = new Completer(); |
| 21 |
| 22 // Has the stream been paused or unsubscribed? |
| 23 bool _unsubscribed = false; |
| 24 |
| 25 // Is there a read currently in progress? |
| 26 bool _readInProgress = true; |
| 27 bool _closed = false; |
| 28 |
| 29 bool _atEnd = false; |
| 30 |
| 31 _FileStream(this._path, this._position, this._end) { |
| 32 if (_position == null) _position = 0; |
| 33 } |
| 34 |
| 35 _FileStream.forStdin() : _position = 0; |
| 36 |
| 37 StreamSubscription<List<int>> listen(void onData(List<int> event), |
| 38 {Function onError, |
| 39 void onDone(), |
| 40 bool cancelOnError}) { |
| 41 _setupController(); |
| 42 return _controller.stream.listen(onData, |
| 43 onError: onError, |
| 44 onDone: onDone, |
| 45 cancelOnError: cancelOnError); |
| 46 } |
| 47 |
| 48 void _setupController() { |
| 49 _controller = new StreamController<List<int>>(sync: true, |
| 50 onListen: _start, |
| 51 onResume: _readBlock, |
| 52 onCancel: () { |
| 53 _unsubscribed = true; |
| 54 return _closeFile(); |
| 55 }); |
| 56 } |
| 57 |
| 58 Future _closeFile() { |
| 59 if (_readInProgress || _closed) { |
| 60 return _closeCompleter.future; |
| 61 } |
| 62 _closed = true; |
| 63 |
| 64 void done() { |
| 65 _closeCompleter.complete(); |
| 66 _controller.close(); |
| 67 } |
| 68 |
| 69 _openedFile.close() |
| 70 .catchError(_controller.addError) |
| 71 .whenComplete(done); |
| 72 return _closeCompleter.future; |
| 73 } |
| 74 |
| 75 void _readBlock() { |
| 76 // Don't start a new read if one is already in progress. |
| 77 if (_readInProgress) return; |
| 78 if (_atEnd) { |
| 79 _closeFile(); |
| 80 return; |
| 81 } |
| 82 _readInProgress = true; |
| 83 int readBytes = _BLOCK_SIZE; |
| 84 if (_end != null) { |
| 85 readBytes = min(readBytes, _end - _position); |
| 86 if (readBytes < 0) { |
| 87 _readInProgress = false; |
| 88 if (!_unsubscribed) { |
| 89 _controller.addError(new RangeError("Bad end position: $_end")); |
| 90 _closeFile(); |
| 91 _unsubscribed = true; |
| 92 } |
| 93 return; |
| 94 } |
| 95 } |
| 96 _openedFile.read(readBytes) |
| 97 .then((block) { |
| 98 _readInProgress = false; |
| 99 if (_unsubscribed) { |
| 100 _closeFile(); |
| 101 return; |
| 102 } |
| 103 _position += block.length; |
| 104 if (block.length < readBytes || |
| 105 (_end != null && _position == _end)) { |
| 106 _atEnd = true; |
| 107 } |
| 108 if (!_atEnd && !_controller.isPaused) { |
| 109 _readBlock(); |
| 110 } |
| 111 _controller.add(block); |
| 112 if (_atEnd) { |
| 113 _closeFile(); |
| 114 } |
| 115 }) |
| 116 .catchError((e, s) { |
| 117 if (!_unsubscribed) { |
| 118 _controller.addError(e, s); |
| 119 _closeFile(); |
| 120 _unsubscribed = true; |
| 121 } |
| 122 }); |
| 123 } |
| 124 |
| 125 void _start() { |
| 126 if (_position < 0) { |
| 127 _controller.addError(new RangeError("Bad start position: $_position")); |
| 128 _controller.close(); |
| 129 _closeCompleter.complete(); |
| 130 return; |
| 131 } |
| 132 |
| 133 void onReady(RandomAccessFile file) { |
| 134 _openedFile = file; |
| 135 _readInProgress = false; |
| 136 _readBlock(); |
| 137 } |
| 138 |
| 139 void onOpenFile(RandomAccessFile file) { |
| 140 if (_position > 0) { |
| 141 file.setPosition(_position) |
| 142 .then(onReady, onError: (e, s) { |
| 143 _controller.addError(e, s); |
| 144 _readInProgress = false; |
| 145 _closeFile(); |
| 146 }); |
| 147 } else { |
| 148 onReady(file); |
| 149 } |
| 150 } |
| 151 |
| 152 void openFailed(error, stackTrace) { |
| 153 _controller.addError(error, stackTrace); |
| 154 _controller.close(); |
| 155 _closeCompleter.complete(); |
| 156 } |
| 157 |
| 158 if (_path != null) { |
| 159 new File(_path).open(mode: FileMode.READ) |
| 160 .then(onOpenFile, onError: openFailed); |
| 161 } else { |
| 162 try { |
| 163 onOpenFile(_File._openStdioSync(0)); |
| 164 } catch (e, s) { |
| 165 openFailed(e, s); |
| 166 } |
| 167 } |
| 168 } |
| 169 } |
| 170 |
| 171 class _FileStreamConsumer extends StreamConsumer<List<int>> { |
| 172 File _file; |
| 173 Future<RandomAccessFile> _openFuture; |
| 174 |
| 175 _FileStreamConsumer(File this._file, FileMode mode) { |
| 176 _openFuture = _file.open(mode: mode); |
| 177 } |
| 178 |
| 179 _FileStreamConsumer.fromStdio(int fd) { |
| 180 assert(1 <= fd && fd <= 2); |
| 181 _openFuture = new Future.value(_File._openStdioSync(fd)); |
| 182 } |
| 183 |
| 184 Future<File> addStream(Stream<List<int>> stream) { |
| 185 Completer<File> completer = new Completer<File>.sync(); |
| 186 _openFuture |
| 187 .then((openedFile) { |
| 188 var _subscription; |
| 189 void error(e, [StackTrace stackTrace]) { |
| 190 _subscription.cancel(); |
| 191 openedFile.close(); |
| 192 completer.completeError(e, stackTrace); |
| 193 } |
| 194 _subscription = stream.listen( |
| 195 (d) { |
| 196 _subscription.pause(); |
| 197 try { |
| 198 openedFile.writeFrom(d, 0, d.length) |
| 199 .then((_) => _subscription.resume(), |
| 200 onError: error); |
| 201 } catch (e, stackTrace) { |
| 202 error(e, stackTrace); |
| 203 } |
| 204 }, |
| 205 onDone: () { |
| 206 completer.complete(_file); |
| 207 }, |
| 208 onError: error, |
| 209 cancelOnError: true); |
| 210 }) |
| 211 .catchError(completer.completeError); |
| 212 return completer.future; |
| 213 } |
| 214 |
| 215 Future<File> close() => |
| 216 _openFuture.then((openedFile) => openedFile.close()); |
| 217 } |
| 218 |
| 219 |
| 220 // Class for encapsulating the native implementation of files. |
| 221 class _File extends FileSystemEntity implements File { |
| 222 final String path; |
| 223 |
| 224 // Constructor for file. |
| 225 _File(this.path) { |
| 226 if (path is! String) { |
| 227 throw new ArgumentError('${Error.safeToString(path)} ' |
| 228 'is not a String'); |
| 229 } |
| 230 } |
| 231 |
| 232 Future<bool> exists() { |
| 233 return _IOService._dispatch(_FILE_EXISTS, [path]).then((response) { |
| 234 if (_isErrorResponse(response)) { |
| 235 throw _exceptionFromResponse(response, "Cannot check existence", path); |
| 236 } |
| 237 return response; |
| 238 }); |
| 239 } |
| 240 |
| 241 external static _exists(String path); |
| 242 |
| 243 bool existsSync() { |
| 244 var result = _exists(path); |
| 245 throwIfError(result, "Cannot check existence of file", path); |
| 246 return result; |
| 247 } |
| 248 |
| 249 File get absolute => new File(_absolutePath); |
| 250 |
| 251 Future<FileStat> stat() => FileStat.stat(path); |
| 252 |
| 253 FileStat statSync() => FileStat.statSync(path); |
| 254 |
| 255 Future<File> create({bool recursive: false}) { |
| 256 var result = recursive ? parent.create(recursive: true) |
| 257 : new Future.value(null); |
| 258 return result |
| 259 .then((_) => _IOService._dispatch(_FILE_CREATE, [path])) |
| 260 .then((response) { |
| 261 if (_isErrorResponse(response)) { |
| 262 throw _exceptionFromResponse(response, "Cannot create file", path); |
| 263 } |
| 264 return this; |
| 265 }); |
| 266 } |
| 267 |
| 268 external static _create(String path); |
| 269 |
| 270 external static _createLink(String path, String target); |
| 271 |
| 272 external static _linkTarget(String path); |
| 273 |
| 274 void createSync({bool recursive: false}) { |
| 275 if (recursive) { |
| 276 parent.createSync(recursive: true); |
| 277 } |
| 278 var result = _create(path); |
| 279 throwIfError(result, "Cannot create file", path); |
| 280 } |
| 281 |
| 282 Future<File> _delete({bool recursive: false}) { |
| 283 if (recursive) { |
| 284 return new Directory(path).delete(recursive: true).then((_) => this); |
| 285 } |
| 286 return _IOService._dispatch(_FILE_DELETE, [path]).then((response) { |
| 287 if (_isErrorResponse(response)) { |
| 288 throw _exceptionFromResponse(response, "Cannot delete file", path); |
| 289 } |
| 290 return this; |
| 291 }); |
| 292 } |
| 293 |
| 294 external static _deleteNative(String path); |
| 295 |
| 296 external static _deleteLinkNative(String path); |
| 297 |
| 298 void _deleteSync({bool recursive: false}) { |
| 299 if (recursive) { |
| 300 return new Directory(path).deleteSync(recursive: true); |
| 301 } |
| 302 var result = _deleteNative(path); |
| 303 throwIfError(result, "Cannot delete file", path); |
| 304 } |
| 305 |
| 306 Future<File> rename(String newPath) { |
| 307 return _IOService._dispatch(_FILE_RENAME, [path, newPath]).then((response) { |
| 308 if (_isErrorResponse(response)) { |
| 309 throw _exceptionFromResponse( |
| 310 response, "Cannot rename file to '$newPath'", path); |
| 311 } |
| 312 return new File(newPath); |
| 313 }); |
| 314 } |
| 315 |
| 316 external static _rename(String oldPath, String newPath); |
| 317 |
| 318 external static _renameLink(String oldPath, String newPath); |
| 319 |
| 320 File renameSync(String newPath) { |
| 321 var result = _rename(path, newPath); |
| 322 throwIfError(result, "Cannot rename file to '$newPath'", path); |
| 323 return new File(newPath); |
| 324 } |
| 325 |
| 326 Future<File> copy(String newPath) { |
| 327 return _IOService._dispatch(_FILE_COPY, [path, newPath]).then((response) { |
| 328 if (_isErrorResponse(response)) { |
| 329 throw _exceptionFromResponse( |
| 330 response, "Cannot copy file to '$newPath'", path); |
| 331 } |
| 332 return new File(newPath); |
| 333 }); |
| 334 } |
| 335 |
| 336 external static _copy(String oldPath, String newPath); |
| 337 |
| 338 File copySync(String newPath) { |
| 339 var result = _copy(path, newPath); |
| 340 throwIfError(result, "Cannot copy file to '$newPath'", path); |
| 341 return new File(newPath); |
| 342 } |
| 343 |
| 344 Future<RandomAccessFile> open({FileMode mode: FileMode.READ}) { |
| 345 if (mode != FileMode.READ && |
| 346 mode != FileMode.WRITE && |
| 347 mode != FileMode.APPEND && |
| 348 mode != FileMode.WRITE_ONLY && |
| 349 mode != FileMode.WRITE_ONLY_APPEND) { |
| 350 return new Future.error( |
| 351 new ArgumentError('Invalid file mode for this operation')); |
| 352 } |
| 353 return _IOService._dispatch(_FILE_OPEN, [path, mode._mode]) |
| 354 .then((response) { |
| 355 if (_isErrorResponse(response)) { |
| 356 throw _exceptionFromResponse(response, "Cannot open file", path); |
| 357 } |
| 358 return new _RandomAccessFile(response, path); |
| 359 }); |
| 360 } |
| 361 |
| 362 Future<int> length() { |
| 363 return _IOService._dispatch(_FILE_LENGTH_FROM_PATH, [path]) |
| 364 .then((response) { |
| 365 if (_isErrorResponse(response)) { |
| 366 throw _exceptionFromResponse(response, |
| 367 "Cannot retrieve length of file", |
| 368 path); |
| 369 } |
| 370 return response; |
| 371 }); |
| 372 } |
| 373 |
| 374 |
| 375 external static _lengthFromPath(String path); |
| 376 |
| 377 int lengthSync() { |
| 378 var result = _lengthFromPath(path); |
| 379 throwIfError(result, "Cannot retrieve length of file", path); |
| 380 return result; |
| 381 } |
| 382 |
| 383 Future<DateTime> lastModified() { |
| 384 return _IOService._dispatch(_FILE_LAST_MODIFIED, [path]).then((response) { |
| 385 if (_isErrorResponse(response)) { |
| 386 throw _exceptionFromResponse(response, |
| 387 "Cannot retrieve modification time", |
| 388 path); |
| 389 } |
| 390 return new DateTime.fromMillisecondsSinceEpoch(response); |
| 391 }); |
| 392 } |
| 393 |
| 394 external static _lastModified(String path); |
| 395 |
| 396 DateTime lastModifiedSync() { |
| 397 var ms = _lastModified(path); |
| 398 throwIfError(ms, "Cannot retrieve modification time", path); |
| 399 return new DateTime.fromMillisecondsSinceEpoch(ms); |
| 400 } |
| 401 |
| 402 external static _open(String path, int mode); |
| 403 |
| 404 RandomAccessFile openSync({FileMode mode: FileMode.READ}) { |
| 405 if (mode != FileMode.READ && |
| 406 mode != FileMode.WRITE && |
| 407 mode != FileMode.APPEND && |
| 408 mode != FileMode.WRITE_ONLY && |
| 409 mode != FileMode.WRITE_ONLY_APPEND) { |
| 410 throw new ArgumentError('Invalid file mode for this operation'); |
| 411 } |
| 412 var id = _open(path, mode._mode); |
| 413 throwIfError(id, "Cannot open file", path); |
| 414 return new _RandomAccessFile(id, path); |
| 415 } |
| 416 |
| 417 external static int _openStdio(int fd); |
| 418 |
| 419 static RandomAccessFile _openStdioSync(int fd) { |
| 420 var id = _openStdio(fd); |
| 421 if (id == 0) { |
| 422 throw new FileSystemException("Cannot open stdio file for: $fd"); |
| 423 } |
| 424 return new _RandomAccessFile(id, ""); |
| 425 } |
| 426 |
| 427 Stream<List<int>> openRead([int start, int end]) { |
| 428 return new _FileStream(path, start, end); |
| 429 } |
| 430 |
| 431 IOSink openWrite({FileMode mode: FileMode.WRITE, |
| 432 Encoding encoding: UTF8}) { |
| 433 if (mode != FileMode.WRITE && |
| 434 mode != FileMode.APPEND && |
| 435 mode != FileMode.WRITE_ONLY && |
| 436 mode != FileMode.WRITE_ONLY_APPEND) { |
| 437 throw new ArgumentError('Invalid file mode for this operation'); |
| 438 } |
| 439 var consumer = new _FileStreamConsumer(this, mode); |
| 440 return new IOSink(consumer, encoding: encoding); |
| 441 } |
| 442 |
| 443 Future<List<int>> readAsBytes() { |
| 444 Future<List<int>> readDataChunked(file) { |
| 445 var builder = new BytesBuilder(copy: false); |
| 446 var completer = new Completer(); |
| 447 void read() { |
| 448 file.read(_BLOCK_SIZE).then((data) { |
| 449 if (data.length > 0) { |
| 450 builder.add(data); |
| 451 read(); |
| 452 } else { |
| 453 completer.complete(builder.takeBytes()); |
| 454 } |
| 455 }, onError: completer.completeError); |
| 456 } |
| 457 read(); |
| 458 return completer.future; |
| 459 } |
| 460 |
| 461 return open().then((file) { |
| 462 return file.length().then((length) { |
| 463 if (length == 0) { |
| 464 // May be character device, try to read it in chunks. |
| 465 return readDataChunked(file); |
| 466 } |
| 467 return file.read(length); |
| 468 }).whenComplete(file.close); |
| 469 }); |
| 470 } |
| 471 |
| 472 List<int> readAsBytesSync() { |
| 473 var opened = openSync(); |
| 474 try { |
| 475 var data; |
| 476 var length = opened.lengthSync(); |
| 477 if (length == 0) { |
| 478 // May be character device, try to read it in chunks. |
| 479 var builder = new BytesBuilder(copy: false); |
| 480 do { |
| 481 data = opened.readSync(_BLOCK_SIZE); |
| 482 if (data.length > 0) builder.add(data); |
| 483 } while (data.length > 0); |
| 484 data = builder.takeBytes(); |
| 485 } else { |
| 486 data = opened.readSync(length); |
| 487 } |
| 488 return data; |
| 489 } finally { |
| 490 opened.closeSync(); |
| 491 } |
| 492 } |
| 493 |
| 494 String _tryDecode(List<int> bytes, Encoding encoding) { |
| 495 try { |
| 496 return encoding.decode(bytes); |
| 497 } catch (_) { |
| 498 throw new FileSystemException( |
| 499 "Failed to decode data using encoding '${encoding.name}'", path); |
| 500 } |
| 501 } |
| 502 |
| 503 Future<String> readAsString({Encoding encoding: UTF8}) => |
| 504 readAsBytes().then((bytes) => _tryDecode(bytes, encoding)); |
| 505 |
| 506 String readAsStringSync({Encoding encoding: UTF8}) => |
| 507 _tryDecode(readAsBytesSync(), encoding); |
| 508 |
| 509 Future<List<String>> readAsLines({Encoding encoding: UTF8}) => |
| 510 readAsString(encoding: encoding).then(const LineSplitter().convert); |
| 511 |
| 512 List<String> readAsLinesSync({Encoding encoding: UTF8}) => |
| 513 const LineSplitter().convert(readAsStringSync(encoding: encoding)); |
| 514 |
| 515 Future<File> writeAsBytes(List<int> bytes, |
| 516 {FileMode mode: FileMode.WRITE, |
| 517 bool flush: false}) { |
| 518 return open(mode: mode).then((file) { |
| 519 return file.writeFrom(bytes, 0, bytes.length) |
| 520 .then((_) { |
| 521 if (flush) return file.flush().then((_) => this); |
| 522 return this; |
| 523 }) |
| 524 .whenComplete(file.close); |
| 525 }); |
| 526 } |
| 527 |
| 528 void writeAsBytesSync(List<int> bytes, |
| 529 {FileMode mode: FileMode.WRITE, |
| 530 bool flush: false}) { |
| 531 RandomAccessFile opened = openSync(mode: mode); |
| 532 try { |
| 533 opened.writeFromSync(bytes, 0, bytes.length); |
| 534 if (flush) opened.flushSync(); |
| 535 } finally { |
| 536 opened.closeSync(); |
| 537 } |
| 538 } |
| 539 |
| 540 Future<File> writeAsString(String contents, |
| 541 {FileMode mode: FileMode.WRITE, |
| 542 Encoding encoding: UTF8, |
| 543 bool flush: false}) { |
| 544 try { |
| 545 return writeAsBytes(encoding.encode(contents), mode: mode, flush: flush); |
| 546 } catch (e) { |
| 547 return new Future.error(e); |
| 548 } |
| 549 } |
| 550 |
| 551 void writeAsStringSync(String contents, |
| 552 {FileMode mode: FileMode.WRITE, |
| 553 Encoding encoding: UTF8, |
| 554 bool flush: false}) { |
| 555 writeAsBytesSync(encoding.encode(contents), mode: mode, flush: flush); |
| 556 } |
| 557 |
| 558 String toString() => "File: '$path'"; |
| 559 |
| 560 static throwIfError(Object result, String msg, String path) { |
| 561 if (result is OSError) { |
| 562 throw new FileSystemException(msg, path, result); |
| 563 } |
| 564 } |
| 565 } |
| 566 |
| 567 abstract class _RandomAccessFileOps { |
| 568 external factory _RandomAccessFileOps(int pointer); |
| 569 |
| 570 int getPointer(); |
| 571 int close(); |
| 572 readByte(); |
| 573 read(int bytes); |
| 574 readInto(List<int> buffer, int start, int end); |
| 575 writeByte(int value); |
| 576 writeFrom(List<int> buffer, int start, int end); |
| 577 position(); |
| 578 setPosition(int position); |
| 579 truncate(int length); |
| 580 length(); |
| 581 flush(); |
| 582 lock(int lock, int start, int end); |
| 583 } |
| 584 |
| 585 class _RandomAccessFile implements RandomAccessFile { |
| 586 static bool _connectedResourceHandler = false; |
| 587 |
| 588 final String path; |
| 589 |
| 590 bool _asyncDispatched = false; |
| 591 SendPort _fileService; |
| 592 |
| 593 _FileResourceInfo _resourceInfo; |
| 594 _RandomAccessFileOps _ops; |
| 595 |
| 596 _RandomAccessFile(int pointer, this.path) { |
| 597 _ops = new _RandomAccessFileOps(pointer); |
| 598 _resourceInfo = new _FileResourceInfo(this); |
| 599 _maybeConnectHandler(); |
| 600 } |
| 601 |
| 602 void _maybePerformCleanup() { |
| 603 if (closed) { |
| 604 _FileResourceInfo.FileClosed(_resourceInfo); |
| 605 } |
| 606 } |
| 607 |
| 608 _maybeConnectHandler() { |
| 609 if (!_connectedResourceHandler) { |
| 610 // TODO(ricow): We probably need to set these in some initialization code. |
| 611 // We need to make sure that these are always available from the |
| 612 // observatory even if no files (or sockets for the socket ones) are |
| 613 // open. |
| 614 registerExtension('ext.dart.io.getOpenFiles', |
| 615 _FileResourceInfo.getOpenFiles); |
| 616 registerExtension('ext.dart.io.getFileByID', |
| 617 _FileResourceInfo.getFileInfoMapByID); |
| 618 _connectedResourceHandler = true; |
| 619 } |
| 620 } |
| 621 |
| 622 Future<RandomAccessFile> close() { |
| 623 return _dispatch(_FILE_CLOSE, [null], markClosed: true).then((result) { |
| 624 if (result != -1) { |
| 625 closed = closed || (result == 0); |
| 626 _maybePerformCleanup(); |
| 627 return this; |
| 628 } else { |
| 629 throw new FileSystemException("Cannot close file", path); |
| 630 } |
| 631 }); |
| 632 } |
| 633 |
| 634 void closeSync() { |
| 635 _checkAvailable(); |
| 636 var id = _ops.close(); |
| 637 if (id == -1) { |
| 638 throw new FileSystemException("Cannot close file", path); |
| 639 } |
| 640 closed = closed || (id == 0); |
| 641 _maybePerformCleanup(); |
| 642 } |
| 643 |
| 644 Future<int> readByte() { |
| 645 return _dispatch(_FILE_READ_BYTE, [null]).then((response) { |
| 646 if (_isErrorResponse(response)) { |
| 647 throw _exceptionFromResponse(response, "readByte failed", path); |
| 648 } |
| 649 _resourceInfo.addRead(1); |
| 650 return response; |
| 651 }); |
| 652 } |
| 653 |
| 654 int readByteSync() { |
| 655 _checkAvailable(); |
| 656 var result = _ops.readByte(); |
| 657 if (result is OSError) { |
| 658 throw new FileSystemException("readByte failed", path, result); |
| 659 } |
| 660 _resourceInfo.addRead(1); |
| 661 return result; |
| 662 } |
| 663 |
| 664 Future<List<int>> read(int bytes) { |
| 665 if (bytes is !int) { |
| 666 throw new ArgumentError(bytes); |
| 667 } |
| 668 return _dispatch(_FILE_READ, [null, bytes]).then((response) { |
| 669 if (_isErrorResponse(response)) { |
| 670 throw _exceptionFromResponse(response, "read failed", path); |
| 671 } |
| 672 _resourceInfo.addRead(response[1].length); |
| 673 return response[1]; |
| 674 }); |
| 675 } |
| 676 |
| 677 List<int> readSync(int bytes) { |
| 678 _checkAvailable(); |
| 679 if (bytes is !int) { |
| 680 throw new ArgumentError(bytes); |
| 681 } |
| 682 var result = _ops.read(bytes); |
| 683 if (result is OSError) { |
| 684 throw new FileSystemException("readSync failed", path, result); |
| 685 } |
| 686 _resourceInfo.addRead(result.length); |
| 687 return result; |
| 688 } |
| 689 |
| 690 Future<int> readInto(List<int> buffer, [int start = 0, int end]) { |
| 691 if ((buffer is !List) || |
| 692 ((start != null) && (start is !int)) || |
| 693 ((end != null) && (end is !int))) { |
| 694 throw new ArgumentError(); |
| 695 } |
| 696 end = RangeError.checkValidRange(start, end, buffer.length); |
| 697 if (end == start) { |
| 698 return new Future.value(0); |
| 699 } |
| 700 int length = end - start; |
| 701 return _dispatch(_FILE_READ_INTO, [null, length]).then((response) { |
| 702 if (_isErrorResponse(response)) { |
| 703 throw _exceptionFromResponse(response, "readInto failed", path); |
| 704 } |
| 705 var read = response[1]; |
| 706 var data = response[2]; |
| 707 buffer.setRange(start, start + read, data); |
| 708 _resourceInfo.addRead(read); |
| 709 return read; |
| 710 }); |
| 711 } |
| 712 |
| 713 int readIntoSync(List<int> buffer, [int start = 0, int end]) { |
| 714 _checkAvailable(); |
| 715 if ((buffer is !List) || |
| 716 ((start != null) && (start is !int)) || |
| 717 ((end != null) && (end is !int))) { |
| 718 throw new ArgumentError(); |
| 719 } |
| 720 end = RangeError.checkValidRange(start, end, buffer.length); |
| 721 if (end == start) { |
| 722 return 0; |
| 723 } |
| 724 var result = _ops.readInto(buffer, start, end); |
| 725 if (result is OSError) { |
| 726 throw new FileSystemException("readInto failed", path, result); |
| 727 } |
| 728 _resourceInfo.addRead(result); |
| 729 return result; |
| 730 } |
| 731 |
| 732 Future<RandomAccessFile> writeByte(int value) { |
| 733 if (value is !int) { |
| 734 throw new ArgumentError(value); |
| 735 } |
| 736 return _dispatch(_FILE_WRITE_BYTE, [null, value]).then((response) { |
| 737 if (_isErrorResponse(response)) { |
| 738 throw _exceptionFromResponse(response, "writeByte failed", path); |
| 739 } |
| 740 _resourceInfo.addWrite(1); |
| 741 return this; |
| 742 }); |
| 743 } |
| 744 |
| 745 int writeByteSync(int value) { |
| 746 _checkAvailable(); |
| 747 if (value is !int) { |
| 748 throw new ArgumentError(value); |
| 749 } |
| 750 var result = _ops.writeByte(value); |
| 751 if (result is OSError) { |
| 752 throw new FileSystemException("writeByte failed", path, result); |
| 753 } |
| 754 _resourceInfo.addWrite(1); |
| 755 return result; |
| 756 } |
| 757 |
| 758 Future<RandomAccessFile> writeFrom( |
| 759 List<int> buffer, [int start = 0, int end]) { |
| 760 if ((buffer is !List) || |
| 761 ((start != null) && (start is !int)) || |
| 762 ((end != null) && (end is !int))) { |
| 763 throw new ArgumentError("Invalid arguments to writeFrom"); |
| 764 } |
| 765 end = RangeError.checkValidRange(start, end, buffer.length); |
| 766 if (end == start) { |
| 767 return new Future.value(this); |
| 768 } |
| 769 _BufferAndStart result; |
| 770 try { |
| 771 result = _ensureFastAndSerializableByteData(buffer, start, end); |
| 772 } catch (e) { |
| 773 return new Future.error(e); |
| 774 } |
| 775 |
| 776 List request = new List(4); |
| 777 request[0] = null; |
| 778 request[1] = result.buffer; |
| 779 request[2] = result.start; |
| 780 request[3] = end - (start - result.start); |
| 781 return _dispatch(_FILE_WRITE_FROM, request).then((response) { |
| 782 if (_isErrorResponse(response)) { |
| 783 throw _exceptionFromResponse(response, "writeFrom failed", path); |
| 784 } |
| 785 _resourceInfo.addWrite(end - (start - result.start)); |
| 786 return this; |
| 787 }); |
| 788 } |
| 789 |
| 790 void writeFromSync(List<int> buffer, [int start = 0, int end]) { |
| 791 _checkAvailable(); |
| 792 if ((buffer is !List) || |
| 793 ((start != null) && (start is !int)) || |
| 794 ((end != null) && (end is !int))) { |
| 795 throw new ArgumentError("Invalid arguments to writeFromSync"); |
| 796 } |
| 797 end = RangeError.checkValidRange(start, end, buffer.length); |
| 798 if (end == start) { |
| 799 return; |
| 800 } |
| 801 _BufferAndStart bufferAndStart = |
| 802 _ensureFastAndSerializableByteData(buffer, start, end); |
| 803 var result = _ops.writeFrom(bufferAndStart.buffer, |
| 804 bufferAndStart.start, |
| 805 end - (start - bufferAndStart.start)); |
| 806 if (result is OSError) { |
| 807 throw new FileSystemException("writeFrom failed", path, result); |
| 808 } |
| 809 _resourceInfo.addWrite(end - (start - bufferAndStart.start)); |
| 810 } |
| 811 |
| 812 Future<RandomAccessFile> writeString(String string, |
| 813 {Encoding encoding: UTF8}) { |
| 814 if (encoding is! Encoding) { |
| 815 throw new ArgumentError(encoding); |
| 816 } |
| 817 var data = encoding.encode(string); |
| 818 return writeFrom(data, 0, data.length); |
| 819 } |
| 820 |
| 821 void writeStringSync(String string, {Encoding encoding: UTF8}) { |
| 822 if (encoding is! Encoding) { |
| 823 throw new ArgumentError(encoding); |
| 824 } |
| 825 var data = encoding.encode(string); |
| 826 writeFromSync(data, 0, data.length); |
| 827 } |
| 828 |
| 829 Future<int> position() { |
| 830 return _dispatch(_FILE_POSITION, [null]).then((response) { |
| 831 if (_isErrorResponse(response)) { |
| 832 throw _exceptionFromResponse(response, "position failed", path); |
| 833 } |
| 834 return response; |
| 835 }); |
| 836 } |
| 837 |
| 838 int positionSync() { |
| 839 _checkAvailable(); |
| 840 var result = _ops.position(); |
| 841 if (result is OSError) { |
| 842 throw new FileSystemException("position failed", path, result); |
| 843 } |
| 844 return result; |
| 845 } |
| 846 |
| 847 Future<RandomAccessFile> setPosition(int position) { |
| 848 return _dispatch(_FILE_SET_POSITION, [null, position]) |
| 849 .then((response) { |
| 850 if (_isErrorResponse(response)) { |
| 851 throw _exceptionFromResponse(response, "setPosition failed", path); |
| 852 } |
| 853 return this; |
| 854 }); |
| 855 } |
| 856 |
| 857 void setPositionSync(int position) { |
| 858 _checkAvailable(); |
| 859 var result = _ops.setPosition(position); |
| 860 if (result is OSError) { |
| 861 throw new FileSystemException("setPosition failed", path, result); |
| 862 } |
| 863 } |
| 864 |
| 865 Future<RandomAccessFile> truncate(int length) { |
| 866 return _dispatch(_FILE_TRUNCATE, [null, length]).then((response) { |
| 867 if (_isErrorResponse(response)) { |
| 868 throw _exceptionFromResponse(response, "truncate failed", path); |
| 869 } |
| 870 return this; |
| 871 }); |
| 872 } |
| 873 |
| 874 void truncateSync(int length) { |
| 875 _checkAvailable(); |
| 876 var result = _ops.truncate(length); |
| 877 if (result is OSError) { |
| 878 throw new FileSystemException("truncate failed", path, result); |
| 879 } |
| 880 } |
| 881 |
| 882 Future<int> length() { |
| 883 return _dispatch(_FILE_LENGTH, [null]).then((response) { |
| 884 if (_isErrorResponse(response)) { |
| 885 throw _exceptionFromResponse(response, "length failed", path); |
| 886 } |
| 887 return response; |
| 888 }); |
| 889 } |
| 890 |
| 891 int lengthSync() { |
| 892 _checkAvailable(); |
| 893 var result = _ops.length(); |
| 894 if (result is OSError) { |
| 895 throw new FileSystemException("length failed", path, result); |
| 896 } |
| 897 return result; |
| 898 } |
| 899 |
| 900 Future<RandomAccessFile> flush() { |
| 901 return _dispatch(_FILE_FLUSH, [null]).then((response) { |
| 902 if (_isErrorResponse(response)) { |
| 903 throw _exceptionFromResponse(response, |
| 904 "flush failed", |
| 905 path); |
| 906 } |
| 907 return this; |
| 908 }); |
| 909 } |
| 910 |
| 911 void flushSync() { |
| 912 _checkAvailable(); |
| 913 var result = _ops.flush(); |
| 914 if (result is OSError) { |
| 915 throw new FileSystemException("flush failed", path, result); |
| 916 } |
| 917 } |
| 918 |
| 919 static final int LOCK_UNLOCK = 0; |
| 920 static final int LOCK_SHARED = 1; |
| 921 static final int LOCK_EXCLUSIVE = 2; |
| 922 |
| 923 Future<RandomAccessFile> lock( |
| 924 [FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end = -1]) { |
| 925 if ((mode is !FileLock) || (start is !int) || (end is !int)) { |
| 926 throw new ArgumentError(); |
| 927 } |
| 928 if ((start < 0) || (end < -1) || ((end != -1) && (start >= end))) { |
| 929 throw new ArgumentError(); |
| 930 } |
| 931 int lock = (mode == FileLock.EXCLUSIVE) ? LOCK_EXCLUSIVE : LOCK_SHARED; |
| 932 return _dispatch(_FILE_LOCK, [null, lock, start, end]) |
| 933 .then((response) { |
| 934 if (_isErrorResponse(response)) { |
| 935 throw _exceptionFromResponse(response, 'lock failed', path); |
| 936 } |
| 937 return this; |
| 938 }); |
| 939 } |
| 940 |
| 941 Future<RandomAccessFile> unlock([int start = 0, int end = -1]) { |
| 942 if ((start is !int) || (end is !int)) { |
| 943 throw new ArgumentError(); |
| 944 } |
| 945 if (start == end) { |
| 946 throw new ArgumentError(); |
| 947 } |
| 948 return _dispatch(_FILE_LOCK, [null, LOCK_UNLOCK, start, end]) |
| 949 .then((response) { |
| 950 if (_isErrorResponse(response)) { |
| 951 throw _exceptionFromResponse(response, 'unlock failed', path); |
| 952 } |
| 953 return this; |
| 954 }); |
| 955 } |
| 956 |
| 957 void lockSync( |
| 958 [FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end = -1]) { |
| 959 _checkAvailable(); |
| 960 if ((mode is !FileLock) || (start is !int) || (end is !int)) { |
| 961 throw new ArgumentError(); |
| 962 } |
| 963 if ((start < 0) || (end < -1) || ((end != -1) && (start >= end))) { |
| 964 throw new ArgumentError(); |
| 965 } |
| 966 int lock = (mode == FileLock.EXCLUSIVE) ? LOCK_EXCLUSIVE : LOCK_SHARED; |
| 967 var result = _ops.lock(lock, start, end); |
| 968 if (result is OSError) { |
| 969 throw new FileSystemException('lock failed', path, result); |
| 970 } |
| 971 } |
| 972 |
| 973 void unlockSync([int start = 0, int end = -1]) { |
| 974 _checkAvailable(); |
| 975 if ((start is !int) || (end is !int)) { |
| 976 throw new ArgumentError(); |
| 977 } |
| 978 if (start == end) { |
| 979 throw new ArgumentError(); |
| 980 } |
| 981 var result = _ops.lock(LOCK_UNLOCK, start, end); |
| 982 if (result is OSError) { |
| 983 throw new FileSystemException('unlock failed', path, result); |
| 984 } |
| 985 } |
| 986 |
| 987 bool closed = false; |
| 988 |
| 989 // Calling this function will increase the reference count on the native |
| 990 // object that implements the file operations. It should only be called to |
| 991 // pass the pointer to the IO Service, which will decrement the reference |
| 992 // count when it is finished with it. |
| 993 int _pointer() => _ops.getPointer(); |
| 994 |
| 995 Future _dispatch(int request, List data, { bool markClosed: false }) { |
| 996 if (closed) { |
| 997 return new Future.error(new FileSystemException("File closed", path)); |
| 998 } |
| 999 if (_asyncDispatched) { |
| 1000 var msg = "An async operation is currently pending"; |
| 1001 return new Future.error(new FileSystemException(msg, path)); |
| 1002 } |
| 1003 if (markClosed) { |
| 1004 // Set closed to true to ensure that no more async requests can be issued |
| 1005 // for this file. |
| 1006 closed = true; |
| 1007 } |
| 1008 _asyncDispatched = true; |
| 1009 data[0] = _pointer(); |
| 1010 return _IOService._dispatch(request, data) |
| 1011 .whenComplete(() { |
| 1012 _asyncDispatched = false; |
| 1013 }); |
| 1014 } |
| 1015 |
| 1016 void _checkAvailable() { |
| 1017 if (_asyncDispatched) { |
| 1018 throw new FileSystemException("An async operation is currently pending", |
| 1019 path); |
| 1020 } |
| 1021 if (closed) { |
| 1022 throw new FileSystemException("File closed", path); |
| 1023 } |
| 1024 } |
| 1025 } |
OLD | NEW |