(index + 1);
132 | }
133 | // when
134 | buffer.position = 1022;
135 | buffer.putArray(array);
136 | // then
137 | buffer.position = 1022;
138 | expect(buffer.get()).toBe(1);
139 | expect(buffer.get()).toBe(2);
140 | expect(buffer.get()).toBe(3);
141 | expect(buffer.get()).toBe(4);
142 | });
143 | });
144 |
--------------------------------------------------------------------------------
/src/PackedHistogram.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a TypeScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 | import JsHistogram from "./JsHistogram";
9 | import { PackedArray } from "./packedarray/PackedArray";
10 | /**
11 | * A High Dynamic Range (HDR) Histogram that uses a packed internal representation
12 | *
13 | * {@link PackedHistogram} supports the recording and analyzing sampled data value counts across a configurable
14 | * integer value range with configurable value precision within the range. Value precision is expressed as the
15 | * number of significant digits in the value recording, and provides control over value quantization behavior
16 | * across the value range and the subsequent value resolution at any given level.
17 | *
18 | * {@link PackedHistogram} tracks value counts in a packed internal representation optimized
19 | * for typical histogram recoded values are sparse in the value range and tend to be incremented in small unit counts.
20 | * This packed representation tends to require significantly smaller amounts of stoarge when compared to unpacked
21 | * representations, but can incur additional recording cost due to resizing and repacking operations that may
22 | * occur as previously unrecorded values are encountered.
23 | *
24 | * For example, a {@link PackedHistogram} could be configured to track the counts of observed integer values between 0 and
25 | * 3,600,000,000,000 while maintaining a value precision of 3 significant digits across that range. Value quantization
26 | * within the range will thus be no larger than 1/1,000th (or 0.1%) of any value. This example Histogram could
27 | * be used to track and analyze the counts of observed response times ranging between 1 nanosecond and 1 hour
28 | * in magnitude, while maintaining a value resolution of 1 microsecond up to 1 millisecond, a resolution of
29 | * 1 millisecond (or better) up to one second, and a resolution of 1 second (or better) up to 1,000 seconds. At its
30 | * maximum tracked value (1 hour), it would still maintain a resolution of 3.6 seconds (or better).
31 | *
32 | * Auto-resizing: When constructed with no specified value range range (or when auto-resize is turned on with {@link
33 | * Histogram#setAutoResize}) a {@link PackedHistogram} will auto-resize its dynamic range to include recorded values as
34 | * they are encountered. Note that recording calls that cause auto-resizing may take longer to execute, as resizing
35 | * incurs allocation and copying of internal data structures.
36 | *
37 | */
38 | class PackedHistogram extends JsHistogram {
39 | packedCounts: PackedArray;
40 |
41 | constructor(
42 | lowestDiscernibleValue: number,
43 | highestTrackableValue: number,
44 | numberOfSignificantValueDigits: number
45 | ) {
46 | super(
47 | lowestDiscernibleValue,
48 | highestTrackableValue,
49 | numberOfSignificantValueDigits
50 | );
51 | this._totalCount = 0;
52 | this.packedCounts = new PackedArray(this.countsArrayLength);
53 | }
54 |
55 | clearCounts() {
56 | this.packedCounts.clear();
57 | }
58 |
59 | incrementCountAtIndex(index: number) {
60 | this.packedCounts.increment(index);
61 | }
62 |
63 | addToCountAtIndex(index: number, value: number) {
64 | this.packedCounts.add(index, value);
65 | }
66 |
67 | setCountAtIndex(index: number, value: number) {
68 | this.packedCounts.set(index, value);
69 | }
70 |
71 | resize(newHighestTrackableValue: number) {
72 | this.establishSize(newHighestTrackableValue);
73 | this.packedCounts.setVirtualLength(this.countsArrayLength);
74 | }
75 |
76 | getCountAtIndex(index: number) {
77 | return this.packedCounts.get(index);
78 | }
79 |
80 | protected _getEstimatedFootprintInBytes() {
81 | return 192 + 8 * this.packedCounts.getPhysicalLength();
82 | }
83 |
84 | copyCorrectedForCoordinatedOmission(
85 | expectedIntervalBetweenValueSamples: number
86 | ) {
87 | const copy = new PackedHistogram(
88 | this.lowestDiscernibleValue,
89 | this.highestTrackableValue,
90 | this.numberOfSignificantValueDigits
91 | );
92 | copy.addWhileCorrectingForCoordinatedOmission(
93 | this,
94 | expectedIntervalBetweenValueSamples
95 | );
96 | return copy;
97 | }
98 |
99 | toString() {
100 | return `PackedHistogram ${JSON.stringify(this, null, 2)}`;
101 | }
102 | }
103 |
104 | export default PackedHistogram;
105 |
--------------------------------------------------------------------------------
/assembly/ZigZagEncoding.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a AssemblyScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 |
9 | import ByteBuffer from "./ByteBuffer";
10 |
11 | /**
12 | * This class provides encoding and decoding methods for writing and reading
13 | * ZigZag-encoded LEB128-64b9B-variant (Little Endian Base 128) values to/from a
14 | * {@link ByteBuffer}. LEB128's variable length encoding provides for using a
15 | * smaller nuber of bytes for smaller values, and the use of ZigZag encoding
16 | * allows small (closer to zero) negative values to use fewer bytes. Details
17 | * on both LEB128 and ZigZag can be readily found elsewhere.
18 | *
19 | * The LEB128-64b9B-variant encoding used here diverges from the "original"
20 | * LEB128 as it extends to 64 bit values: In the original LEB128, a 64 bit
21 | * value can take up to 10 bytes in the stream, where this variant's encoding
22 | * of a 64 bit values will max out at 9 bytes.
23 | *
24 | * As such, this encoder/decoder should NOT be used for encoding or decoding
25 | * "standard" LEB128 formats (e.g. Google Protocol Buffers).
26 | */
27 | class ZigZagEncoding {
28 | /**
29 | * Writes a 64b value to the given buffer in LEB128 ZigZag encoded format
30 | * (negative numbers not supported)
31 | * @param buffer the buffer to write to
32 | * @param value the value to write to the buffer
33 | */
34 | static encode(buffer: ByteBuffer, value: i64): void {
35 | value = (value << 1) ^ (value >> 63);
36 | if (value >>> 7 === 0) {
37 | buffer.put(value);
38 | } else {
39 | buffer.put(((value & 0x7f) | 0x80));
40 | if (value >>> 14 === 0) {
41 | buffer.put((value >>> 7));
42 | } else {
43 | buffer.put(((value >>> 7) | 0x80));
44 | if (value >>> 21 === 0) {
45 | buffer.put((value >>> 14));
46 | } else {
47 | buffer.put(((value >>> 14) | 0x80));
48 | if (value >>> 28 === 0) {
49 | buffer.put((value >>> 21));
50 | } else {
51 | buffer.put(((value >>> 21) | 0x80));
52 | if (value >>> 35 === 0) {
53 | buffer.put((value >>> 28));
54 | } else {
55 | buffer.put(((value >>> 28) | 0x80));
56 | if (value >>> 42 === 0) {
57 | buffer.put((value >>> 35));
58 | } else {
59 | buffer.put(((value >>> 35) | 0x80));
60 | if (value >>> 49 === 0) {
61 | buffer.put((value >>> 42));
62 | } else {
63 | buffer.put(((value >>> 42) | 0x80));
64 | if (value >>> 56 === 0) {
65 | buffer.put((value >>> 49));
66 | } else {
67 | buffer.put(((value >>> 49) | 0x80));
68 | buffer.put((value >>> 56));
69 | }
70 | }
71 | }
72 | }
73 | }
74 | }
75 | }
76 | }
77 | }
78 |
79 | /**
80 | * Read an LEB128-64b9B ZigZag encoded long value from the given buffer
81 | * (negative numbers not supported)
82 | * @param buffer the buffer to read from
83 | * @return the value read from the buffer
84 | */
85 | static decode(buffer: ByteBuffer): i64 {
86 | let v = buffer.get();
87 | let value: i64 = (v) & (0x7f);
88 | if ((v & 0x80) != 0) {
89 | v = buffer.get();
90 | value |= (v & 0x7f) << 7;
91 | if ((v & 0x80) != 0) {
92 | v = buffer.get();
93 | value |= (v & 0x7f) << 14;
94 | if ((v & 0x80) != 0) {
95 | v = buffer.get();
96 | value |= (v & 0x7f) << 21;
97 | if ((v & 0x80) != 0) {
98 | v = buffer.get();
99 | value |= (v & 0x7f) << 28;
100 | if ((v & 0x80) != 0) {
101 | v = buffer.get();
102 | value |= (v & 0x7f) << 35;
103 | if ((v & 0x80) != 0) {
104 | v = buffer.get();
105 | value |= (v & 0x7f) << 42;
106 | if ((v & 0x80) != 0) {
107 | v = buffer.get();
108 | value |= (v & 0x7f) << 49;
109 | if ((v & 0x80) != 0) {
110 | v = buffer.get();
111 | value |= v << 56;
112 | }
113 | }
114 | }
115 | }
116 | }
117 | }
118 | }
119 | }
120 | value = (value >>> 1) ^ -(value & 1);
121 | return value;
122 | }
123 | }
124 |
125 | export default ZigZagEncoding;
126 |
--------------------------------------------------------------------------------
/src/packedarray/PackedArray.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a TypeScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 | import { PackedArrayContext } from "./PackedArrayContext";
9 | import { PackedArray } from "./PackedArray";
10 |
11 | const { pow } = Math;
12 |
13 | describe("Packed array context", () => {
14 | it("Should initialize array", () => {
15 | const ctx = new PackedArrayContext(1024, 128);
16 | expect(ctx.isPacked).toBe(true);
17 | expect(ctx.getPopulatedShortLength()).toBeGreaterThan(0);
18 | });
19 | });
20 |
21 | describe("Packed array", () => {
22 | it("Should initialize array", () => {
23 | const array = new PackedArray(1024, 128);
24 | expect(array.getPhysicalLength()).toBe(128);
25 | expect(array.length()).toBe(1024);
26 | });
27 |
28 | it("Should retrieve data stored in array", () => {
29 | // given
30 | const array = new PackedArray(1024, 16);
31 |
32 | // when
33 | array.set(16, 1);
34 | array.set(12, 42);
35 |
36 | // then
37 | expect(array.get(12)).toBe(42);
38 | expect(array.get(16)).toBe(1);
39 | });
40 |
41 | it("Should resize array when storing data", () => {
42 | // given
43 | const array = new PackedArray(1024, 16);
44 |
45 | // when
46 | array.set(12, 361);
47 |
48 | // then
49 | const storedData = array.get(12);
50 | expect(storedData).toBe(361);
51 | });
52 |
53 | it("Should retrieve big numbers stored in array", () => {
54 | // given
55 | const array = new PackedArray(1024, 16);
56 |
57 | // when
58 | array.set(12, Math.pow(2, 16) + 1);
59 |
60 | // then
61 | const storedData = array.get(12);
62 | expect(storedData).toBe(Math.pow(2, 16) + 1);
63 | });
64 |
65 | it("Should copy data when resizing array", () => {
66 | const array = new PackedArray(1024);
67 | for (let value = 1; value <= 272; value++) {
68 | array.set(value, value);
69 | }
70 |
71 | expect(array.get(1)).toBe(1);
72 | expect(array.get(255)).toBe(255);
73 | expect(array.get(272)).toBe(272);
74 | });
75 |
76 | it("Should increment data stored in array", () => {
77 | // given
78 | const array = new PackedArray(1024, 16);
79 | array.set(16, 1);
80 |
81 | // when
82 | array.add(16, 41);
83 |
84 | // then
85 | expect(array.get(16)).toBe(42);
86 | });
87 |
88 | it("Should increment data stored in array with big numbers", () => {
89 | // given
90 | const array = new PackedArray(1024, 16);
91 | array.set(16, 42);
92 |
93 | // when
94 | array.add(16, pow(2, 33));
95 |
96 | // then
97 | expect(array.get(16)).toBe(pow(2, 33) + 42);
98 | });
99 |
100 | it("Should increment data stored in array with big numbers when a resize is needed", () => {
101 | // given
102 | const array = new PackedArray(10000, 16);
103 | array.set(6144, 243);
104 | array.set(60, 243);
105 | array.set(1160, 243);
106 |
107 | // when
108 | array.add(6144, 25);
109 |
110 | // then
111 | expect(array.get(6144)).toBe(268);
112 | });
113 |
114 | it("Should increment data stored in array with big numbers", () => {
115 | // given
116 | const array = new PackedArray(1024, 16);
117 | array.set(16, 42);
118 |
119 | // when
120 | array.add(16, pow(2, 33));
121 |
122 | // then
123 | expect(array.get(16)).toBe(pow(2, 33) + 42);
124 | });
125 |
126 | it("Should clear data stored in array", () => {
127 | // given
128 | const array = new PackedArray(1024, 16);
129 | array.set(16, 42);
130 |
131 | // when
132 | array.clear();
133 |
134 | // then
135 | expect(array.get(16)).toBe(0);
136 | });
137 |
138 | it("Should resize array when virtual length change", () => {
139 | // given
140 | const array = new PackedArray(16, 16);
141 | array.set(7, 42);
142 |
143 | // when
144 | array.setVirtualLength(pow(2, 20));
145 | array.add(pow(2, 19), 42);
146 |
147 | // then
148 | expect(array.get(7)).toBe(42);
149 | expect(array.get(pow(2, 19))).toBe(42);
150 | });
151 |
152 | it("should handle properly big numbers", () => {
153 | // given
154 | const array = new PackedArray(45056, 16);
155 | // when
156 | array.set(32768, 1);
157 | // then
158 | expect(array.get(32768)).toBe(1);
159 | expect(array.get(0)).toBe(0);
160 | });
161 | });
162 |
163 | describe("Unpacked array", () => {
164 | it("Should increment data stored in array", () => {
165 | // given
166 | const array = new PackedArray(1024, pow(2, 20));
167 | array.set(16, 1);
168 |
169 | // when
170 | array.add(16, 41);
171 |
172 | // then
173 | expect(array.get(16)).toBe(42);
174 | });
175 | });
176 |
--------------------------------------------------------------------------------
/src/ZigZagEncoding.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a TypeScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 | import ByteBuffer from "./ByteBuffer";
9 |
10 | const { pow, floor } = Math;
11 |
12 | const TWO_POW_7 = pow(2, 7);
13 | const TWO_POW_14 = pow(2, 14);
14 | const TWO_POW_21 = pow(2, 21);
15 | const TWO_POW_28 = pow(2, 28);
16 | const TWO_POW_35 = pow(2, 35);
17 | const TWO_POW_42 = pow(2, 42);
18 | const TWO_POW_49 = pow(2, 49);
19 | const TWO_POW_56 = pow(2, 56);
20 |
21 | /**
22 | * This class provides encoding and decoding methods for writing and reading
23 | * ZigZag-encoded LEB128-64b9B-variant (Little Endian Base 128) values to/from a
24 | * {@link ByteBuffer}. LEB128's variable length encoding provides for using a
25 | * smaller nuber of bytes for smaller values, and the use of ZigZag encoding
26 | * allows small (closer to zero) negative values to use fewer bytes. Details
27 | * on both LEB128 and ZigZag can be readily found elsewhere.
28 | *
29 | * The LEB128-64b9B-variant encoding used here diverges from the "original"
30 | * LEB128 as it extends to 64 bit values: In the original LEB128, a 64 bit
31 | * value can take up to 10 bytes in the stream, where this variant's encoding
32 | * of a 64 bit values will max out at 9 bytes.
33 | *
34 | * As such, this encoder/decoder should NOT be used for encoding or decoding
35 | * "standard" LEB128 formats (e.g. Google Protocol Buffers).
36 | */
37 | class ZigZagEncoding {
38 | /**
39 | * Writes a long value to the given buffer in LEB128 ZigZag encoded format
40 | * (negative numbers not supported)
41 | * @param buffer the buffer to write to
42 | * @param value the value to write to the buffer
43 | */
44 | static encode(buffer: ByteBuffer, value: number) {
45 | if (value >= 0) {
46 | value = value * 2;
47 | } else {
48 | value = -value * 2 - 1;
49 | }
50 |
51 | if (value < TWO_POW_7) {
52 | buffer.put(value);
53 | } else {
54 | buffer.put(value | 0x80);
55 | if (value < TWO_POW_14) {
56 | buffer.put(floor(value / TWO_POW_7));
57 | } else {
58 | buffer.put(floor(value / TWO_POW_7) | 0x80);
59 | if (value < TWO_POW_21) {
60 | buffer.put(floor(value / TWO_POW_14));
61 | } else {
62 | buffer.put(floor(value / TWO_POW_14) | 0x80);
63 | if (value < TWO_POW_28) {
64 | buffer.put(floor(value / TWO_POW_21));
65 | } else {
66 | buffer.put(floor(value / TWO_POW_21) | 0x80);
67 | if (value < TWO_POW_35) {
68 | buffer.put(floor(value / TWO_POW_28));
69 | } else {
70 | buffer.put(floor(value / TWO_POW_28) | 0x80);
71 | if (value < TWO_POW_42) {
72 | buffer.put(floor(value / TWO_POW_35));
73 | } else {
74 | buffer.put(floor(value / TWO_POW_35) | 0x80);
75 | if (value < TWO_POW_49) {
76 | buffer.put(floor(value / TWO_POW_42));
77 | } else {
78 | buffer.put(floor(value / TWO_POW_42) | 0x80);
79 | if (value < TWO_POW_56) {
80 | buffer.put(floor(value / TWO_POW_49));
81 | } else {
82 | // should not happen
83 | buffer.put(floor(value / TWO_POW_49) + 0x80);
84 | buffer.put(floor(value / TWO_POW_56));
85 | }
86 | }
87 | }
88 | }
89 | }
90 | }
91 | }
92 | }
93 | }
94 |
95 | /**
96 | * Read an LEB128-64b9B ZigZag encoded long value from the given buffer
97 | * (negative numbers not supported)
98 | * @param buffer the buffer to read from
99 | * @return the value read from the buffer
100 | */
101 | static decode(buffer: ByteBuffer): number {
102 | let v = buffer.get();
103 | let value = v & 0x7f;
104 | if ((v & 0x80) != 0) {
105 | v = buffer.get();
106 | value += (v & 0x7f) * TWO_POW_7;
107 | if ((v & 0x80) != 0) {
108 | v = buffer.get();
109 | value += (v & 0x7f) * TWO_POW_14;
110 | if ((v & 0x80) != 0) {
111 | v = buffer.get();
112 | value += (v & 0x7f) * TWO_POW_21;
113 | if ((v & 0x80) != 0) {
114 | v = buffer.get();
115 | value += (v & 0x7f) * TWO_POW_28;
116 | if ((v & 0x80) != 0) {
117 | v = buffer.get();
118 | value += (v & 0x7f) * TWO_POW_35;
119 | if ((v & 0x80) != 0) {
120 | v = buffer.get();
121 | value += (v & 0x7f) * TWO_POW_42;
122 | if ((v & 0x80) != 0) {
123 | v = buffer.get();
124 | value += (v & 0x7f) * TWO_POW_49;
125 | if ((v & 0x80) != 0) {
126 | v = buffer.get();
127 | value += (v & 0x7f) * TWO_POW_56;
128 | }
129 | }
130 | }
131 | }
132 | }
133 | }
134 | }
135 | }
136 | if (value % 2 === 0) {
137 | value = value / 2;
138 | } else {
139 | value = -(value + 1) / 2;
140 | }
141 |
142 | return value;
143 | }
144 | }
145 |
146 | export default ZigZagEncoding;
147 |
--------------------------------------------------------------------------------
/src/JsHistogramIterator.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a TypeScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 | import JsHistogram from "./JsHistogram";
9 | import HistogramIterationValue from "./HistogramIterationValue";
10 |
11 | /**
12 | * Used for iterating through histogram values.
13 | */
14 | abstract class JsHistogramIterator /* implements Iterator */ {
15 | histogram: JsHistogram;
16 | savedHistogramTotalRawCount: number;
17 | currentIndex: number;
18 | currentValueAtIndex: number;
19 | nextValueAtIndex: number;
20 | prevValueIteratedTo: number;
21 | totalCountToPrevIndex: number;
22 | totalCountToCurrentIndex: number;
23 | totalValueToCurrentIndex: number;
24 | arrayTotalCount: number;
25 | countAtThisValue: number;
26 |
27 | private freshSubBucket: boolean;
28 |
29 | currentIterationValue: HistogramIterationValue = new HistogramIterationValue();
30 |
31 | resetIterator(histogram: JsHistogram) {
32 | this.histogram = histogram;
33 | this.savedHistogramTotalRawCount = histogram.totalCount;
34 | this.arrayTotalCount = histogram.totalCount;
35 | this.currentIndex = 0;
36 | this.currentValueAtIndex = 0;
37 | this.nextValueAtIndex = Math.pow(2, histogram.unitMagnitude);
38 | this.prevValueIteratedTo = 0;
39 | this.totalCountToPrevIndex = 0;
40 | this.totalCountToCurrentIndex = 0;
41 | this.totalValueToCurrentIndex = 0;
42 | this.countAtThisValue = 0;
43 | this.freshSubBucket = true;
44 | this.currentIterationValue.reset();
45 | }
46 |
47 | /**
48 | * Returns true if the iteration has more elements. (In other words, returns true if next would return an
49 | * element rather than throwing an exception.)
50 | *
51 | * @return true if the iterator has more elements.
52 | */
53 | public hasNext(): boolean {
54 | if (this.histogram.totalCount !== this.savedHistogramTotalRawCount) {
55 | throw "Concurrent Modification Exception";
56 | }
57 | return this.totalCountToCurrentIndex < this.arrayTotalCount;
58 | }
59 |
60 | /**
61 | * Returns the next element in the iteration.
62 | *
63 | * @return the {@link HistogramIterationValue} associated with the next element in the iteration.
64 | */
65 | public next(): HistogramIterationValue {
66 | // Move through the sub buckets and buckets until we hit the next reporting level:
67 | while (!this.exhaustedSubBuckets()) {
68 | this.countAtThisValue = this.histogram.getCountAtIndex(this.currentIndex);
69 | if (this.freshSubBucket) {
70 | // Don't add unless we've incremented since last bucket...
71 | this.totalCountToCurrentIndex += this.countAtThisValue;
72 | this.totalValueToCurrentIndex +=
73 | this.countAtThisValue *
74 | this.histogram.highestEquivalentValue(this.currentValueAtIndex);
75 | this.freshSubBucket = false;
76 | }
77 | if (this.reachedIterationLevel()) {
78 | const valueIteratedTo = this.getValueIteratedTo();
79 |
80 | Object.assign(this.currentIterationValue, {
81 | valueIteratedTo,
82 | valueIteratedFrom: this.prevValueIteratedTo,
83 | countAtValueIteratedTo: this.countAtThisValue,
84 | countAddedInThisIterationStep:
85 | this.totalCountToCurrentIndex - this.totalCountToPrevIndex,
86 | totalCountToThisValue: this.totalCountToCurrentIndex,
87 | totalValueToThisValue: this.totalValueToCurrentIndex,
88 | percentile:
89 | (100 * this.totalCountToCurrentIndex) / this.arrayTotalCount,
90 | percentileLevelIteratedTo: this.getPercentileIteratedTo(),
91 | });
92 |
93 | this.prevValueIteratedTo = valueIteratedTo;
94 | this.totalCountToPrevIndex = this.totalCountToCurrentIndex;
95 | this.incrementIterationLevel();
96 | if (this.histogram.totalCount !== this.savedHistogramTotalRawCount) {
97 | throw new Error("Concurrent Modification Exception");
98 | }
99 | return this.currentIterationValue;
100 | }
101 | this.incrementSubBucket();
102 | }
103 | throw new Error("Index Out Of Bounds Exception");
104 | }
105 |
106 | abstract incrementIterationLevel(): void;
107 |
108 | /**
109 | * @return true if the current position's data should be emitted by the iterator
110 | */
111 | abstract reachedIterationLevel(): boolean;
112 |
113 | getPercentileIteratedTo(): number {
114 | return (100 * this.totalCountToCurrentIndex) / this.arrayTotalCount;
115 | }
116 |
117 | getPercentileIteratedFrom(): number {
118 | return (100 * this.totalCountToPrevIndex) / this.arrayTotalCount;
119 | }
120 |
121 | getValueIteratedTo(): number {
122 | return this.histogram.highestEquivalentValue(this.currentValueAtIndex);
123 | }
124 |
125 | private exhaustedSubBuckets(): boolean {
126 | return this.currentIndex >= this.histogram.countsArrayLength;
127 | }
128 |
129 | incrementSubBucket() {
130 | this.freshSubBucket = true;
131 | this.currentIndex++;
132 | this.currentValueAtIndex = this.histogram.valueFromIndex(this.currentIndex);
133 | this.nextValueAtIndex = this.histogram.valueFromIndex(
134 | this.currentIndex + 1
135 | );
136 | }
137 | }
138 |
139 | export default JsHistogramIterator;
140 |
--------------------------------------------------------------------------------
/test_files/tagged-Log.logV2.hlog:
--------------------------------------------------------------------------------
1 | #[Logged with jHiccup version 2.0.7-SNAPSHOT, manually edited to duplicate contents with Tag=A]
2 | #[Histogram log format version 1.2]
3 | #[StartTime: 1441812279.474 (seconds since epoch), Wed Sep 09 08:24:39 PDT 2015]
4 | "StartTimestamp","Interval_Length","Interval_Max","Interval_Compressed_Histogram"
5 | 0.127,1.007,2.769,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBEJISEuATEZMQ4uASkhIR4nrxg9v2lMaxhvMekILGZkKmcCAEf2CsI=
6 | Tag=A,0.127,1.007,2.769,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBEJISEuATEZMQ4uASkhIR4nrxg9v2lMaxhvMekILGZkKmcCAEf2CsI=
7 | 1.134,0.999,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWLj45FTExAT4pBSEBKa6UkAgBi1uM7xjfMMlwMDABAC0CCjM=
8 | Tag=A,1.134,0.999,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWLj45FTExAT4pBSEBKa6UkAgBi1uM7xjfMMlwMDABAC0CCjM=
9 | 2.133,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE+Ph4OLgk5OSkeIS4+LgEeswIDo1+MbmdYNASYAA51CSo=
10 | Tag=A,2.133,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE+Ph4OLgk5OSkeIS4+LgEeswIDo1+MbmdYNASYAA51CSo=
11 | 3.134,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBExPiEpITEFGTkRKSEeOR6FkCg1hTeMXvNYlHhYABQ5CTo=
12 | Tag=A,3.134,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBExPiEpITEFGTkRKSEeOR6FkCg1hTeMXvNYlHhYABQ5CTo=
13 | 4.135,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE2PiERBREpBREhER4+Hj4uvQAdrTlMBldYDDhYAAugCKk=
14 | Tag=A,4.135,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE2PiERBREpBREhER4+Hj4uvQAdrTlMBldYDDhYAAugCKk=
15 | 5.132,1.002,0.426,HISTFAAAAEF42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEWPhElOR4pARUpKTkpGQkxq2mMegZnGI0+MZuIcAEAHo8Jvw==
16 | Tag=A,5.132,1.002,0.426,HISTFAAAAEF42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEWPhElOR4pARUpKTkpGQkxq2mMegZnGI0+MZuIcAEAHo8Jvw==
17 | 6.134,0.999,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIS4FITEhDiEJERE+GT6ZkhZGLbl7jEqrWHREmFgAIbAJMw==
18 | Tag=A,6.134,0.999,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIS4FITEhDiEJERE+GT6ZkhZGLbl7jEqrWHREmFgAIbAJMw==
19 | 7.133,0.999,0.459,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEhEtMQEBBTk5MQERCRkBEQEWlh9FJbg9jE+MS5ig1LhYmADkkCcE=
20 | Tag=A,7.133,0.999,0.459,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEhEtMQEBBTk5MQERCRkBEQEWlh9FJbg9jE+MS5ig1LhYmADkkCcE=
21 | 8.132,1.000,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIREgEOIQEuGT4xHg41Oo0pIqu8LYwVImwMfGBAAfkgkw
22 | Tag=A,8.132,1.000,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIREgEOIQEuGT4xHg41Oo0pIqu8LYwVImwMfGBAAfkgkw
23 | 9.132,1.751,1551.892,HISTFAAAAJZ42pNpmSzMwMB0nQECmCCUnwIDA7ObwY4FDPYfYDJMXFxsbGwMbBwszDwsDDxsHFw6RWJMLJMZmcqBMJrJmskSiA2ZZJmkgRBCgmheIORGI1H5rEzMQAyDzFhY2EWRWUwMWCBxQtQQhAIWJiyAaEHyFbKwsLHAADYWAWmiFeKS5gACLsIEzdQICAgBIQShEfhFABXDF+M=
24 | Tag=A,9.132,1.751,1551.892,HISTFAAAAJZ42pNpmSzMwMB0nQECmCCUnwIDA7ObwY4FDPYfYDJMXFxsbGwMbBwszDwsDDxsHFw6RWJMLJMZmcqBMJrJmskSiA2ZZJmkgRBCgmheIORGI1H5rEzMQAyDzFhY2EWRWUwMWCBxQtQQhAIWJiyAaEHyFbKwsLHAADYWAWmiFeKS5gACLsIEzdQICAgBIQShEfhFABXDF+M=
25 | 10.883,0.250,0.426,HISTFAAAAD142pNpmSzMwMAgxQABTBDKT4GBgdnNYMcCBvsPEBEeFi4mPg4WLhY2BjY2FhYOBSkpASEtoRA+NgDkCQZR
26 | Tag=A,10.883,0.250,0.426,HISTFAAAAD142pNpmSzMwMAgxQABTBDKT4GBgdnNYMcCBvsPEBEeFi4mPg4WLhY2BjY2FhYOBSkpASEtoRA+NgDkCQZR
27 | 11.133,1.003,0.524,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBk2HgkZKREpEQUeGSEBAQ6xSYxhCnp7GJ02sWgJsbCwMgEAO0AJSQ==
28 | Tag=A,11.133,1.003,0.524,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBk2HgkZKREpEQUeGSEBAQ6xSYxhCnp7GJ02sWgJsbCwMgEAO0AJSQ==
29 | 12.136,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2AT4eCQURHgkuEREOHjERlSQhhWuMSV9Y7ERYWAAa4gko
30 | Tag=A,12.136,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2AT4eCQURHgkuEREOHjERlSQhhWuMSV9Y7ERYWAAa4gko
31 | 13.133,0.998,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkRIR4RMRk5KQE+PgEhMRmzEjWZJ4whW1hMBNiYAB42CTA=
32 | Tag=A,13.133,0.998,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkRIR4RMRk5KQE+PgEhMRmzEjWZJ4whW1hMBNiYAB42CTA=
33 | 14.131,1.000,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBkWFhE5GT4FKQkRCR4ZCREpqwmMBhpHGG16WHx42JgYmAA6swk+
34 | Tag=A,14.131,1.000,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBkWFhE5GT4FKQkRCR4ZCREpqwmMBhpHGG16WHx42JgYmAA6swk+
35 | 15.131,1.001,0.442,HISTFAAAAD542pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPMBkuMTEFHgklFRkRATkJERGdKgudfYwRTSwGalwAF2IJOw==
36 | Tag=A,15.131,1.001,0.442,HISTFAAAAD542pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPMBkuMTEFHgklFRkRATkJERGdKgudfYwRTSwGalwAF2IJOw==
37 | 16.132,1.001,0.524,HISTFAAAAEZ42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPEBE2IQEFCQkpGREpHj4hKS6NU4z7GDMkuBoYDSYw2wiwMLEyAQBQ3wne
38 | Tag=A,16.132,1.001,0.524,HISTFAAAAEZ42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPEBE2IQEFCQkpGREpHj4hKS6NU4z7GDMkuBoYDSYw2wiwMLEyAQBQ3wne
39 | 17.133,0.998,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2DjElIR4RHiExKQE5IT61iCodtXWMdn0sKVJMTAAekAk0
40 | Tag=A,17.133,0.998,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2DjElIR4RHiExKQE5IT61iCodtXWMdn0sKVJMTAAekAk0
41 | 18.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBkWISERJSUJESklHhEJEREhqwZGLakPjDZdLBYCHCwAKOkJPg==
42 | Tag=A,18.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBkWISERJSUJESklHhEJEREhqwZGLakPjDZdLBYCHCwAKOkJPg==
43 | 19.131,1.000,0.475,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUAk2HjkJBSk+Pi4BMT4xIQE9pxIluTOMPhtYbITY2JgAKLoJOQ==
44 | Tag=A,19.131,1.000,0.475,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUAk2HjkJBSk+Pi4BMT4xIQE9pxIluTOMPhtYbITY2JgAKLoJOQ==
45 | 20.131,1.004,0.475,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmPhEJOSEhDi4+ETEeASEhswIVi1+MFjtYvCRYGJgAIP8JNw==
46 | Tag=A,20.131,1.004,0.475,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmPhEJOSEhDi4+ETEeASEhswIVi1+MFjtYvCRYGJgAIP8JNw==
47 |
--------------------------------------------------------------------------------
/src/Histogram.fc.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a TypeScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 | import * as fc from "fast-check";
9 | import * as hdr from "./index";
10 | import { initWebAssembly } from "./wasm";
11 | import Histogram, { BitBucketSize } from "./Histogram";
12 |
13 | const runFromStryker = __dirname.includes("stryker");
14 |
15 | const runnerOptions = {
16 | numRuns: runFromStryker ? 10 : 1000,
17 | verbose: true,
18 | };
19 |
20 | describe("Histogram percentile computation", () => {
21 | beforeAll(initWebAssembly);
22 |
23 | const numberOfSignificantValueDigits = 3;
24 | [true, false].forEach((useWebAssembly) =>
25 | [16, "packed"].forEach((bitBucketSize: BitBucketSize) =>
26 | it(`Histogram ${bitBucketSize} (wasm: ${useWebAssembly}) should be accurate according to its significant figures`, async () => {
27 | await initWebAssembly();
28 |
29 | fc.assert(
30 | fc.property(arbData(2000), (numbers) => {
31 | const histogram = hdr.build({
32 | bitBucketSize,
33 | numberOfSignificantValueDigits,
34 | useWebAssembly,
35 | });
36 | numbers.forEach((n) => histogram.recordValue(n));
37 | const actual = quantile(numbers, 90);
38 | const got = histogram.getValueAtPercentile(90);
39 | const relativeError = Math.abs(1 - got / actual);
40 | const variation = Math.pow(10, -numberOfSignificantValueDigits);
41 | histogram.destroy();
42 | return relativeError < variation;
43 | }),
44 | runnerOptions
45 | );
46 | })
47 | )
48 | );
49 | });
50 |
51 | describe("Histogram percentile computation (packed vs classic)", () => {
52 | const numberOfSignificantValueDigits = 3;
53 | const classicHistogram = hdr.build({
54 | numberOfSignificantValueDigits,
55 | });
56 | const histogram = hdr.build({
57 | numberOfSignificantValueDigits,
58 | bitBucketSize: "packed",
59 | useWebAssembly: false,
60 | });
61 |
62 | it(`should be accurate according to its significant figures`, () => {
63 | fc.assert(
64 | fc.property(arbData(5), (numbers) => {
65 | histogram.reset();
66 | classicHistogram.reset();
67 | numbers.forEach((n) => histogram.recordValue(n));
68 | numbers.forEach((n) => classicHistogram.recordValue(n));
69 | const actual = classicHistogram.getValueAtPercentile(90);
70 | const got = histogram.getValueAtPercentile(90);
71 | return actual === got;
72 | }),
73 | runnerOptions
74 | );
75 | });
76 | });
77 |
78 | describe("Histogram percentile computation with CO correction (wasm vs js)", () => {
79 | beforeAll(initWebAssembly);
80 |
81 | let jsHistogram: Histogram;
82 | let wasmHistogram: Histogram;
83 |
84 | beforeEach(() => {
85 | jsHistogram = hdr.build({
86 | useWebAssembly: false,
87 | });
88 | wasmHistogram = hdr.build({
89 | useWebAssembly: true,
90 | });
91 | });
92 |
93 | afterEach(() => {
94 | jsHistogram.destroy();
95 | wasmHistogram.destroy();
96 | });
97 |
98 | it(`should be accurate according to its significant figures`, () => {
99 | fc.assert(
100 | fc.property(arbData(1, 100 * 1000), (numbers) => {
101 | jsHistogram.reset();
102 | wasmHistogram.reset();
103 | numbers.forEach((n) => {
104 | jsHistogram.recordValueWithExpectedInterval(n, 1000);
105 | });
106 | numbers.forEach((n) => {
107 | wasmHistogram.recordValueWithExpectedInterval(n, 1000);
108 | });
109 | const js = jsHistogram.getValueAtPercentile(90);
110 | const wasm = wasmHistogram.getValueAtPercentile(90);
111 | const relativeError = Math.abs(1 - js / wasm);
112 | const variation = Math.pow(10, -3);
113 | if (relativeError >= variation) {
114 | console.log({ js, wasm });
115 | }
116 | return relativeError < variation;
117 | }),
118 | runnerOptions
119 | );
120 | });
121 | });
122 |
123 | describe("Histogram encoding/decoding", () => {
124 | beforeAll(initWebAssembly);
125 |
126 | const numberOfSignificantValueDigits = 3;
127 | [true, false].forEach((useWebAssembly) =>
128 | [8, 16, 32, 64, "packed"].forEach((bitBucketSize: BitBucketSize) => {
129 | it(`Histogram ${bitBucketSize} (wasm: ${useWebAssembly}) should keep all data after an encoding/decoding roundtrip`, () => {
130 | fc.assert(
131 | fc.property(arbData(1), fc.double(50, 100), (numbers, percentile) => {
132 | const histogram = hdr.build({
133 | bitBucketSize,
134 | numberOfSignificantValueDigits,
135 | useWebAssembly,
136 | });
137 | numbers.forEach((n) => histogram.recordValue(n));
138 | const encodedHistogram = hdr.encodeIntoCompressedBase64(histogram);
139 | const decodedHistogram = hdr.decodeFromCompressedBase64(
140 | encodedHistogram
141 | );
142 | const actual = histogram.getValueAtPercentile(percentile);
143 | const got = decodedHistogram.getValueAtPercentile(percentile);
144 | histogram.destroy();
145 | decodedHistogram.destroy();
146 | return actual === got;
147 | }),
148 | runnerOptions
149 | );
150 | });
151 | })
152 | );
153 | });
154 |
155 | const arbData = (size: number, max: number = Number.MAX_SAFE_INTEGER) =>
156 | fc.array(fc.integer(1, max), size, size);
157 |
158 | // reference implementation
159 | const quantile = (inputData: number[], percentile: number) => {
160 | const data = [...inputData].sort((a, b) => a - b);
161 | const index = (percentile / 100) * (data.length - 1);
162 | let result: number;
163 | if (Math.floor(index) === index) {
164 | result = data[index];
165 | } else {
166 | const i = Math.floor(index);
167 | const fraction = index - i;
168 | result = data[i] + (data[i + 1] - data[i]) * fraction;
169 | }
170 | return result;
171 | };
172 |
--------------------------------------------------------------------------------
/assembly/RecordedValuesIterator.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a AssemblyScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 |
9 | import Histogram from "./Histogram";
10 | import HistogramIterationValue from "./HistogramIterationValue";
11 |
12 | /**
13 | * Used for iterating through all recorded histogram values using the finest granularity steps supported by the
14 | * underlying representation. The iteration steps through all non-zero recorded value counts, and terminates when
15 | * all recorded histogram values are exhausted.
16 | */
17 | class RecordedValuesIterator {
18 | visitedIndex: i32;
19 | histogram!: Histogram;
20 | savedHistogramTotalRawCount: u64;
21 | currentIndex: i32;
22 | currentValueAtIndex: u64;
23 | nextValueAtIndex: u64;
24 | prevValueIteratedTo: u64;
25 | totalCountToPrevIndex: u64;
26 | totalCountToCurrentIndex: u64;
27 | totalValueToCurrentIndex: u64;
28 | arrayTotalCount: u64;
29 | countAtThisValue: u64;
30 |
31 | private freshSubBucket: boolean;
32 |
33 | currentIterationValue: HistogramIterationValue = new HistogramIterationValue();
34 |
35 | /**
36 | * @param histogram The histogram this iterator will operate on
37 | */
38 | constructor(histogram: Histogram) {
39 | this.doReset(histogram);
40 | }
41 |
42 | /**
43 | * Reset iterator for re-use in a fresh iteration over the same histogram data set.
44 | */
45 | public reset(): void {
46 | this.doReset(this.histogram);
47 | }
48 |
49 | private doReset(histogram: Histogram): void {
50 | this.resetIterator(histogram);
51 | this.visitedIndex = -1;
52 | }
53 |
54 | incrementIterationLevel(): void {
55 | this.visitedIndex = this.currentIndex;
56 | }
57 |
58 | reachedIterationLevel(): bool {
59 | const currentCount = this.histogram.getCountAtIndex(this.currentIndex);
60 | return currentCount != 0 && this.visitedIndex !== this.currentIndex;
61 | }
62 |
63 | resetIterator(histogram: Histogram): void {
64 | this.histogram = histogram;
65 | this.savedHistogramTotalRawCount = histogram.totalCount;
66 | this.arrayTotalCount = histogram.totalCount;
67 | this.currentIndex = 0;
68 | this.currentValueAtIndex = 0;
69 | this.nextValueAtIndex = 1 << histogram.unitMagnitude;
70 | this.prevValueIteratedTo = 0;
71 | this.totalCountToPrevIndex = 0;
72 | this.totalCountToCurrentIndex = 0;
73 | this.totalValueToCurrentIndex = 0;
74 | this.countAtThisValue = 0;
75 | this.freshSubBucket = true;
76 | this.currentIterationValue.reset();
77 | }
78 |
79 | /**
80 | * Returns true if the iteration has more elements. (In other words, returns true if next would return an
81 | * element rather than throwing an exception.)
82 | *
83 | * @return true if the iterator has more elements.
84 | */
85 | public hasNext(): boolean {
86 | if (this.histogram.totalCount !== this.savedHistogramTotalRawCount) {
87 | throw "Concurrent Modification Exception";
88 | }
89 | return this.totalCountToCurrentIndex < this.arrayTotalCount;
90 | }
91 |
92 | /**
93 | * Returns the next element in the iteration.
94 | *
95 | * @return the {@link HistogramIterationValue} associated with the next element in the iteration.
96 | */
97 | public next(): HistogramIterationValue {
98 | // Move through the sub buckets and buckets until we hit the next reporting level:
99 | while (!this.exhaustedSubBuckets()) {
100 | this.countAtThisValue = this.histogram.getCountAtIndex(this.currentIndex);
101 | if (this.freshSubBucket) {
102 | // Don't add unless we've incremented since last bucket...
103 | this.totalCountToCurrentIndex += this.countAtThisValue;
104 | this.totalValueToCurrentIndex +=
105 | this.countAtThisValue *
106 | this.histogram.highestEquivalentValue(this.currentValueAtIndex);
107 | this.freshSubBucket = false;
108 | }
109 | if (this.reachedIterationLevel()) {
110 | const valueIteratedTo = this.getValueIteratedTo();
111 |
112 | //Object.assign(this.currentIterationValue, {
113 | this.currentIterationValue.valueIteratedTo = valueIteratedTo;
114 | this.currentIterationValue.valueIteratedFrom = this.prevValueIteratedTo;
115 | this.currentIterationValue.countAtValueIteratedTo = this.countAtThisValue;
116 | this.currentIterationValue.countAddedInThisIterationStep =
117 | this.totalCountToCurrentIndex - this.totalCountToPrevIndex;
118 | this.currentIterationValue.totalCountToThisValue = this.totalCountToCurrentIndex;
119 | this.currentIterationValue.totalValueToThisValue = this.totalValueToCurrentIndex;
120 | this.currentIterationValue.percentile =
121 | (100 * this.totalCountToCurrentIndex) /
122 | this.arrayTotalCount;
123 | this.currentIterationValue.percentileLevelIteratedTo = this.getPercentileIteratedTo();
124 | this.prevValueIteratedTo = valueIteratedTo;
125 | this.totalCountToPrevIndex = this.totalCountToCurrentIndex;
126 | this.incrementIterationLevel();
127 | if (this.histogram.totalCount !== this.savedHistogramTotalRawCount) {
128 | throw new Error("Concurrent Modification Exception");
129 | }
130 | return this.currentIterationValue;
131 | }
132 | this.incrementSubBucket();
133 | }
134 | throw new Error("Index Out Of Bounds Exception");
135 | }
136 |
137 | getPercentileIteratedTo(): f64 {
138 | return (
139 | (100 * this.totalCountToCurrentIndex) /
140 | this.arrayTotalCount
141 | );
142 | }
143 |
144 | getPercentileIteratedFrom(): f64 {
145 | return (
146 | (100 * this.totalCountToPrevIndex) / this.arrayTotalCount
147 | );
148 | }
149 |
150 | getValueIteratedTo(): u64 {
151 | return this.histogram.highestEquivalentValue(this.currentValueAtIndex);
152 | }
153 |
154 | private exhaustedSubBuckets(): boolean {
155 | return this.currentIndex >= this.histogram.countsArrayLength;
156 | }
157 |
158 | incrementSubBucket(): void {
159 | this.freshSubBucket = true;
160 | this.currentIndex++;
161 | this.currentValueAtIndex = this.histogram.valueFromIndex(this.currentIndex);
162 | this.nextValueAtIndex = this.histogram.valueFromIndex(
163 | this.currentIndex + 1
164 | );
165 | }
166 | }
167 |
168 | export default RecordedValuesIterator;
169 |
--------------------------------------------------------------------------------
/assembly/encoding.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a AssemblyScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 |
9 | import ByteBuffer from "./ByteBuffer";
10 | import Histogram from "./Histogram";
11 | import ZigZagEncoding from "./ZigZagEncoding";
12 |
13 | const V2EncodingCookieBase = 0x1c849303;
14 | const V2CompressedEncodingCookieBase = 0x1c849304;
15 | const V2maxWordSizeInBytes = 9; // LEB128-64b9B + ZigZag require up to 9 bytes per word
16 | const encodingCookie = V2EncodingCookieBase | 0x10; // LSBit of wordsize byte indicates TLZE Encoding
17 | const compressedEncodingCookie = V2CompressedEncodingCookieBase | 0x10; // LSBit of wordsize byte indicates TLZE Encoding
18 |
19 | function fillBufferFromCountsArray(
20 | self: Histogram,
21 | buffer: ByteBuffer
22 | ): void {
23 | const countsLimit = self.countsArrayIndex(self.maxValue) + 1;
24 | let srcIndex = 0;
25 |
26 | while (srcIndex < countsLimit) {
27 | // V2 encoding format uses a ZigZag LEB128-64b9B encoded long. Positive values are counts,
28 | // while negative values indicate a repeat zero counts.
29 | const count = self.getCountAtIndex(srcIndex++);
30 | if (count < 0) {
31 | throw new Error(
32 | "Cannot encode histogram containing negative counts (" +
33 | count.toString() +
34 | ") at index " +
35 | srcIndex.toString() +
36 | ", corresponding the value range [" +
37 | self.lowestEquivalentValue(self.valueFromIndex(srcIndex)).toString() +
38 | "," +
39 | self
40 | .nextNonEquivalentValue(self.valueFromIndex(srcIndex))
41 | .toString() +
42 | ")"
43 | );
44 | }
45 | // Count trailing 0s (which follow this count):
46 | let zerosCount = 0;
47 | if (count == 0) {
48 | zerosCount = 1;
49 | while (srcIndex < countsLimit && self.getCountAtIndex(srcIndex) == 0) {
50 | zerosCount++;
51 | srcIndex++;
52 | }
53 | }
54 | if (zerosCount > 1) {
55 | ZigZagEncoding.encode(buffer, -zerosCount);
56 | } else {
57 | ZigZagEncoding.encode(buffer, count);
58 | }
59 | }
60 | }
61 |
62 | /**
63 | * Encode this histogram into a ByteBuffer
64 | * @param buffer The buffer to encode into
65 | * @return The number of bytes written to the buffer
66 | */
67 | export function encodeIntoByteBuffer(
68 | self: Histogram,
69 | buffer: ByteBuffer
70 | ): i32 {
71 | const initialPosition = buffer.position;
72 | buffer.putInt32(encodingCookie);
73 | buffer.putInt32(0); // Placeholder for payload length in bytes.
74 | buffer.putInt32(1);
75 | buffer.putInt32(self.numberOfSignificantValueDigits);
76 | buffer.putInt64(self.lowestDiscernibleValue);
77 | buffer.putInt64(self.highestTrackableValue);
78 | buffer.putInt64(1);
79 |
80 | const payloadStartPosition = buffer.position;
81 | fillBufferFromCountsArray(self, buffer);
82 |
83 | const backupIndex = buffer.position;
84 | buffer.position = initialPosition + 4;
85 | buffer.putInt32(backupIndex - payloadStartPosition); // Record the payload length
86 |
87 | buffer.position = backupIndex;
88 |
89 | return backupIndex - initialPosition;
90 | }
91 |
92 | function fillCountsArrayFromSourceBuffer(
93 | self: Histogram,
94 | sourceBuffer: ByteBuffer,
95 | lengthInBytes: u32,
96 | wordSizeInBytes: u32
97 | ): i32 {
98 | if (
99 | wordSizeInBytes != 2 &&
100 | wordSizeInBytes != 4 &&
101 | wordSizeInBytes != 8 &&
102 | wordSizeInBytes != V2maxWordSizeInBytes
103 | ) {
104 | throw new Error(
105 | "word size must be 2, 4, 8, or V2maxWordSizeInBytes (" +
106 | V2maxWordSizeInBytes.toString() +
107 | ") bytes"
108 | );
109 | }
110 | let dstIndex: i32 = 0;
111 | const endPosition = sourceBuffer.position + lengthInBytes;
112 | while (sourceBuffer.position < endPosition) {
113 | let zerosCount: i32 = 0;
114 | let count = ZigZagEncoding.decode(sourceBuffer);
115 | if (count < 0) {
116 | zerosCount = -count;
117 | dstIndex += zerosCount; // No need to set zeros in array. Just skip them.
118 | } else {
119 | self.setCountAtIndex(dstIndex++, count);
120 | }
121 | }
122 | return dstIndex; // this is the destination length
123 | }
124 |
125 | function getCookieBase(cookie: u32): u32 {
126 | return cookie & ~0xf0;
127 | }
128 |
129 | function getWordSizeInBytesFromCookie(cookie: u32): u32 {
130 | if (
131 | getCookieBase(cookie) == V2EncodingCookieBase ||
132 | getCookieBase(cookie) == V2CompressedEncodingCookieBase
133 | ) {
134 | return V2maxWordSizeInBytes;
135 | }
136 | const sizeByte = (cookie & 0xf0) >> 4;
137 | return sizeByte & 0xe;
138 | }
139 |
140 | export function decodeFromByteBuffer(
141 | buffer: ByteBuffer,
142 | minBarForHighestTrackableValue: u64
143 | ): Histogram {
144 | const cookie = buffer.getInt32();
145 |
146 | let payloadLengthInBytes: u32;
147 | let numberOfSignificantValueDigits: u8;
148 | let lowestTrackableUnitValue: u64;
149 | let highestTrackableValue: u64;
150 |
151 | if (getCookieBase(cookie) === V2EncodingCookieBase) {
152 | if (getWordSizeInBytesFromCookie(cookie) != V2maxWordSizeInBytes) {
153 | throw new Error(
154 | "The buffer does not contain a Histogram (no valid cookie found)"
155 | );
156 | }
157 | payloadLengthInBytes = buffer.getInt32();
158 | buffer.getInt32(); // normalizingIndexOffset not used
159 | numberOfSignificantValueDigits = buffer.getInt32();
160 | lowestTrackableUnitValue = buffer.getInt64();
161 | highestTrackableValue = buffer.getInt64();
162 | buffer.getInt64(); // integerToDoubleValueConversionRatio not used
163 | } else {
164 | throw new Error(
165 | "The buffer does not contain a Histogram (no valid V2 encoding cookie found)"
166 | );
167 | }
168 |
169 | highestTrackableValue = max(
170 | highestTrackableValue,
171 | minBarForHighestTrackableValue
172 | );
173 |
174 | const histogram: Histogram = instantiate>(
175 | lowestTrackableUnitValue,
176 | highestTrackableValue,
177 | numberOfSignificantValueDigits
178 | );
179 |
180 | const filledLength = fillCountsArrayFromSourceBuffer(
181 | histogram,
182 | buffer,
183 | payloadLengthInBytes,
184 | V2maxWordSizeInBytes
185 | );
186 |
187 | histogram.establishInternalTackingValues(filledLength);
188 |
189 | return histogram;
190 | }
191 |
--------------------------------------------------------------------------------
/src/Recorder.spec.ts:
--------------------------------------------------------------------------------
1 | import Recorder from "./Recorder";
2 | import Int32Histogram from "./Int32Histogram";
3 | import PackedHistogram from "./PackedHistogram";
4 | import JsHistogram from "./JsHistogram";
5 | import { initWebAssembly, WasmHistogram } from "./wasm";
6 |
7 | describe("Recorder", () => {
8 | beforeAll(initWebAssembly);
9 |
10 | it("should record value", () => {
11 | // given
12 | const recorder = new Recorder();
13 | // when
14 | recorder.recordValue(123);
15 | // then
16 | const histogram = recorder.getIntervalHistogram();
17 | expect(histogram.totalCount).toBe(1);
18 | });
19 |
20 | it("should record value in a packed histogram", () => {
21 | // given
22 | const recorder = new Recorder({
23 | numberOfSignificantValueDigits: 5,
24 | bitBucketSize: "packed",
25 | });
26 | // when
27 | recorder.recordValue(123);
28 | // then
29 | expect(recorder.getIntervalHistogram() instanceof PackedHistogram).toBe(
30 | true
31 | );
32 | expect(recorder.getIntervalHistogram() instanceof PackedHistogram).toBe(
33 | true
34 | );
35 | });
36 |
37 | it("should record value in a WASM histogram", () => {
38 | // given
39 | const recorder = new Recorder({
40 | numberOfSignificantValueDigits: 5,
41 | bitBucketSize: "packed",
42 | useWebAssembly: true,
43 | });
44 | try {
45 | // when
46 | recorder.recordValue(123);
47 | // then
48 | expect(recorder.getIntervalHistogram() instanceof WasmHistogram).toBe(
49 | true
50 | );
51 | } finally {
52 | recorder.destroy();
53 | }
54 | });
55 |
56 | it("should record value with count", () => {
57 | // given
58 | const recorder = new Recorder();
59 | // when
60 | recorder.recordValueWithCount(123, 3);
61 | // then
62 | const histogram = recorder.getIntervalHistogram();
63 | expect(histogram.totalCount).toBe(3);
64 | });
65 |
66 | it("should record value with expected interval", () => {
67 | // given
68 | const recorder = new Recorder();
69 | // when
70 | recorder.recordValueWithExpectedInterval(223, 100);
71 | // then
72 | const histogram = recorder.getIntervalHistogram();
73 | expect(histogram.totalCount).toBe(2);
74 | });
75 |
76 | it("should record value in a packed histogram", () => {
77 | // given
78 | const recorder = new Recorder({ bitBucketSize: "packed" });
79 | recorder.recordValue(42);
80 | // when
81 | const histogram = recorder.getIntervalHistogram();
82 | // then
83 | expect(histogram instanceof PackedHistogram).toBe(true);
84 | });
85 |
86 | it("should record value only on one interval histogram", () => {
87 | // given
88 | const recorder = new Recorder();
89 | // when
90 | recorder.recordValue(123);
91 | const firstHistogram = recorder.getIntervalHistogram();
92 | // then
93 | const secondHistogram = recorder.getIntervalHistogram();
94 | expect(secondHistogram.totalCount).toBe(0);
95 | });
96 |
97 | it("should not record value on returned interval histogram", () => {
98 | // given
99 | const recorder = new Recorder();
100 | const firstHistogram = recorder.getIntervalHistogram();
101 | const secondHistogram = recorder.getIntervalHistogram();
102 | // when
103 | firstHistogram.recordValue(42); // should have 0 impact on recorder
104 | const thirdHistogram = recorder.getIntervalHistogram();
105 | // then
106 | expect(thirdHistogram.totalCount).toBe(0);
107 | });
108 |
109 | it("should return interval histograms with expected significant digits", () => {
110 | // given
111 | const recorder = new Recorder({ numberOfSignificantValueDigits: 4 });
112 | const firstHistogram = recorder.getIntervalHistogram();
113 | const secondHistogram = recorder.getIntervalHistogram();
114 | // when
115 | const thirdHistogram = recorder.getIntervalHistogram();
116 | // then
117 | expect((thirdHistogram as JsHistogram).numberOfSignificantValueDigits).toBe(
118 | 4
119 | );
120 | });
121 |
122 | it("should return recycled histograms when asking for interval histogram", () => {
123 | // given
124 | const recorder = new Recorder();
125 | const firstHistogram = recorder.getIntervalHistogram();
126 | // when
127 | const secondHistogram = recorder.getIntervalHistogram(firstHistogram);
128 | const thirdHistogram = recorder.getIntervalHistogram();
129 | // then
130 | expect(thirdHistogram === firstHistogram).toBe(true);
131 | });
132 |
133 | it("should throw an error when trying to recycle an histogram not created by the recorder", () => {
134 | // given
135 | const recorder = new Recorder();
136 | const somehistogram = new Int32Histogram(1, 2, 3);
137 | // when & then
138 | expect(() => recorder.getIntervalHistogram(somehistogram)).toThrowError();
139 | });
140 |
141 | it("should reset histogram when recycling", () => {
142 | // given
143 | const recorder = new Recorder();
144 | recorder.recordValue(42);
145 | const firstHistogram = recorder.getIntervalHistogram();
146 | // when
147 | const secondHistogram = recorder.getIntervalHistogram(firstHistogram);
148 | const thirdHistogram = recorder.getIntervalHistogram();
149 | // then
150 | expect(thirdHistogram.totalCount).toBe(0);
151 | });
152 |
153 | it("should set timestamps on first interval histogram", () => {
154 | // given
155 | let currentTime = 42;
156 | let clock = () => currentTime;
157 | const recorder = new Recorder({}, clock);
158 | // when
159 | currentTime = 123;
160 | const histogram = recorder.getIntervalHistogram();
161 | // then
162 | expect(histogram.startTimeStampMsec).toBe(42);
163 | expect(histogram.endTimeStampMsec).toBe(123);
164 | });
165 |
166 | it("should set timestamps on any interval histogram", () => {
167 | // given
168 | let currentTime = 42;
169 | let clock = () => currentTime;
170 | const recorder = new Recorder({}, clock);
171 | currentTime = 51;
172 | const firstHistogram = recorder.getIntervalHistogram();
173 | // when
174 | currentTime = 56;
175 | const secondHistogram = recorder.getIntervalHistogram();
176 | // then
177 | expect(secondHistogram.startTimeStampMsec).toBe(51);
178 | expect(secondHistogram.endTimeStampMsec).toBe(56);
179 | });
180 |
181 | it("should copy interval histogram", () => {
182 | // given
183 | let currentTime = 42;
184 | let clock = () => currentTime;
185 | const recorder = new Recorder({ numberOfSignificantValueDigits: 4 }, clock);
186 | recorder.recordValue(123);
187 | // when
188 | const histogram = new Int32Histogram(1, Number.MAX_SAFE_INTEGER, 3);
189 | currentTime = 51;
190 | recorder.getIntervalHistogramInto(histogram);
191 | // then
192 | expect(histogram.totalCount).toBe(1);
193 | expect(histogram.startTimeStampMsec).toBe(42);
194 | expect(histogram.endTimeStampMsec).toBe(51);
195 | });
196 |
197 | it("should reset values and timestamp", () => {
198 | // given
199 | let currentTime = 42;
200 | let clock = () => currentTime;
201 | const recorder = new Recorder({ numberOfSignificantValueDigits: 4 }, clock);
202 | recorder.recordValue(123);
203 | // when
204 | currentTime = 55;
205 | recorder.reset();
206 | const histogram = recorder.getIntervalHistogram();
207 | // then
208 | expect(histogram.totalCount).toBe(0);
209 | expect(histogram.startTimeStampMsec).toBe(55);
210 | });
211 | });
212 |
--------------------------------------------------------------------------------
/test_files/jHiccup-no-header-2.0.7S.logV2.hlog:
--------------------------------------------------------------------------------
1 | 0.127,1.007,2.769,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBEJISEuATEZMQ4uASkhIR4nrxg9v2lMaxhvMekILGZkKmcCAEf2CsI=
2 | 1.134,0.999,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWLj45FTExAT4pBSEBKa6UkAgBi1uM7xjfMMlwMDABAC0CCjM=
3 | 2.133,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE+Ph4OLgk5OSkeIS4+LgEeswIDo1+MbmdYNASYAA51CSo=
4 | 3.134,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBExPiEpITEFGTkRKSEeOR6FkCg1hTeMXvNYlHhYABQ5CTo=
5 | 4.135,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE2PiERBREpBREhER4+Hj4uvQAdrTlMBldYDDhYAAugCKk=
6 | 5.132,1.002,0.426,HISTFAAAAEF42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEWPhElOR4pARUpKTkpGQkxq2mMegZnGI0+MZuIcAEAHo8Jvw==
7 | 6.134,0.999,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIS4FITEhDiEJERE+GT6ZkhZGLbl7jEqrWHREmFgAIbAJMw==
8 | 7.133,0.999,0.459,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEhEtMQEBBTk5MQERCRkBEQEWlh9FJbg9jE+MS5ig1LhYmADkkCcE=
9 | 8.132,1.000,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIREgEOIQEuGT4xHg41Oo0pIqu8LYwVImwMfGBAAfkgkw
10 | 9.132,1.751,1551.892,HISTFAAAAJZ42pNpmSzMwMB0nQECmCCUnwIDA7ObwY4FDPYfYDJMXFxsbGwMbBwszDwsDDxsHFw6RWJMLJMZmcqBMJrJmskSiA2ZZJmkgRBCgmheIORGI1H5rEzMQAyDzFhY2EWRWUwMWCBxQtQQhAIWJiyAaEHyFbKwsLHAADYWAWmiFeKS5gACLsIEzdQICAgBIQShEfhFABXDF+M=
11 | 10.883,0.250,0.426,HISTFAAAAD142pNpmSzMwMAgxQABTBDKT4GBgdnNYMcCBvsPEBEeFi4mPg4WLhY2BjY2FhYOBSkpASEtoRA+NgDkCQZR
12 | 11.133,1.003,0.524,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBk2HgkZKREpEQUeGSEBAQ6xSYxhCnp7GJ02sWgJsbCwMgEAO0AJSQ==
13 | 12.136,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2AT4eCQURHgkuEREOHjERlSQhhWuMSV9Y7ERYWAAa4gko
14 | 13.133,0.998,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkRIR4RMRk5KQE+PgEhMRmzEjWZJ4whW1hMBNiYAB42CTA=
15 | 14.131,1.000,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBkWFhE5GT4FKQkRCR4ZCREpqwmMBhpHGG16WHx42JgYmAA6swk+
16 | 15.131,1.001,0.442,HISTFAAAAD542pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPMBkuMTEFHgklFRkRATkJERGdKgudfYwRTSwGalwAF2IJOw==
17 | 16.132,1.001,0.524,HISTFAAAAEZ42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPEBE2IQEFCQkpGREpHj4hKS6NU4z7GDMkuBoYDSYw2wiwMLEyAQBQ3wne
18 | 17.133,0.998,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2DjElIR4RHiExKQE5IT61iCodtXWMdn0sKVJMTAAekAk0
19 | 18.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBkWISERJSUJESklHhEJEREhqwZGLakPjDZdLBYCHCwAKOkJPg==
20 | 19.131,1.000,0.475,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUAk2HjkJBSk+Pi4BMT4xIQE9pxIluTOMPhtYbITY2JgAKLoJOQ==
21 | 20.131,1.004,0.475,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmPhEJOSEhDi4+ETEeASEhswIVi1+MFjtYvCRYGJgAIP8JNw==
22 | 21.135,0.999,0.492,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhk1AjINDRECAj4+Hi49LKS5CS2EGo1kXa4ANExMDEwAmOQil
23 | 22.134,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBFmHhE+MRExCTEZAS4RMQERvRI1hSuMTidY3KQ4mAAXhgks
24 | 23.131,1.004,0.508,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhotHSEBASEyMg09MQUSIT6tKS2YKY8gfFj8tJmYmJgAsowkz
25 | 24.135,0.998,0.492,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEBLjkhETEpET4BISEhCR6FsqAQFY1jjBoTWPQEOJiZAC2aCUY=
26 | 25.133,1.002,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBkuHh4BITEpMSEpLiE5AS6FoAgdpQuMJk9YzMRYmAAdngk2
27 | 26.135,0.998,0.508,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUAkOKSEJKTUJOT4+IQkeIT69LYwVCnIbGI0eMZtJsTAxMwEAQvkJyg==
28 | 27.133,0.998,0.442,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBE2CQUZFTkZOSURKQkRMT6NKYwhbYxaOocY/a4xSUmwAQA4pQpb
29 | 28.131,1.002,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGtFDcHIy0jDQUdPjENFZUzjNNYHCT4uBQkzJiYADIGCcY=
30 | 29.133,1.460,968.884,HISTFAAAAJZ42pNpmSzMwMDUwgABTBDKT4GBgdnNYMcCBvsPEBE5AwMDJSUFISk2ETYuAS6PQ0xSXCzsTEw7GZnKgdCTyZLJGog1maSZZIFYGkpLMnEz8QIhOolgcTKxAiEzmGRFYxMShbEYUCAalzRBsjSjARYmTIBNjDKFSIIsIMDGAgPYWJRJE1DIxQEEaAQHF2GCNDVsAE2dFJE=
31 | 30.593,0.541,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEFCxUNBRkFMTE+Pj4ZHgGHFYwGIkJcMiIpbEwMTAAdQQhJ
32 | 31.134,0.997,0.737,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEJGAHsYexqKaIAcPPRMVKTEhoR6mJUxqfBx8LFwCTOxM0kwAfR8KqA==
33 | 32.131,1.002,0.508,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEJKCDMcHJw8jOTUfNSEZGQuUb4x9GHxkJDg2hMA4WViYmAHWrC2k=
34 | 33.133,1.000,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGXGK8QHS09PRM9BRMxBa55jBOY03REhByE3DhYADicCkc=
35 | 34.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBE1NzsfJwMVEw0pFS0hOZm4FqYKPy2FAoUJjFIsTAA/mQql
36 | 35.131,1.000,0.459,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBERMy0jPTk5LRUFJQk1GamYdUzHGO0UxIrUljBKsbEwAQBKXgqU
37 | 36.131,1.001,0.557,HISTFAAAAEd42pNpmSzMwMCgygABTBDKT4GBgdnNYMcCBvsPEBExJzcNMyU5PRUpLSkJKYWwHqYWRjslkTKNC4wKHGwMTExArUwAi/IKnA==
38 | 37.132,1.002,0.442,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEFLRsVPQkTKTkhPT4ZBTm3V4yTGD20pFoYtZqYxESYAEjICok=
39 | 38.134,1.000,0.803,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNERM7Hwk3LRslMSkZMQExDLGQL0yTGIC2pKJ1VjCwcTJpMAFufCso=
40 | 39.134,0.997,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE5Oz8DPRsFORM5FQkNKaGCA8wtjCoSfBYSTYxCLEBtTABiWgor
41 | 40.131,1.000,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBExJQUNFTElFRUZBRUZDTGfJqYKHzmhHka5ZUwSQmwANK0J+g==
42 | 41.131,1.002,0.475,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE2Hj45PiEFGSU5EQkpKREJuVmMLYwaWk8YQyYwa3CxMTABAEOgCdQ=
43 | 42.133,1.000,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBk+Lg4+ER4hMT4hIT4lLh69OAOZZ4wOr1hCpFiYABjUCSY=
44 | 43.133,1.002,0.442,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBFmLgEJMTERHjEuCRERBSERoww5rRuMendYPFRYAA3tCTM=
45 | 44.135,0.998,0.590,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBk+FT0lJTktJSUjOTE1OQGpmnOMdnorGF3WMemxCTIBAEAhCnU=
46 | 45.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWMS0DIyMFOSsNPTEFMSGNA4x+LxidfOp0VjBKcLAAAECLCv4=
47 | 46.131,1.004,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEuMS0VEyMlLSkzGQUJOSkJj6RnjE56WxjNWpik2JgAO34KfQ==
48 | 47.135,0.996,0.475,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUgk2GR0ZOQkSAR4aLS0KKTyNtDqOWxjVGu2fMGlJMTEwANsIJvA==
49 | 48.131,1.950,1803.551,HISTFAAAAKF42pNpmSzMwMD0mQECmCCUnwIDA7ObwY4FDPYfICKsTExMLCysLCxsbEwMTAIsDHIsWTwsbNsZmcqZKpncmayZLIFYnUmWSRoMIbQkEy8TNxQjkwgWJxMrGDJDaews/KIMKBCNSytBZCYqYGHCBNjEiBckoJAFBNhYYADBwipIhkIC0lwcQIBGcHARJqigBkwKCQgICSAIFA75IlwAeB8ZpQ==
50 | 50.081,0.050,0.393,HISTFAAAADl42pNpmSzMwMAgxgABTBDKT4GBgdnNYMcCBvsPEBE2BiYWNiYWZiYGJiZmJg4OLiYuFiYWAMWGBSM=
51 | 50.131,1.001,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2Lj4VAQkuJT45KTkOKSExI68eRgeDvB2MfcxxckwAJD8JyA==
52 | 51.132,0.999,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2NgUFGSkNAQEeJSkuKSmxhAojhZADjKuYiyS4WAAlWgm/
53 | 52.131,1.002,0.557,HISTFAAAAER42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPUBkWPjEFGSMZKQMJJSEhPgkJiyodjZIHjB+YSvh4mBiYWJkAVc8KVw==
54 | 53.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2HjklJR0VPSUDHTUxJSkJs02MuxhtrLxKHjH6cbEAADjeCuw=
55 | 54.131,1.003,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkNPzMLIw0NLQ0pFTERCTGLT4wpQSVbGFcwynExAQA/uwsC
56 | 55.134,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPUBkWFTUjCy01BQ0VFRUJGSkJjRamiqA5jHmXGIV4ACoyCmo=
57 | 56.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUhk1FzsrAQElFQ0xCQkJOTEDnE6ObxwrGDsYuJjUODiYASN8KbA==
58 | 57.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPMBk5FT0JAzUNKTklKQ0FMaGUJ4wJFjcYk+4wqnAwMAEAQooK6Q==
59 | 58.131,1.002,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEuCRMNJwMlIzUtLR0ZMREZv6IHjFYGdUXLGE14WAA4OwsG
60 | 59.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBklExUdIwcdFRUlOTMZPhWXB4wBTssYsy4xKnGwAQA8bAry
61 | 60.131,1.000,0.524,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmIRcjPR0bFR0lDSk5KQkZpXlMXkF5qxh3MMqIcDIBADy8CoE=
62 | 61.131,1.000,26.083,HISTFAAAAF542pNpmSzMwMAQyAABTBDKT4GBgdnNYMcCBvsPMBkFHSMrCzEZLSUFCSkJOTmTf4xRQW2MYT8Y5diYdjIylTNVMrkzWTJZA7EmkzQYykJpSSZeJm4ghpAQFgATDg85
63 |
--------------------------------------------------------------------------------
/src/HistogramLogReader.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a TypeScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 | import { NO_TAG } from "./Histogram";
9 | import { decodeFromCompressedBase64 } from "./encoding";
10 | import Histogram, { BitBucketSize } from "./Histogram";
11 |
12 | const TAG_PREFIX = "Tag=";
13 | const TAG_PREFIX_LENGTH = "Tag=".length;
14 |
15 | /**
16 | * A histogram log reader.
17 | *
18 | * Histogram logs are used to capture full fidelity, per-time-interval
19 | * histograms of a recorded value.
20 | *
21 | * For example, a histogram log can be used to capture high fidelity
22 | * reaction-time logs for some measured system or subsystem component.
23 | * Such a log would capture a full reaction time histogram for each
24 | * logged interval, and could be used to later reconstruct a full
25 | * HdrHistogram of the measured reaction time behavior for any arbitrary
26 | * time range within the log, by adding [only] the relevant interval
27 | * histograms.
28 | *
Histogram log format:
29 | * A histogram log file consists of text lines. Lines beginning with
30 | * the "#" character are optional and treated as comments. Lines
31 | * containing the legend (starting with "Timestamp") are also optional
32 | * and ignored in parsing the histogram log. All other lines must
33 | * be valid interval description lines. Text fields are delimited by
34 | * commas, spaces.
35 | *
36 | * A valid interval description line contains an optional Tag=tagString
37 | * text field, followed by an interval description.
38 | *
39 | * A valid interval description must contain exactly four text fields:
40 | *
41 | * - StartTimestamp: The first field must contain a number parse-able as a Double value,
42 | * representing the start timestamp of the interval in seconds.
43 | * - intervalLength: The second field must contain a number parse-able as a Double value,
44 | * representing the length of the interval in seconds.
45 | * - Interval_Max: The third field must contain a number parse-able as a Double value,
46 | * which generally represents the maximum value of the interval histogram.
47 | * - Interval_Compressed_Histogram: The fourth field must contain a text field
48 | * parse-able as a Base64 text representation of a compressed HdrHistogram.
49 | *
50 | * The log file may contain an optional indication of a starting time. Starting time
51 | * is indicated using a special comments starting with "#[StartTime: " and followed
52 | * by a number parse-able as a double, representing the start time (in seconds)
53 | * that may be added to timestamps in the file to determine an absolute
54 | * timestamp (e.g. since the epoch) for each interval.
55 | */
56 | class HistogramLogReader {
57 | startTimeSec: number;
58 | baseTimeSec: number;
59 |
60 | lines: string[];
61 | currentLineIndex: number;
62 | bitBucketSize: BitBucketSize;
63 | useWebAssembly: boolean;
64 |
65 | constructor(
66 | logContent: string,
67 | bitBucketSize: BitBucketSize = 32,
68 | useWebAssembly: boolean = false
69 | ) {
70 | this.lines = splitLines(logContent);
71 | this.currentLineIndex = 0;
72 | this.bitBucketSize = bitBucketSize;
73 | this.useWebAssembly = useWebAssembly;
74 | }
75 |
76 | /**
77 | * Read the next interval histogram from the log. Returns a Histogram object if
78 | * an interval line was found, or null if not.
79 | * Upon encountering any unexpected format errors in reading the next interval
80 | * from the file, this method will return a null.
81 | * @return a DecodedInterval, or a null if no appropriate interval found
82 | */
83 | public nextIntervalHistogram(
84 | rangeStartTimeSec = 0,
85 | rangeEndTimeSec = Number.MAX_VALUE
86 | ): Histogram | null {
87 | while (this.currentLineIndex < this.lines.length) {
88 | const currentLine = this.lines[this.currentLineIndex];
89 | this.currentLineIndex++;
90 | if (currentLine.startsWith("#[StartTime:")) {
91 | this.parseStartTimeFromLine(currentLine);
92 | } else if (currentLine.startsWith("#[BaseTime:")) {
93 | this.parseBaseTimeFromLine(currentLine);
94 | } else if (
95 | currentLine.startsWith("#") ||
96 | currentLine.startsWith('"StartTimestamp"')
97 | ) {
98 | // skip legend & meta data for now
99 | } else if (currentLine.includes(",")) {
100 | const tokens = currentLine.split(",");
101 | const [firstToken] = tokens;
102 | let tag: string;
103 | if (firstToken.startsWith(TAG_PREFIX)) {
104 | tag = firstToken.substring(TAG_PREFIX_LENGTH);
105 | tokens.shift();
106 | } else {
107 | tag = NO_TAG;
108 | }
109 |
110 | const [
111 | rawLogTimeStampInSec,
112 | rawIntervalLengthSec,
113 | ,
114 | base64Histogram,
115 | ] = tokens;
116 | const logTimeStampInSec = Number.parseFloat(rawLogTimeStampInSec);
117 |
118 | if (!this.baseTimeSec) {
119 | // No explicit base time noted. Deduce from 1st observed time (compared to start time):
120 | if (logTimeStampInSec < this.startTimeSec - 365 * 24 * 3600.0) {
121 | // Criteria Note: if log timestamp is more than a year in the past (compared to
122 | // StartTime), we assume that timestamps in the log are not absolute
123 | this.baseTimeSec = this.startTimeSec;
124 | } else {
125 | // Timestamps are absolute
126 | this.baseTimeSec = 0.0;
127 | }
128 | }
129 |
130 | if (rangeEndTimeSec < logTimeStampInSec) {
131 | return null;
132 | }
133 | if (logTimeStampInSec < rangeStartTimeSec) {
134 | continue;
135 | }
136 | const histogram = decodeFromCompressedBase64(
137 | base64Histogram,
138 | this.bitBucketSize,
139 | this.useWebAssembly
140 | );
141 | histogram.startTimeStampMsec =
142 | (this.baseTimeSec + logTimeStampInSec) * 1000;
143 | const intervalLengthSec = Number.parseFloat(rawIntervalLengthSec);
144 | histogram.endTimeStampMsec =
145 | (this.baseTimeSec + logTimeStampInSec + intervalLengthSec) * 1000;
146 |
147 | histogram.tag = tag;
148 |
149 | return histogram;
150 | }
151 | }
152 | return null;
153 | }
154 |
155 | private parseStartTimeFromLine(line: string) {
156 | this.startTimeSec = Number.parseFloat(line.split(" ")[1]);
157 | }
158 |
159 | private parseBaseTimeFromLine(line: string) {
160 | this.baseTimeSec = Number.parseFloat(line.split(" ")[1]);
161 | }
162 | }
163 |
164 | const splitLines = (logContent: string) => logContent.split(/\r\n|\r|\n/g);
165 |
166 | const shouldIncludeNoTag = (lines: string[]) =>
167 | lines.find(
168 | (line) =>
169 | !line.startsWith("#") &&
170 | !line.startsWith('"') &&
171 | !line.startsWith(TAG_PREFIX) &&
172 | line.includes(",")
173 | );
174 |
175 | export const listTags = (content: string) => {
176 | const lines = splitLines(content);
177 | const tags = lines
178 | .filter((line) => line.includes(",") && line.startsWith(TAG_PREFIX))
179 | .map((line) => line.substring(TAG_PREFIX_LENGTH, line.indexOf(",")));
180 | const tagsWithoutDuplicates = new Set(tags);
181 | const result = Array.from(tagsWithoutDuplicates);
182 | if (shouldIncludeNoTag(lines)) {
183 | result.unshift("NO TAG");
184 | }
185 | return result;
186 | };
187 |
188 | export default HistogramLogReader;
189 |
--------------------------------------------------------------------------------
/assembly/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a AssemblyScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 |
9 | import Histogram from "./Histogram";
10 | import {
11 | Uint8Storage,
12 | Uint16Storage,
13 | Uint32Storage,
14 | Uint64Storage,
15 | } from "./Histogram";
16 | import { decodeFromByteBuffer } from "./encoding";
17 | import ByteBuffer from "./ByteBuffer";
18 | import { PackedArray } from "./packedarray/PackedArray";
19 |
20 | export const UINT8ARRAY_ID = idof();
21 |
22 | class HistogramAdapter {
23 | private _histogram: Histogram;
24 | constructor(
25 | lowestDiscernibleValue: f64,
26 | highestTrackableValue: f64,
27 | numberOfSignificantValueDigits: f64,
28 | autoResize: boolean,
29 | histogram: Histogram | null = null
30 | ) {
31 | if (histogram) {
32 | this._histogram = histogram;
33 | } else {
34 | this._histogram = new Histogram(
35 | lowestDiscernibleValue,
36 | highestTrackableValue,
37 | numberOfSignificantValueDigits
38 | );
39 | this._histogram.autoResize = autoResize;
40 | }
41 | }
42 |
43 | public get autoResize(): boolean {
44 | return this._histogram.autoResize;
45 | }
46 |
47 | public set autoResize(resize: boolean) {
48 | this._histogram.autoResize = resize;
49 | }
50 |
51 | public get highestTrackableValue(): f64 {
52 | return this._histogram.highestTrackableValue;
53 | }
54 |
55 | public set highestTrackableValue(value: f64) {
56 | this._histogram.highestTrackableValue = value;
57 | }
58 |
59 | public get startTimeStampMsec(): f64 {
60 | return this._histogram.startTimeStampMsec;
61 | }
62 |
63 | public set startTimeStampMsec(value: f64) {
64 | this._histogram.startTimeStampMsec = value;
65 | }
66 |
67 | public get endTimeStampMsec(): f64 {
68 | return this._histogram.endTimeStampMsec;
69 | }
70 |
71 | public set endTimeStampMsec(value: f64) {
72 | this._histogram.endTimeStampMsec = value;
73 | }
74 |
75 | public get minNonZeroValue(): f64 {
76 | return this._histogram.minNonZeroValue;
77 | }
78 |
79 | public get maxValue(): f64 {
80 | return this._histogram.maxValue;
81 | }
82 |
83 | public get totalCount(): f64 {
84 | return this._histogram.totalCount;
85 | }
86 |
87 | public get stdDeviation(): f64 {
88 | return this._histogram.getStdDeviation();
89 | }
90 |
91 | public get mean(): f64 {
92 | return this._histogram.getMean();
93 | }
94 |
95 | public get estimatedFootprintInBytes(): f64 {
96 | return (
97 | (offsetof>() +
98 | this._histogram.estimatedFootprintInBytes)
99 | );
100 | }
101 |
102 | recordValue(value: f64): void {
103 | this._histogram.recordSingleValue(value);
104 | }
105 |
106 | recordValueWithCount(value: f64, count: f64): void {
107 | this._histogram.recordCountAtValue(count, value);
108 | }
109 |
110 | recordValueWithExpectedInterval(
111 | value: f64,
112 | expectedIntervalBetweenValueSamples: f64
113 | ): void {
114 | this._histogram.recordSingleValueWithExpectedInterval(
115 | value,
116 | expectedIntervalBetweenValueSamples
117 | );
118 | }
119 |
120 | getValueAtPercentile(percentile: f64): f64 {
121 | return this._histogram.getValueAtPercentile(percentile);
122 | }
123 |
124 | outputPercentileDistribution(
125 | percentileTicksPerHalfDistance: f64,
126 | outputValueUnitScalingRatio: f64
127 | ): string {
128 | return this._histogram.outputPercentileDistribution(
129 | percentileTicksPerHalfDistance,
130 | outputValueUnitScalingRatio
131 | );
132 | }
133 |
134 | copyCorrectedForCoordinatedOmission(
135 | expectedIntervalBetweenValueSamples: f64
136 | ): HistogramAdapter {
137 | const copy = this._histogram.copyCorrectedForCoordinatedOmission(
138 | expectedIntervalBetweenValueSamples
139 | );
140 |
141 | return new HistogramAdapter(0, 0, 0, false, copy);
142 | }
143 |
144 | addHistogram8(otherHistogram: Histogram8): void {
145 | this._histogram.add(otherHistogram._histogram);
146 | }
147 | addHistogram16(otherHistogram: Histogram16): void {
148 | this._histogram.add(otherHistogram._histogram);
149 | }
150 | addHistogram32(otherHistogram: Histogram32): void {
151 | this._histogram.add(otherHistogram._histogram);
152 | }
153 | addHistogram64(otherHistogram: Histogram64): void {
154 | this._histogram.add(otherHistogram._histogram);
155 | }
156 | addPackedHistogram(otherHistogram: PackedHistogram): void {
157 | this._histogram.add(otherHistogram._histogram);
158 | }
159 |
160 | subtractHistogram8(otherHistogram: Histogram8): void {
161 | this._histogram.subtract(otherHistogram._histogram);
162 | }
163 | subtractHistogram16(otherHistogram: Histogram16): void {
164 | this._histogram.subtract(otherHistogram._histogram);
165 | }
166 | subtractHistogram32(otherHistogram: Histogram32): void {
167 | this._histogram.subtract(otherHistogram._histogram);
168 | }
169 | subtractHistogram64(otherHistogram: Histogram64): void {
170 | this._histogram.subtract(otherHistogram._histogram);
171 | }
172 | subtractPackedHistogram(otherHistogram: PackedHistogram): void {
173 | this._histogram.subtract(otherHistogram._histogram);
174 | }
175 |
176 | encode(): Uint8Array {
177 | return this._histogram.encode();
178 | }
179 |
180 | reset(): void {
181 | this._histogram.reset();
182 | }
183 | }
184 |
185 | export class Histogram8 extends HistogramAdapter {}
186 | export class Histogram16 extends HistogramAdapter {}
187 | export class Histogram32 extends HistogramAdapter {}
188 | export class Histogram64 extends HistogramAdapter {}
189 | export class PackedHistogram extends HistogramAdapter {}
190 |
191 | function decodeHistogram(
192 | data: Uint8Array,
193 | minBarForHighestTrackableValue: f64
194 | ): HistogramAdapter {
195 | const buffer = new ByteBuffer(data);
196 | const histogram = decodeFromByteBuffer(
197 | buffer,
198 | minBarForHighestTrackableValue
199 | );
200 | return new HistogramAdapter(0, 0, 0, false, histogram);
201 | }
202 |
203 | export function decodeHistogram8(
204 | data: Uint8Array,
205 | minBarForHighestTrackableValue: f64
206 | ): HistogramAdapter {
207 | return decodeHistogram(
208 | data,
209 | minBarForHighestTrackableValue
210 | );
211 | }
212 | export function decodeHistogram16(
213 | data: Uint8Array,
214 | minBarForHighestTrackableValue: f64
215 | ): HistogramAdapter {
216 | return decodeHistogram(
217 | data,
218 | minBarForHighestTrackableValue
219 | );
220 | }
221 | export function decodeHistogram32(
222 | data: Uint8Array,
223 | minBarForHighestTrackableValue: f64
224 | ): HistogramAdapter {
225 | return decodeHistogram(
226 | data,
227 | minBarForHighestTrackableValue
228 | );
229 | }
230 | export function decodeHistogram64(
231 | data: Uint8Array,
232 | minBarForHighestTrackableValue: f64
233 | ): HistogramAdapter {
234 | return decodeHistogram(
235 | data,
236 | minBarForHighestTrackableValue
237 | );
238 | }
239 | export function decodePackedHistogram(
240 | data: Uint8Array,
241 | minBarForHighestTrackableValue: f64
242 | ): HistogramAdapter {
243 | return decodeHistogram(
244 | data,
245 | minBarForHighestTrackableValue
246 | );
247 | }
248 |
--------------------------------------------------------------------------------
/test_files/jHiccup-2.0.7S.logV2.hlog:
--------------------------------------------------------------------------------
1 | #[Logged with jHiccup version 2.0.7-SNAPSHOT]
2 | #[Histogram log format version 1.2]
3 | #[StartTime: 1441812279.474 (seconds since epoch), Wed Sep 09 08:24:39 PDT 2015]
4 | "StartTimestamp","Interval_Length","Interval_Max","Interval_Compressed_Histogram"
5 | 0.127,1.007,2.769,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBEJISEuATEZMQ4uASkhIR4nrxg9v2lMaxhvMekILGZkKmcCAEf2CsI=
6 | 1.134,0.999,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWLj45FTExAT4pBSEBKa6UkAgBi1uM7xjfMMlwMDABAC0CCjM=
7 | 2.133,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE+Ph4OLgk5OSkeIS4+LgEeswIDo1+MbmdYNASYAA51CSo=
8 | 3.134,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBExPiEpITEFGTkRKSEeOR6FkCg1hTeMXvNYlHhYABQ5CTo=
9 | 4.135,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE2PiERBREpBREhER4+Hj4uvQAdrTlMBldYDDhYAAugCKk=
10 | 5.132,1.002,0.426,HISTFAAAAEF42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEWPhElOR4pARUpKTkpGQkxq2mMegZnGI0+MZuIcAEAHo8Jvw==
11 | 6.134,0.999,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIS4FITEhDiEJERE+GT6ZkhZGLbl7jEqrWHREmFgAIbAJMw==
12 | 7.133,0.999,0.459,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEhEtMQEBBTk5MQERCRkBEQEWlh9FJbg9jE+MS5ig1LhYmADkkCcE=
13 | 8.132,1.000,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIREgEOIQEuGT4xHg41Oo0pIqu8LYwVImwMfGBAAfkgkw
14 | 9.132,1.751,1551.892,HISTFAAAAJZ42pNpmSzMwMB0nQECmCCUnwIDA7ObwY4FDPYfYDJMXFxsbGwMbBwszDwsDDxsHFw6RWJMLJMZmcqBMJrJmskSiA2ZZJmkgRBCgmheIORGI1H5rEzMQAyDzFhY2EWRWUwMWCBxQtQQhAIWJiyAaEHyFbKwsLHAADYWAWmiFeKS5gACLsIEzdQICAgBIQShEfhFABXDF+M=
15 | 10.883,0.250,0.426,HISTFAAAAD142pNpmSzMwMAgxQABTBDKT4GBgdnNYMcCBvsPEBEeFi4mPg4WLhY2BjY2FhYOBSkpASEtoRA+NgDkCQZR
16 | 11.133,1.003,0.524,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBk2HgkZKREpEQUeGSEBAQ6xSYxhCnp7GJ02sWgJsbCwMgEAO0AJSQ==
17 | 12.136,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2AT4eCQURHgkuEREOHjERlSQhhWuMSV9Y7ERYWAAa4gko
18 | 13.133,0.998,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkRIR4RMRk5KQE+PgEhMRmzEjWZJ4whW1hMBNiYAB42CTA=
19 | 14.131,1.000,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBkWFhE5GT4FKQkRCR4ZCREpqwmMBhpHGG16WHx42JgYmAA6swk+
20 | 15.131,1.001,0.442,HISTFAAAAD542pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPMBkuMTEFHgklFRkRATkJERGdKgudfYwRTSwGalwAF2IJOw==
21 | 16.132,1.001,0.524,HISTFAAAAEZ42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPEBE2IQEFCQkpGREpHj4hKS6NU4z7GDMkuBoYDSYw2wiwMLEyAQBQ3wne
22 | 17.133,0.998,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2DjElIR4RHiExKQE5IT61iCodtXWMdn0sKVJMTAAekAk0
23 | 18.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBkWISERJSUJESklHhEJEREhqwZGLakPjDZdLBYCHCwAKOkJPg==
24 | 19.131,1.000,0.475,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUAk2HjkJBSk+Pi4BMT4xIQE9pxIluTOMPhtYbITY2JgAKLoJOQ==
25 | 20.131,1.004,0.475,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmPhEJOSEhDi4+ETEeASEhswIVi1+MFjtYvCRYGJgAIP8JNw==
26 | 21.135,0.999,0.492,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhk1AjINDRECAj4+Hi49LKS5CS2EGo1kXa4ANExMDEwAmOQil
27 | 22.134,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBFmHhE+MRExCTEZAS4RMQERvRI1hSuMTidY3KQ4mAAXhgks
28 | 23.131,1.004,0.508,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhotHSEBASEyMg09MQUSIT6tKS2YKY8gfFj8tJmYmJgAsowkz
29 | 24.135,0.998,0.492,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEBLjkhETEpET4BISEhCR6FsqAQFY1jjBoTWPQEOJiZAC2aCUY=
30 | 25.133,1.002,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBkuHh4BITEpMSEpLiE5AS6FoAgdpQuMJk9YzMRYmAAdngk2
31 | 26.135,0.998,0.508,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUAkOKSEJKTUJOT4+IQkeIT69LYwVCnIbGI0eMZtJsTAxMwEAQvkJyg==
32 | 27.133,0.998,0.442,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBE2CQUZFTkZOSURKQkRMT6NKYwhbYxaOocY/a4xSUmwAQA4pQpb
33 | 28.131,1.002,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGtFDcHIy0jDQUdPjENFZUzjNNYHCT4uBQkzJiYADIGCcY=
34 | 29.133,1.460,968.884,HISTFAAAAJZ42pNpmSzMwMDUwgABTBDKT4GBgdnNYMcCBvsPEBE5AwMDJSUFISk2ETYuAS6PQ0xSXCzsTEw7GZnKgdCTyZLJGog1maSZZIFYGkpLMnEz8QIhOolgcTKxAiEzmGRFYxMShbEYUCAalzRBsjSjARYmTIBNjDKFSIIsIMDGAgPYWJRJE1DIxQEEaAQHF2GCNDVsAE2dFJE=
35 | 30.593,0.541,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEFCxUNBRkFMTE+Pj4ZHgGHFYwGIkJcMiIpbEwMTAAdQQhJ
36 | 31.134,0.997,0.737,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEJGAHsYexqKaIAcPPRMVKTEhoR6mJUxqfBx8LFwCTOxM0kwAfR8KqA==
37 | 32.131,1.002,0.508,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEJKCDMcHJw8jOTUfNSEZGQuUb4x9GHxkJDg2hMA4WViYmAHWrC2k=
38 | 33.133,1.000,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGXGK8QHS09PRM9BRMxBa55jBOY03REhByE3DhYADicCkc=
39 | 34.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBE1NzsfJwMVEw0pFS0hOZm4FqYKPy2FAoUJjFIsTAA/mQql
40 | 35.131,1.000,0.459,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBERMy0jPTk5LRUFJQk1GamYdUzHGO0UxIrUljBKsbEwAQBKXgqU
41 | 36.131,1.001,0.557,HISTFAAAAEd42pNpmSzMwMCgygABTBDKT4GBgdnNYMcCBvsPEBExJzcNMyU5PRUpLSkJKYWwHqYWRjslkTKNC4wKHGwMTExArUwAi/IKnA==
42 | 37.132,1.002,0.442,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEFLRsVPQkTKTkhPT4ZBTm3V4yTGD20pFoYtZqYxESYAEjICok=
43 | 38.134,1.000,0.803,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNERM7Hwk3LRslMSkZMQExDLGQL0yTGIC2pKJ1VjCwcTJpMAFufCso=
44 | 39.134,0.997,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE5Oz8DPRsFORM5FQkNKaGCA8wtjCoSfBYSTYxCLEBtTABiWgor
45 | 40.131,1.000,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBExJQUNFTElFRUZBRUZDTGfJqYKHzmhHka5ZUwSQmwANK0J+g==
46 | 41.131,1.002,0.475,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE2Hj45PiEFGSU5EQkpKREJuVmMLYwaWk8YQyYwa3CxMTABAEOgCdQ=
47 | 42.133,1.000,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBk+Lg4+ER4hMT4hIT4lLh69OAOZZ4wOr1hCpFiYABjUCSY=
48 | 43.133,1.002,0.442,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBFmLgEJMTERHjEuCRERBSERoww5rRuMendYPFRYAA3tCTM=
49 | 44.135,0.998,0.590,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBk+FT0lJTktJSUjOTE1OQGpmnOMdnorGF3WMemxCTIBAEAhCnU=
50 | 45.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWMS0DIyMFOSsNPTEFMSGNA4x+LxidfOp0VjBKcLAAAECLCv4=
51 | 46.131,1.004,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEuMS0VEyMlLSkzGQUJOSkJj6RnjE56WxjNWpik2JgAO34KfQ==
52 | 47.135,0.996,0.475,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUgk2GR0ZOQkSAR4aLS0KKTyNtDqOWxjVGu2fMGlJMTEwANsIJvA==
53 | 48.131,1.950,1803.551,HISTFAAAAKF42pNpmSzMwMD0mQECmCCUnwIDA7ObwY4FDPYfICKsTExMLCysLCxsbEwMTAIsDHIsWTwsbNsZmcqZKpncmayZLIFYnUmWSRoMIbQkEy8TNxQjkwgWJxMrGDJDaews/KIMKBCNSytBZCYqYGHCBNjEiBckoJAFBNhYYADBwipIhkIC0lwcQIBGcHARJqigBkwKCQgICSAIFA75IlwAeB8ZpQ==
54 | 50.081,0.050,0.393,HISTFAAAADl42pNpmSzMwMAgxgABTBDKT4GBgdnNYMcCBvsPEBE2BiYWNiYWZiYGJiZmJg4OLiYuFiYWAMWGBSM=
55 | 50.131,1.001,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2Lj4VAQkuJT45KTkOKSExI68eRgeDvB2MfcxxckwAJD8JyA==
56 | 51.132,0.999,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2NgUFGSkNAQEeJSkuKSmxhAojhZADjKuYiyS4WAAlWgm/
57 | 52.131,1.002,0.557,HISTFAAAAER42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPUBkWPjEFGSMZKQMJJSEhPgkJiyodjZIHjB+YSvh4mBiYWJkAVc8KVw==
58 | 53.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2HjklJR0VPSUDHTUxJSkJs02MuxhtrLxKHjH6cbEAADjeCuw=
59 | 54.131,1.003,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkNPzMLIw0NLQ0pFTERCTGLT4wpQSVbGFcwynExAQA/uwsC
60 | 55.134,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPUBkWFTUjCy01BQ0VFRUJGSkJjRamiqA5jHmXGIV4ACoyCmo=
61 | 56.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUhk1FzsrAQElFQ0xCQkJOTEDnE6ObxwrGDsYuJjUODiYASN8KbA==
62 | 57.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPMBk5FT0JAzUNKTklKQ0FMaGUJ4wJFjcYk+4wqnAwMAEAQooK6Q==
63 | 58.131,1.002,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEuCRMNJwMlIzUtLR0ZMREZv6IHjFYGdUXLGE14WAA4OwsG
64 | 59.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBklExUdIwcdFRUlOTMZPhWXB4wBTssYsy4xKnGwAQA8bAry
65 | 60.131,1.000,0.524,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmIRcjPR0bFR0lDSk5KQkZpXlMXkF5qxh3MMqIcDIBADy8CoE=
66 | 61.131,1.000,26.083,HISTFAAAAF542pNpmSzMwMAQyAABTBDKT4GBgdnNYMcCBvsPMBkFHSMrCzEZLSUFCSkJOTmTf4xRQW2MYT8Y5diYdjIylTNVMrkzWTJZA7EmkzQYykJpSSZeJm4ghpAQFgATDg85
67 |
--------------------------------------------------------------------------------
/test_files/jHiccup-with-basetime-2.0.7S.logV2.hlog:
--------------------------------------------------------------------------------
1 | #[Logged with jHiccup version 2.0.7-SNAPSHOT]
2 | #[Histogram log format version 1.2]
3 | #[StartTime: 1441812279.474 (seconds since epoch), Wed Sep 09 08:24:39 PDT 2015]
4 | #[BaseTime: 1441812123.123 (seconds since epoch), Wed Sep 09 08:24:39 PDT 2015]
5 | "StartTimestamp","Interval_Length","Interval_Max","Interval_Compressed_Histogram"
6 | 0.127,1.007,2.769,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBEJISEuATEZMQ4uASkhIR4nrxg9v2lMaxhvMekILGZkKmcCAEf2CsI=
7 | 1.134,0.999,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWLj45FTExAT4pBSEBKa6UkAgBi1uM7xjfMMlwMDABAC0CCjM=
8 | 2.133,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE+Ph4OLgk5OSkeIS4+LgEeswIDo1+MbmdYNASYAA51CSo=
9 | 3.134,1.001,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBExPiEpITEFGTkRKSEeOR6FkCg1hTeMXvNYlHhYABQ5CTo=
10 | 4.135,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBE2PiERBREpBREhER4+Hj4uvQAdrTlMBldYDDhYAAugCKk=
11 | 5.132,1.002,0.426,HISTFAAAAEF42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEWPhElOR4pARUpKTkpGQkxq2mMegZnGI0+MZuIcAEAHo8Jvw==
12 | 6.134,0.999,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIS4FITEhDiEJERE+GT6ZkhZGLbl7jEqrWHREmFgAIbAJMw==
13 | 7.133,0.999,0.459,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEhEtMQEBBTk5MQERCRkBEQEWlh9FJbg9jE+MS5ig1LhYmADkkCcE=
14 | 8.132,1.000,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWIREgEOIQEuGT4xHg41Oo0pIqu8LYwVImwMfGBAAfkgkw
15 | 9.132,1.751,1551.892,HISTFAAAAJZ42pNpmSzMwMB0nQECmCCUnwIDA7ObwY4FDPYfYDJMXFxsbGwMbBwszDwsDDxsHFw6RWJMLJMZmcqBMJrJmskSiA2ZZJmkgRBCgmheIORGI1H5rEzMQAyDzFhY2EWRWUwMWCBxQtQQhAIWJiyAaEHyFbKwsLHAADYWAWmiFeKS5gACLsIEzdQICAgBIQShEfhFABXDF+M=
16 | 10.883,0.250,0.426,HISTFAAAAD142pNpmSzMwMAgxQABTBDKT4GBgdnNYMcCBvsPEBEeFi4mPg4WLhY2BjY2FhYOBSkpASEtoRA+NgDkCQZR
17 | 11.133,1.003,0.524,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBk2HgkZKREpEQUeGSEBAQ6xSYxhCnp7GJ02sWgJsbCwMgEAO0AJSQ==
18 | 12.136,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2AT4eCQURHgkuEREOHjERlSQhhWuMSV9Y7ERYWAAa4gko
19 | 13.133,0.998,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkRIR4RMRk5KQE+PgEhMRmzEjWZJ4whW1hMBNiYAB42CTA=
20 | 14.131,1.000,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUBkWFhE5GT4FKQkRCR4ZCREpqwmMBhpHGG16WHx42JgYmAA6swk+
21 | 15.131,1.001,0.442,HISTFAAAAD542pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPMBkuMTEFHgklFRkRATkJERGdKgudfYwRTSwGalwAF2IJOw==
22 | 16.132,1.001,0.524,HISTFAAAAEZ42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPEBE2IQEFCQkpGREpHj4hKS6NU4z7GDMkuBoYDSYw2wiwMLEyAQBQ3wne
23 | 17.133,0.998,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2DjElIR4RHiExKQE5IT61iCodtXWMdn0sKVJMTAAekAk0
24 | 18.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBkWISERJSUJESklHhEJEREhqwZGLakPjDZdLBYCHCwAKOkJPg==
25 | 19.131,1.000,0.475,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUAk2HjkJBSk+Pi4BMT4xIQE9pxIluTOMPhtYbITY2JgAKLoJOQ==
26 | 20.131,1.004,0.475,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmPhEJOSEhDi4+ETEeASEhswIVi1+MFjtYvCRYGJgAIP8JNw==
27 | 21.135,0.999,0.492,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhk1AjINDRECAj4+Hi49LKS5CS2EGo1kXa4ANExMDEwAmOQil
28 | 22.134,0.997,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBFmHhE+MRExCTEZAS4RMQERvRI1hSuMTidY3KQ4mAAXhgks
29 | 23.131,1.004,0.508,HISTFAAAAEB42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNMhotHSEBASEyMg09MQUSIT6tKS2YKY8gfFj8tJmYmJgAsowkz
30 | 24.135,0.998,0.492,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEBLjkhETEpET4BISEhCR6FsqAQFY1jjBoTWPQEOJiZAC2aCUY=
31 | 25.133,1.002,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBkuHh4BITEpMSEpLiE5AS6FoAgdpQuMJk9YzMRYmAAdngk2
32 | 26.135,0.998,0.508,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPUAkOKSEJKTUJOT4+IQkeIT69LYwVCnIbGI0eMZtJsTAxMwEAQvkJyg==
33 | 27.133,0.998,0.442,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBE2CQUZFTkZOSURKQkRMT6NKYwhbYxaOocY/a4xSUmwAQA4pQpb
34 | 28.131,1.002,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGtFDcHIy0jDQUdPjENFZUzjNNYHCT4uBQkzJiYADIGCcY=
35 | 29.133,1.460,968.884,HISTFAAAAJZ42pNpmSzMwMDUwgABTBDKT4GBgdnNYMcCBvsPEBE5AwMDJSUFISk2ETYuAS6PQ0xSXCzsTEw7GZnKgdCTyZLJGog1maSZZIFYGkpLMnEz8QIhOolgcTKxAiEzmGRFYxMShbEYUCAalzRBsjSjARYmTIBNjDKFSIIsIMDGAgPYWJRJE1DIxQEEaAQHF2GCNDVsAE2dFJE=
36 | 30.593,0.541,0.459,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEFCxUNBRkFMTE+Pj4ZHgGHFYwGIkJcMiIpbEwMTAAdQQhJ
37 | 31.134,0.997,0.737,HISTFAAAAER42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEJGAHsYexqKaIAcPPRMVKTEhoR6mJUxqfBx8LFwCTOxM0kwAfR8KqA==
38 | 32.131,1.002,0.508,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNEJKCDMcHJw8jOTUfNSEZGQuUb4x9GHxkJDg2hMA4WViYmAHWrC2k=
39 | 33.133,1.000,0.426,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBGXGK8QHS09PRM9BRMxBa55jBOY03REhByE3DhYADicCkc=
40 | 34.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBE1NzsfJwMVEw0pFS0hOZm4FqYKPy2FAoUJjFIsTAA/mQql
41 | 35.131,1.000,0.459,HISTFAAAAEN42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBERMy0jPTk5LRUFJQk1GamYdUzHGO0UxIrUljBKsbEwAQBKXgqU
42 | 36.131,1.001,0.557,HISTFAAAAEd42pNpmSzMwMCgygABTBDKT4GBgdnNYMcCBvsPEBExJzcNMyU5PRUpLSkJKYWwHqYWRjslkTKNC4wKHGwMTExArUwAi/IKnA==
43 | 37.132,1.002,0.442,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPEBEFLRsVPQkTKTkhPT4ZBTm3V4yTGD20pFoYtZqYxESYAEjICok=
44 | 38.134,1.000,0.803,HISTFAAAAEJ42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNERM7Hwk3LRslMSkZMQExDLGQL0yTGIC2pKJ1VjCwcTJpMAFufCso=
45 | 39.134,0.997,0.492,HISTFAAAAEN42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE5Oz8DPRsFORM5FQkNKaGCA8wtjCoSfBYSTYxCLEBtTABiWgor
46 | 40.131,1.000,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBExJQUNFTElFRUZBRUZDTGfJqYKHzmhHka5ZUwSQmwANK0J+g==
47 | 41.131,1.002,0.475,HISTFAAAAEV42pNpmSzMwMCgyAABTBDKT4GBgdnNYMcCBvsPEBE2Hj45PiEFGSU5EQkpKREJuVmMLYwaWk8YQyYwa3CxMTABAEOgCdQ=
48 | 42.133,1.000,0.459,HISTFAAAAD942pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBk+Lg4+ER4hMT4hIT4lLh69OAOZZ4wOr1hCpFiYABjUCSY=
49 | 43.133,1.002,0.442,HISTFAAAAD942pNpmSzMwMAgwwABTBDKT4GBgdnNYMcCBvsPEBFmLgEJMTERHjEuCRERBSERoww5rRuMendYPFRYAA3tCTM=
50 | 44.135,0.998,0.590,HISTFAAAAEJ42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPUBk+FT0lJTktJSUjOTE1OQGpmnOMdnorGF3WMemxCTIBAEAhCnU=
51 | 45.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEWMS0DIyMFOSsNPTEFMSGNA4x+LxidfOp0VjBKcLAAAECLCv4=
52 | 46.131,1.004,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBEuMS0VEyMlLSkzGQUJOSkJj6RnjE56WxjNWpik2JgAO34KfQ==
53 | 47.135,0.996,0.475,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUgk2GR0ZOQkSAR4aLS0KKTyNtDqOWxjVGu2fMGlJMTEwANsIJvA==
54 | 48.131,1.950,1803.551,HISTFAAAAKF42pNpmSzMwMD0mQECmCCUnwIDA7ObwY4FDPYfICKsTExMLCysLCxsbEwMTAIsDHIsWTwsbNsZmcqZKpncmayZLIFYnUmWSRoMIbQkEy8TNxQjkwgWJxMrGDJDaews/KIMKBCNSytBZCYqYGHCBNjEiBckoJAFBNhYYADBwipIhkIC0lwcQIBGcHARJqigBkwKCQgICSAIFA75IlwAeB8ZpQ==
55 | 50.081,0.050,0.393,HISTFAAAADl42pNpmSzMwMAgxgABTBDKT4GBgdnNYMcCBvsPEBE2BiYWNiYWZiYGJiZmJg4OLiYuFiYWAMWGBSM=
56 | 50.131,1.001,0.442,HISTFAAAAEF42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2Lj4VAQkuJT45KTkOKSExI68eRgeDvB2MfcxxckwAJD8JyA==
57 | 51.132,0.999,0.459,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPUBk2NgUFGSkNAQEeJSkuKSmxhAojhZADjKuYiyS4WAAlWgm/
58 | 52.131,1.002,0.557,HISTFAAAAER42pNpmSzMwMCgxAABTBDKT4GBgdnNYMcCBvsPUBkWPjEFGSMZKQMJJSEhPgkJiyodjZIHjB+YSvh4mBiYWJkAVc8KVw==
59 | 53.133,0.998,0.442,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBE2HjklJR0VPSUDHTUxJSkJs02MuxhtrLxKHjH6cbEAADjeCuw=
60 | 54.131,1.003,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBkNPzMLIw0NLQ0pFTERCTGLT4wpQSVbGFcwynExAQA/uwsC
61 | 55.134,0.997,0.426,HISTFAAAAD942pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPUBkWFTUjCy01BQ0VFRUJGSkJjRamiqA5jHmXGIV4ACoyCmo=
62 | 56.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMCgwAABTBDKD8hndjPYsYDB/gNUhk1FzsrAQElFQ0xCQkJOTEDnE6ObxwrGDsYuJjUODiYASN8KbA==
63 | 57.131,1.000,0.459,HISTFAAAAEF42pNpmSzMwMAgzwABTBDKT4GBgdnNYMcCBvsPMBk5FT0JAzUNKTklKQ0FMaGUJ4wJFjcYk+4wqnAwMAEAQooK6Q==
64 | 58.131,1.002,0.442,HISTFAAAAEB42pNpmSzMwMAgywABTBDKT4GBgdnNYMcCBvsPEBEuCRMNJwMlIzUtLR0ZMREZv6IHjFYGdUXLGE14WAA4OwsG
65 | 59.133,0.998,0.442,HISTFAAAAEB42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPMBklExUdIwcdFRUlOTMZPhWXB4wBTssYsy4xKnGwAQA8bAry
66 | 60.131,1.000,0.524,HISTFAAAAEJ42pNpmSzMwMAgxwABTBDKT4GBgdnNYMcCBvsPEBFmIRcjPR0bFR0lDSk5KQkZpXlMXkF5qxh3MMqIcDIBADy8CoE=
67 | 61.131,1.000,26.083,HISTFAAAAF542pNpmSzMwMAQyAABTBDKT4GBgdnNYMcCBvsPMBkFHSMrCzEZLSUFCSkJOTmTf4xRQW2MYT8Y5diYdjIylTNVMrkzWTJZA7EmkzQYykJpSSZeJm4ghpAQFgATDg85
68 |
--------------------------------------------------------------------------------
/src/packedarray/PackedArray.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a TypeScript port of the original Java version, which was written by
3 | * Gil Tene as described in
4 | * https://github.com/HdrHistogram/HdrHistogram
5 | * and released to the public domain, as explained at
6 | * http://creativecommons.org/publicdomain/zero/1.0/
7 | */
8 | import {
9 | PackedArrayContext,
10 | MINIMUM_INITIAL_PACKED_ARRAY_CAPACITY
11 | } from "./PackedArrayContext";
12 |
13 | const NUMBER_OF_SETS = 8;
14 | const { pow, floor } = Math;
15 |
16 | /**
17 | * A Packed array of signed 64 bit values, and supports {@link #get get()}, {@link #set set()},
18 | * {@link #add add()} and {@link #increment increment()} operations on the logical contents of the array.
19 | *
20 | * An {@link PackedLongArray} Uses {@link PackedArrayContext} to track
21 | * the array's logical contents. Contexts may be switched when a context requires resizing
22 | * to complete logical array operations (get, set, add, increment). Contexts are
23 | * established and used within critical sections in order to facilitate concurrent
24 | * implementors.
25 | *
26 | */
27 | export class PackedArray {
28 | private arrayContext: PackedArrayContext;
29 |
30 | constructor(
31 | virtualLength: number,
32 | initialPhysicalLength: number = MINIMUM_INITIAL_PACKED_ARRAY_CAPACITY
33 | ) {
34 | this.arrayContext = new PackedArrayContext(
35 | virtualLength,
36 | initialPhysicalLength
37 | );
38 | }
39 |
40 | public setVirtualLength(newVirtualArrayLength: number) {
41 | if (newVirtualArrayLength < this.length()) {
42 | throw new Error(
43 | "Cannot set virtual length, as requested length " +
44 | newVirtualArrayLength +
45 | " is smaller than the current virtual length " +
46 | this.length()
47 | );
48 | }
49 | const currentArrayContext = this.arrayContext;
50 | if (
51 | currentArrayContext.isPacked &&
52 | currentArrayContext.determineTopLevelShiftForVirtualLength(
53 | newVirtualArrayLength
54 | ) == currentArrayContext.getTopLevelShift()
55 | ) {
56 | // No changes to the array context contents is needed. Just change the virtual length.
57 | currentArrayContext.setVirtualLength(newVirtualArrayLength);
58 | return;
59 | }
60 | this.arrayContext = currentArrayContext.copyAndIncreaseSize(
61 | this.getPhysicalLength(),
62 | newVirtualArrayLength
63 | );
64 | }
65 |
66 | /**
67 | * Get value at virtual index in the array
68 | * @param index the virtual array index
69 | * @return the array value at the virtual index given
70 | */
71 | get(index: number) {
72 | let value = 0;
73 | for (let byteNum = 0; byteNum < NUMBER_OF_SETS; byteNum++) {
74 | let byteValueAtPackedIndex = 0;
75 |
76 | // Deal with unpacked context:
77 | if (!this.arrayContext.isPacked) {
78 | return this.arrayContext.getAtUnpackedIndex(index);
79 | }
80 | // Context is packed:
81 | const packedIndex = this.arrayContext.getPackedIndex(
82 | byteNum,
83 | index,
84 | false
85 | );
86 | if (packedIndex < 0) {
87 | return value;
88 | }
89 | byteValueAtPackedIndex =
90 | this.arrayContext.getAtByteIndex(packedIndex) * pow(2, byteNum << 3);
91 | value += byteValueAtPackedIndex;
92 | }
93 | return value;
94 | }
95 |
96 | /**
97 | * Increment value at a virrual index in the array
98 | * @param index virtual index of value to increment
99 | */
100 | public increment(index: number) {
101 | this.add(index, 1);
102 | }
103 |
104 | private safeGetPackedIndexgetPackedIndex(
105 | setNumber: number,
106 | virtualIndex: number
107 | ) {
108 | //do {
109 | //try {
110 | return this.arrayContext.getPackedIndex(setNumber, virtualIndex, true);
111 | /*} catch (ex) {
112 | if (ex instanceof ResizeError) {
113 | this.arrayContext.resizeArray(ex.newSize);
114 | } else {
115 | throw ex;
116 | }
117 | }*/
118 | //} while (true);
119 | }
120 |
121 | /**
122 | * Add to a value at a virtual index in the array
123 | * @param index the virtual index of the value to be added to
124 | * @param value the value to add
125 | */
126 | public add(index: number, value: number) {
127 | let remainingValueToAdd = value;
128 |
129 | for (
130 | let byteNum = 0, byteShift = 0;
131 | byteNum < NUMBER_OF_SETS;
132 | byteNum++, byteShift += 8
133 | ) {
134 | // Deal with unpacked context:
135 | if (!this.arrayContext.isPacked) {
136 | this.arrayContext.addAndGetAtUnpackedIndex(index, value);
137 | return;
138 | }
139 | // Context is packed:
140 | const packedIndex = this.safeGetPackedIndexgetPackedIndex(byteNum, index);
141 |
142 | const byteToAdd = remainingValueToAdd & 0xff;
143 |
144 | const afterAddByteValue = this.arrayContext.addAtByteIndex(
145 | packedIndex,
146 | byteToAdd
147 | );
148 |
149 | // Reduce remaining value to add by amount just added:
150 | remainingValueToAdd -= byteToAdd;
151 |
152 | remainingValueToAdd = remainingValueToAdd / pow(2, 8);
153 | // Account for carry:
154 | remainingValueToAdd += floor(afterAddByteValue / pow(2, 8));
155 |
156 | if (remainingValueToAdd == 0) {
157 | return; // nothing to add to higher magnitudes
158 | }
159 | }
160 | }
161 |
162 | /**
163 | * Set the value at a virtual index in the array
164 | * @param index the virtual index of the value to set
165 | * @param value the value to set
166 | */
167 | set(index: number, value: number) {
168 | let bytesAlreadySet = 0;
169 | let valueForNextLevels = value;
170 | for (let byteNum = 0; byteNum < NUMBER_OF_SETS; byteNum++) {
171 | // Establish context within: critical section
172 |
173 | // Deal with unpacked context:
174 | if (!this.arrayContext.isPacked) {
175 | this.arrayContext.setAtUnpackedIndex(index, value);
176 | return;
177 | }
178 | // Context is packed:
179 | if (valueForNextLevels == 0) {
180 | // Special-case zeros to avoid inflating packed array for no reason
181 | const packedIndex = this.arrayContext.getPackedIndex(
182 | byteNum,
183 | index,
184 | false
185 | );
186 | if (packedIndex < 0) {
187 | return; // no need to create entries for zero values if they don't already exist
188 | }
189 | }
190 | // Make sure byte is populated:
191 | const packedIndex = this.arrayContext.getPackedIndex(
192 | byteNum,
193 | index,
194 | true
195 | );
196 |
197 | // Determine value to write, and prepare for next levels
198 | const byteToWrite = valueForNextLevels & 0xff;
199 | valueForNextLevels = floor(valueForNextLevels / pow(2, 8));
200 |
201 | if (byteNum < bytesAlreadySet) {
202 | // We want to avoid writing to the same byte twice when not doing so for the
203 | // entire 64 bit value atomically, as doing so opens a race with e.g. concurrent
204 | // adders. So dobn't actually write the byte if has been written before.
205 | continue;
206 | }
207 | this.arrayContext.setAtByteIndex(packedIndex, byteToWrite);
208 | bytesAlreadySet++;
209 | }
210 | }
211 |
212 | /**
213 | * Get the current physical length (in longs) of the array's backing storage
214 | * @return the current physical length (in longs) of the array's current backing storage
215 | */
216 | getPhysicalLength() {
217 | return this.arrayContext.physicalLength;
218 | }
219 |
220 | /**
221 | * Get the (virtual) length of the array
222 | * @return the (virtual) length of the array
223 | */
224 | length() {
225 | return this.arrayContext.getVirtualLength();
226 | }
227 |
228 | /**
229 | * Clear the array contents
230 | */
231 | public clear() {
232 | this.arrayContext.clear();
233 | }
234 |
235 | public toString() {
236 | let output = "PackedArray:\n";
237 | output += this.arrayContext.toString();
238 | return output;
239 | }
240 | }
241 |
--------------------------------------------------------------------------------