Make JetStream 2
[WebKit-https.git] / PerformanceTests / JetStream2 / simple / file-system.js
1 "use strict";
2
3 /*
4  * Copyright (C) 2018 Apple Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25  * THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 let seed;
29 function resetSeed() {
30     seed = 49734321;
31 }
32 resetSeed();
33
34 Math.random = (function() {
35     return function() {
36         // Robert Jenkins' 32 bit integer hash function.
37         seed = ((seed + 0x7ed55d16) + (seed << 12))  & 0xffffffff;
38         seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
39         seed = ((seed + 0x165667b1) + (seed << 5))   & 0xffffffff;
40         seed = ((seed + 0xd3a2646c) ^ (seed << 9))   & 0xffffffff;
41         seed = ((seed + 0xfd7046c5) + (seed << 3))   & 0xffffffff;
42         seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
43         return (seed & 0xfffffff) / 0x10000000;
44     };
45 })();
46
47 function computeIsLittleEndian() {
48     let buf = new ArrayBuffer(4);
49     let dv = new DataView(buf);
50     dv.setUint32(0, 0x11223344, true);
51     let view = new Uint8Array(buf);
52     return view[0] === 0x44;
53 }
54
55 const isLittleEndian = computeIsLittleEndian();
56
57 function randomFileContents(bytes = ((Math.random() * 128) >>> 0) + 2056) {
58     let result = new ArrayBuffer(bytes);
59     let view = new Uint8Array(result);
60     for (let i = 0; i < bytes; ++i)
61         view[i] = (Math.random() * 255) >>> 0;
62     return new DataView(result);
63 }
64
65 class File {
66     constructor(dataView, permissions) {
67         this._data = dataView;
68     }
69
70     get data() { return this._data; }
71
72     set data(dataView) { this._data = dataView; }
73
74     swapByteOrder() {
75         for (let i = 0; i < Math.floor(this.data.byteLength / 8) * 8; i += 8) {
76             this.data.setFloat64(i, this.data.getFloat64(i, isLittleEndian), !isLittleEndian);
77         }
78     }
79 }
80
81 class Directory {
82     constructor() {
83         this.structure = new Map;
84     }
85
86     async addFile(name, file) {
87         let entry = this.structure.get(name);
88         if (entry !== undefined) {
89             if (entry instanceof File)
90                 throw new Error("Can't replace file with file.");
91             if (entry instanceof Directory)
92                 throw new Error("Can't replace a file with a new directory.");
93             throw new Error("Should not reach this code");
94         }
95
96         this.structure.set(name, file);
97         return file;
98     }
99
100     async addDirectory(name, directory = new Directory) {
101         let entry = this.structure.get(name);
102         if (entry !== undefined) {
103             if (entry instanceof File)
104                 throw new Error("Can't replace file with directory.");
105             if (entry instanceof Directory)
106                 throw new Error("Can't replace directory with new directory.");
107             throw new Error("Should not reach this code");
108         }
109
110         this.structure.set(name, directory);
111         return directory;
112     }
113
114     async* ls() {
115         for (let [name, entry] of this.structure)
116             yield { name, entry, isDirectory: entry instanceof Directory };
117     }
118
119     async* forEachFile() {
120         for await (let item of this.ls()) {
121             if (!item.isDirectory)
122                 yield item;
123         }
124     }
125
126     async* forEachFileRecursively() {
127         for await (let item of this.ls()) {
128             if (item.isDirectory) {
129                 for await (let file of item.entry.forEachFileRecursively())
130                     yield file;
131             } else {
132                 yield item;
133             }
134         } 
135     }
136
137     async* forEachDirectoryRecursively() {
138         for await (let item of this.ls()) {
139             if (!item.isDirectory)
140                 continue;
141
142             for await (let dirItem of item.entry.forEachDirectoryRecursively())
143                 yield dirItem;
144
145             yield item;
146         } 
147     }
148
149     async fileCount() {
150         let count = 0;
151         for await (let item of this.ls()) {
152             if (!item.isDirectory)
153                 ++count;
154         }
155
156         return count;
157     }
158
159     async rm(name) {
160         return this.structure.delete(name);
161     }
162 }
163
164 async function setupDirectory() {
165     const fs = new Directory;
166     let dirs = [fs];
167     for (let dir of dirs) {
168         for (let i = 0; i < 8; ++i) {
169             if (dirs.length < 250 && Math.random() >= 0.3) {
170                 dirs.push(await dir.addDirectory(`dir-${i}`));
171             }
172         }
173     }
174
175     for (let dir of dirs) {
176         for (let i = 0; i < 5; ++i) {
177             if (Math.random() >= 0.6) {
178                 await dir.addFile(`file-${i}`, new File(randomFileContents()));
179             }
180         }
181     }
182
183     return fs;
184 }
185
186 class Benchmark {
187     async runIteration() {
188         resetSeed();
189
190         try {
191             const fs = await setupDirectory();
192
193             for await (let { entry: file } of fs.forEachFileRecursively()) {
194                 file.swapByteOrder();
195             }
196
197             for await (let { name, entry: dir } of fs.forEachDirectoryRecursively()) {
198                 if ((await dir.fileCount()) > 3) {
199                     for await (let { name } of dir.forEachFile()) {
200                         let result = await dir.rm(name);
201                         if (!result)
202                             throw new Error("rm should have returned true");
203                         
204                     }
205                 }
206             }
207         } catch(e) {
208             console.log("Error running benchmark: ", e, e.line);
209         }
210     }
211 }