Initial work on #227 - improve storage efficiency of provider API

This commit is contained in:
David Humphrey (:humph) david.humphrey@senecacollege.ca 2014-08-16 16:22:41 -04:00
parent 2d653d474e
commit d55bf23f1b
14 changed files with 327 additions and 540 deletions

View File

@ -31,7 +31,8 @@
"bower": "~1.3.8",
"request": "^2.36.0",
"browser-request": "git://github.com/humphd/browser-request.git#959ea95bf200d64939ed76897d3b06bb684f3a0d",
"jszip": "git://github.com/humphd/jszip.git#ad3f356bb165aba1cafeabe1bb3e49293803f975"
"jszip": "git://github.com/humphd/jszip.git#ad3f356bb165aba1cafeabe1bb3e49293803f975",
"base64-arraybuffer": "^0.1.2"
},
"devDependencies": {
"chai": "~1.9.1",

View File

@ -37,6 +37,7 @@ var OpenFileDescription = require('../open-file-description.js');
var SuperNode = require('../super-node.js');
var Node = require('../node.js');
var Stats = require('../stats.js');
var Buffer = require('../buffer.js');
/**
* Many functions below use this callback pattern. If it's not
@ -124,7 +125,7 @@ function update_node_times(context, path, node, times, callback) {
}
if(update) {
context.put(node.id, node, complete);
context.putObject(node.id, node, complete);
} else {
complete();
}
@ -167,7 +168,7 @@ function make_node(context, path, mode, callback) {
} else if(error && !(error instanceof Errors.ENOENT)) {
callback(error);
} else {
context.get(parentNode.data, create_node);
context.getObject(parentNode.data, create_node);
}
}
@ -184,7 +185,7 @@ function make_node(context, path, mode, callback) {
}
node = result;
node.nlinks += 1;
context.put(node.id, node, update_parent_node_data);
context.putObject(node.id, node, update_parent_node_data);
});
}
}
@ -205,7 +206,7 @@ function make_node(context, path, mode, callback) {
callback(error);
} else {
parentNodeData[name] = new DirectoryEntry(node.id, mode);
context.put(parentNode.data, parentNodeData, update_time);
context.putObject(parentNode.data, parentNodeData, update_time);
}
}
@ -233,7 +234,7 @@ function find_node(context, path, callback) {
} else if(!superNode || superNode.mode !== MODE_META || !superNode.rnode) {
callback(new Errors.EFILESYSTEMERROR());
} else {
context.get(superNode.rnode, check_root_directory_node);
context.getObject(superNode.rnode, check_root_directory_node);
}
}
@ -255,7 +256,7 @@ function find_node(context, path, callback) {
} else if(parentDirectoryNode.mode !== MODE_DIRECTORY || !parentDirectoryNode.data) {
callback(new Errors.ENOTDIR('a component of the path prefix is not a directory'));
} else {
context.get(parentDirectoryNode.data, get_node_from_parent_directory_data);
context.getObject(parentDirectoryNode.data, get_node_from_parent_directory_data);
}
}
@ -269,7 +270,7 @@ function find_node(context, path, callback) {
callback(new Errors.ENOENT());
} else {
var nodeId = parentDirectoryData[name].id;
context.get(nodeId, is_symbolic_link);
context.getObject(nodeId, is_symbolic_link);
}
}
}
@ -296,14 +297,14 @@ function find_node(context, path, callback) {
parentPath = dirname(data);
name = basename(data);
if(ROOT_DIRECTORY_NAME == name) {
context.get(SUPER_NODE_ID, read_root_directory_node);
context.getObject(SUPER_NODE_ID, read_root_directory_node);
} else {
find_node(context, parentPath, read_parent_directory_data);
}
}
if(ROOT_DIRECTORY_NAME == name) {
context.get(SUPER_NODE_ID, read_root_directory_node);
context.getObject(SUPER_NODE_ID, read_root_directory_node);
} else {
find_node(context, parentPath, read_parent_directory_data);
}
@ -338,7 +339,7 @@ function set_extended_attribute (context, path_or_fd, name, value, flag, callbac
}
else {
node.xattrs[name] = value;
context.put(node.id, node, update_time);
context.putObject(node.id, node, update_time);
}
}
@ -348,7 +349,7 @@ function set_extended_attribute (context, path_or_fd, name, value, flag, callbac
}
else if (typeof path_or_fd == 'object' && typeof path_or_fd.id == 'string') {
path = path_or_fd.path;
context.get(path_or_fd.id, set_xattr);
context.getObject(path_or_fd.id, set_xattr);
}
else {
callback(new Errors.EINVAL('path or file descriptor of wrong type'));
@ -380,7 +381,7 @@ function ensure_root_directory(context, callback) {
return;
}
superNode = result;
context.put(superNode.id, superNode, write_directory_node);
context.putObject(superNode.id, superNode, write_directory_node);
});
}
}
@ -396,7 +397,7 @@ function ensure_root_directory(context, callback) {
}
directoryNode = result;
directoryNode.nlinks += 1;
context.put(directoryNode.id, directoryNode, write_directory_data);
context.putObject(directoryNode.id, directoryNode, write_directory_data);
});
}
}
@ -406,11 +407,11 @@ function ensure_root_directory(context, callback) {
callback(error);
} else {
directoryData = {};
context.put(directoryNode.data, directoryData, callback);
context.putObject(directoryNode.data, directoryData, callback);
}
}
context.get(SUPER_NODE_ID, ensure_super_node);
context.getObject(SUPER_NODE_ID, ensure_super_node);
}
/**
@ -441,7 +442,7 @@ function make_directory(context, path, callback) {
callback(error);
} else {
parentDirectoryNode = result;
context.get(parentDirectoryNode.data, write_directory_node);
context.getObject(parentDirectoryNode.data, write_directory_node);
}
}
@ -457,7 +458,7 @@ function make_directory(context, path, callback) {
}
directoryNode = result;
directoryNode.nlinks += 1;
context.put(directoryNode.id, directoryNode, write_directory_data);
context.putObject(directoryNode.id, directoryNode, write_directory_data);
});
}
}
@ -467,7 +468,7 @@ function make_directory(context, path, callback) {
callback(error);
} else {
directoryData = {};
context.put(directoryNode.data, directoryData, update_parent_directory_data);
context.putObject(directoryNode.data, directoryData, update_parent_directory_data);
}
}
@ -485,7 +486,7 @@ function make_directory(context, path, callback) {
callback(error);
} else {
parentDirectoryData[name] = new DirectoryEntry(directoryNode.id, MODE_DIRECTORY);
context.put(parentDirectoryNode.data, parentDirectoryData, update_time);
context.putObject(parentDirectoryNode.data, parentDirectoryData, update_time);
}
}
@ -510,7 +511,7 @@ function remove_directory(context, path, callback) {
callback(error);
} else {
parentDirectoryNode = result;
context.get(parentDirectoryNode.data, check_if_node_exists);
context.getObject(parentDirectoryNode.data, check_if_node_exists);
}
}
@ -524,7 +525,7 @@ function remove_directory(context, path, callback) {
} else {
parentDirectoryData = result;
directoryNode = parentDirectoryData[name].id;
context.get(directoryNode, check_if_node_is_directory);
context.getObject(directoryNode, check_if_node_is_directory);
}
}
@ -535,7 +536,7 @@ function remove_directory(context, path, callback) {
callback(new Errors.ENOTDIR());
} else {
directoryNode = result;
context.get(directoryNode.data, check_if_directory_is_empty);
context.getObject(directoryNode.data, check_if_directory_is_empty);
}
}
@ -563,7 +564,7 @@ function remove_directory(context, path, callback) {
function remove_directory_entry_from_parent_directory_node() {
delete parentDirectoryData[name];
context.put(parentDirectoryNode.data, parentDirectoryData, update_time);
context.putObject(parentDirectoryNode.data, parentDirectoryData, update_time);
}
function remove_directory_node(error) {
@ -615,7 +616,7 @@ function open_file(context, path, flags, callback) {
callback(new Errors.ENOENT());
} else {
directoryNode = result;
context.get(directoryNode.data, check_if_file_exists);
context.getObject(directoryNode.data, check_if_file_exists);
}
}
@ -632,7 +633,7 @@ function open_file(context, path, flags, callback) {
if(directoryEntry.type == MODE_DIRECTORY && _(flags).contains(O_WRITE)) {
callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set'));
} else {
context.get(directoryEntry.id, check_if_symbolic_link);
context.getObject(directoryEntry.id, check_if_symbolic_link);
}
}
} else {
@ -694,7 +695,7 @@ function open_file(context, path, flags, callback) {
}
fileNode = result;
fileNode.nlinks += 1;
context.put(fileNode.id, fileNode, write_file_data);
context.putObject(fileNode.id, fileNode, write_file_data);
});
}
@ -704,7 +705,7 @@ function open_file(context, path, flags, callback) {
} else {
fileData = new Buffer(0);
fileData.fill(0);
context.put(fileNode.data, fileData, update_directory_data);
context.putBuffer(fileNode.data, fileData, update_directory_data);
}
}
@ -722,7 +723,7 @@ function open_file(context, path, flags, callback) {
callback(error);
} else {
directoryData[name] = new DirectoryEntry(fileNode.id, MODE_FILE);
context.put(directoryNode.data, directoryData, update_time);
context.putObject(directoryNode.data, directoryData, update_time);
}
}
@ -759,7 +760,7 @@ function replace_data(context, ofd, buffer, offset, length, callback) {
if(error) {
callback(error);
} else {
context.put(fileNode.id, fileNode, update_time);
context.putObject(fileNode.id, fileNode, update_time);
}
}
@ -776,11 +777,11 @@ function replace_data(context, ofd, buffer, offset, length, callback) {
fileNode.size = length;
fileNode.version += 1;
context.put(fileNode.data, newData, update_file_node);
context.putBuffer(fileNode.data, newData, update_file_node);
}
}
context.get(ofd.id, write_file_data);
context.getObject(ofd.id, write_file_data);
}
function write_data(context, ofd, buffer, offset, length, position, callback) {
@ -808,7 +809,7 @@ function write_data(context, ofd, buffer, offset, length, position, callback) {
if(error) {
callback(error);
} else {
context.put(fileNode.id, fileNode, update_time);
context.putObject(fileNode.id, fileNode, update_time);
}
}
@ -835,7 +836,7 @@ function write_data(context, ofd, buffer, offset, length, position, callback) {
fileNode.size = newSize;
fileNode.version += 1;
context.put(fileNode.data, newData, update_file_node);
context.putBuffer(fileNode.data, newData, update_file_node);
}
}
@ -844,11 +845,11 @@ function write_data(context, ofd, buffer, offset, length, position, callback) {
callback(error);
} else {
fileNode = result;
context.get(fileNode.data, update_file_data);
context.getBuffer(fileNode.data, update_file_data);
}
}
context.get(ofd.id, read_file_data);
context.getObject(ofd.id, read_file_data);
}
function read_data(context, ofd, buffer, offset, length, position, callback) {
@ -878,11 +879,11 @@ function read_data(context, ofd, buffer, offset, length, position, callback) {
callback(error);
} else {
fileNode = result;
context.get(fileNode.data, handle_file_data);
context.getBuffer(fileNode.data, handle_file_data);
}
}
context.get(ofd.id, read_file_data);
context.getObject(ofd.id, read_file_data);
}
function stat_file(context, path, callback) {
@ -892,7 +893,7 @@ function stat_file(context, path, callback) {
}
function fstat_file(context, ofd, callback) {
context.get(ofd.id, standard_check_result_cb(callback));
context.getObject(ofd.id, standard_check_result_cb(callback));
}
function lstat_file(context, path, callback) {
@ -914,7 +915,7 @@ function lstat_file(context, path, callback) {
callback(error);
} else {
directoryNode = result;
context.get(directoryNode.data, check_if_file_exists);
context.getObject(directoryNode.data, check_if_file_exists);
}
}
@ -926,7 +927,7 @@ function lstat_file(context, path, callback) {
if(!_(directoryData).has(name)) {
callback(new Errors.ENOENT('a component of the path does not name an existing file'));
} else {
context.get(directoryData[name].id, standard_check_result_cb(callback));
context.getObject(directoryData[name].id, standard_check_result_cb(callback));
}
}
}
@ -961,7 +962,7 @@ function link_node(context, oldpath, newpath, callback) {
} else {
fileNode = result;
fileNode.nlinks += 1;
context.put(fileNode.id, fileNode, update_time);
context.putObject(fileNode.id, fileNode, update_time);
}
}
@ -969,7 +970,7 @@ function link_node(context, oldpath, newpath, callback) {
if(error) {
callback(error);
} else {
context.get(newDirectoryData[newname].id, update_file_node);
context.getObject(newDirectoryData[newname].id, update_file_node);
}
}
@ -982,7 +983,7 @@ function link_node(context, oldpath, newpath, callback) {
callback(new Errors.EEXIST('newpath resolves to an existing file'));
} else {
newDirectoryData[newname] = oldDirectoryData[oldname];
context.put(newDirectoryNode.data, newDirectoryData, read_directory_entry);
context.putObject(newDirectoryNode.data, newDirectoryData, read_directory_entry);
}
}
}
@ -992,7 +993,7 @@ function link_node(context, oldpath, newpath, callback) {
callback(error);
} else {
newDirectoryNode = result;
context.get(newDirectoryNode.data, check_if_new_file_exists);
context.getObject(newDirectoryNode.data, check_if_new_file_exists);
}
}
@ -1014,7 +1015,7 @@ function link_node(context, oldpath, newpath, callback) {
callback(error);
} else {
oldDirectoryNode = result;
context.get(oldDirectoryNode.data, check_if_old_file_exists);
context.getObject(oldDirectoryNode.data, check_if_old_file_exists);
}
}
@ -1035,7 +1036,7 @@ function unlink_node(context, path, callback) {
callback(error);
} else {
delete directoryData[name];
context.put(directoryNode.data, directoryData, function(error) {
context.putObject(directoryNode.data, directoryData, function(error) {
var now = Date.now();
update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, callback);
});
@ -1059,7 +1060,7 @@ function unlink_node(context, path, callback) {
if(fileNode.nlinks < 1) {
context.delete(fileNode.id, delete_file_data);
} else {
context.put(fileNode.id, fileNode, function(error) {
context.putObject(fileNode.id, fileNode, function(error) {
update_node_times(context, path, fileNode, { ctime: Date.now() }, update_directory_data);
});
}
@ -1074,7 +1075,7 @@ function unlink_node(context, path, callback) {
if(!_(directoryData).has(name)) {
callback(new Errors.ENOENT('a component of the path does not name an existing file'));
} else {
context.get(directoryData[name].id, update_file_node);
context.getObject(directoryData[name].id, update_file_node);
}
}
}
@ -1084,7 +1085,7 @@ function unlink_node(context, path, callback) {
callback(error);
} else {
directoryNode = result;
context.get(directoryNode.data, check_if_file_exists);
context.getObject(directoryNode.data, check_if_file_exists);
}
}
@ -1113,7 +1114,7 @@ function read_directory(context, path, callback) {
callback(error);
} else {
directoryNode = result;
context.get(directoryNode.data, handle_directory_data);
context.getObject(directoryNode.data, handle_directory_data);
}
}
@ -1140,7 +1141,7 @@ function make_symbolic_link(context, srcpath, dstpath, callback) {
callback(error);
} else {
directoryNode = result;
context.get(directoryNode.data, check_if_file_exists);
context.getObject(directoryNode.data, check_if_file_exists);
}
}
@ -1167,7 +1168,7 @@ function make_symbolic_link(context, srcpath, dstpath, callback) {
fileNode.nlinks += 1;
fileNode.size = srcpath.length;
fileNode.data = srcpath;
context.put(fileNode.id, fileNode, update_directory_data);
context.putObject(fileNode.id, fileNode, update_directory_data);
});
}
@ -1185,7 +1186,7 @@ function make_symbolic_link(context, srcpath, dstpath, callback) {
callback(error);
} else {
directoryData[name] = new DirectoryEntry(fileNode.id, MODE_SYMBOLIC_LINK);
context.put(directoryNode.data, directoryData, update_time);
context.putObject(directoryNode.data, directoryData, update_time);
}
}
}
@ -1205,7 +1206,7 @@ function read_link(context, path, callback) {
callback(error);
} else {
directoryNode = result;
context.get(directoryNode.data, check_if_file_exists);
context.getObject(directoryNode.data, check_if_file_exists);
}
}
@ -1217,7 +1218,7 @@ function read_link(context, path, callback) {
if(!_(directoryData).has(name)) {
callback(new Errors.ENOENT('a component of the path does not name an existing file'));
} else {
context.get(directoryData[name].id, check_if_symbolic);
context.getObject(directoryData[name].id, check_if_symbolic);
}
}
}
@ -1247,7 +1248,7 @@ function truncate_file(context, path, length, callback) {
callback(new Errors.EISDIR());
} else{
fileNode = node;
context.get(fileNode.data, truncate_file_data);
context.getObject(fileNode.data, truncate_file_data);
}
}
@ -1264,7 +1265,7 @@ function truncate_file(context, path, length, callback) {
if(fileData) {
fileData.copy(data);
}
context.put(fileNode.data, data, update_file_node);
context.putBuffer(fileNode.data, data, update_file_node);
}
}
@ -1283,7 +1284,7 @@ function truncate_file(context, path, length, callback) {
} else {
fileNode.size = length;
fileNode.version += 1;
context.put(fileNode.id, fileNode, update_time);
context.putObject(fileNode.id, fileNode, update_time);
}
}
@ -1304,7 +1305,7 @@ function ftruncate_file(context, ofd, length, callback) {
callback(new Errors.EISDIR());
} else{
fileNode = node;
context.get(fileNode.data, truncate_file_data);
context.getObject(fileNode.data, truncate_file_data);
}
}
@ -1323,7 +1324,7 @@ function ftruncate_file(context, ofd, length, callback) {
data = new Buffer(length);
data.fill(0);
}
context.put(fileNode.data, data, update_file_node);
context.putBuffer(fileNode.data, data, update_file_node);
}
}
@ -1342,14 +1343,14 @@ function ftruncate_file(context, ofd, length, callback) {
} else {
fileNode.size = length;
fileNode.version += 1;
context.put(fileNode.id, fileNode, update_time);
context.putObject(fileNode.id, fileNode, update_time);
}
}
if(length < 0) {
callback(new Errors.EINVAL('length cannot be negative'));
} else {
context.get(ofd.id, read_file_data);
context.getObject(ofd.id, read_file_data);
}
}
@ -1392,7 +1393,7 @@ function futimes_file(context, ofd, atime, mtime, callback) {
callback(new Errors.EINVAL('atime and mtime must be positive integers'));
}
else {
context.get(ofd.id, update_times);
context.getObject(ofd.id, update_times);
}
}
@ -1481,7 +1482,7 @@ function fgetxattr_file (context, ofd, name, callback) {
callback(new Errors.EINVAL('attribute name cannot be an empty string'));
}
else {
context.get(ofd.id, get_xattr);
context.getObject(ofd.id, get_xattr);
}
}
@ -1507,7 +1508,7 @@ function removexattr_file (context, path, name, callback) {
}
else {
delete node.xattrs[name];
context.put(node.id, node, update_time);
context.putObject(node.id, node, update_time);
}
}
@ -1541,7 +1542,7 @@ function fremovexattr_file (context, ofd, name, callback) {
}
else {
delete node.xattrs[name];
context.put(node.id, node, update_time);
context.putObject(node.id, node, update_time);
}
}
@ -1552,7 +1553,7 @@ function fremovexattr_file (context, ofd, name, callback) {
callback(new Errors.EINVAL('attribute name cannot be an empty string'));
}
else {
context.get(ofd.id, remove_xattr);
context.getObject(ofd.id, remove_xattr);
}
}

View File

@ -156,7 +156,7 @@ function FileSystem(options, callback) {
// Otherwise (default) make sure this id is unused first
function guidWithCheck(callback) {
var id = guid();
context.get(id, function(err, value) {
context.getObject(id, function(err, value) {
if(err) {
callback(err);
return;

View File

@ -3,6 +3,7 @@ var FILE_STORE_NAME = require('../constants.js').FILE_STORE_NAME;
var IDB_RW = require('../constants.js').IDB_RW;
var IDB_RO = require('../constants.js').IDB_RO;
var Errors = require('../errors.js');
var FilerBuffer = require('../buffer.js');
var indexedDB = global.indexedDB ||
global.mozIndexedDB ||
@ -13,6 +14,7 @@ function IndexedDBContext(db, mode) {
var transaction = db.transaction(FILE_STORE_NAME, mode);
this.objectStore = transaction.objectStore(FILE_STORE_NAME);
}
IndexedDBContext.prototype.clear = function(callback) {
try {
var request = this.objectStore.clear();
@ -26,9 +28,10 @@ IndexedDBContext.prototype.clear = function(callback) {
callback(e);
}
};
IndexedDBContext.prototype.get = function(key, callback) {
function _get(objectStore, key, callback) {
try {
var request = this.objectStore.get(key);
var request = objectStore.get(key);
request.onsuccess = function onsuccess(event) {
var result = event.target.result;
callback(null, result);
@ -39,10 +42,22 @@ IndexedDBContext.prototype.get = function(key, callback) {
} catch(e) {
callback(e);
}
}
IndexedDBContext.prototype.getObject = function(key, callback) {
_get(this.objectStore, key, callback);
};
IndexedDBContext.prototype.put = function(key, value, callback) {
IndexedDBContext.prototype.getBuffer = function(key, callback) {
_get(this.objectStore, key, function(err, arrayBuffer) {
if(err) {
return callback(err);
}
callback(null, new FilerBuffer(arrayBuffer));
});
};
function _put(objectStore, key, value, callback) {
try {
var request = this.objectStore.put(value, key);
var request = objectStore.put(value, key);
request.onsuccess = function onsuccess(event) {
var result = event.target.result;
callback(null, result);
@ -53,7 +68,14 @@ IndexedDBContext.prototype.put = function(key, value, callback) {
} catch(e) {
callback(e);
}
}
IndexedDBContext.prototype.putObject = function(key, value, callback) {
_put(this.objectStore, key, value, callback);
};
IndexedDBContext.prototype.putBuffer = function(key, uint8BackedBuffer, callback) {
_put(this.objectStore, key, uint8BackedBuffer.buffer, callback);
};
IndexedDBContext.prototype.delete = function(key, callback) {
try {
var request = this.objectStore.delete(key);

View File

@ -24,6 +24,7 @@ function MemoryContext(db, readOnly) {
this.readOnly = readOnly;
this.objectStore = db;
}
MemoryContext.prototype.clear = function(callback) {
if(this.readOnly) {
asyncCallback(function() {
@ -37,13 +38,19 @@ MemoryContext.prototype.clear = function(callback) {
});
asyncCallback(callback);
};
MemoryContext.prototype.get = function(key, callback) {
// Memory context doesn't care about differences between Object and Buffer
MemoryContext.prototype.getObject =
MemoryContext.prototype.getBuffer =
function(key, callback) {
var that = this;
asyncCallback(function() {
callback(null, that.objectStore[key]);
});
};
MemoryContext.prototype.put = function(key, value, callback) {
MemoryContext.prototype.putObject =
MemoryContext.prototype.putBuffer =
function(key, value, callback) {
if(this.readOnly) {
asyncCallback(function() {
callback("[MemoryContext] Error: write operation on read only context");
@ -53,6 +60,7 @@ MemoryContext.prototype.put = function(key, value, callback) {
this.objectStore[key] = value;
asyncCallback(callback);
};
MemoryContext.prototype.delete = function(key, callback) {
if(this.readOnly) {
asyncCallback(function() {

View File

@ -3,8 +3,9 @@ var FILE_STORE_NAME = require('../constants.js').FILE_STORE_NAME;
var WSQL_VERSION = require('../constants.js').WSQL_VERSION;
var WSQL_SIZE = require('../constants.js').WSQL_SIZE;
var WSQL_DESC = require('../constants.js').WSQL_DESC;
var u8toArray = require('../shared.js').u8toArray;
var Errors = require('../errors.js');
var FilerBuffer = require('../buffer.js');
var base64ArrayBuffer = require('base64-arraybuffer');
function WebSQLContext(db, isReadOnly) {
var that = this;
@ -20,6 +21,7 @@ function WebSQLContext(db, isReadOnly) {
});
};
}
WebSQLContext.prototype.clear = function(callback) {
function onError(transaction, error) {
callback(error);
@ -32,52 +34,74 @@ WebSQLContext.prototype.clear = function(callback) {
[], onSuccess, onError);
});
};
WebSQLContext.prototype.get = function(key, callback) {
function _get(getTransaction, key, callback) {
function onSuccess(transaction, result) {
// If the key isn't found, return null
var value = result.rows.length === 0 ? null : result.rows.item(0).data;
try {
if(value) {
value = JSON.parse(value);
// Deal with special-cased flattened typed arrays in WebSQL (see put() below)
if(value.__isUint8Array) {
value = new Uint8Array(value.__array);
}
}
callback(null, value);
} catch(e) {
callback(e);
}
callback(null, value);
}
function onError(transaction, error) {
callback(error);
}
this.getTransaction(function(transaction) {
transaction.executeSql("SELECT data FROM " + FILE_STORE_NAME + " WHERE id = ?;",
getTransaction(function(transaction) {
transaction.executeSql("SELECT data FROM " + FILE_STORE_NAME + " WHERE id = ? LIMIT 1;",
[key], onSuccess, onError);
});
}
WebSQLContext.prototype.getObject = function(key, callback) {
_get(this.getTransaction, key, function(err, result) {
if(err) {
return callback(err);
}
try {
if(result) {
result = JSON.parse(result);
}
} catch(e) {
return callback(e);
}
callback(null, result);
});
};
WebSQLContext.prototype.put = function(key, value, callback) {
// We do extra work to make sure typed arrays survive
// being stored in the db and still get the right prototype later.
if(Object.prototype.toString.call(value) === "[object Uint8Array]") {
value = {
__isUint8Array: true,
__array: u8toArray(value)
};
}
value = JSON.stringify(value);
WebSQLContext.prototype.getBuffer = function(key, callback) {
_get(this.getTransaction, key, function(err, result) {
if(err) {
return callback(err);
}
if(result) {
var arrayBuffer = base64ArrayBuffer.decode(result);
result = new FilerBuffer(arrayBuffer);
}
callback(null, result);
});
};
function _put(getTransaction, key, value, callback) {
function onSuccess(transaction, result) {
callback(null);
}
function onError(transaction, error) {
callback(error);
}
this.getTransaction(function(transaction) {
getTransaction(function(transaction) {
transaction.executeSql("INSERT OR REPLACE INTO " + FILE_STORE_NAME + " (id, data) VALUES (?, ?);",
[key, value], onSuccess, onError);
});
}
WebSQLContext.prototype.putObject = function(key, value, callback) {
var json = JSON.stringify(value);
_put(this.getTransaction, key, json, callback);
};
WebSQLContext.prototype.putBuffer = function(key, uint8BackedBuffer, callback) {
var base64 = base64ArrayBuffer.encode(uint8BackedBuffer.buffer);
_put(this.getTransaction, key, base64, callback);
};
WebSQLContext.prototype.delete = function(key, callback) {
function onSuccess(transaction, result) {
callback(null);

View File

@ -18,7 +18,7 @@ function IndexedDBTestProvider(name) {
function cleanup(callback) {
if(!that.provider || _done) {
return;
return callback();
}
// We have to force any other connections to close
@ -49,5 +49,8 @@ function IndexedDBTestProvider(name) {
this.init = init;
this.cleanup = cleanup;
}
IndexedDBTestProvider.isSupported = function() {
return Filer.FileSystem.providers.IndexedDB.isSupported();
};
module.exports = IndexedDBTestProvider;

View File

@ -18,5 +18,8 @@ function MemoryTestProvider(name) {
this.init = init;
this.cleanup = cleanup;
}
MemoryTestProvider.isSupported = function() {
return Filer.FileSystem.providers.Memory.isSupported();
};
module.exports = MemoryTestProvider;

View File

@ -13,11 +13,11 @@ function WebSQLTestProvider(name) {
function cleanup(callback) {
if(!that.provider || _done) {
return;
return callback();
}
// Provider is there, but db was never touched
if(!that.provider.db) {
return;
return callback();
}
var context = that.provider.getReadWriteContext();
@ -39,5 +39,8 @@ function WebSQLTestProvider(name) {
this.init = init;
this.cleanup = cleanup;
}
WebSQLTestProvider.isSupported = function() {
return Filer.FileSystem.providers.WebSQL.isSupported();
};
module.exports = WebSQLTestProvider;

View File

@ -0,0 +1,146 @@
var Buffer = require('../../..').Buffer;
var util = require('../../lib/test-utils.js');
var expect = require('chai').expect;
/**
* Due to the different setup/cleanup needs of the built-in providers,
* we use the test provider wrappers instead of the raw providers themselves.
*/
module.exports = function createProviderTestsFor(providerName, testProvider) {
if(!testProvider.isSupported()) {
console.log("Skipping provider tests for `" + providerName +"'--not supported in current environment.");
return;
}
describe("Filer Provider Tests for " + providerName, function() {
var _provider;
var provider;
beforeEach(function() {
_provider = new testProvider(util.uniqueName());
_provider.init();
provider = _provider.provider;
});
afterEach(function(done) {
_provider.cleanup(done);
_provider = null;
provider = null;
});
it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() {
expect(provider.open).to.be.a('function');
expect(provider.getReadOnlyContext).to.be.a('function');
expect(provider.getReadWriteContext).to.be.a('function');
});
it("should open a new IndexedDB database", function(done) {
provider.open(function(error, firstAccess) {
expect(error).not.to.exist;
expect(firstAccess).to.be.true;
done();
});
});
it("should allow putObject() and getObject()", function(done) {
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
// Simple JS Object
var value = {
a: "a",
b: 1,
c: true,
d: [1,2,3],
e: {
e1: ['a', 'b', 'c']
}
};
context.putObject("key", value, function(error) {
if(error) throw error;
context.getObject("key", function(error, result) {
expect(error).not.to.exist;
expect(result).to.be.an('object');
expect(result).to.deep.equal(value);
done();
});
});
});
});
it("should allow putBuffer() and getBuffer()", function(done) {
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
// Filer Buffer
var buf = new Buffer([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
context.putBuffer("key", buf, function(error) {
if(error) throw error;
context.getBuffer("key", function(error, result) {
expect(error).not.to.exist;
expect(Buffer.isBuffer(result)).to.be.true;
expect(result).to.deep.equal(buf);
done();
});
});
});
});
it("should allow delete()", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.putObject("key", "value", function(error) {
if(error) throw error;
context.delete("key", function(error) {
if(error) throw error;
context.getObject("key", function(error, result) {
expect(error).not.to.exist;
expect(result).not.to.exist;
done();
});
});
});
});
});
it("should allow clear()", function(done) {
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.putObject("key1", "value1", function(error) {
if(error) throw error;
context.putObject("key2", "value2", function(error) {
if(error) throw error;
context.clear(function(err) {
if(error) throw error;
context.getObject("key1", function(error, result) {
if(error) throw error;
expect(result).not.to.exist;
context.getObject("key2", function(error, result) {
if(error) throw error;
expect(result).not.to.exist;
done();
});
});
});
});
});
});
});
});
};

View File

@ -1,148 +1,4 @@
var Filer = require('../../..');
var util = require('../../lib/test-utils.js');
var expect = require('chai').expect;
var providerBase = require('./providers.base.js');
if(!Filer.FileSystem.providers.IndexedDB.isSupported()) {
console.log("Skipping Filer.FileSystem.providers.IndexedDB tests, since IndexedDB isn't supported.");
} else {
describe("Filer.FileSystem.providers.IndexedDB", function() {
it("is supported -- if it isn't, none of these tests can run.", function() {
expect(Filer.FileSystem.providers.IndexedDB.isSupported()).to.be.true;
});
it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() {
var indexedDBProvider = new Filer.FileSystem.providers.IndexedDB();
expect(indexedDBProvider.open).to.be.a('function');
expect(indexedDBProvider.getReadOnlyContext).to.be.a('function');
expect(indexedDBProvider.getReadWriteContext).to.be.a('function');
});
describe("open an IndexedDB provider", function() {
var _provider;
beforeEach(function() {
_provider = new util.providers.IndexedDB(util.uniqueName());
_provider.init();
});
afterEach(function(done) {
_provider.cleanup(done);
_provider = null;
});
it("should open a new IndexedDB database", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
expect(error).not.to.exist;
expect(firstAccess).to.be.true;
done();
});
});
});
describe("Read/Write operations on an IndexedDB provider", function() {
var _provider;
beforeEach(function() {
_provider = new util.providers.IndexedDB(util.uniqueName());
_provider.init();
});
afterEach(function(done) {
_provider.cleanup(done);
_provider = null;
});
it("should allow put() and get()", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.put("key", "value", function(error, result) {
if(error) throw error;
context.get("key", function(error, result) {
expect(error).not.to.exist;
expect(result).to.equal('value');
done();
});
});
});
});
it("should allow delete()", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.put("key", "value", function(error, result) {
if(error) throw error;
context.delete("key", function(error, result) {
if(error) throw error;
context.get("key", function(error, result) {
expect(error).not.to.exist;
expect(result).not.to.exist;
done();
});
});
});
});
});
it("should allow clear()", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.put("key1", "value1", function(error, result) {
if(error) throw error;
context.put("key2", "value2", function(error, result) {
if(error) throw error;
context.clear(function(err) {
if(error) throw error;
context.get("key1", function(error, result) {
if(error) throw error;
expect(result).not.to.exist;
context.get("key2", function(error, result) {
if(error) throw error;
expect(result).not.to.exist;
done();
});
});
});
});
});
});
});
/**
* With issue 123 (see https://github.com/js-platform/filer/issues/128) we had to
* start using readwrite contexts everywhere with IndexedDB. Skipping for now.
*/
it.skip("should fail when trying to write on ReadOnlyContext", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadOnlyContext();
context.put("key1", "value1", function(error, result) {
expect(error).to.exist;
expect(result).not.to.exist;
done();
});
});
});
});
});
}
providerBase('IndexedDB', util.providers.IndexedDB);

View File

@ -1,145 +1,4 @@
var Filer = require('../../..');
var expect = require('chai').expect;
var util = require('../../lib/test-utils.js');
var providerBase = require('./providers.base.js');
describe("Filer.FileSystem.providers.Memory", function() {
it("is supported -- if it isn't, none of these tests can run.", function() {
expect(Filer.FileSystem.providers.Memory.isSupported()).to.be.true;
});
it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() {
var memoryProvider = new Filer.FileSystem.providers.Memory();
expect(memoryProvider.open).to.be.a('function');
expect(memoryProvider.getReadOnlyContext).to.be.a('function');
expect(memoryProvider.getReadWriteContext).to.be.a('function');
});
describe("Memory provider DBs are sharable", function() {
it("should share a single memory db when name is the same", function(done) {
var provider1;
var provider2;
var provider3;
var name1 = 'memory-db';
var name2 = 'memory-db2';
provider1 = new Filer.FileSystem.providers.Memory(name1);
provider1.open(function(error, firstAccess) {
expect(error).not.to.exist;
expect(firstAccess).to.be.true;
provider2 = new Filer.FileSystem.providers.Memory(name1);
provider2.open(function(error, firstAccess) {
expect(error).not.to.exist;
expect(firstAccess).to.be.false;
expect(provider1.db).to.equal(provider2.db);
provider3 = new Filer.FileSystem.providers.Memory(name2);
provider3.open(function(error, firstAccess) {
expect(error).not.to.exist;
expect(firstAccess).to.be.true;
expect(provider3.db).not.to.equal(provider2.db);
done();
});
});
});
});
});
describe("open an Memory provider", function() {
it("should open a new Memory database", function(done) {
var provider = new Filer.FileSystem.providers.Memory();
provider.open(function(error, firstAccess) {
expect(error).not.to.exist;
expect(firstAccess).to.be.true;
done();
});
});
});
describe("Read/Write operations on an Memory provider", function() {
it("should allow put() and get()", function(done) {
var provider = new Filer.FileSystem.providers.Memory();
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.put("key", "value", function(error, result) {
if(error) throw error;
context.get("key", function(error, result) {
expect(error).not.to.exist;
expect(result).to.equal("value");
done();
});
});
});
});
it("should allow delete()", function(done) {
var provider = new Filer.FileSystem.providers.Memory();
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.put("key", "value", function(error, result) {
if(error) throw error;
context.delete("key", function(error, result) {
if(error) throw error;
context.get("key", function(error, result) {
expect(error).not.to.exist;
expect(result).not.to.exist;
done();
});
});
});
});
});
it("should allow clear()", function(done) {
var provider = new Filer.FileSystem.providers.Memory();
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.put("key1", "value1", function(error, result) {
if(error) throw error;
context.put("key2", "value2", function(error, result) {
if(error) throw error;
context.clear(function(err) {
if(error) throw error;
context.get("key1", function(error, result) {
if(error) throw error;
expect(result).not.to.exist;
context.get("key2", function(error, result) {
if(error) throw error;
expect(result).not.to.exist;
done();
});
});
});
});
});
});
});
it("should fail when trying to write on ReadOnlyContext", function(done) {
var provider = new Filer.FileSystem.providers.Memory();
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadOnlyContext();
context.put("key1", "value1", function(error, result) {
expect(error).to.exist;
expect(result).not.to.exist;
done();
});
});
});
});
});
providerBase('Memory', util.providers.Memory);

View File

@ -1,143 +1,4 @@
var Filer = require('../../..');
var util = require('../../lib/test-utils.js');
var expect = require('chai').expect;
var providerBase = require('./providers.base.js');
if(!Filer.FileSystem.providers.WebSQL.isSupported()) {
console.log("Skipping Filer.FileSystem.providers.WebSQL tests, since WebSQL isn't supported.");
} else {
describe("Filer.FileSystem.providers.WebSQL", function() {
it("is supported -- if it isn't, none of these tests can run.", function() {
expect(Filer.FileSystem.providers.WebSQL.isSupported()).to.be.true;
});
it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() {
var webSQLProvider = new Filer.FileSystem.providers.WebSQL();
expect(webSQLProvider.open).to.be.a('function');
expect(webSQLProvider.getReadOnlyContext).to.be.a('function');
expect(webSQLProvider.getReadWriteContext).to.be.a('function');
});
describe("open an WebSQL provider", function() {
var _provider;
beforeEach(function() {
_provider = new util.providers.WebSQL(util.uniqueName());
_provider.init();
});
afterEach(function(done) {
_provider.cleanup(done);
_provider = null;
});
it("should open a new WebSQL database", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
expect(error).not.to.exist;
expect(firstAccess).to.be.true;
done();
});
});
});
describe("Read/Write operations on an WebSQL provider", function() {
var _provider;
beforeEach(function() {
_provider = new util.providers.WebSQL(util.uniqueName());
_provider.init();
});
afterEach(function(done) {
_provider.cleanup(done);
_provider = null;
});
it("should allow put() and get()", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.put("key", "value", function(error, result) {
if(error) throw error;
context.get("key", function(error, result) {
expect(error).not.to.exist;
expect(result).to.equal("value");
done();
});
});
});
});
it("should allow delete()", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.put("key", "value", function(error, result) {
if(error) throw error;
context.delete("key", function(error, result) {
if(error) throw error;
context.get("key", function(error, result) {
expect(error).not.to.exist;
expect(result).not.to.exist;
done();
});
});
});
});
});
it("should allow clear()", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadWriteContext();
context.put("key1", "value1", function(error, result) {
if(error) throw error;
context.put("key2", "value2", function(error, result) {
if(error) throw error;
context.clear(function(err) {
if(error) throw error;
context.get("key1", function(error, result) {
if(error) throw error;
expect(result).not.to.exist;
context.get("key2", function(error, result) {
expect(error).not.to.exist;
expect(result).not.to.exist;
done();
});
});
});
});
});
});
});
it("should fail when trying to write on ReadOnlyContext", function(done) {
var provider = _provider.provider;
provider.open(function(error, firstAccess) {
if(error) throw error;
var context = provider.getReadOnlyContext();
context.put("key1", "value1", function(error, result) {
expect(error).to.exist;
expect(result).not.to.exist;
done();
});
});
});
});
});
}
providerBase('WebSQL', util.providers.WebSQL);

View File

@ -358,12 +358,12 @@ describe('node times (atime, mtime, ctime)', function() {
var buffer = new Filer.Buffer([1, 2, 3, 4, 5, 6, 7, 8]);
createTree(function() {
fs.open('/myfile', 'w', function(err, fd) {
if(err) throw error;
fs.open('/myfile', 'w', function(error, fd) {
if(error) throw error;
stat('/myfile', function(stats1) {
fs.write(fd, buffer, 0, buffer.length, 0, function(err, nbytes) {
if(err) throw error;
fs.write(fd, buffer, 0, buffer.length, 0, function(error, nbytes) {
if(error) throw error;
fs.close(fd, function(error) {
if(error) throw error;