Merge pull request #6 from humphd/utf8

Add fs.readFile, fs.writeFile with tests
This commit is contained in:
Alan K 2013-11-11 20:01:30 -08:00
commit c590f930e5
13 changed files with 5412 additions and 205 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
node_modules node_modules
*~

View File

@ -97,12 +97,46 @@ Write bytes from `buffer` to the file specified by `fd`, where `offset` and `len
The callback gets `(error, nbytes)`, where `nbytes` is the number of bytes written. The callback gets `(error, nbytes)`, where `nbytes` is the number of bytes written.
#### fs.writeFile(filename, data, [options], callback)
Asynchronously writes data to a file. `data` can be a string or a buffer, in which case any encoding option is ignored. The `options` argument is optional, and can take the form `"utf8"` (i.e., an encoding) or be an object literal: `{ encoding: "utf8", flag: "w" }`. If no encoding is specified, and `data` is a string, the encoding defaults to `'utf8'`. The callback gets `(error)`.
```javascript
// Write UTF8 text file
fs.writeFile('/myfile.txt', "...data...", function (err) {
if (err) throw err;
});
// Write binary file
fs.writeFile('/myfile', buffer, function (err) {
if (err) throw err;
});
```
#### fs.read(fd, buffer, offset, length, position, callback) #### fs.read(fd, buffer, offset, length, position, callback)
Read bytes from the file specified by `fd` into `buffer`, where `offset` and `length` describe the part of the buffer to be used. The `position` refers to the offset from the beginning of the file where this data should be read. If `position` is `null`, the data will be written at the current position. See pread(2). Read bytes from the file specified by `fd` into `buffer`, where `offset` and `length` describe the part of the buffer to be used. The `position` refers to the offset from the beginning of the file where this data should be read. If `position` is `null`, the data will be written at the current position. See pread(2).
The callback gets `(error, nbytes)`, where `nbytes` is the number of bytes read. The callback gets `(error, nbytes)`, where `nbytes` is the number of bytes read.
#### fs.readFile(filename, [options], callback)
Asynchronously reads the entire contents of a file. The `options` argument is optional, and can take the form `"utf8"` (i.e., an encoding) or be an object literal: `{ encoding: "utf8", flag: "r" }`. If no encoding is specified, the raw binary buffer is returned on the callback. The callback gets `(error, data)`, where data is the contents of the file.
```javascript
// Read UTF8 text file
fs.readFile('/myfile.txt', 'utf8', function (err, data) {
if (err) throw err;
console.log(data);
});
// Read binary file
fs.readFile('/myfile.txt', function (err, data) {
if (err) throw err;
console.log(data);
});
```
#### fs.lseek(fd, offset, whence, callback) #### fs.lseek(fd, offset, whence, callback)
Asynchronous lseek(2), where `whence` can be `SET`, `CUR`, or `END`. Callback gets `(error, pos)`, where `pos` is the resulting offset, in bytes, from the beginning of the file. Asynchronous lseek(2), where `whence` can be `SET`, `CUR`, or `END`. Callback gets `(error, pos)`, where `pos` is the resulting offset, in bytes, from the beginning of the file.

2712
dist/idbfs.js vendored

File diff suppressed because one or more lines are too long

7
dist/idbfs.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -16,6 +16,11 @@ module.exports = function(grunt) {
} }
}, },
jshint: {
// Don't bother with src/path.js
all: ['gruntfile.js', 'src/constants.js', 'src/error.js', 'src/fs.js', 'srs/shared.js']
},
requirejs: { requirejs: {
develop: { develop: {
options: { options: {
@ -40,9 +45,11 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-requirejs'); grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('develop', ['clean', 'requirejs']); grunt.registerTask('develop', ['clean', 'requirejs']);
grunt.registerTask('release', ['develop', 'uglify']); grunt.registerTask('release', ['develop', 'uglify']);
grunt.registerTask('check', ['jshint']);
grunt.registerTask('default', ['develop']); grunt.registerTask('default', ['develop']);
}; };

43
lib/encoding-indexes.js Normal file

File diff suppressed because one or more lines are too long

2318
lib/encoding.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
"grunt-contrib-compress": "~0.4.1", "grunt-contrib-compress": "~0.4.1",
"grunt-contrib-connect": "~0.1.2", "grunt-contrib-connect": "~0.1.2",
"grunt-contrib-jasmine": "~0.3.3", "grunt-contrib-jasmine": "~0.3.3",
"grunt-contrib-concat": "~0.1.3" "grunt-contrib-concat": "~0.1.3",
"grunt-contrib-jshint": "~0.7.1"
} }
} }

View File

@ -48,7 +48,7 @@ define(function(require) {
FS_READY: 'READY', FS_READY: 'READY',
FS_PENDING: 'PENDING', FS_PENDING: 'PENDING',
FS_ERROR: 'ERROR', FS_ERROR: 'ERROR'
}; };
}); });

View File

@ -16,84 +16,84 @@ define(function(require) {
function EExists(message){ function EExists(message){
this.message = message || ''; this.message = message || '';
}; }
EExists.prototype = new Error(); EExists.prototype = new Error();
EExists.prototype.name = "EExists"; EExists.prototype.name = "EExists";
EExists.prototype.constructor = EExists; EExists.prototype.constructor = EExists;
function EIsDirectory(message){ function EIsDirectory(message){
this.message = message || ''; this.message = message || '';
}; }
EIsDirectory.prototype = new Error(); EIsDirectory.prototype = new Error();
EIsDirectory.prototype.name = "EIsDirectory"; EIsDirectory.prototype.name = "EIsDirectory";
EIsDirectory.prototype.constructor = EIsDirectory; EIsDirectory.prototype.constructor = EIsDirectory;
function ENoEntry(message){ function ENoEntry(message){
this.message = message || ''; this.message = message || '';
}; }
ENoEntry.prototype = new Error(); ENoEntry.prototype = new Error();
ENoEntry.prototype.name = "ENoEntry"; ENoEntry.prototype.name = "ENoEntry";
ENoEntry.prototype.constructor = ENoEntry; ENoEntry.prototype.constructor = ENoEntry;
function EBusy(message){ function EBusy(message){
this.message = message || ''; this.message = message || '';
}; }
EBusy.prototype = new Error(); EBusy.prototype = new Error();
EBusy.prototype.name = "EBusy"; EBusy.prototype.name = "EBusy";
EBusy.prototype.constructor = EBusy; EBusy.prototype.constructor = EBusy;
function ENotEmpty(message){ function ENotEmpty(message){
this.message = message || ''; this.message = message || '';
}; }
ENotEmpty.prototype = new Error(); ENotEmpty.prototype = new Error();
ENotEmpty.prototype.name = "ENotEmpty"; ENotEmpty.prototype.name = "ENotEmpty";
ENotEmpty.prototype.constructor = ENotEmpty; ENotEmpty.prototype.constructor = ENotEmpty;
function ENotDirectory(message){ function ENotDirectory(message){
this.message = message || ''; this.message = message || '';
}; }
ENotDirectory.prototype = new Error(); ENotDirectory.prototype = new Error();
ENotDirectory.prototype.name = "ENotDirectory"; ENotDirectory.prototype.name = "ENotDirectory";
ENotDirectory.prototype.constructor = ENotDirectory; ENotDirectory.prototype.constructor = ENotDirectory;
function EBadFileDescriptor(message){ function EBadFileDescriptor(message){
this.message = message || ''; this.message = message || '';
}; }
EBadFileDescriptor.prototype = new Error(); EBadFileDescriptor.prototype = new Error();
EBadFileDescriptor.prototype.name = "EBadFileDescriptor"; EBadFileDescriptor.prototype.name = "EBadFileDescriptor";
EBadFileDescriptor.prototype.constructor = EBadFileDescriptor; EBadFileDescriptor.prototype.constructor = EBadFileDescriptor;
function ENotImplemented(message){ function ENotImplemented(message){
this.message = message || ''; this.message = message || '';
}; }
ENotImplemented.prototype = new Error(); ENotImplemented.prototype = new Error();
ENotImplemented.prototype.name = "ENotImplemented"; ENotImplemented.prototype.name = "ENotImplemented";
ENotImplemented.prototype.constructor = ENotImplemented; ENotImplemented.prototype.constructor = ENotImplemented;
function ENotMounted(message){ function ENotMounted(message){
this.message = message || ''; this.message = message || '';
}; }
ENotMounted.prototype = new Error(); ENotMounted.prototype = new Error();
ENotMounted.prototype.name = "ENotMounted"; ENotMounted.prototype.name = "ENotMounted";
ENotMounted.prototype.constructor = ENotMounted; ENotMounted.prototype.constructor = ENotMounted;
function EInvalid(message){ function EInvalid(message){
this.message = message || ''; this.message = message || '';
}; }
EInvalid.prototype = new Error(); EInvalid.prototype = new Error();
EInvalid.prototype.name = "EInvalid"; EInvalid.prototype.name = "EInvalid";
EInvalid.prototype.constructor = EInvalid; EInvalid.prototype.constructor = EInvalid;
function EIO(message){ function EIO(message){
this.message = message || ''; this.message = message || '';
}; }
EIO.prototype = new Error(); EIO.prototype = new Error();
EIO.prototype.name = "EIO"; EIO.prototype.name = "EIO";
EIO.prototype.constructor = EIO; EIO.prototype.constructor = EIO;
function EFileSystemError(message){ function EFileSystemError(message){
this.message = message || ''; this.message = message || '';
}; }
EFileSystemError.prototype = new Error(); EFileSystemError.prototype = new Error();
EFileSystemError.prototype.name = "EFileSystemError"; EFileSystemError.prototype.name = "EFileSystemError";
EFileSystemError.prototype.constructor = EFileSystemError; EFileSystemError.prototype.constructor = EFileSystemError;
@ -109,7 +109,7 @@ define(function(require) {
ENotImplemented: ENotImplemented, ENotImplemented: ENotImplemented,
ENotMounted: ENotMounted, ENotMounted: ENotMounted,
EInvalid: EInvalid, EInvalid: EInvalid,
EIO: EIO, EIO: EIO
}; };
}); });

305
src/fs.js
View File

@ -5,6 +5,11 @@ define(function(require) {
var _ = require('lodash'); var _ = require('lodash');
var when = require('when'); var when = require('when');
// TextEncoder and TextDecoder will either already be present, or use this shim.
// Because of the way the spec is defined, we need to get them off the global.
require('encoding-indexes');
require('encoding');
var normalize = require('src/path').normalize; var normalize = require('src/path').normalize;
var dirname = require('src/path').dirname; var dirname = require('src/path').dirname;
var basename = require('src/path').basename; var basename = require('src/path').basename;
@ -53,7 +58,7 @@ define(function(require) {
function DirectoryEntry(id, type) { function DirectoryEntry(id, type) {
this.id = id; this.id = id;
this.type = type || MODE_FILE; this.type = type || MODE_FILE;
}; }
/* /*
* OpenFileDescription * OpenFileDescription
@ -63,7 +68,7 @@ define(function(require) {
this.id = id; this.id = id;
this.flags = flags; this.flags = flags;
this.position = position; this.position = position;
}; }
/* /*
* Node * Node
@ -72,7 +77,7 @@ define(function(require) {
function Node(id, mode, size, atime, ctime, mtime, flags, xattrs, nlinks, version) { function Node(id, mode, size, atime, ctime, mtime, flags, xattrs, nlinks, version) {
var now = Date.now(); var now = Date.now();
this.id = id || hash(guid()), this.id = id || hash(guid());
this.mode = mode || MODE_FILE; // node type (file, directory, etc) this.mode = mode || MODE_FILE; // node type (file, directory, etc)
this.size = size || 0; // size (bytes for files, entries for directories) this.size = size || 0; // size (bytes for files, entries for directories)
this.atime = atime || now; // access time this.atime = atime || now; // access time
@ -85,7 +90,7 @@ define(function(require) {
this.blksize = undefined; // block size this.blksize = undefined; // block size
this.nblocks = 1; // blocks count this.nblocks = 1; // blocks count
this.data = hash(guid()); // id for data object this.data = hash(guid()); // id for data object
}; }
/* /*
* Stats * Stats
@ -100,7 +105,7 @@ define(function(require) {
this.mtime = fileNode.mtime; this.mtime = fileNode.mtime;
this.ctime = fileNode.ctime; this.ctime = fileNode.ctime;
this.type = fileNode.mode; this.type = fileNode.mode;
}; }
/* /*
* find_node * find_node
@ -125,7 +130,7 @@ define(function(require) {
} else { } else {
callback(undefined, rootDirectoryNode); callback(undefined, rootDirectoryNode);
} }
}; }
read_object(objectStore, ROOT_NODE_ID, check_root_directory_node); read_object(objectStore, ROOT_NODE_ID, check_root_directory_node);
} else { } else {
@ -139,7 +144,7 @@ define(function(require) {
} else { } else {
read_object(objectStore, parentDirectoryNode.data, get_node_id_from_parent_directory_data); read_object(objectStore, parentDirectoryNode.data, get_node_id_from_parent_directory_data);
} }
}; }
// in: parent directory data // in: parent directory data
// out: searched node id // out: searched node id
@ -154,11 +159,11 @@ define(function(require) {
read_object(objectStore, nodeId, callback); read_object(objectStore, nodeId, callback);
} }
} }
}; }
find_node(objectStore, parentPath, read_parent_directory_data); find_node(objectStore, parentPath, read_parent_directory_data);
} }
}; }
/* /*
* read_object * read_object
@ -177,7 +182,7 @@ define(function(require) {
} catch(error) { } catch(error) {
callback(new EIO(error.message)); callback(new EIO(error.message));
} }
}; }
/* /*
* write_object * write_object
@ -196,7 +201,7 @@ define(function(require) {
} catch(error) { } catch(error) {
callback(new EIO(error.message)); callback(new EIO(error.message));
} }
}; }
/* /*
* delete_object * delete_object
@ -211,7 +216,7 @@ define(function(require) {
deleteRequest.onerror = function(error) { deleteRequest.onerror = function(error) {
callback(error); callback(error);
}; };
}; }
/* /*
* make_root_directory * make_root_directory
@ -232,7 +237,7 @@ define(function(require) {
directoryNode.nlinks += 1; directoryNode.nlinks += 1;
write_object(objectStore, directoryNode, directoryNode.id, write_directory_data); write_object(objectStore, directoryNode, directoryNode.id, write_directory_data);
} }
}; }
function write_directory_data(error) { function write_directory_data(error) {
if(error) { if(error) {
@ -241,10 +246,10 @@ define(function(require) {
directoryData = {}; directoryData = {};
write_object(objectStore, directoryData, directoryNode.data, callback); write_object(objectStore, directoryData, directoryNode.data, callback);
} }
}; }
find_node(objectStore, ROOT_DIRECTORY_NAME, write_directory_node); find_node(objectStore, ROOT_DIRECTORY_NAME, write_directory_node);
}; }
/* /*
* make_directory * make_directory
@ -277,7 +282,7 @@ define(function(require) {
parentDirectoryNode = result; parentDirectoryNode = result;
read_object(objectStore, parentDirectoryNode.data, write_directory_node); read_object(objectStore, parentDirectoryNode.data, write_directory_node);
} }
}; }
function write_directory_node(error, result) { function write_directory_node(error, result) {
if(error) { if(error) {
@ -288,7 +293,7 @@ define(function(require) {
directoryNode.nlinks += 1; directoryNode.nlinks += 1;
write_object(objectStore, directoryNode, directoryNode.id, write_directory_data); write_object(objectStore, directoryNode, directoryNode.id, write_directory_data);
} }
}; }
function write_directory_data(error) { function write_directory_data(error) {
if(error) { if(error) {
@ -297,7 +302,7 @@ define(function(require) {
directoryData = {}; directoryData = {};
write_object(objectStore, directoryData, directoryNode.data, update_parent_directory_data); write_object(objectStore, directoryData, directoryNode.data, update_parent_directory_data);
} }
}; }
function update_parent_directory_data(error) { function update_parent_directory_data(error) {
if(error) { if(error) {
@ -309,7 +314,7 @@ define(function(require) {
} }
find_node(objectStore, path, check_if_directory_exists); find_node(objectStore, path, check_if_directory_exists);
}; }
/* /*
* remove_directory * remove_directory
@ -349,7 +354,7 @@ define(function(require) {
find_node(objectStore, parentPath, read_parent_directory_data); find_node(objectStore, parentPath, read_parent_directory_data);
} }
} }
}; }
function read_parent_directory_data(error, result) { function read_parent_directory_data(error, result) {
if(error) { if(error) {
@ -358,7 +363,7 @@ define(function(require) {
parentDirectoryNode = result; parentDirectoryNode = result;
read_object(objectStore, parentDirectoryNode.data, remove_directory_entry_from_parent_directory_node); read_object(objectStore, parentDirectoryNode.data, remove_directory_entry_from_parent_directory_node);
} }
}; }
function remove_directory_entry_from_parent_directory_node(error, result) { function remove_directory_entry_from_parent_directory_node(error, result) {
if(error) { if(error) {
@ -368,7 +373,7 @@ define(function(require) {
delete parentDirectoryData[name]; delete parentDirectoryData[name];
write_object(objectStore, parentDirectoryData, parentDirectoryNode.data, remove_directory_node); write_object(objectStore, parentDirectoryData, parentDirectoryNode.data, remove_directory_node);
} }
}; }
function remove_directory_node(error) { function remove_directory_node(error) {
if(error) { if(error) {
@ -376,7 +381,7 @@ define(function(require) {
} else { } else {
delete_object(objectStore, directoryNode.id, remove_directory_data); delete_object(objectStore, directoryNode.id, remove_directory_data);
} }
}; }
function remove_directory_data(error) { function remove_directory_data(error) {
if(error) { if(error) {
@ -384,10 +389,10 @@ define(function(require) {
} else { } else {
delete_object(objectStore, directoryNode.data, callback); delete_object(objectStore, directoryNode.data, callback);
} }
}; }
find_node(objectStore, path, check_if_directory_exists); find_node(objectStore, path, check_if_directory_exists);
}; }
function open_file(fs, objectStore, path, flags, callback) { function open_file(fs, objectStore, path, flags, callback) {
path = normalize(path); path = normalize(path);
@ -402,7 +407,7 @@ define(function(require) {
if(ROOT_DIRECTORY_NAME == name) { if(ROOT_DIRECTORY_NAME == name) {
if(_(flags).contains(O_WRITE)) { if(_(flags).contains(O_WRITE)) {
callback(new EIsDirectory('the named file is a directory and O_WRITE is set')) callback(new EIsDirectory('the named file is a directory and O_WRITE is set'));
} else { } else {
find_node(objectStore, path, set_file_node); find_node(objectStore, path, set_file_node);
} }
@ -417,7 +422,7 @@ define(function(require) {
directoryNode = result; directoryNode = result;
read_object(objectStore, directoryNode.data, check_if_file_exists); read_object(objectStore, directoryNode.data, check_if_file_exists);
} }
}; }
function check_if_file_exists(error, result) { function check_if_file_exists(error, result) {
if(error) { if(error) {
@ -426,11 +431,11 @@ define(function(require) {
directoryData = result; directoryData = result;
if(_(directoryData).has(name)) { if(_(directoryData).has(name)) {
if(_(flags).contains(O_EXCLUSIVE)) { if(_(flags).contains(O_EXCLUSIVE)) {
callback(new ENoEntry('O_CREATE and O_EXCLUSIVE are set, and the named file exists')) callback(new ENoEntry('O_CREATE and O_EXCLUSIVE are set, and the named file exists'));
} else { } else {
directoryEntry = directoryData[name]; directoryEntry = directoryData[name];
if(directoryEntry.type == MODE_DIRECTORY && _(flags).contains(O_WRITE)) { if(directoryEntry.type == MODE_DIRECTORY && _(flags).contains(O_WRITE)) {
callback(new EIsDirectory('the named file is a directory and O_WRITE is set')) callback(new EIsDirectory('the named file is a directory and O_WRITE is set'));
} else { } else {
read_object(objectStore, directoryEntry.id, set_file_node); read_object(objectStore, directoryEntry.id, set_file_node);
} }
@ -443,7 +448,7 @@ define(function(require) {
} }
} }
} }
}; }
function set_file_node(error, result) { function set_file_node(error, result) {
if(error) { if(error) {
@ -452,13 +457,13 @@ define(function(require) {
fileNode = result; fileNode = result;
callback(undefined, fileNode); callback(undefined, fileNode);
} }
}; }
function write_file_node() { function write_file_node() {
fileNode = new Node(undefined, MODE_FILE); fileNode = new Node(undefined, MODE_FILE);
fileNode.nlinks += 1; fileNode.nlinks += 1;
write_object(objectStore, fileNode, fileNode.id, write_file_data); write_object(objectStore, fileNode, fileNode.id, write_file_data);
}; }
function write_file_data(error) { function write_file_data(error) {
if(error) { if(error) {
@ -467,7 +472,7 @@ define(function(require) {
fileData = new Uint8Array(0); fileData = new Uint8Array(0);
write_object(objectStore, fileData, fileNode.data, update_directory_data); write_object(objectStore, fileData, fileNode.data, update_directory_data);
} }
}; }
function update_directory_data(error) { function update_directory_data(error) {
if(error) { if(error) {
@ -476,7 +481,7 @@ define(function(require) {
directoryData[name] = new DirectoryEntry(fileNode.id, MODE_FILE); directoryData[name] = new DirectoryEntry(fileNode.id, MODE_FILE);
write_object(objectStore, directoryData, directoryNode.data, handle_update_result); write_object(objectStore, directoryData, directoryNode.data, handle_update_result);
} }
}; }
function handle_update_result(error) { function handle_update_result(error) {
if(error) { if(error) {
@ -484,8 +489,8 @@ define(function(require) {
} else { } else {
callback(undefined, fileNode); callback(undefined, fileNode);
} }
}; }
}; }
function write_data(objectStore, ofd, buffer, offset, length, position, callback) { function write_data(objectStore, ofd, buffer, offset, length, position, callback) {
var fileNode; var fileNode;
@ -500,7 +505,7 @@ define(function(require) {
fileNode = result; fileNode = result;
read_object(objectStore, fileNode.data, update_file_data); read_object(objectStore, fileNode.data, update_file_data);
} }
}; }
function update_file_data(error, result) { function update_file_data(error, result) {
if(error) { if(error) {
@ -524,7 +529,7 @@ define(function(require) {
write_object(objectStore, newData, fileNode.data, update_file_node); write_object(objectStore, newData, fileNode.data, update_file_node);
} }
}; }
function update_file_node(error) { function update_file_node(error) {
if(error) { if(error) {
@ -532,7 +537,7 @@ define(function(require) {
} else { } else {
write_object(objectStore, fileNode, fileNode.id, return_nbytes); write_object(objectStore, fileNode, fileNode.id, return_nbytes);
} }
}; }
function return_nbytes(error) { function return_nbytes(error) {
if(error) { if(error) {
@ -540,8 +545,8 @@ define(function(require) {
} else { } else {
callback(undefined, length); callback(undefined, length);
} }
}; }
}; }
function read_data(objectStore, ofd, buffer, offset, length, position, callback) { function read_data(objectStore, ofd, buffer, offset, length, position, callback) {
var fileNode; var fileNode;
@ -556,7 +561,7 @@ define(function(require) {
fileNode = result; fileNode = result;
read_object(objectStore, fileNode.data, handle_file_data); read_object(objectStore, fileNode.data, handle_file_data);
} }
}; }
function handle_file_data(error, result) { function handle_file_data(error, result) {
if(error) { if(error) {
@ -572,8 +577,8 @@ define(function(require) {
} }
callback(undefined, length); callback(undefined, length);
} }
}; }
}; }
function stat_file(objectStore, path, callback) { function stat_file(objectStore, path, callback) {
path = normalize(path); path = normalize(path);
@ -587,8 +592,8 @@ define(function(require) {
} else { } else {
callback(undefined, result); callback(undefined, result);
} }
}; }
}; }
function fstat_file(objectStore, ofd, callback) { function fstat_file(objectStore, ofd, callback) {
read_object(objectStore, ofd.id, check_file); read_object(objectStore, ofd.id, check_file);
@ -599,8 +604,8 @@ define(function(require) {
} else { } else {
callback(undefined, result); callback(undefined, result);
} }
}; }
}; }
function link_node(objectStore, oldpath, newpath, callback) { function link_node(objectStore, oldpath, newpath, callback) {
oldpath = normalize(oldpath); oldpath = normalize(oldpath);
@ -626,7 +631,7 @@ define(function(require) {
oldDirectoryNode = result; oldDirectoryNode = result;
read_object(objectStore, oldDirectoryNode.data, check_if_old_file_exists); read_object(objectStore, oldDirectoryNode.data, check_if_old_file_exists);
} }
}; }
function check_if_old_file_exists(error, result) { function check_if_old_file_exists(error, result) {
if(error) { if(error) {
@ -639,7 +644,7 @@ define(function(require) {
find_node(objectStore, newParentPath, read_new_directory_data); find_node(objectStore, newParentPath, read_new_directory_data);
} }
} }
}; }
function read_new_directory_data(error, result) { function read_new_directory_data(error, result) {
if(error) { if(error) {
@ -648,7 +653,7 @@ define(function(require) {
newDirectoryNode = result; newDirectoryNode = result;
read_object(objectStore, newDirectoryNode.data, check_if_new_file_exists); read_object(objectStore, newDirectoryNode.data, check_if_new_file_exists);
} }
}; }
function check_if_new_file_exists(error, result) { function check_if_new_file_exists(error, result) {
if(error) { if(error) {
@ -662,7 +667,7 @@ define(function(require) {
write_object(objectStore, newDirectoryData, newDirectoryNode.data, read_directory_entry); write_object(objectStore, newDirectoryData, newDirectoryNode.data, read_directory_entry);
} }
} }
}; }
function read_directory_entry(error, result) { function read_directory_entry(error, result) {
if(error) { if(error) {
@ -677,11 +682,11 @@ define(function(require) {
callback(error); callback(error);
} else { } else {
fileNode = result; fileNode = result;
fileNode.nlinks += 1 fileNode.nlinks += 1;
write_object(objectStore, fileNode, fileNode.id, callback); write_object(objectStore, fileNode, fileNode.id, callback);
} }
}; }
}; }
function unlink_node(objectStore, path, callback) { function unlink_node(objectStore, path, callback) {
path = normalize(path); path = normalize(path);
@ -701,7 +706,7 @@ define(function(require) {
directoryNode = result; directoryNode = result;
read_object(objectStore, directoryNode.data, check_if_file_exists); read_object(objectStore, directoryNode.data, check_if_file_exists);
} }
}; }
function check_if_file_exists(error, result) { function check_if_file_exists(error, result) {
if(error) { if(error) {
@ -714,7 +719,7 @@ define(function(require) {
read_object(objectStore, directoryData[name].id, update_file_node); read_object(objectStore, directoryData[name].id, update_file_node);
} }
} }
}; }
function update_file_node(error, result) { function update_file_node(error, result) {
if(error) { if(error) {
@ -728,7 +733,7 @@ define(function(require) {
write_object(objectStore, fileNode, fileNode.id, update_directory_data); write_object(objectStore, fileNode, fileNode.id, update_directory_data);
} }
} }
}; }
function delete_file_data(error) { function delete_file_data(error) {
if(error) { if(error) {
@ -736,7 +741,7 @@ define(function(require) {
} else { } else {
delete_object(objectStore, fileNode.data, update_directory_data); delete_object(objectStore, fileNode.data, update_directory_data);
} }
}; }
function update_directory_data(error) { function update_directory_data(error) {
if(error) { if(error) {
@ -746,7 +751,7 @@ define(function(require) {
write_object(objectStore, directoryData, directoryNode.data, callback); write_object(objectStore, directoryData, directoryNode.data, callback);
} }
} }
}; }
function read_directory(objectStore, path, callback) { function read_directory(objectStore, path, callback) {
path = normalize(path); path = normalize(path);
@ -764,7 +769,7 @@ define(function(require) {
directoryNode = result; directoryNode = result;
read_object(objectStore, directoryNode.data, handle_directory_data); read_object(objectStore, directoryNode.data, handle_directory_data);
} }
}; }
function handle_directory_data(error, result) { function handle_directory_data(error, result) {
if(error) { if(error) {
@ -774,8 +779,16 @@ define(function(require) {
var files = Object.keys(directoryData); var files = Object.keys(directoryData);
callback(undefined, files); callback(undefined, files);
} }
}; }
}; }
function validate_flags(flags) {
if(!_(O_FLAGS).has(flags)) {
return null;
} else {
return O_FLAGS[flags];
}
}
/* /*
* FileSystem * FileSystem
@ -818,7 +831,7 @@ define(function(require) {
that.readyState = FS_READY; that.readyState = FS_READY;
deferred.resolve(); deferred.resolve();
} }
}; }
if(format) { if(format) {
var clearRequest = files.clear(); var clearRequest = files.clear();
@ -845,7 +858,7 @@ define(function(require) {
this.nextDescriptor = nextDescriptor; this.nextDescriptor = nextDescriptor;
this.openFiles = openFiles; this.openFiles = openFiles;
this.name = name; this.name = name;
}; }
FileSystem.prototype._allocate_descriptor = function _allocate_descriptor(openFileDescription) { FileSystem.prototype._allocate_descriptor = function _allocate_descriptor(openFileDescription) {
var fd = this.nextDescriptor ++; var fd = this.nextDescriptor ++;
this.openFiles[fd] = openFileDescription; this.openFiles[fd] = openFileDescription;
@ -877,12 +890,11 @@ define(function(require) {
var fd = that._allocate_descriptor(openFileDescription); var fd = that._allocate_descriptor(openFileDescription);
deferred.resolve(fd); deferred.resolve(fd);
} }
}; }
if(!_(O_FLAGS).has(flags)) { flags = validate_flags(flags);
if(!flags) {
deferred.reject(new EInvalid('flags is not valid')); deferred.reject(new EInvalid('flags is not valid'));
} else {
flags = O_FLAGS[flags];
} }
open_file(this, files, path, flags, check_result); open_file(this, files, path, flags, check_result);
@ -934,7 +946,7 @@ define(function(require) {
} else { } else {
deferred.resolve(); deferred.resolve();
} }
}; }
make_directory(files, path, check_result); make_directory(files, path, check_result);
deferred.promise.then( deferred.promise.then(
@ -966,7 +978,7 @@ define(function(require) {
} else { } else {
deferred.resolve(); deferred.resolve();
} }
}; }
remove_directory(files, path, check_result); remove_directory(files, path, check_result);
deferred.promise.then( deferred.promise.then(
@ -999,7 +1011,7 @@ define(function(require) {
var stats = new Stats(result, that.name); var stats = new Stats(result, that.name);
deferred.resolve(stats); deferred.resolve(stats);
} }
}; }
stat_file(files, path, check_result); stat_file(files, path, check_result);
deferred.promise.then( deferred.promise.then(
@ -1032,7 +1044,7 @@ define(function(require) {
var stats = new Stats(result, that.name); var stats = new Stats(result, that.name);
deferred.resolve(stats); deferred.resolve(stats);
} }
}; }
var ofd = that.openFiles[fd]; var ofd = that.openFiles[fd];
@ -1071,7 +1083,7 @@ define(function(require) {
} else { } else {
deferred.resolve(); deferred.resolve();
} }
}; }
link_node(files, oldpath, newpath, check_result); link_node(files, oldpath, newpath, check_result);
@ -1104,7 +1116,7 @@ define(function(require) {
} else { } else {
deferred.resolve(); deferred.resolve();
} }
}; }
unlink_node(files, path, check_result); unlink_node(files, path, check_result);
@ -1140,7 +1152,7 @@ define(function(require) {
} else { } else {
deferred.resolve(nbytes); deferred.resolve(nbytes);
} }
}; }
var ofd = that.openFiles[fd]; var ofd = that.openFiles[fd];
@ -1166,6 +1178,79 @@ define(function(require) {
} }
); );
}; };
FileSystem.prototype.readFile = function readFile(path, options, callback) {
var that = this;
this.promise.then(
function() {
var deferred = when.defer();
var transaction = that.db.transaction([FILE_STORE_NAME], IDB_RO);
var files = transaction.objectStore(FILE_STORE_NAME);
if(!options) {
options = { encoding: null, flag: 'r' };
} else if(typeof options === "function") {
callback = options;
options = { encoding: null, flag: 'r' };
} else if(typeof options === "string") {
options = { encoding: options, flag: 'r' };
}
var flags = validate_flags(options.flag || 'r');
if(!flags) {
deferred.reject(new EInvalid('flags is not valid'));
}
open_file(this, files, path, flags, function(err, fileNode) {
if(err) {
// TODO: abort transaction?
return deferred.reject(err);
}
var ofd = new OpenFileDescription(fileNode.id, flags, 0);
var fd = that._allocate_descriptor(ofd);
fstat_file(files, ofd, function(err2, fstatResult) {
if(err2) {
// TODO: abort transaction?
return deferred.reject(err2);
}
var stats = new Stats(fstatResult, that.name);
var size = stats.size;
var buffer = new Uint8Array(size);
read_data(files, ofd, buffer, 0, size, 0, function(err3, nbytes) {
if(err3) {
// TODO: abort transaction?
return deferred.reject(err3);
}
that._release_descriptor(fd);
var data;
if(options.encoding === 'utf8') {
data = new TextDecoder('utf-8').decode(buffer);
} else {
data = buffer;
}
deferred.resolve(data);
});
});
});
deferred.promise.then(
function(result) {
callback(undefined, result);
},
function(error) {
callback(error);
}
);
},
function() {
callback(new EFileSystemError('unknown error'));
}
);
};
FileSystem.prototype.write = function write(fd, buffer, offset, length, position, callback) { FileSystem.prototype.write = function write(fd, buffer, offset, length, position, callback) {
var that = this; var that = this;
this.promise.then( this.promise.then(
@ -1183,7 +1268,7 @@ define(function(require) {
} else { } else {
deferred.resolve(nbytes); deferred.resolve(nbytes);
} }
}; }
var ofd = that.openFiles[fd]; var ofd = that.openFiles[fd];
@ -1211,6 +1296,64 @@ define(function(require) {
} }
); );
}; };
FileSystem.prototype.writeFile = function writeFile(path, data, options, callback) {
var that = this;
this.promise.then(
function() {
var deferred = when.defer();
var transaction = that.db.transaction([FILE_STORE_NAME], IDB_RW);
var files = transaction.objectStore(FILE_STORE_NAME);
if(!options) {
options = { encoding: 'utf8', flag: 'w' };
} else if(typeof options === "function") {
callback = options;
options = { encoding: 'utf8', flag: 'w' };
} else if(typeof options === "string") {
options = { encoding: options, flag: 'w' };
}
var flags = validate_flags(options.flag || 'w');
if(!flags) {
deferred.reject(new EInvalid('flags is not valid'));
}
if(typeof data === "string" && options.encoding === 'utf8') {
data = new TextEncoder('utf-8').encode(data);
}
open_file(this, files, path, flags, function(err, fileNode) {
if(err) {
// TODO: abort transaction?
return deferred.reject(err);
}
var ofd = new OpenFileDescription(fileNode.id, flags, 0);
var fd = that._allocate_descriptor(ofd);
write_data(files, ofd, data, 0, data.length, 0, function(err2, nbytes) {
if(err2) {
// TODO: abort transaction?
return deferred.reject(err2);
}
that._release_descriptor(fd);
deferred.resolve();
});
});
deferred.promise.then(
function() {
callback(undefined);
},
function(error) {
callback(error);
}
);
},
function() {
callback(new EFileSystemError('unknown error'));
}
);
};
FileSystem.prototype.getxattr = function getxattr(path, name, callback) { FileSystem.prototype.getxattr = function getxattr(path, name, callback) {
}; };
@ -1229,7 +1372,7 @@ define(function(require) {
} else { } else {
deferred.resolve(offset); deferred.resolve(offset);
} }
}; }
var ofd = that.openFiles[fd]; var ofd = that.openFiles[fd];
@ -1266,7 +1409,7 @@ define(function(require) {
deferred.resolve(ofd.position); deferred.resolve(ofd.position);
} }
} }
}; }
fstat_file(files, ofd, update_descriptor_position); fstat_file(files, ofd, update_descriptor_position);
} else { } else {
@ -1302,7 +1445,7 @@ define(function(require) {
} else { } else {
deferred.resolve(files); deferred.resolve(files);
} }
}; }
read_directory(files, path, check_result); read_directory(files, path, check_result);
@ -1347,7 +1490,7 @@ define(function(require) {
return { return {
FileSystem: FileSystem, FileSystem: FileSystem
}; };
}); });

View File

@ -7,18 +7,18 @@ define(function(require) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16); return v.toString(16);
}).toUpperCase(); }).toUpperCase();
}; }
function hash(string) { function hash(string) {
return Crypto.SHA256(string).toString(Crypto.enc.hex); return Crypto.SHA256(string).toString(Crypto.enc.hex);
}; }
function nop() {}; function nop() {}
return { return {
guid: guid, guid: guid,
hash: hash, hash: hash,
nop: nop, nop: nop
}; };
}); });

View File

@ -714,6 +714,153 @@ describe('fs.write', function() {
}); });
}); });
describe('fs.writeFile, fs.readFile', function() {
beforeEach(function() {
this.db_name = mk_db_name();
this.fs = new IDBFS.FileSystem(this.db_name, 'FORMAT');
});
afterEach(function() {
indexedDB.deleteDatabase(this.db_name);
delete this.fs;
});
it('should be a function', function() {
expect(typeof this.fs.writeFile).toEqual('function');
expect(typeof this.fs.readFile).toEqual('function');
});
it('should error when path is wrong to readFile', function() {
var complete = false;
var _error, _result;
var that = this;
var contents = "This is a file.";
that.fs.readFile('/no-such-file', 'utf8', function(error, data) {
_error = error;
_result = data;
complete = true;
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).toBeDefined();
expect(_result).not.toBeDefined();
});
});
it('should write, read a utf8 file without specifying utf8 in writeFile', function() {
var complete = false;
var _error, _result;
var that = this;
var contents = "This is a file.";
that.fs.writeFile('/myfile', contents, function(error) {
if(error) throw error;
that.fs.readFile('/myfile', 'utf8', function(error2, data) {
if(error2) throw error2;
_result = data;
complete = true;
});
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).not.toBeDefined();
expect(_result).toEqual(contents);
});
});
it('should write, read a utf8 file with "utf8" option to writeFile', function() {
var complete = false;
var _error, _result;
var that = this;
var contents = "This is a file.";
that.fs.writeFile('/myfile', contents, 'utf8', function(error) {
if(error) throw error;
that.fs.readFile('/myfile', 'utf8', function(error2, data) {
if(error2) throw error2;
_result = data;
complete = true;
});
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).not.toBeDefined();
expect(_result).toEqual(contents);
});
});
it('should write, read a utf8 file with {encoding: "utf8"} option to writeFile', function() {
var complete = false;
var _error, _result;
var that = this;
var contents = "This is a file.";
that.fs.writeFile('/myfile', contents, { encoding: 'utf8' }, function(error) {
if(error) throw error;
that.fs.readFile('/myfile', 'utf8', function(error2, data) {
if(error2) throw error2;
_result = data;
complete = true;
});
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).not.toBeDefined();
expect(_result).toEqual(contents);
});
});
it('should write, read a binary file', function() {
var complete = false;
var _error, _result;
var that = this;
// String and utf8 binary encoded versions of the same thing:
var contents = "This is a file.";
var binary = new Uint8Array([84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 105, 108, 101, 46]);
that.fs.writeFile('/myfile', binary, function(error) {
if(error) throw error;
that.fs.readFile('/myfile', function(error2, data) {
if(error2) throw error2;
_result = data;
complete = true;
});
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).not.toBeDefined();
expect(_result).toEqual(binary);
});
});
});
describe('fs.read', function() { describe('fs.read', function() {
beforeEach(function() { beforeEach(function() {
this.db_name = mk_db_name(); this.db_name = mk_db_name();