Merge pull request #677 from andreasgal/revert-647-merge-stat

Revert "merge the stat and data records"
This commit is contained in:
Myk Melez 2014-12-04 10:29:11 -08:00
Родитель c64e659f96 a4e7309adb
Коммит f3d9a5e818
9 изменённых файлов: 98 добавлений и 968 удалений

Просмотреть файл

@ -11,10 +11,6 @@ test: all
cd tests && python sslEchoServer.py &
cd tests && python waitServers.py
casperjs --engine=slimerjs test `pwd`/tests/automation.js > test.log
mkdir test-profile-fs-v1
casperjs --engine=slimerjs -profile `pwd`/test-profile-fs-v1 `pwd`/tests/fs/make-fs-v1.js >> test.log
casperjs --engine=slimerjs test -profile `pwd`/test-profile-fs-v1 `pwd`/tests/automation.js >> test.log
rm -rf test-profile-fs-v1
killall python Python || true
python dumplog.py
if grep -q FAIL test.log; \

Просмотреть файл

@ -9,65 +9,16 @@ var fs = (function() {
};
Store.DBNAME = "asyncStorage";
Store.DBVERSION = 2;
Store.DBSTORENAME = "fs";
Store.DBVERSION = 1;
Store.DBSTORENAME = "keyvaluepairs";
Store.prototype.init = function(cb) {
var openreq = indexedDB.open(Store.DBNAME, Store.DBVERSION);
openreq.onerror = function() {
console.error("error opening database: " + openreq.error.name);
};
openreq.onupgradeneeded = function(event) {
if (DEBUG_FS) { console.log("upgrade needed from " + event.oldVersion + " to " + event.newVersion); }
var db = event.target.result;
var transaction = openreq.transaction;
if (event.oldVersion == 0) {
// If the database doesn't exist yet, then all we have to do
// is create the object store for the latest version of the database.
openreq.result.createObjectStore(Store.DBSTORENAME);
} else if (event.oldVersion == 1) {
// Create new object store.
var newObjectStore = openreq.result.createObjectStore(Store.DBSTORENAME);
// Iterate the keys in the old object store and copy their values
// to the new one, converting them from old- to new-style records.
var oldObjectStore = transaction.objectStore("keyvaluepairs");
var oldRecords = {};
oldObjectStore.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
oldRecords[cursor.key] = cursor.value;
cursor.continue();
return;
}
// Convert the old records to new ones.
for (var key in oldRecords) {
// Records that start with an exclamation mark are stats,
// which we don't iterate (but do use below when processing
// their equivalent data records).
if (key[0] == "!") {
continue;
}
var oldRecord = oldRecords[key];
var oldStat = oldRecords["!" + key];
var newRecord = oldStat;
if (newRecord.isDir) {
newRecord.files = oldRecord;
} else {
newRecord.data = oldRecord;
}
newObjectStore.put(newRecord, key);
}
db.deleteObjectStore("keyvaluepairs");
};
}
openreq.onupgradeneeded = function() {
openreq.result.createObjectStore(Store.DBSTORENAME);
};
openreq.onsuccess = (function() {
this.db = openreq.result;
@ -240,11 +191,8 @@ var fs = (function() {
if (data) {
cb();
} else {
store.setItem("/", {
isDir: true,
mtime: Date.now(),
files: [],
});
store.setItem("/", []);
setStat("/", { mtime: Date.now(), isDir: true });
cb();
}
});
@ -262,8 +210,8 @@ var fs = (function() {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs open " + path); }
store.getItem(path, function(record) {
if (record == null || record.isDir) {
store.getItem(path, function(blob) {
if (blob == null || !(blob instanceof Blob)) {
cb(-1);
} else {
var reader = new FileReader();
@ -273,11 +221,10 @@ var fs = (function() {
path: path,
buffer: new FileBuffer(new Uint8Array(reader.result)),
position: 0,
record: record,
}) - 1;
cb(fd);
});
reader.readAsArrayBuffer(record.data);
reader.readAsArrayBuffer(blob);
}
});
}
@ -335,8 +282,7 @@ var fs = (function() {
var file = openedFiles[fd];
file.position = from + data.byteLength;
file.record.mtime = Date.now();
file.record.size = buffer.contentSize;
file.stat = { mtime: Date.now(), isDir: false, size: buffer.contentSize };
file.dirty = true;
}
@ -366,9 +312,12 @@ var fs = (function() {
return;
}
openedFile.record.data = new Blob([openedFile.buffer.getContent()]);
store.setItem(openedFile.path, openedFile.record);
var blob = new Blob([openedFile.buffer.getContent()]);
store.setItem(openedFile.path, blob);
openedFile.dirty = false;
if (openedFile.stat) {
setStat(openedFile.path, openedFile.stat);
}
}
function flushAll() {
@ -393,11 +342,11 @@ var fs = (function() {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs list " + path); }
store.getItem(path, function(record) {
if (record == null || !record.isDir) {
store.getItem(path, function(files) {
if (files == null || files instanceof Blob) {
cb(null);
} else {
cb(record.files);
cb(files);
}
});
}
@ -406,8 +355,8 @@ var fs = (function() {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs exists " + path); }
store.getItem(path, function(record) {
cb(record ? true : false);
stat(path, function(stat) {
cb(stat ? true : false);
});
}
@ -415,15 +364,13 @@ var fs = (function() {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs truncate " + path); }
store.getItem(path, function(record) {
if (record == null || record.isDir) {
cb(false);
} else {
record.data = new Blob();
record.mtime = Date.now();
record.size = 0;
store.setItem(path, record);
stat(path, function(stat) {
if (stat && !stat.isDir) {
store.setItem(path, new Blob());
setStat(path, { mtime: Date.now(), isDir: false, size: 0 });
cb(true);
} else {
cb(false);
}
});
}
@ -435,8 +382,7 @@ var fs = (function() {
if (size != file.buffer.contentSize) {
file.buffer.setSize(size);
file.dirty = true;
file.record.mtime = Date.now();
file.record.size = size;
file.stat = { mtime: Date.now(), isDir: false, size: size };
}
}
@ -449,9 +395,8 @@ var fs = (function() {
return;
}
store.getItem(path, function(record) {
// If it's a directory that isn't empty, then we can't remove it.
if (record && record.isDir && record.files.length > 0) {
list(path, function(files) {
if (files != null && files.length > 0) {
cb(false);
return;
}
@ -459,44 +404,36 @@ var fs = (function() {
var name = basename(path);
var dir = dirname(path);
store.getItem(dir, function(parentRecord) {
list(dir, function(files) {
var index = -1;
// If it isn't in the parent directory, then we can't remove it.
if (parentRecord == null || (index = parentRecord.files.indexOf(name)) < 0) {
if (files == null || (index = files.indexOf(name)) < 0) {
cb(false);
return;
}
parentRecord.files.splice(index, 1);
store.setItem(dir, parentRecord);
files.splice(index, 1);
store.setItem(dir, files);
store.removeItem(path);
removeStat(path);
cb(true);
});
});
}
function createInternal(path, record, cb) {
function createInternal(path, data, cb) {
var name = basename(path);
var dir = dirname(path);
store.getItem(dir, function(parentRecord) {
// If the parent directory doesn't exist or isn't a directory,
// then we can't create the file.
if (parentRecord == null || !parentRecord.isDir) {
list(dir, function(files) {
if (files == null || files.indexOf(name) >= 0) {
cb(false);
return;
}
// If the file already exists, we can't create it.
if (parentRecord.files.indexOf(name) >= 0) {
cb(false);
return;
}
parentRecord.files.push(name);
store.setItem(dir, parentRecord);
store.setItem(path, record);
files.push(name);
store.setItem(dir, files);
store.setItem(path, data);
cb(true);
});
}
@ -505,27 +442,24 @@ var fs = (function() {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs create " + path); }
var record = {
isDir: false,
mtime: Date.now(),
data: blob,
size: blob.size,
};
createInternal(path, record, cb);
createInternal(path, blob, function(created) {
if (created) {
setStat(path, { mtime: Date.now(), isDir: false, size: blob.size });
}
cb(created);
});
}
function mkdir(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs mkdir " + path); }
var record = {
isDir: true,
mtime: Date.now(),
files: [],
};
createInternal(path, record, cb);
createInternal(path, [], function(created) {
if (created) {
setStat(path, { mtime: Date.now(), isDir: true });
}
cb(created);
});
}
function mkdirp(path, cb) {
@ -552,12 +486,12 @@ var fs = (function() {
partPath += "/" + parts.shift();
store.getItem(partPath, function(record) {
if (!record) {
stat(partPath, function(stat) {
if (!stat) {
// The part doesn't exist; make it, then continue to next part.
mkdir(partPath, mkpart);
}
else if (record.isDir) {
else if (stat.isDir) {
// The part exists and is a directory; continue to next part.
mkpart(true);
}
@ -576,11 +510,11 @@ var fs = (function() {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs size " + path); }
store.getItem(path, function(record) {
if (record == null || record.isDir) {
store.getItem(path, function(blob) {
if (blob == null || !(blob instanceof Blob)) {
cb(-1);
} else {
cb(record.data.size);
cb(blob.size);
}
});
}
@ -597,63 +531,57 @@ var fs = (function() {
return;
}
store.getItem(oldPath, function(oldRecord) {
// If the old path doesn't exist, we can't move it.
if (oldRecord == null) {
list(oldPath, function(files) {
if (files != null && files.length > 0) {
cb(false);
return;
}
// If the old path is a dir with files in it, we don't move it.
// XXX Shouldn't we move it along with its files?
if (oldRecord.isDir && oldRecord.files.length > 0) {
cb(false);
return;
}
remove(oldPath, function(removed) {
if (!removed) {
store.getItem(oldPath, function(data) {
if (data == null) {
cb(false);
return;
}
if (oldRecord.isDir) {
mkdir(newPath, cb);
} else {
create(newPath, oldRecord.data, cb);
}
remove(oldPath, function(removed) {
if (!removed) {
cb(false);
return;
}
if (data instanceof Blob) {
create(newPath, data, cb);
} else {
mkdir(newPath, cb);
}
});
});
});
}
function setStat(path, stat) {
if (DEBUG_FS) { console.log("fs setStat " + path); }
store.setItem("!" + path, stat);
}
function removeStat(path) {
if (DEBUG_FS) { console.log("fs removeStat " + path); }
store.removeItem("!" + path);
}
function stat(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs stat " + path); }
var file = openedFiles.find(function (file) { return file && file.path === path });
var file = openedFiles.find(function (file) { return file && file.stat && file.path === path });
if (file) {
var stat = {
isDir: file.record.isDir,
mtime: file.record.mtime,
size: file.record.size,
};
setZeroTimeout(function() { cb(stat); });
setZeroTimeout(function() { cb(file.stat); });
return;
}
store.getItem(path, function(record) {
if (record == null) {
cb(null);
return;
}
var stat = {
isDir: record.isDir,
mtime: record.mtime,
size: record.size,
};
cb(stat);
});
store.getItem("!" + path, cb);
}
function clear(cb) {

Просмотреть файл

@ -15,52 +15,6 @@ public class TestFileSystemPerf {
FileConnection file = (FileConnection)Connector.open(dirPath + "test.txt");
System.out.println("Time to open file: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
boolean exists = file.exists();
System.out.println("Time to check if file exists: " + (System.currentTimeMillis() - then) + "ms");
if (exists) {
then = System.currentTimeMillis();
InputStream in = file.openInputStream();
byte[] input = new byte[1024];
int numBytes;
int totalNumBytes = 0;
while ((numBytes = in.read(input)) != -1) {
totalNumBytes += numBytes;
}
in.close();
System.out.println("Time to read " + totalNumBytes + " bytes: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file.delete();
System.out.println("Time to delete file: " + (System.currentTimeMillis() - then) + "ms");
}
then = System.currentTimeMillis();
file = (FileConnection)Connector.open(dirPath + "test2.txt");
System.out.println("Time to open test2: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
exists = file.exists();
System.out.println("Time to check if test2 exists: " + (System.currentTimeMillis() - then) + "ms");
if (exists) {
then = System.currentTimeMillis();
InputStream in = file.openInputStream();
byte[] input = new byte[1024];
int numBytes;
int totalNumBytes = 0;
while ((numBytes = in.read(input)) != -1) {
totalNumBytes += numBytes;
}
in.close();
System.out.println("Time to read " + totalNumBytes + " bytes: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file.delete();
System.out.println("Time to delete test2: " + (System.currentTimeMillis() - then) + "ms");
}
then = System.currentTimeMillis();
file.create();
System.out.println("Time to create file: " + (System.currentTimeMillis() - then) + "ms");
@ -71,61 +25,26 @@ public class TestFileSystemPerf {
out.write(bytes);
out.flush();
}
System.out.println("Time to write/flush 1,000 times: " + (System.currentTimeMillis() - then) + "ms");
System.out.println("Time to write/flush to output stream: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
out.close();
System.out.println("Time to close output stream: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file.close();
System.out.println("Time to close file: " + (System.currentTimeMillis() - then) + "ms");
file.delete();
System.out.println("Time to delete file: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file = (FileConnection)Connector.open(dirPath + "uncached");
file.create();
out = file.openOutputStream();
out.write(bytes);
out.flush();
out.close();
file.delete();
file.close();
System.out.println("open/create/write/delete/close uncached: " + (System.currentTimeMillis() - then) + "ms");
System.out.println("Time to close file: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file = (FileConnection)Connector.open(dirPath + "test.txt");
System.out.println("Time to reopen file: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file.close();
System.out.println("Time to reclose file: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file = (FileConnection)Connector.open(dirPath + "test2.txt");
System.out.println("Time to open another file: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file.create();
out = file.openOutputStream();
for (int i = 0; i < 100000; i++) {
out.write(bytes);
}
System.out.println("Time to write 100,000 times: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
out.flush();
System.out.println("Time to flush once: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
out.close();
System.out.println("Time to close output stream: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file.close();
System.out.println("Time to close file: " + (System.currentTimeMillis() - then) + "ms");
then = System.currentTimeMillis();
file = (FileConnection)Connector.open(dirPath + "uncached2");
file.create();
out = file.openOutputStream();
out.write(bytes);
@ -133,7 +52,8 @@ public class TestFileSystemPerf {
out.close();
file.delete();
file.close();
System.out.println("open/create/write/delete/close uncached2: " + (System.currentTimeMillis() - then) + "ms");
System.out.println("Time to access another file: " + (System.currentTimeMillis() - then) + "ms");
} catch (Exception e) {
System.out.println("Unexpected exception: " + e);
e.printStackTrace();

Просмотреть файл

@ -100,7 +100,7 @@ casper.test.begin("unit tests", 7 + gfxTests.length, function(test) {
});
casper
.thenOpen("http://localhost:8000/tests/fs/fstests.html")
.thenOpen("http://localhost:8000/tests/fstests.html")
.waitForText("DONE", function() {
test.assertTextExists("DONE: 127 PASS, 0 FAIL", "run fs.js unit tests");
});

Просмотреть файл

@ -1,621 +0,0 @@
'use strict';
var DEBUG_FS = false;
var fs = (function() {
var Store = function() {
this.map = new Map();
this.db = null;
};
Store.DBNAME = "asyncStorage";
Store.DBVERSION = 1;
Store.DBSTORENAME = "keyvaluepairs";
Store.prototype.init = function(cb) {
var openreq = indexedDB.open(Store.DBNAME, Store.DBVERSION);
openreq.onerror = function() {
console.error("error opening database: " + openreq.error.name);
};
openreq.onupgradeneeded = function() {
openreq.result.createObjectStore(Store.DBSTORENAME);
};
openreq.onsuccess = (function() {
this.db = openreq.result;
cb();
}).bind(this);
};
Store.prototype.getItem = function(key, cb) {
if (this.map.has(key)) {
var value = this.map.get(key);
window.setZeroTimeout(function() { cb(value) });
} else {
var transaction = this.db.transaction(Store.DBSTORENAME, "readonly");
if (DEBUG_FS) { console.log("get " + key + " initiated"); }
var objectStore = transaction.objectStore(Store.DBSTORENAME);
var req = objectStore.get(key);
req.onerror = function() {
console.error("Error getting " + key + ": " + req.error.name);
};
transaction.oncomplete = (function() {
if (DEBUG_FS) { console.log("get " + key + " completed"); }
var value = req.result;
if (value === undefined) {
value = null;
}
this.map.set(key, value);
cb(value);
}).bind(this);
}
};
Store.prototype.setItem = function(key, value) {
this.map.set(key, value);
var transaction = this.db.transaction(Store.DBSTORENAME, "readwrite");
if (DEBUG_FS) { console.log("put " + key + " initiated"); }
var objectStore = transaction.objectStore(Store.DBSTORENAME);
var req = objectStore.put(value, key);
req.onerror = function() {
console.error("Error putting " + key + ": " + req.error.name);
};
transaction.oncomplete = function() {
if (DEBUG_FS) { console.log("put " + key + " completed"); }
};
};
Store.prototype.removeItem = function(key) {
this.map.delete(key);
var transaction = this.db.transaction(Store.DBSTORENAME, "readwrite");
if (DEBUG_FS) { console.log("delete " + key + " initiated"); }
var objectStore = transaction.objectStore(Store.DBSTORENAME);
var req = objectStore.delete(key);
req.onerror = function() {
console.error("Error deleting " + key + ": " + req.error.name);
};
transaction.oncomplete = function() {
if (DEBUG_FS) { console.log("delete " + key + " completed"); }
};
};
Store.prototype.clear = function() {
this.map.clear();
var transaction = this.db.transaction(Store.DBSTORENAME, "readwrite");
if (DEBUG_FS) { console.log("clear initiated"); }
var objectStore = transaction.objectStore(Store.DBSTORENAME);
var req = objectStore.clear();
req.onerror = function() {
console.error("Error clearing store: " + req.error.name);
};
transaction.oncomplete = function() {
if (DEBUG_FS) { console.log("clear completed"); }
};
}
Store.prototype.sync = function(cb) {
// Process a readwrite transaction to ensure previous writes have completed,
// so we leave the datastore in a consistent state. This is a bit hacky;
// we should instead monitor ongoing transactions and call our callback
// once they've all completed.
var transaction = this.db.transaction(Store.DBSTORENAME, "readwrite");
if (DEBUG_FS) { console.log("get \"\" initiated"); }
var objectStore = transaction.objectStore(Store.DBSTORENAME);
objectStore.get("");
transaction.oncomplete = function() {
if (DEBUG_FS) { console.log("get \"\" completed"); }
cb();
};
}
var store = new Store();
var FileBuffer = function(array) {
this.array = array;
this.contentSize = array.byteLength;
}
FileBuffer.prototype.setSize = function(newContentSize) {
if (newContentSize < this.array.byteLength) {
this.contentSize = newContentSize;
return;
}
var newBufferSize = 512;
// The buffer grows exponentially until the content size
// reaches 65536. After this threshold, it starts to grow
// linearly in increments of 65536 bytes.
if (newContentSize < 65536) {
while (newContentSize > newBufferSize) {
newBufferSize <<= 1;
}
} else {
while (newContentSize > newBufferSize) {
newBufferSize += 65536;
}
}
var newArray = new Uint8Array(newBufferSize);
newArray.set(this.array);
this.array = newArray;
this.contentSize = newContentSize;
}
FileBuffer.prototype.getContent = function() {
return this.array.subarray(0, this.contentSize);
}
function normalizePath(path) {
// Remove a trailing slash.
if (path.length != 1 && path.lastIndexOf("/") == path.length-1) {
path = path.substring(0, path.length-1);
}
// Coalesce multiple consecutive slashes.
path = path.replace(/\/{2,}/, "/");
// XXX Replace "." and ".." parts.
return path;
}
function dirname(path) {
path = normalizePath(path);
var index = path.lastIndexOf("/");
if (index == -1) {
return ".";
}
while (index >= 0 && path[index] == "/") {
--index;
}
var dir = path.slice(0, index + 1);
if (dir == "") {
dir = "/";
}
return dir;
}
function basename(path) {
return path.slice(path.lastIndexOf("/") + 1);
}
function initRootDir(cb) {
store.getItem("/", function(data) {
if (data) {
cb();
} else {
store.setItem("/", []);
setStat("/", { mtime: Date.now(), isDir: true });
cb();
}
});
}
function init(cb) {
store.init(function() {
initRootDir(cb || function() {});
});
}
var openedFiles = [null, null, null];
function open(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs open " + path); }
store.getItem(path, function(blob) {
if (blob == null || !(blob instanceof Blob)) {
cb(-1);
} else {
var reader = new FileReader();
reader.addEventListener("loadend", function() {
var fd = openedFiles.push({
dirty: false,
path: path,
buffer: new FileBuffer(new Uint8Array(reader.result)),
position: 0,
}) - 1;
cb(fd);
});
reader.readAsArrayBuffer(blob);
}
});
}
function close(fd) {
if (fd >= 0 && openedFiles[fd]) {
if (DEBUG_FS) { console.log("fs close " + openedFiles[fd].path); }
flush(fd);
openedFiles.splice(fd, 1, null);
}
}
function read(fd, from, to) {
if (!openedFiles[fd]) {
return null;
}
if (DEBUG_FS) { console.log("fs read " + openedFiles[fd].path); }
var buffer = openedFiles[fd].buffer;
if (typeof from === "undefined") {
from = openedFiles[fd].position;
}
if (!to || to > buffer.contentSize) {
to = buffer.contentSize;
}
if (from > buffer.contentSize) {
from = buffer.contentSize;
}
openedFiles[fd].position += to - from;
return buffer.array.subarray(from, to);
}
function write(fd, data, from) {
if (DEBUG_FS) { console.log("fs write " + openedFiles[fd].path); }
if (typeof from == "undefined") {
from = openedFiles[fd].position;
}
var buffer = openedFiles[fd].buffer;
if (from > buffer.contentSize) {
from = buffer.contentSize;
}
var newLength = (from + data.byteLength > buffer.contentSize) ? (from + data.byteLength) : (buffer.contentSize);
buffer.setSize(newLength);
buffer.array.set(data, from);
var file = openedFiles[fd];
file.position = from + data.byteLength;
file.stat = { mtime: Date.now(), isDir: false, size: buffer.contentSize };
file.dirty = true;
}
function getpos(fd) {
return openedFiles[fd].position;
}
function setpos(fd, pos) {
openedFiles[fd].position = pos;
}
function getsize(fd) {
if (!openedFiles[fd]) {
return -1;
}
return openedFiles[fd].buffer.contentSize;
}
function flush(fd) {
if (DEBUG_FS) { console.log("fs flush " + openedFiles[fd].path); }
var openedFile = openedFiles[fd];
// Bail early if the file has not been modified.
if (!openedFile.dirty) {
return;
}
var blob = new Blob([openedFile.buffer.getContent()]);
store.setItem(openedFile.path, blob);
openedFile.dirty = false;
if (openedFile.stat) {
setStat(openedFile.path, openedFile.stat);
}
}
function flushAll() {
for (var fd = 0; fd < openedFiles.length; fd++) {
if (!openedFiles[fd] || !openedFiles[fd].dirty) {
continue;
}
flush(fd);
}
}
// Due to bug #227, we don't support Object::finalize(). But the Java
// filesystem implementation requires the `finalize` method to save cached
// file data if user doesn't flush or close the file explicitly. To avoid
// losing data, we flush files periodically.
setInterval(flushAll, 5000);
// Flush files when app goes into background.
window.addEventListener("pagehide", flushAll);
function list(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs list " + path); }
store.getItem(path, function(files) {
if (files == null || files instanceof Blob) {
cb(null);
} else {
cb(files);
}
});
}
function exists(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs exists " + path); }
stat(path, function(stat) {
cb(stat ? true : false);
});
}
function truncate(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs truncate " + path); }
stat(path, function(stat) {
if (stat && !stat.isDir) {
store.setItem(path, new Blob());
setStat(path, { mtime: Date.now(), isDir: false, size: 0 });
cb(true);
} else {
cb(false);
}
});
}
function ftruncate(fd, size) {
if (DEBUG_FS) { console.log("fs ftruncate " + openedFiles[fd].path); }
var file = openedFiles[fd];
if (size != file.buffer.contentSize) {
file.buffer.setSize(size);
file.dirty = true;
file.stat = { mtime: Date.now(), isDir: false, size: size };
}
}
function remove(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs remove " + path); }
if (openedFiles.findIndex(function(file) { return file && file.path === path; }) != -1) {
setZeroTimeout(function() { cb(false); });
return;
}
list(path, function(files) {
if (files != null && files.length > 0) {
cb(false);
return;
}
var name = basename(path);
var dir = dirname(path);
list(dir, function(files) {
var index = -1;
if (files == null || (index = files.indexOf(name)) < 0) {
cb(false);
return;
}
files.splice(index, 1);
store.setItem(dir, files);
store.removeItem(path);
removeStat(path);
cb(true);
});
});
}
function createInternal(path, data, cb) {
var name = basename(path);
var dir = dirname(path);
list(dir, function(files) {
if (files == null || files.indexOf(name) >= 0) {
cb(false);
return;
}
files.push(name);
store.setItem(dir, files);
store.setItem(path, data);
cb(true);
});
}
function create(path, blob, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs create " + path); }
createInternal(path, blob, function(created) {
if (created) {
setStat(path, { mtime: Date.now(), isDir: false, size: blob.size });
}
cb(created);
});
}
function mkdir(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs mkdir " + path); }
createInternal(path, [], function(created) {
if (created) {
setStat(path, { mtime: Date.now(), isDir: true });
}
cb(created);
});
}
function mkdirp(path, cb) {
if (DEBUG_FS) { console.log("fs mkdirp " + path); }
if (path[0] !== "/") {
console.error("mkdirp called on relative path: " + path);
cb(false);
}
// Split the path into parts across "/", discarding the initial, empty part.
var parts = normalizePath(path).split("/").slice(1);
var partPath = "";
function mkpart(created) {
if (!created) {
return cb(false);
}
if (!parts.length) {
return cb(true);
}
partPath += "/" + parts.shift();
stat(partPath, function(stat) {
if (!stat) {
// The part doesn't exist; make it, then continue to next part.
mkdir(partPath, mkpart);
}
else if (stat.isDir) {
// The part exists and is a directory; continue to next part.
mkpart(true);
}
else {
// The part exists but isn't a directory; fail.
console.error("mkdirp called on path with non-dir part: " + partPath);
cb(false);
}
});
}
mkpart(true);
}
function size(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs size " + path); }
store.getItem(path, function(blob) {
if (blob == null || !(blob instanceof Blob)) {
cb(-1);
} else {
cb(blob.size);
}
});
}
// Callers of this function should make sure
// newPath doesn't exist.
function rename(oldPath, newPath, cb) {
oldPath = normalizePath(oldPath);
newPath = normalizePath(newPath);
if (DEBUG_FS) { console.log("fs rename " + oldPath + " -> " + newPath); }
if (openedFiles.findIndex(function(file) { return file && file.path === oldPath; }) != -1) {
setZeroTimeout(function() { cb(false); });
return;
}
list(oldPath, function(files) {
if (files != null && files.length > 0) {
cb(false);
return;
}
store.getItem(oldPath, function(data) {
if (data == null) {
cb(false);
return;
}
remove(oldPath, function(removed) {
if (!removed) {
cb(false);
return;
}
if (data instanceof Blob) {
create(newPath, data, cb);
} else {
mkdir(newPath, cb);
}
});
});
});
}
function setStat(path, stat) {
if (DEBUG_FS) { console.log("fs setStat " + path); }
store.setItem("!" + path, stat);
}
function removeStat(path) {
if (DEBUG_FS) { console.log("fs removeStat " + path); }
store.removeItem("!" + path);
}
function stat(path, cb) {
path = normalizePath(path);
if (DEBUG_FS) { console.log("fs stat " + path); }
var file = openedFiles.find(function (file) { return file && file.stat && file.path === path });
if (file) {
setZeroTimeout(function() { cb(file.stat); });
return;
}
store.getItem("!" + path, cb);
}
function clear(cb) {
store.clear();
initRootDir(cb || function() {});
}
function storeSync(cb) {
store.sync(cb);
}
return {
dirname: dirname,
init: init,
open: open,
close: close,
read: read,
write: write,
getpos: getpos,
setpos: setpos,
getsize: getsize,
flush: flush,
list: list,
exists: exists,
truncate: truncate,
ftruncate: ftruncate,
remove: remove,
create: create,
mkdir: mkdir,
mkdirp: mkdirp,
size: size,
rename: rename,
stat: stat,
clear: clear,
storeSync: storeSync,
};
})();

Просмотреть файл

@ -1,72 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="../../timer.js"></script>
<script type="text/javascript" src="fs-v1.js"></script>
</head>
<body>
<script type="text/javascript">
function load(file, responseType) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", file, true);
xhr.responseType = responseType;
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function() {
reject();
};
xhr.send(null);
});
}
new Promise(function(resolve, reject) {
fs.init(resolve);
}).then(function() {
return Promise.all([
new Promise(function(resolve, reject) {
fs.mkdir("/Persistent", resolve);
}),
new Promise(function(resolve, reject) {
fs.exists("/_main.ks", function(exists) {
if (exists) {
resolve();
} else {
load("../../certs/_main.ks", "blob").then(function(data) {
fs.create("/_main.ks", data, function() {
resolve();
});
});
}
});
}),
new Promise(function(resolve, reject) {
fs.exists("/_test.ks", function(exists) {
if (exists) {
resolve();
} else {
load("../../certs/_test.ks", "blob").then(function(data) {
fs.create("/_test.ks", data, function() {
resolve();
});
});
}
});
}),
]);
}).then(function() {
return new Promise(function(resolve, reject) {
fs.storeSync(resolve);
});
}).then(function() {
document.body.appendChild(document.createTextNode("DONE"));
});
</script>
</body>
</html>

Просмотреть файл

@ -1,21 +0,0 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set shiftwidth=4 tabstop=4 autoindent cindent expandtab: */
var casper = require('casper').create({
verbose: true,
logLevel: "debug",
});
casper.start("http://localhost:8000/tests/fs/make-fs-v1.html");
casper.waitForText(
"DONE",
function() {
casper.exit(0);
},
function() {
casper.exit(1);
}
);
casper.run();

Просмотреть файл

@ -4,8 +4,8 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1.0">
<script type="text/javascript" src="../../libs/fs.js" defer></script>
<script type="text/javascript" src="../../timer.js" defer></script>
<script type="text/javascript" src="../libs/fs.js" defer></script>
<script type="text/javascript" src="../timer.js" defer></script>
<script type="text/javascript">
console.log = function() {
var s = Array.prototype.join.call(arguments, ",") +"\n";

Просмотреть файл