blob: ce60209d129e6eb8f842f077d208d37a25602c29 [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.
part of dart.io;
class _FileStream extends Stream<List<int>> {
// Stream controller.
StreamController<List<int>> _controller;
// Read the file in blocks of size 64k.
final int _blockSize = 64 * 1024;
// Information about the underlying file.
String _name;
RandomAccessFile _openedFile;
int _position;
// Has the stream been paused or unsubscribed?
bool _paused = false;
bool _unsubscribed = false;
// Is there a read currently in progress?
bool _readInProgress = false;
// Block read but not yet send because stream is paused.
List<int> _currentBlock;
_FileStream(String this._name) : _position = 0 {
_setupController();
}
_FileStream.forStdin() : _position = 0 {
_setupController();
}
StreamSubscription<List<int>> listen(void onData(List<int> event),
{void onError(AsyncError error),
void onDone(),
bool unsubscribeOnError}) {
return _controller.stream.listen(onData,
onError: onError,
onDone: onDone,
unsubscribeOnError: unsubscribeOnError);
}
void _setupController() {
_controller = new StreamController<List<int>>(
onSubscriptionStateChange: _onSubscriptionStateChange,
onPauseStateChange: _onPauseStateChange);
}
Future _closeFile() {
Future closeFuture;
if (_openedFile != null) {
Future closeFuture = _openedFile.close();
_openedFile = null;
return closeFuture;
} else {
return new Future.immediate(null);
}
}
void _readBlock() {
// Don't start a new read if one is already in progress.
if (_readInProgress) return;
_readInProgress = true;
_openedFile.length()
.then((length) {
if (_position >= length) {
_readInProgress = false;
if (!_unsubscribed) {
_closeFile().then((_) { _controller.close(); });
_unsubscribed = true;
}
return null;
} else {
return _openedFile.read(_blockSize);
}
})
.then((block) {
_readInProgress = false;
if (block == null || _unsubscribed) {
return;
}
_position += block.length;
if (_paused) {
_currentBlock = block;
} else {
_controller.add(block);
_readBlock();
}
})
.catchError((e) {
if (!_unsubscribed) {
_controller.signalError(e);
_closeFile().then((_) { _controller.close(); });
_unsubscribed = true;
}
});
}
void _start() {
Future<RandomAccessFile> openFuture;
if (_name != null) {
openFuture = new File(_name).open(FileMode.READ);
} else {
openFuture = new Future.immediate(_File._openStdioSync(0));
}
openFuture
.then((RandomAccessFile opened) {
_openedFile = opened;
_readBlock();
})
.catchError((e) {
_controller.signalError(e);
_controller.close();
});
}
void _resume() {
_paused = false;
if (_currentBlock != null) {
_controller.add(_currentBlock);
_currentBlock = null;
}
// Resume reading unless we are already done.
if (_openedFile != null) _readBlock();
}
void _onSubscriptionStateChange() {
if (_controller.hasSubscribers) {
_start();
} else {
_unsubscribed = true;
_closeFile();
}
}
void _onPauseStateChange() {
if (_controller.isPaused) {
_paused = true;
} else {
_resume();
}
}
}
class _FileStreamConsumer extends StreamConsumer<List<int>, File> {
File _file;
Future<RandomAccessFile> _openFuture;
StreamSubscription _subscription;
_FileStreamConsumer(File this._file, FileMode mode) {
_openFuture = _file.open(mode);
}
_FileStreamConsumer.fromStdio(int fd) {
assert(1 <= fd && fd <= 2);
_openFuture = new Future.immediate(_File._openStdioSync(fd));
}
Future<File> consume(Stream<List<int>> stream) {
Completer<File> completer = new Completer<File>();
_openFuture
.then((openedFile) {
_subscription = stream.listen(
(d) {
_subscription.pause();
openedFile.writeList(d, 0, d.length)
.then((_) => _subscription.resume())
.catchError((e) {
openedFile.close();
completer.completeError(e);
});
},
onDone: () {
// Wait for the file to close (and therefore flush) before
// completing the future.
openedFile.close()
.then((_) {
completer.complete(_file);
})
.catchError((e) {
completer.completeError(e);
});
},
onError: (e) {
openedFile.close();
completer.completeError(e);
},
unsubscribeOnError: true);
})
.catchError((e) {
completer.completeError(e);
});
return completer.future;
}
}
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_REQUEST = 16;
const int _READ_LIST_REQUEST = 17;
const int _WRITE_LIST_REQUEST = 18;
// 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('${Error.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.fixedLength(2);
request[0] = _EXISTS_REQUEST;
request[1] = _name;
return _fileService.call(request).then((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.fixedLength(2);
request[0] = _CREATE_REQUEST;
request[1] = _name;
return _fileService.call(request).then((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.fixedLength(2);
request[0] = _DELETE_REQUEST;
request[1] = _name;
return _fileService.call(request).then((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.fixedLength(2);
request[0] = _DIRECTORY_REQUEST;
request[1] = _name;
return _fileService.call(request).then((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) {
Timer.run(() {
completer.completeError(new ArgumentError());
});
return completer.future;
}
List request = new List.fixedLength(3);
request[0] = _OPEN_REQUEST;
request[1] = _name;
request[2] = mode._mode; // Direct int value for serialization.
return _fileService.call(request).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "Cannot open file '$_name'");
}
return new _RandomAccessFile(response, _name);
});
}
Future<int> length() {
_ensureFileService();
List request = new List.fixedLength(2);
request[0] = _LENGTH_FROM_NAME_REQUEST;
request[1] = _name;
return _fileService.call(request).then((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<DateTime> lastModified() {
_ensureFileService();
List request = new List.fixedLength(2);
request[0] = _LAST_MODIFIED_REQUEST;
request[1] = _name;
return _fileService.call(request).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response,
"Cannot retrieve modification time "
"for file '$_name'");
}
return new DateTime.fromMillisecondsSinceEpoch(response);
});
}
external static _lastModified(String name);
DateTime lastModifiedSync() {
var ms = _lastModified(name);
throwIfError(ms, "Cannot retrieve modification time for file '$_name'");
return new DateTime.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.fixedLength(2);
request[0] = _FULL_PATH_REQUEST;
request[1] = _name;
return _fileService.call(request).then((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;
}
Stream<List<int>> openRead() {
return new _FileStream(_name);
}
IOSink<File> openWrite([FileMode mode = FileMode.WRITE]) {
if (mode != FileMode.WRITE &&
mode != FileMode.APPEND) {
throw new FileIOException(
"Wrong FileMode. Use FileMode.WRITE or FileMode.APPEND");
}
var consumer = new _FileStreamConsumer(this, mode);
return new IOSink<File>(consumer);
}
Future<List<int>> readAsBytes() {
_ensureFileService();
Completer<List<int>> completer = new Completer<List<int>>();
var chunks = new _BufferList();
openRead().listen(
(d) => chunks.add(d),
onDone: () {
var result = chunks.readBytes(chunks.length);
if (result == null) result = <int>[];
completer.complete(result);
},
onError: (e) {
completer.completeError(e);
},
unsubscribeOnError: true);
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> readAsString([Encoding encoding = Encoding.UTF_8]) {
_ensureFileService();
return readAsBytes().then((bytes) {
return _decodeString(bytes, encoding);
});
}
String readAsStringSync([Encoding encoding = Encoding.UTF_8]) {
List<int> bytes = readAsBytesSync();
return _decodeString(bytes, encoding);
}
static List<String> _decodeLines(List<int> bytes, Encoding encoding) {
if (bytes.length == 0) return [];
var list = [];
var controller = new StreamController();
controller.stream
.transform(new StringDecoder(encoding))
.transform(new LineTransformer())
.listen((line) => list.add(line));
controller.add(bytes);
controller.close();
return list;
}
Future<List<String>> readAsLines([Encoding encoding = Encoding.UTF_8]) {
_ensureFileService();
Completer<List<String>> completer = new Completer<List<String>>();
return readAsBytes().then((bytes) {
return _decodeLines(bytes, encoding);
});
}
List<String> readAsLinesSync([Encoding encoding = Encoding.UTF_8]) {
return _decodeLines(readAsBytesSync(), encoding);
}
Future<File> writeAsBytes(List<int> bytes,
[FileMode mode = FileMode.WRITE]) {
Completer<File> completer = new Completer<File>();
try {
var stream = openWrite(mode);
stream.add(bytes);
stream.close();
stream.done
.then((_) {
completer.complete(this);
})
.catchError((e) {
completer.completeError(e);
});
} catch (e) {
Timer.run(() => completer.completeError(e));
return completer.future;
}
return completer.future;
}
void writeAsBytesSync(List<int> bytes, [FileMode mode = FileMode.WRITE]) {
RandomAccessFile opened = openSync(mode);
opened.writeListSync(bytes, 0, bytes.length);
opened.closeSync();
}
Future<File> writeAsString(String contents,
{FileMode mode: FileMode.WRITE,
Encoding encoding: Encoding.UTF_8}) {
try {
return writeAsBytes(_encodeString(contents, encoding), mode);
} catch (e) {
var completer = new Completer();
Timer.run(() => completer.completeError(e));
return completer.future;
}
}
void writeAsStringSync(String contents,
{FileMode mode: FileMode.WRITE,
Encoding encoding: Encoding.UTF_8}) {
writeAsBytesSync(_encodeString(contents, encoding), mode);
}
String get name => _name;
String toString() => "File: '$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.fixedLength(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).then((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.fixedLength(2);
request[0] = _READ_BYTE_REQUEST;
request[1] = _id;
return _fileService.call(request).then((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<List<int>> read(int bytes) {
_ensureFileService();
Completer<List<int>> completer = new Completer<List<int>>();
if (bytes is !int) {
// Complete asynchronously so the user has a chance to setup
// handlers without getting exceptions when registering the
// then handler.
Timer.run(() {
completer.completeError(new FileIOException(
"Invalid arguments to read for file '$_name'"));
});
return completer.future;
};
if (closed) return _completeWithClosedException(completer);
List request = new List(3);
request[0] = _READ_REQUEST;
request[1] = _id;
request[2] = bytes;
return _fileService.call(request).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response,
"read failed for file '$_name'");
}
return response[1];
});
}
external static _read(int id, int bytes);
List<int> readSync(int bytes) {
if (bytes is !int) {
throw new FileIOException(
"Invalid arguments to readSync for file '$_name'");
}
return _read(_id, bytes);
}
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.
Timer.run(() {
completer.completeError(new FileIOException(
"Invalid arguments to readList for file '$_name'"));
});
return completer.future;
};
if (closed) return _completeWithClosedException(completer);
List request = new List.fixedLength(3);
request[0] = _READ_LIST_REQUEST;
request[1] = _id;
request[2] = bytes;
return _fileService.call(request).then((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.
Timer.run(() {
completer.completeError(new FileIOException(
"Invalid argument to writeByte for file '$_name'"));
});
return completer.future;
}
if (closed) return _completeWithClosedException(completer);
List request = new List.fixedLength(3);
request[0] = _WRITE_BYTE_REQUEST;
request[1] = _id;
request[2] = value;
return _fileService.call(request).then((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.
Timer.run(() {
completer.completeError(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.
Timer.run(() => completer.completeError(e));
return completer.future;
}
List request = new List.fixedLength(5);
request[0] = _WRITE_LIST_REQUEST;
request[1] = _id;
request[2] = result.buffer;
request[3] = result.offset;
request[4] = bytes;
return _fileService.call(request).then((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();
Timer.run(() {
completer.completeError(new FileIOException(
"Invalid encoding in writeString: $encoding"));
});
return completer.future;
}
var data = _encodeString(string, encoding);
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 = _encodeString(string, encoding);
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.fixedLength(2);
request[0] = _POSITION_REQUEST;
request[1] = _id;
return _fileService.call(request).then((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.fixedLength(3);
request[0] = _SET_POSITION_REQUEST;
request[1] = _id;
request[2] = position;
return _fileService.call(request).then((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.fixedLength(3);
request[0] = _TRUNCATE_REQUEST;
request[1] = _id;
request[2] = length;
return _fileService.call(request).then((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.fixedLength(2);
request[0] = _LENGTH_REQUEST;
request[1] = _id;
return _fileService.call(request).then((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.fixedLength(2);
request[0] = _FLUSH_REQUEST;
request[1] = _id;
return _fileService.call(request).then((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) {
Timer.run(() {
completer.completeError(
new FileIOException("File closed '$_name'"));
});
return completer.future;
}
final String _name;
int _id;
SendPort _fileService;
}