| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // 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. |
| |
| class _FileInputStream extends _BaseDataInputStream implements InputStream { |
| _FileInputStream(String name) |
| : _data = const [], |
| _position = 0, |
| _filePosition = 0 { |
| var file = new File(name); |
| var future = file.open(FileMode.READ); |
| future.handleException((e) { |
| _reportError(e); |
| return true; |
| }); |
| future.then(_setupOpenedFile); |
| } |
| |
| _FileInputStream.fromStdio(int fd) |
| : _data = const [], |
| _position = 0, |
| _filePosition = 0 { |
| assert(fd == 0); |
| _setupOpenedFile(_File._openStdioSync(fd)); |
| } |
| |
| void _setupOpenedFile(RandomAccessFile openedFile) { |
| _openedFile = openedFile; |
| if (_streamMarkedClosed) { |
| // This input stream has already been closed. |
| _fileLength = 0; |
| _closeFile(); |
| return; |
| } |
| var futureOpen = _openedFile.length(); |
| futureOpen.then((len) { |
| _fileLength = len; |
| _fillBuffer(); |
| }); |
| futureOpen.handleException((e) { |
| _reportError(e); |
| return true; |
| }); |
| } |
| |
| void _closeFile() { |
| if (_openedFile == null) { |
| _streamMarkedClosed = true; |
| return; |
| } |
| if (available() == 0) _cancelScheduledDataCallback(); |
| if (!_openedFile.closed) { |
| _openedFile.close().then((ignore) { |
| _streamMarkedClosed = true; |
| _checkScheduleCallbacks(); |
| }); |
| } |
| } |
| |
| void _fillBuffer() { |
| Expect.equals(_position, _data.length); |
| if (_openedFile == null) return; // Called before the file is opened. |
| int size = min(_bufferLength, _fileLength - _filePosition); |
| if (size == 0) { |
| _closeFile(); |
| return; |
| } |
| // If there is currently a _fillBuffer call waiting on readList, |
| // let it fill the buffer instead of us. |
| if (_activeFillBufferCall) return; |
| _activeFillBufferCall = true; |
| if (_data.length != size) { |
| _data = new Uint8List(size); |
| // Maintain the invariant signalling that the buffer is empty. |
| _position = _data.length; |
| } |
| var future = _openedFile.readList(_data, 0, _data.length); |
| future.then((read) { |
| _filePosition += read; |
| if (read != _data.length) { |
| _data = _data.getRange(0, read); |
| } |
| _position = 0; |
| _activeFillBufferCall = false; |
| |
| if (_fileLength == _filePosition) { |
| _closeFile(); |
| } |
| _checkScheduleCallbacks(); |
| }); |
| future.handleException((e) { |
| _activeFillBufferCall = false; |
| _reportError(e); |
| return true; |
| }); |
| } |
| |
| int available() { |
| return closed ? 0 : _data.length - _position; |
| } |
| |
| void pipe(OutputStream output, {bool close: true}) { |
| _pipe(this, output, close: close); |
| } |
| |
| void _finishRead() { |
| if (_position == _data.length && !_streamMarkedClosed) { |
| _fillBuffer(); |
| } else { |
| _checkScheduleCallbacks(); |
| } |
| } |
| |
| List<int> _read(int bytesToRead) { |
| List<int> result; |
| if (_position == 0 && bytesToRead == _data.length) { |
| result = _data; |
| _data = const []; |
| } else { |
| result = new Uint8List(bytesToRead); |
| result.setRange(0, bytesToRead, _data, _position); |
| _position += bytesToRead; |
| } |
| _finishRead(); |
| return result; |
| } |
| |
| int _readInto(List<int> buffer, int offset, int len) { |
| buffer.setRange(offset, len, _data, _position); |
| _position += len; |
| _finishRead(); |
| return len; |
| } |
| |
| void _close() { |
| _data = const []; |
| _position = 0; |
| _filePosition = 0; |
| _fileLength = 0; |
| _closeFile(); |
| } |
| |
| static const int _bufferLength = 64 * 1024; |
| |
| RandomAccessFile _openedFile; |
| List<int> _data; |
| int _position; |
| int _filePosition; |
| int _fileLength; |
| bool _activeFillBufferCall = false; |
| } |
| |
| |
| class _PendingOperation { |
| const _PendingOperation(this._id); |
| static const _PendingOperation CLOSE = const _PendingOperation(0); |
| static const _PendingOperation FLUSH = const _PendingOperation(1); |
| final int _id; |
| } |
| |
| |
| class _FileOutputStream extends _BaseOutputStream implements OutputStream { |
| _FileOutputStream(String name, FileMode mode) { |
| _pendingOperations = new List(); |
| var f = new File(name); |
| var openFuture = f.open(mode); |
| openFuture.then((openedFile) { |
| _file = openedFile; |
| _processPendingOperations(); |
| }); |
| openFuture.handleException((e) { |
| _reportError(e); |
| return true; |
| }); |
| } |
| |
| _FileOutputStream.fromStdio(int fd) { |
| assert(1 <= fd && fd <= 2); |
| _file = _File._openStdioSync(fd); |
| } |
| |
| bool write(List<int> buffer, [bool copyBuffer = false]) { |
| var data = buffer; |
| if (copyBuffer) { |
| var length = buffer.length; |
| data = new Uint8List(length); |
| data.setRange(0, length, buffer, 0); |
| } |
| if (_file == null) { |
| _pendingOperations.add(data); |
| } else { |
| _write(data, 0, data.length); |
| } |
| return false; |
| } |
| |
| bool writeFrom(List<int> buffer, [int offset = 0, int len]) { |
| // A copy is required by the interface. |
| var length = buffer.length - offset; |
| if (len != null) { |
| if (len > length) throw new RangeError.value(len); |
| length = len; |
| } |
| var copy = new Uint8List(length); |
| copy.setRange(0, length, buffer, offset); |
| return write(copy); |
| } |
| |
| |
| void flush() { |
| if (_file == null) { |
| _pendingOperations.add(_PendingOperation.FLUSH); |
| } else { |
| _file.flush().then((ignored) => null); |
| } |
| } |
| |
| |
| void close() { |
| _streamMarkedClosed = true; |
| if (_file == null) { |
| _pendingOperations.add(_PendingOperation.CLOSE); |
| } else if (!_closeCallbackScheduled) { |
| _file.close().then((ignore) { |
| if (_onClosed != null) _onClosed(); |
| }); |
| _closeCallbackScheduled = true; |
| } |
| } |
| |
| void set onNoPendingWrites(void callback()) { |
| _onNoPendingWrites = callback; |
| if ((_pendingOperations == null || _pendingOperations.length == 0) && |
| outstandingWrites == 0 && |
| !_streamMarkedClosed && |
| _onNoPendingWrites != null) { |
| new Timer(0, (t) { |
| if (_onNoPendingWrites != null) { |
| _onNoPendingWrites(); |
| } |
| }); |
| } |
| } |
| |
| void set onClosed(void callback()) { |
| _onClosed = callback; |
| } |
| |
| void _processPendingOperations() { |
| _pendingOperations.forEach((buffer) { |
| if (buffer is _PendingOperation) { |
| if (identical(buffer, _PendingOperation.CLOSE)) { |
| close(); |
| } else { |
| assert(identical(buffer, _PendingOperation.FLUSH)); |
| flush(); |
| } |
| } else { |
| write(buffer); |
| } |
| }); |
| _pendingOperations = null; |
| } |
| |
| void _write(List<int> buffer, int offset, int len) { |
| outstandingWrites++; |
| var writeListFuture = _file.writeList(buffer, offset, len); |
| writeListFuture.then((ignore) { |
| outstandingWrites--; |
| if (outstandingWrites == 0 && |
| !_streamMarkedClosed && |
| _onNoPendingWrites != null) { |
| _onNoPendingWrites(); |
| } |
| }); |
| writeListFuture.handleException((e) { |
| outstandingWrites--; |
| _reportError(e); |
| return true; |
| }); |
| } |
| |
| bool get closed => _streamMarkedClosed; |
| |
| RandomAccessFile _file; |
| |
| // When this is set to true the stream is marked closed. When a |
| // stream is marked closed no more data can be written. |
| bool _streamMarkedClosed = false; |
| |
| // When this is set to true, the close callback has been scheduled and the |
| // stream will be fully closed once it's called. |
| bool _closeCallbackScheduled = false; |
| |
| // Number of writes that have not yet completed. |
| int outstandingWrites = 0; |
| |
| // List of pending writes that were issued before the underlying |
| // file was successfully opened. |
| List _pendingOperations; |
| |
| Function _onNoPendingWrites; |
| Function _onClosed; |
| } |
| |
| const int _EXISTS_REQUEST = 0; |
| const int _CREATE_REQUEST = 1; |
| const int _DELETE_REQUEST = 2; |
| const int _OPEN_REQUEST = 3; |
| const int _FULL_PATH_REQUEST = 4; |
| const int _DIRECTORY_REQUEST = 5; |
| const int _CLOSE_REQUEST = 6; |
| const int _POSITION_REQUEST = 7; |
| const int _SET_POSITION_REQUEST = 8; |
| const int _TRUNCATE_REQUEST = 9; |
| const int _LENGTH_REQUEST = 10; |
| const int _LENGTH_FROM_NAME_REQUEST = 11; |
| const int _LAST_MODIFIED_REQUEST = 12; |
| const int _FLUSH_REQUEST = 13; |
| const int _READ_BYTE_REQUEST = 14; |
| const int _WRITE_BYTE_REQUEST = 15; |
| const int _READ_LIST_REQUEST = 16; |
| const int _WRITE_LIST_REQUEST = 17; |
| |
| // Base class for _File and _RandomAccessFile with shared functions. |
| class _FileBase { |
| bool _isErrorResponse(response) { |
| return response is List && response[0] != _SUCCESS_RESPONSE; |
| } |
| |
| _exceptionFromResponse(response, String message) { |
| assert(_isErrorResponse(response)); |
| switch (response[_ERROR_RESPONSE_ERROR_TYPE]) { |
| case _ILLEGAL_ARGUMENT_RESPONSE: |
| return new ArgumentError(); |
| case _OSERROR_RESPONSE: |
| var err = new OSError(response[_OSERROR_RESPONSE_MESSAGE], |
| response[_OSERROR_RESPONSE_ERROR_CODE]); |
| return new FileIOException(message, err); |
| case _FILE_CLOSED_RESPONSE: |
| return new FileIOException("File closed"); |
| default: |
| return new Exception("Unknown error"); |
| } |
| } |
| } |
| |
| // TODO(ager): The only reason for this class is that the patching |
| // mechanism doesn't seem to like patching a private top level |
| // function. |
| class _FileUtils { |
| external static SendPort _newServicePort(); |
| } |
| |
| // Class for encapsulating the native implementation of files. |
| class _File extends _FileBase implements File { |
| // Constructor for file. |
| _File(String this._name) { |
| if (_name is! String) { |
| throw new ArgumentError('${NoSuchMethodError.safeToString(_name)} ' |
| 'is not a String'); |
| } |
| } |
| |
| // Constructor from Path for file. |
| _File.fromPath(Path path) : this(path.toNativePath()); |
| |
| Future<bool> exists() { |
| _ensureFileService(); |
| List request = new List(2); |
| request[0] = _EXISTS_REQUEST; |
| request[1] = _name; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, "Cannot open file '$_name'"); |
| } |
| return response; |
| }); |
| } |
| |
| external static _exists(String name); |
| |
| bool existsSync() { |
| var result = _exists(_name); |
| throwIfError(result, "Cannot check existence of file '$_name'"); |
| return result; |
| } |
| |
| Future<File> create() { |
| _ensureFileService(); |
| List request = new List(2); |
| request[0] = _CREATE_REQUEST; |
| request[1] = _name; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, "Cannot create file '$_name'"); |
| } |
| return this; |
| }); |
| } |
| |
| external static _create(String name); |
| |
| void createSync() { |
| var result = _create(_name); |
| throwIfError(result, "Cannot create file '$_name'"); |
| } |
| |
| Future<File> delete() { |
| _ensureFileService(); |
| List request = new List(2); |
| request[0] = _DELETE_REQUEST; |
| request[1] = _name; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, "Cannot delete file '$_name'"); |
| } |
| return this; |
| }); |
| } |
| |
| external static _delete(String name); |
| |
| void deleteSync() { |
| var result = _delete(_name); |
| throwIfError(result, "Cannot delete file '$_name'"); |
| } |
| |
| Future<Directory> directory() { |
| _ensureFileService(); |
| List request = new List(2); |
| request[0] = _DIRECTORY_REQUEST; |
| request[1] = _name; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "Cannot retrieve directory for " |
| "file '$_name'"); |
| } |
| return new Directory(response); |
| }); |
| } |
| |
| external static _directory(String name); |
| |
| Directory directorySync() { |
| var result = _directory(name); |
| throwIfError(result, "Cannot retrieve directory for file '$_name'"); |
| return new Directory(result); |
| } |
| |
| Future<RandomAccessFile> open([FileMode mode = FileMode.READ]) { |
| _ensureFileService(); |
| Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| if (mode != FileMode.READ && |
| mode != FileMode.WRITE && |
| mode != FileMode.APPEND) { |
| new Timer(0, (t) { |
| completer.completeException(new ArgumentError()); |
| }); |
| return completer.future; |
| } |
| List request = new List(3); |
| request[0] = _OPEN_REQUEST; |
| request[1] = _name; |
| request[2] = mode._mode; // Direct int value for serialization. |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, "Cannot open file '$_name'"); |
| } |
| return new _RandomAccessFile(response, _name); |
| }); |
| } |
| |
| Future<int> length() { |
| _ensureFileService(); |
| List request = new List(2); |
| request[0] = _LENGTH_FROM_NAME_REQUEST; |
| request[1] = _name; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "Cannot retrieve length of " |
| "file '$_name'"); |
| } |
| return response; |
| }); |
| } |
| |
| |
| external static _lengthFromName(String name); |
| |
| int lengthSync() { |
| var result = _lengthFromName(_name); |
| throwIfError(result, "Cannot retrieve length of file '$_name'"); |
| return result; |
| } |
| |
| Future<Date> lastModified() { |
| _ensureFileService(); |
| List request = new List(2); |
| request[0] = _LAST_MODIFIED_REQUEST; |
| request[1] = _name; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "Cannot retrieve modification time " |
| "for file '$_name'"); |
| } |
| return new Date.fromMillisecondsSinceEpoch(response); |
| }); |
| } |
| |
| external static _lastModified(String name); |
| |
| Date lastModifiedSync() { |
| var ms = _lastModified(name); |
| throwIfError(ms, "Cannot retrieve modification time for file '$_name'"); |
| return new Date.fromMillisecondsSinceEpoch(ms); |
| } |
| |
| external static _open(String name, int mode); |
| |
| RandomAccessFile openSync([FileMode mode = FileMode.READ]) { |
| if (mode != FileMode.READ && |
| mode != FileMode.WRITE && |
| mode != FileMode.APPEND) { |
| throw new FileIOException("Unknown file mode. Use FileMode.READ, " |
| "FileMode.WRITE or FileMode.APPEND."); |
| } |
| var id = _open(_name, mode._mode); |
| throwIfError(id, "Cannot open file '$_name'"); |
| return new _RandomAccessFile(id, _name); |
| } |
| |
| external static int _openStdio(int fd); |
| |
| static RandomAccessFile _openStdioSync(int fd) { |
| var id = _openStdio(fd); |
| if (id == 0) { |
| throw new FileIOException("Cannot open stdio file for: $fd"); |
| } |
| return new _RandomAccessFile(id, ""); |
| } |
| |
| Future<String> fullPath() { |
| _ensureFileService(); |
| List request = new List(2); |
| request[0] = _FULL_PATH_REQUEST; |
| request[1] = _name; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "Cannot retrieve full path" |
| " for '$_name'"); |
| } |
| return response; |
| }); |
| } |
| |
| external static _fullPath(String name); |
| |
| String fullPathSync() { |
| var result = _fullPath(_name); |
| throwIfError(result, "Cannot retrieve full path for file '$_name'"); |
| return result; |
| } |
| |
| InputStream openInputStream() { |
| return new _FileInputStream(_name); |
| } |
| |
| OutputStream openOutputStream([FileMode mode = FileMode.WRITE]) { |
| if (mode != FileMode.WRITE && |
| mode != FileMode.APPEND) { |
| throw new FileIOException( |
| "Wrong FileMode. Use FileMode.WRITE or FileMode.APPEND"); |
| } |
| return new _FileOutputStream(_name, mode); |
| } |
| |
| Future<List<int>> readAsBytes() { |
| _ensureFileService(); |
| Completer<List<int>> completer = new Completer<List<int>>(); |
| var chunks = new _BufferList(); |
| var stream = openInputStream(); |
| stream.onClosed = () { |
| var result = chunks.readBytes(chunks.length); |
| if (result == null) result = <int>[]; |
| completer.complete(result); |
| }; |
| stream.onData = () { |
| var chunk = stream.read(); |
| chunks.add(chunk); |
| }; |
| stream.onError = completer.completeException; |
| return completer.future; |
| } |
| |
| List<int> readAsBytesSync() { |
| var opened = openSync(); |
| var length = opened.lengthSync(); |
| var result = new Uint8List(length); |
| var read = opened.readListSync(result, 0, length); |
| if (read != length) { |
| throw new FileIOException("Failed to read file"); |
| } |
| opened.closeSync(); |
| return result; |
| } |
| |
| Future<String> readAsText([Encoding encoding = Encoding.UTF_8]) { |
| _ensureFileService(); |
| return readAsBytes().transform((bytes) { |
| if (bytes.length == 0) return ""; |
| var decoder = _StringDecoders.decoder(encoding); |
| decoder.write(bytes); |
| return decoder.decoded(); |
| }); |
| } |
| |
| String readAsTextSync([Encoding encoding = Encoding.UTF_8]) { |
| var decoder = _StringDecoders.decoder(encoding); |
| List<int> bytes = readAsBytesSync(); |
| if (bytes.length == 0) return ""; |
| decoder.write(bytes); |
| return decoder.decoded(); |
| } |
| |
| List<String> _getDecodedLines(_StringDecoder decoder) { |
| List<String> result = []; |
| var line = decoder.decodedLine; |
| while (line != null) { |
| result.add(line); |
| line = decoder.decodedLine; |
| } |
| // If there is more data with no terminating line break we treat |
| // it as the last line. |
| var data = decoder.decoded(); |
| if (data != null) { |
| result.add(data); |
| } |
| return result; |
| } |
| |
| Future<List<String>> readAsLines([Encoding encoding = Encoding.UTF_8]) { |
| _ensureFileService(); |
| Completer<List<String>> completer = new Completer<List<String>>(); |
| return readAsBytes().transform((bytes) { |
| var decoder = _StringDecoders.decoder(encoding); |
| decoder.write(bytes); |
| return _getDecodedLines(decoder); |
| }); |
| } |
| |
| List<String> readAsLinesSync([Encoding encoding = Encoding.UTF_8]) { |
| var decoder = _StringDecoders.decoder(encoding); |
| List<int> bytes = readAsBytesSync(); |
| decoder.write(bytes); |
| return _getDecodedLines(decoder); |
| } |
| |
| String get name => _name; |
| |
| void _ensureFileService() { |
| if (_fileService == null) { |
| _fileService = _FileUtils._newServicePort(); |
| } |
| } |
| |
| static throwIfError(Object result, String msg) { |
| if (result is OSError) { |
| throw new FileIOException(msg, result); |
| } |
| } |
| |
| final String _name; |
| |
| SendPort _fileService; |
| } |
| |
| |
| class _RandomAccessFile extends _FileBase implements RandomAccessFile { |
| _RandomAccessFile(int this._id, String this._name); |
| |
| Future<RandomAccessFile> close() { |
| Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| if (closed) return _completeWithClosedException(completer); |
| _ensureFileService(); |
| List request = new List(2); |
| request[0] = _CLOSE_REQUEST; |
| request[1] = _id; |
| // Set the id_ to 0 (NULL) to ensure the no more async requests |
| // can be issued for this file. |
| _id = 0; |
| return _fileService.call(request).transform((result) { |
| if (result != -1) { |
| _id = result; |
| return this; |
| } else { |
| throw new FileIOException("Cannot close file '$_name'"); |
| } |
| }); |
| } |
| |
| external static int _close(int id); |
| |
| void closeSync() { |
| _checkNotClosed(); |
| var id = _close(_id); |
| if (id == -1) { |
| throw new FileIOException("Cannot close file '$_name'"); |
| } |
| _id = id; |
| } |
| |
| Future<int> readByte() { |
| _ensureFileService(); |
| Completer<int> completer = new Completer<int>(); |
| if (closed) return _completeWithClosedException(completer); |
| List request = new List(2); |
| request[0] = _READ_BYTE_REQUEST; |
| request[1] = _id; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "readByte failed for file '$_name'"); |
| } |
| return response; |
| }); |
| } |
| |
| external static _readByte(int id); |
| |
| int readByteSync() { |
| _checkNotClosed(); |
| var result = _readByte(_id); |
| if (result is OSError) { |
| throw new FileIOException("readByte failed for file '$_name'", result); |
| } |
| return result; |
| } |
| |
| Future<int> readList(List<int> buffer, int offset, int bytes) { |
| _ensureFileService(); |
| Completer<int> completer = new Completer<int>(); |
| if (buffer is !List || offset is !int || bytes is !int) { |
| // Complete asynchronously so the user has a chance to setup |
| // handlers without getting exceptions when registering the |
| // then handler. |
| new Timer(0, (t) { |
| completer.completeException(new FileIOException( |
| "Invalid arguments to readList for file '$_name'")); |
| }); |
| return completer.future; |
| }; |
| if (closed) return _completeWithClosedException(completer); |
| List request = new List(3); |
| request[0] = _READ_LIST_REQUEST; |
| request[1] = _id; |
| request[2] = bytes; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "readList failed for file '$_name'"); |
| } |
| var read = response[1]; |
| var data = response[2]; |
| buffer.setRange(offset, read, data); |
| return read; |
| }); |
| } |
| |
| static void _checkReadWriteListArguments(int length, int offset, int bytes) { |
| if (offset < 0) throw new RangeError.value(offset); |
| if (bytes < 0) throw new RangeError.value(bytes); |
| if ((offset + bytes) > length) { |
| throw new RangeError.value(offset + bytes); |
| } |
| } |
| |
| external static _readList(int id, List<int> buffer, int offset, int bytes); |
| |
| int readListSync(List<int> buffer, int offset, int bytes) { |
| _checkNotClosed(); |
| if (buffer is !List || offset is !int || bytes is !int) { |
| throw new FileIOException( |
| "Invalid arguments to readList for file '$_name'"); |
| } |
| if (bytes == 0) return 0; |
| _checkReadWriteListArguments(buffer.length, offset, bytes); |
| var result = _readList(_id, buffer, offset, bytes); |
| if (result is OSError) { |
| throw new FileIOException("readList failed for file '$_name'", |
| result); |
| } |
| return result; |
| } |
| |
| Future<RandomAccessFile> writeByte(int value) { |
| _ensureFileService(); |
| Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| if (value is !int) { |
| // Complete asynchronously so the user has a chance to setup |
| // handlers without getting exceptions when registering the |
| // then handler. |
| new Timer(0, (t) { |
| completer.completeException(new FileIOException( |
| "Invalid argument to writeByte for file '$_name'")); |
| }); |
| return completer.future; |
| } |
| if (closed) return _completeWithClosedException(completer); |
| List request = new List(3); |
| request[0] = _WRITE_BYTE_REQUEST; |
| request[1] = _id; |
| request[2] = value; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "writeByte failed for file '$_name'"); |
| } |
| return this; |
| }); |
| } |
| |
| external static _writeByte(int id, int value); |
| |
| int writeByteSync(int value) { |
| _checkNotClosed(); |
| if (value is !int) { |
| throw new FileIOException( |
| "Invalid argument to writeByte for file '$_name'"); |
| } |
| var result = _writeByte(_id, value); |
| if (result is OSError) { |
| throw new FileIOException("writeByte failed for file '$_name'", |
| result); |
| } |
| return result; |
| } |
| |
| Future<RandomAccessFile> writeList(List<int> buffer, int offset, int bytes) { |
| _ensureFileService(); |
| Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| if (buffer is !List || offset is !int || bytes is !int) { |
| // Complete asynchronously so the user has a chance to setup |
| // handlers without getting exceptions when registering the |
| // then handler. |
| new Timer(0, (t) { |
| completer.completeException(new FileIOException( |
| "Invalid arguments to writeList for file '$_name'")); |
| }); |
| return completer.future; |
| } |
| if (closed) return _completeWithClosedException(completer); |
| |
| _BufferAndOffset result; |
| try { |
| result = _ensureFastAndSerializableBuffer(buffer, offset, bytes); |
| } catch (e) { |
| // Complete asynchronously so the user has a chance to setup |
| // handlers without getting exceptions when registering the |
| // then handler. |
| new Timer(0, (t) => completer.completeException(e)); |
| return completer.future; |
| } |
| |
| List request = new List(5); |
| request[0] = _WRITE_LIST_REQUEST; |
| request[1] = _id; |
| request[2] = result.buffer; |
| request[3] = result.offset; |
| request[4] = bytes; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "writeList failed for file '$_name'"); |
| } |
| return this; |
| }); |
| } |
| |
| external static _writeList(int id, List<int> buffer, int offset, int bytes); |
| |
| int writeListSync(List<int> buffer, int offset, int bytes) { |
| _checkNotClosed(); |
| if (buffer is !List || offset is !int || bytes is !int) { |
| throw new FileIOException( |
| "Invalid arguments to writeList for file '$_name'"); |
| } |
| if (bytes == 0) return 0; |
| _checkReadWriteListArguments(buffer.length, offset, bytes); |
| _BufferAndOffset bufferAndOffset = |
| _ensureFastAndSerializableBuffer(buffer, offset, bytes); |
| var result = |
| _writeList(_id, bufferAndOffset.buffer, bufferAndOffset.offset, bytes); |
| if (result is OSError) { |
| throw new FileIOException("writeList failed for file '$_name'", result); |
| } |
| return result; |
| } |
| |
| Future<RandomAccessFile> writeString(String string, |
| [Encoding encoding = Encoding.UTF_8]) { |
| if (encoding is! Encoding) { |
| var completer = new Completer(); |
| new Timer(0, (t) { |
| completer.completeException(new FileIOException( |
| "Invalid encoding in writeString: $encoding")); |
| }); |
| return completer.future; |
| } |
| var data = _StringEncoders.encoder(encoding).encodeString(string); |
| return writeList(data, 0, data.length); |
| } |
| |
| int writeStringSync(String string, [Encoding encoding = Encoding.UTF_8]) { |
| if (encoding is! Encoding) { |
| throw new FileIOException( |
| "Invalid encoding in writeStringSync: $encoding"); |
| } |
| var data = _StringEncoders.encoder(encoding).encodeString(string); |
| return writeListSync(data, 0, data.length); |
| } |
| |
| Future<int> position() { |
| _ensureFileService(); |
| Completer<int> completer = new Completer<int>(); |
| if (closed) return _completeWithClosedException(completer); |
| List request = new List(2); |
| request[0] = _POSITION_REQUEST; |
| request[1] = _id; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "position failed for file '$_name'"); |
| } |
| return response; |
| }); |
| } |
| |
| external static _position(int id); |
| |
| int positionSync() { |
| _checkNotClosed(); |
| var result = _position(_id); |
| if (result is OSError) { |
| throw new FileIOException("position failed for file '$_name'", result); |
| } |
| return result; |
| } |
| |
| Future<RandomAccessFile> setPosition(int position) { |
| _ensureFileService(); |
| Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| if (closed) return _completeWithClosedException(completer); |
| List request = new List(3); |
| request[0] = _SET_POSITION_REQUEST; |
| request[1] = _id; |
| request[2] = position; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "setPosition failed for file '$_name'"); |
| } |
| return this; |
| }); |
| } |
| |
| external static _setPosition(int id, int position); |
| |
| void setPositionSync(int position) { |
| _checkNotClosed(); |
| var result = _setPosition(_id, position); |
| if (result is OSError) { |
| throw new FileIOException("setPosition failed for file '$_name'", result); |
| } |
| } |
| |
| Future<RandomAccessFile> truncate(int length) { |
| _ensureFileService(); |
| Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| if (closed) return _completeWithClosedException(completer); |
| List request = new List(3); |
| request[0] = _TRUNCATE_REQUEST; |
| request[1] = _id; |
| request[2] = length; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "truncate failed for file '$_name'"); |
| } |
| return this; |
| }); |
| } |
| |
| external static _truncate(int id, int length); |
| |
| void truncateSync(int length) { |
| _checkNotClosed(); |
| var result = _truncate(_id, length); |
| if (result is OSError) { |
| throw new FileIOException("truncate failed for file '$_name'", result); |
| } |
| } |
| |
| Future<int> length() { |
| _ensureFileService(); |
| Completer<int> completer = new Completer<int>(); |
| if (closed) return _completeWithClosedException(completer); |
| List request = new List(2); |
| request[0] = _LENGTH_REQUEST; |
| request[1] = _id; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "length failed for file '$_name'"); |
| } |
| return response; |
| }); |
| } |
| |
| external static _length(int id); |
| |
| int lengthSync() { |
| _checkNotClosed(); |
| var result = _length(_id); |
| if (result is OSError) { |
| throw new FileIOException("length failed for file '$_name'", result); |
| } |
| return result; |
| } |
| |
| Future<RandomAccessFile> flush() { |
| _ensureFileService(); |
| Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| if (closed) return _completeWithClosedException(completer); |
| List request = new List(2); |
| request[0] = _FLUSH_REQUEST; |
| request[1] = _id; |
| return _fileService.call(request).transform((response) { |
| if (_isErrorResponse(response)) { |
| throw _exceptionFromResponse(response, |
| "flush failed for file '$_name'"); |
| } |
| return this; |
| }); |
| } |
| |
| external static _flush(int id); |
| |
| void flushSync() { |
| _checkNotClosed(); |
| var result = _flush(_id); |
| if (result is OSError) { |
| throw new FileIOException("flush failed for file '$_name'", result); |
| } |
| } |
| |
| String get name => _name; |
| |
| void _ensureFileService() { |
| if (_fileService == null) { |
| _fileService = _FileUtils._newServicePort(); |
| } |
| } |
| |
| bool get closed => _id == 0; |
| |
| void _checkNotClosed() { |
| if (closed) { |
| throw new FileIOException("File closed '$_name'"); |
| } |
| } |
| |
| Future _completeWithClosedException(Completer completer) { |
| new Timer(0, (t) { |
| completer.completeException( |
| new FileIOException("File closed '$_name'")); |
| }); |
| return completer.future; |
| } |
| |
| final String _name; |
| int _id; |
| |
| SendPort _fileService; |
| } |