From d37a49e876c7721827640a6572321b898005c149 Mon Sep 17 00:00:00 2001 From: alexvictoor Date: Sun, 8 Nov 2026 15:21:37 +0100 Subject: [PATCH 1/2] fix: WASM add/subtract with a packed histogram operand The JS wrapper builds the export name as `addHistogram${otherSuffix}to${thisSuffix}` (and the `subtract...from...` twin), but the AssemblyScript module exported the packed-operand variants as `addPackedHistogramto*` / `subtractPackedHistogramfrom*`. Any `wasmHistogram.add(wasmPackedHistogram)` therefore threw "wasm[...] is not a function", whatever the receiver's bucket size. Rename the ten exports to follow the regular naming scheme and add a test covering every (receiver, operand) pair of WASM bucket sizes. Co-Authored-By: Claude Fable 5.1 --- assembly/index.ts | 20 ++++++++++---------- src/Histogram.spec.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/assembly/index.ts b/assembly/index.ts index 4314cc9..bb8f252 100644 --- a/assembly/index.ts +++ b/assembly/index.ts @@ -474,53 +474,53 @@ export function addHistogram8to8(h: Histogram8, other: Histogram8): void { h.add export function addHistogram16to8(h: Histogram8, other: Histogram16): void { h.addHistogram16(other); } export function addHistogram32to8(h: Histogram8, other: Histogram32): void { h.addHistogram32(other); } export function addHistogram64to8(h: Histogram8, other: Histogram64): void { h.addHistogram64(other); } -export function addPackedHistogramto8(h: Histogram8, other: PackedHistogram): void { h.addPackedHistogram(other); } +export function addHistogramPackedto8(h: Histogram8, other: PackedHistogram): void { h.addPackedHistogram(other); } export function addHistogram8to16(h: Histogram16, other: Histogram8): void { h.addHistogram8(other); } export function addHistogram16to16(h: Histogram16, other: Histogram16): void { h.addHistogram16(other); } export function addHistogram32to16(h: Histogram16, other: Histogram32): void { h.addHistogram32(other); } export function addHistogram64to16(h: Histogram16, other: Histogram64): void { h.addHistogram64(other); } -export function addPackedHistogramto16(h: Histogram16, other: PackedHistogram): void { h.addPackedHistogram(other); } +export function addHistogramPackedto16(h: Histogram16, other: PackedHistogram): void { h.addPackedHistogram(other); } export function addHistogram8to32(h: Histogram32, other: Histogram8): void { h.addHistogram8(other); } export function addHistogram16to32(h: Histogram32, other: Histogram16): void { h.addHistogram16(other); } export function addHistogram32to32(h: Histogram32, other: Histogram32): void { h.addHistogram32(other); } export function addHistogram64to32(h: Histogram32, other: Histogram64): void { h.addHistogram64(other); } -export function addPackedHistogramto32(h: Histogram32, other: PackedHistogram): void { h.addPackedHistogram(other); } +export function addHistogramPackedto32(h: Histogram32, other: PackedHistogram): void { h.addPackedHistogram(other); } export function addHistogram8to64(h: Histogram64, other: Histogram8): void { h.addHistogram8(other); } export function addHistogram16to64(h: Histogram64, other: Histogram16): void { h.addHistogram16(other); } export function addHistogram32to64(h: Histogram64, other: Histogram32): void { h.addHistogram32(other); } export function addHistogram64to64(h: Histogram64, other: Histogram64): void { h.addHistogram64(other); } -export function addPackedHistogramto64(h: Histogram64, other: PackedHistogram): void { h.addPackedHistogram(other); } +export function addHistogramPackedto64(h: Histogram64, other: PackedHistogram): void { h.addPackedHistogram(other); } export function addHistogram8toPacked(h: PackedHistogram, other: Histogram8): void { h.addHistogram8(other); } export function addHistogram16toPacked(h: PackedHistogram, other: Histogram16): void { h.addHistogram16(other); } export function addHistogram32toPacked(h: PackedHistogram, other: Histogram32): void { h.addHistogram32(other); } export function addHistogram64toPacked(h: PackedHistogram, other: Histogram64): void { h.addHistogram64(other); } -export function addPackedHistogramtoPacked(h: PackedHistogram, other: PackedHistogram): void { h.addPackedHistogram(other); } +export function addHistogramPackedtoPacked(h: PackedHistogram, other: PackedHistogram): void { h.addPackedHistogram(other); } export function subtractHistogram8from8(h: Histogram8, other: Histogram8): void { h.subtractHistogram8(other); } export function subtractHistogram16from8(h: Histogram8, other: Histogram16): void { h.subtractHistogram16(other); } export function subtractHistogram32from8(h: Histogram8, other: Histogram32): void { h.subtractHistogram32(other); } export function subtractHistogram64from8(h: Histogram8, other: Histogram64): void { h.subtractHistogram64(other); } -export function subtractPackedHistogramfrom8(h: Histogram8, other: PackedHistogram): void { h.subtractPackedHistogram(other); } +export function subtractHistogramPackedfrom8(h: Histogram8, other: PackedHistogram): void { h.subtractPackedHistogram(other); } export function subtractHistogram8from16(h: Histogram16, other: Histogram8): void { h.subtractHistogram8(other); } export function subtractHistogram16from16(h: Histogram16, other: Histogram16): void { h.subtractHistogram16(other); } export function subtractHistogram32from16(h: Histogram16, other: Histogram32): void { h.subtractHistogram32(other); } export function subtractHistogram64from16(h: Histogram16, other: Histogram64): void { h.subtractHistogram64(other); } -export function subtractPackedHistogramfrom16(h: Histogram16, other: PackedHistogram): void { h.subtractPackedHistogram(other); } +export function subtractHistogramPackedfrom16(h: Histogram16, other: PackedHistogram): void { h.subtractPackedHistogram(other); } export function subtractHistogram8from32(h: Histogram32, other: Histogram8): void { h.subtractHistogram8(other); } export function subtractHistogram16from32(h: Histogram32, other: Histogram16): void { h.subtractHistogram16(other); } export function subtractHistogram32from32(h: Histogram32, other: Histogram32): void { h.subtractHistogram32(other); } export function subtractHistogram64from32(h: Histogram32, other: Histogram64): void { h.subtractHistogram64(other); } -export function subtractPackedHistogramfrom32(h: Histogram32, other: PackedHistogram): void { h.subtractPackedHistogram(other); } +export function subtractHistogramPackedfrom32(h: Histogram32, other: PackedHistogram): void { h.subtractPackedHistogram(other); } export function subtractHistogram8from64(h: Histogram64, other: Histogram8): void { h.subtractHistogram8(other); } export function subtractHistogram16from64(h: Histogram64, other: Histogram16): void { h.subtractHistogram16(other); } export function subtractHistogram32from64(h: Histogram64, other: Histogram32): void { h.subtractHistogram32(other); } export function subtractHistogram64from64(h: Histogram64, other: Histogram64): void { h.subtractHistogram64(other); } -export function subtractPackedHistogramfrom64(h: Histogram64, other: PackedHistogram): void { h.subtractPackedHistogram(other); } +export function subtractHistogramPackedfrom64(h: Histogram64, other: PackedHistogram): void { h.subtractPackedHistogram(other); } export function subtractHistogram8fromPacked(h: PackedHistogram, other: Histogram8): void { h.subtractHistogram8(other); } export function subtractHistogram16fromPacked(h: PackedHistogram, other: Histogram16): void { h.subtractHistogram16(other); } export function subtractHistogram32fromPacked(h: PackedHistogram, other: Histogram32): void { h.subtractHistogram32(other); } export function subtractHistogram64fromPacked(h: PackedHistogram, other: Histogram64): void { h.subtractHistogram64(other); } -export function subtractPackedHistogramfromPacked(h: PackedHistogram, other: PackedHistogram): void { h.subtractPackedHistogram(other); } +export function subtractHistogramPackedfromPacked(h: PackedHistogram, other: PackedHistogram): void { h.subtractPackedHistogram(other); } export function addWhileCorrectingForCoordinatedOmission8(h: Histogram8, other: Histogram8, expectedIntervalBetweenValueSamples: f64): void { h.addWhileCorrectingForCoordinatedOmission(other, expectedIntervalBetweenValueSamples); } export function addWhileCorrectingForCoordinatedOmission16(h: Histogram16, other: Histogram16, expectedIntervalBetweenValueSamples: f64): void { h.addWhileCorrectingForCoordinatedOmission(other, expectedIntervalBetweenValueSamples); } diff --git a/src/Histogram.spec.ts b/src/Histogram.spec.ts index 0c741cd..307ab39 100644 --- a/src/Histogram.spec.ts +++ b/src/Histogram.spec.ts @@ -502,6 +502,40 @@ describe("Histogram add & substract", () => { expect(histogram.summary).toStrictEqual(before); }); + const wasmBucketSizes = [8, 16, 32, 64, "packed"] as const; + wasmBucketSizes.forEach((thisSize) => { + wasmBucketSizes.forEach((otherSize) => { + it(`should add and subtract a WASM ${otherSize} histogram to/from a WASM ${thisSize} histogram`, () => { + // given + const histogram = build({ + bitBucketSize: thisSize, + useWebAssembly: true, + numberOfSignificantValueDigits: 3, + }); + const histogram2 = build({ + bitBucketSize: otherSize, + useWebAssembly: true, + numberOfSignificantValueDigits: 3, + }); + histogram.recordValueWithCount(100, 2); + histogram2.recordValueWithCount(100, 1); + histogram2.recordValueWithCount(42000, 1); + const before = histogram.summary; + // when + histogram.add(histogram2); + // then + expect(histogram.totalCount).toBe(4); + expect(histogram.maxValue).toBeGreaterThanOrEqual(42000); + // when + histogram.subtract(histogram2); + // then + expect(histogram.summary).toStrictEqual(before); + histogram.destroy(); + histogram2.destroy(); + }); + }); + }); + it("should update percentiles when another histogram of same characteristics is substracted", () => { // given const histogram = build({ numberOfSignificantValueDigits: 3 }); From 915756a90190f89d95dec4871985d6ffc25bb217 Mon Sep 17 00:00:00 2001 From: alexvictoor Date: Sun, 8 Nov 2026 18:30:58 +0100 Subject: [PATCH 2/2] test: align establishInternalTackingValues expectation with the maxValue fix Commit 21b452b changed updatedMaxValue from `value + unitMagnitudeMask` to `value | unitMagnitudeMask` (as in the Java original) but left the AssemblyScript kernel test expecting the old arithmetic, so `npm run astest:ci` has failed on master since then and CI never reached the vitest step. Co-Authored-By: Claude Fable 5.1 --- assembly/__tests__/HistogramCountsKernels.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assembly/__tests__/HistogramCountsKernels.test.ts b/assembly/__tests__/HistogramCountsKernels.test.ts index 8ca9a7f..5b1676d 100644 --- a/assembly/__tests__/HistogramCountsKernels.test.ts +++ b/assembly/__tests__/HistogramCountsKernels.test.ts @@ -111,7 +111,7 @@ function checkEstablishTrackingValues(h: Histogram): void { expect(h.totalCount).equal(total); if (maxIndex >= 0) { expect(h.maxValue).equal( - h.highestEquivalentValue(h.valueFromIndex(maxIndex)) + h.unitMagnitudeMask + h.highestEquivalentValue(h.valueFromIndex(maxIndex)) | h.unitMagnitudeMask ); } else { expect(h.maxValue).equal(0);