blob: 3b33d91ab2553bddff81514fc8666afa8606b742 [file] [log] [blame]
/*
* Copyright (C) 2023 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.
*/
"use strict";
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;
}
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;
}
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;
}
* ls() {
for (let [name, entry] of this.structure)
yield { name, entry, isDirectory: entry instanceof Directory };
}
* forEachFile() {
for (let item of this.ls()) {
if (!item.isDirectory)
yield item;
}
}
* forEachFileRecursively() {
for (let item of this.ls()) {
if (item.isDirectory)
yield* item.entry.forEachFileRecursively();
else
yield item;
}
}
* forEachDirectoryRecursively() {
for (let item of this.ls()) {
if (!item.isDirectory)
continue;
yield* item.entry.forEachDirectoryRecursively();
yield item;
}
}
fileCount() {
let count = 0;
for (let item of this.ls()) {
if (!item.isDirectory)
++count;
}
return count;
}
rm(name) {
return this.structure.delete(name);
}
}
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(dir.addDirectory(`dir-${i}`));
}
}
}
for (let dir of dirs) {
for (let i = 0; i < 5; ++i) {
if (Math.random() >= 0.6) {
dir.addFile(`file-${i}`, new File(randomFileContents()));
}
}
}
return fs;
}
class Benchmark {
runIteration() {
const fs = setupDirectory();
for (let { entry: file } of fs.forEachFileRecursively()) {
file.swapByteOrder();
}
for (let { name, entry: dir } of fs.forEachDirectoryRecursively()) {
if (dir.fileCount() > 3) {
for (let { name } of dir.forEachFile()) {
let result = dir.rm(name);
if (!result)
throw new Error("rm should have returned true");
}
}
}
}
}