blob: 4b1d3eb0ec6e02ef828bfdf2e6d7002bf8b428e1 [file] [log] [blame]
"use strict";
/*
* Copyright (C) 2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
let seed;
function resetSeed() {
seed = 49734321;
}
resetSeed();
Math.random = (function() {
return function() {
// Robert Jenkins' 32 bit integer hash function.
seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;
seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;
seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;
seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;
seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
return (seed & 0xfffffff) / 0x10000000;
};
})();
function computeIsLittleEndian() {
let buf = new ArrayBuffer(4);
let dv = new DataView(buf);
dv.setUint32(0, 0x11223344, true);
let view = new Uint8Array(buf);
return view[0] === 0x44;
}
const isLittleEndian = computeIsLittleEndian();
function randomFileContents(bytes = ((Math.random() * 128) >>> 0) + 2056) {
let result = new ArrayBuffer(bytes);
let view = new Uint8Array(result);
for (let i = 0; i < bytes; ++i)
view[i] = (Math.random() * 255) >>> 0;
return new DataView(result);
}
class File {
constructor(dataView, permissions) {
this._data = dataView;
}
get data() { return this._data; }
set data(dataView) { this._data = dataView; }
swapByteOrder() {
for (let i = 0; i < Math.floor(this.data.byteLength / 8) * 8; i += 8) {
this.data.setFloat64(i, this.data.getFloat64(i, isLittleEndian), !isLittleEndian);
}
}
}
class Directory {
constructor() {
this.structure = new Map;
}
async addFile(name, file) {
let entry = this.structure.get(name);
if (entry !== undefined) {
if (entry instanceof File)
throw new Error("Can't replace file with file.");
if (entry instanceof Directory)
throw new Error("Can't replace a file with a new directory.");
throw new Error("Should not reach this code");
}
this.structure.set(name, file);
return file;
}
async addDirectory(name, directory = new Directory) {
let entry = this.structure.get(name);
if (entry !== undefined) {
if (entry instanceof File)
throw new Error("Can't replace file with directory.");
if (entry instanceof Directory)
throw new Error("Can't replace directory with new directory.");
throw new Error("Should not reach this code");
}
this.structure.set(name, directory);
return directory;
}
async* ls() {
for (let [name, entry] of this.structure)
yield { name, entry, isDirectory: entry instanceof Directory };
}
async* forEachFile() {
for await (let item of this.ls()) {
if (!item.isDirectory)
yield item;
}
}
async* forEachFileRecursively() {
for await (let item of this.ls()) {
if (item.isDirectory) {
for await (let file of item.entry.forEachFileRecursively())
yield file;
} else {
yield item;
}
}
}
async* forEachDirectoryRecursively() {
for await (let item of this.ls()) {
if (!item.isDirectory)
continue;
for await (let dirItem of item.entry.forEachDirectoryRecursively())
yield dirItem;
yield item;
}
}
async fileCount() {
let count = 0;
for await (let item of this.ls()) {
if (!item.isDirectory)
++count;
}
return count;
}
async rm(name) {
return this.structure.delete(name);
}
}
async function setupDirectory() {
const fs = new Directory;
let dirs = [fs];
for (let dir of dirs) {
for (let i = 0; i < 8; ++i) {
if (dirs.length < 250 && Math.random() >= 0.3) {
dirs.push(await dir.addDirectory(`dir-${i}`));
}
}
}
for (let dir of dirs) {
for (let i = 0; i < 5; ++i) {
if (Math.random() >= 0.6) {
await dir.addFile(`file-${i}`, new File(randomFileContents()));
}
}
}
return fs;
}
class Benchmark {
async runIteration() {
resetSeed();
try {
const fs = await setupDirectory();
for await (let { entry: file } of fs.forEachFileRecursively()) {
file.swapByteOrder();
}
for await (let { name, entry: dir } of fs.forEachDirectoryRecursively()) {
if ((await dir.fileCount()) > 3) {
for await (let { name } of dir.forEachFile()) {
let result = await dir.rm(name);
if (!result)
throw new Error("rm should have returned true");
}
}
}
} catch(e) {
console.log("Error running benchmark: ", e, e.line);
}
}
}