blob: 5eeb768cb71e3dee435fc5f143ddb89cb45d678c [file] [log] [blame]
// 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 IndexOutOfRangeException(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 (buffer === _PendingOperation.CLOSE) {
close();
} else {
assert(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;
const int _WRITE_STRING_REQUEST = 18;
// Base class for _File and _RandomAccessFile with shared functions.
class _FileBase {
bool _isErrorResponse(response) {
return response is List && response[0] != _SUCCESS_RESPONSE;
}
Exception _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");
}
}
}
SendPort _newServicePort() native "File_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;
});
}
static _exists(String name) native "File_Exists";
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;
});
}
static _create(String name) native "File_Create";
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;
});
}
static _delete(String name) native "File_Delete";
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);
});
}
static _directory(String name) native "File_Directory";
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;
});
}
static _lengthFromName(String name) native "File_LengthFromName";
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);
});
}
static _lastModified(String name) native "File_LastModified";
Date lastModifiedSync() {
var ms = _lastModified(name);
throwIfError(ms, "Cannot retrieve modification time for file '$_name'");
return new Date.fromMillisecondsSinceEpoch(ms);
}
static _open(String name, int mode) native "File_Open";
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);
}
static int _openStdio(int fd) native "File_OpenStdio";
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;
});
}
static _fullPath(String name) native "File_FullPath";
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 = _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'");
}
});
}
static int _close(int id) native "File_Close";
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;
});
}
static _readByte(int id) native "File_ReadByte";
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 IndexOutOfRangeException(offset);
if (bytes < 0) throw new IndexOutOfRangeException(bytes);
if ((offset + bytes) > length) {
throw new IndexOutOfRangeException(offset + bytes);
}
}
static _readList(int id, List<int> buffer, int offset, int bytes)
native "File_ReadList";
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;
});
}
static _writeByte(int id, int value) native "File_WriteByte";
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;
});
}
static _writeList(int id, List<int> buffer, int offset, int bytes)
native "File_WriteList";
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]) {
_ensureFileService();
Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>();
if (closed) return _completeWithClosedException(completer);
List request = new List(3);
request[0] = _WRITE_STRING_REQUEST;
request[1] = _id;
request[2] = string;
return _fileService.call(request).transform((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response,
"writeString failed for file '$_name'");
}
return this;
});
}
static _writeString(int id, String string) native "File_WriteString";
int writeStringSync(String string, [Encoding encoding = Encoding.UTF_8]) {
_checkNotClosed();
if (string is !String) throw new ArgumentError();
var result = _writeString(_id, string);
if (result is OSError) {
throw new FileIOException("writeString failed for file '$_name'");
}
return result;
}
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;
});
}
static _position(int id) native "File_Position";
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;
});
}
static _setPosition(int id, int position) native "File_SetPosition";
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;
});
}
static _truncate(int id, int length) native "File_Truncate";
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;
});
}
static _length(int id) native "File_Length";
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;
});
}
static _flush(int id) native "File_Flush";
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 = _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;
}