1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
/**
* A helper class that generates ranges of dummy data for buffer or texture operations
* efficiently. Tries to minimize allocations and data updates.
*/
export class DataArrayGenerator {
private dataBuffer = new Uint8Array(256);
private lastOffset = 0;
private lastStart = 0;
private lastByteSize = 0;
/** Find the nearest power of two greater than or equal to the input value. */
private nextPowerOfTwo(value: number) {
return 1 << (32 - Math.clz32(value - 1));
}
private generateData(byteSize: number, start: number = 0, offset: number = 0) {
const prevSize = this.dataBuffer.length;
if (prevSize < byteSize) {
// If the requested data is larger than the allocated buffer, reallocate it to a buffer large
// enough to handle the new request.
const newData = new Uint8Array(this.nextPowerOfTwo(byteSize));
if (this.lastOffset === offset && this.lastStart === start && this.lastByteSize) {
// Do a fast copy of any previous data that was generated.
newData.set(this.dataBuffer);
}
this.dataBuffer = newData;
} else if (this.lastOffset < offset) {
// Ensure all values up to the offset are zeroed out.
this.dataBuffer.fill(0, this.lastOffset, offset);
}
// If the offset or start values have changed, the whole data range needs to be regenerated.
if (this.lastOffset !== offset || this.lastStart !== start) {
this.lastByteSize = 0;
}
// Generate any new values that are required
if (this.lastByteSize < byteSize) {
for (let i = this.lastByteSize; i < byteSize - offset; ++i) {
this.dataBuffer[i + offset] = ((i ** 3 + i + start) % 251) + 1; // Ensure data is always non-zero
}
this.lastOffset = offset;
this.lastStart = start;
this.lastByteSize = byteSize;
}
}
/**
* Returns a new view into the generated data that's the correct length. Because this is a view
* previously returned views from the same generator will have their values overwritten as well.
* @param {number} byteSize - Number of bytes the returned view should contain.
* @param {number} [start] - The value of the first element generated in the view.
* @param {number} [offset] - Offset of the generated data within the view. Preceeding values will be 0.
* @returns {Uint8Array} A new Uint8Array view into the generated data.
*/
generateView(byteSize: number, start: number = 0, offset: number = 0): Uint8Array {
this.generateData(byteSize, start, offset);
if (this.dataBuffer.length === byteSize) {
return this.dataBuffer;
}
return new Uint8Array(this.dataBuffer.buffer, 0, byteSize);
}
/**
* Returns a copy of the generated data. Note that this still changes the underlying buffer, so
* any previously generated views will still be overwritten, but the returned copy won't reflect
* future generate* calls.
* @param {number} byteSize - Number of bytes the returned array should contain.
* @param {number} [start] - The value of the first element generated in the view.
* @param {number} [offset] - Offset of the generated data within the view. Preceeding values will be 0.
* @returns {Uint8Array} A new Uint8Array copy of the generated data.
*/
generateAndCopyView(byteSize: number, start: number = 0, offset: number = 0) {
this.generateData(byteSize, start, offset);
return this.dataBuffer.slice(0, byteSize);
}
}
|