Skip to content

Commit aac1deb

Browse files
committed
Support 64-bit integers
1 parent 34e0a67 commit aac1deb

File tree

14 files changed

+157
-92
lines changed

14 files changed

+157
-92
lines changed

packages/app/src/metadata-viewer/utils.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import {
2+
isBigIntegerType,
3+
isFloatType,
24
isH5WebComplex,
35
isIntegerType,
46
isNumericType,
57
isScalarShape,
68
} from '@h5web/shared/guards';
79
import {
10+
type BigIntegerType,
811
type ComplexArray,
912
type DType,
1013
DTypeClass,
1114
type H5WebComplex,
15+
type IntegerType,
16+
type NumericType,
1217
type Shape,
1318
} from '@h5web/shared/hdf5-models';
1419
import { formatScalarComplex } from '@h5web/shared/vis-utils';
@@ -27,18 +32,21 @@ export function renderShape(shape: Shape): string {
2732
: `${shape.join(' x ')} = ${shape.reduce((acc, value) => acc * value)}`;
2833
}
2934

30-
export function renderType(type: DType): string {
31-
if (isNumericType(type)) {
32-
const { endianness, size } = type;
35+
function renderIntSign(type: IntegerType | BigIntegerType): string {
36+
return type.signed ? ' (signed)' : ' (unsigned)';
37+
}
38+
39+
function renderEndianness(type: NumericType | BigIntegerType): string {
40+
return type.endianness ? `, ${type.endianness}` : '';
41+
}
3342

34-
const endiannessStr = endianness ? `, ${endianness}` : '';
35-
const signStr = isIntegerType(type)
36-
? type.signed
37-
? ' (signed)'
38-
: ' (unsigned)'
39-
: '';
43+
export function renderType(type: DType): string {
44+
if (isIntegerType(type) || isBigIntegerType(type)) {
45+
return `Integer${renderIntSign(type)}, ${type.size}-bit${renderEndianness(type)}`;
46+
}
4047

41-
return `${type.class}${signStr}, ${size}-bit${endiannessStr}`;
48+
if (isFloatType(type)) {
49+
return `Float, ${type.size}-bit${renderEndianness(type)}`;
4250
}
4351

4452
if (type.class === DTypeClass.String) {

packages/app/src/providers/mock/mock-file.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { H5T_CSET, H5T_STR } from '@h5web/shared/h5t';
22
import { type GroupWithChildren } from '@h5web/shared/hdf5-models';
33
import {
44
arrayType,
5+
bigintType,
56
boolType,
67
compoundType,
78
cplx,
@@ -152,6 +153,7 @@ export function makeMockFile(): GroupWithChildren {
152153
group('typed_arrays', [
153154
array('uint8', { type: intType(false, 8) }),
154155
array('int16', { type: intType(true, 16) }),
156+
array('int64', { type: bigintType() }),
155157
array('float32', { type: floatType(32) }),
156158
array('float64', { type: floatType(64) }),
157159
withImageAttr(array('uint8_rgb', { type: intType(false, 8) })),

packages/app/src/vis-packs/core/matrix/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export function getFormatter(
6565
return createEnumFormatter(type.mapping);
6666
}
6767

68-
return (val) => (val as string).toString(); // call `toString()` for safety, in case type cast is wrong
68+
return (val) => (val as string | bigint).toString(); // call `toString()` for safety, in case type cast is wrong
6969
}
7070

7171
export function getCellWidth(

packages/app/src/vis-packs/core/scalar/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@ export function getFormatter(
2727
return createEnumFormatter(dataset.type.mapping);
2828
}
2929

30-
return (val) => (val as number | string).toString();
30+
return (val) => (val as number | bigint | string).toString();
3131
}

packages/app/src/vis-packs/core/utils.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { type InteractionInfo } from '@h5web/lib';
2-
import { isIntegerType, isNumericType } from '@h5web/shared/guards';
2+
import {
3+
isBigIntegerType,
4+
isBigIntTypeArray,
5+
isIntegerType,
6+
isNumericType,
7+
} from '@h5web/shared/guards';
38
import {
49
type ArrayValue,
510
DTypeClass,
@@ -93,11 +98,23 @@ export function getImageInteractions(keepRatio: boolean): InteractionInfo[] {
9398
return keepRatio ? BASE_INTERACTIONS : INTERACTIONS_WITH_AXIAL_ZOOM;
9499
}
95100

101+
function isBigIntArray(val: ArrayValue<NumericLikeType>): val is bigint[] {
102+
return Array.isArray(val) && typeof val[0] === 'bigint';
103+
}
104+
96105
function isBoolArray(val: ArrayValue<NumericLikeType>): val is boolean[] {
97106
return Array.isArray(val) && typeof val[0] === 'boolean';
98107
}
99108

100109
export function toNumArray(arr: ArrayValue<NumericLikeType>): NumArray {
110+
if (isBigIntTypeArray(arr)) {
111+
return Float64Array.from(arr, Number); // cast to float 64
112+
}
113+
114+
if (isBigIntArray(arr)) {
115+
return arr.map(Number); // cast to float 64
116+
}
117+
101118
if (isBoolArray(arr)) {
102119
return arr.map((val) => (val ? 1 : 0));
103120
}
@@ -109,11 +126,12 @@ const TYPE_STRINGS: Record<NumericLikeType['class'], string> = {
109126
[DTypeClass.Bool]: 'bool',
110127
[DTypeClass.Enum]: 'enum',
111128
[DTypeClass.Integer]: 'int',
129+
[DTypeClass.BigInteger]: 'int',
112130
[DTypeClass.Float]: 'float',
113131
};
114132

115133
export function formatNumLikeType(type: NumericLikeType): string {
116-
const unsignedPrefix = isIntegerType(type) && !type.signed ? 'u' : '';
117-
const sizeSuffix = isNumericType(type) ? type.size : '';
134+
const unsignedPrefix = 'signed' in type && !type.signed ? 'u' : '';
135+
const sizeSuffix = 'size' in type ? type.size : '';
118136
return `${unsignedPrefix}${TYPE_STRINGS[type.class]}${sizeSuffix}`;
119137
}

packages/h5wasm/src/h5wasm-api.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@ import {
1616
import { transfer } from 'comlink';
1717

1818
import { type Plugin } from './models';
19-
import {
20-
getEnhancedError,
21-
hasBigInts,
22-
PLUGINS_BY_FILTER_ID,
23-
sanitizeBigInts,
24-
} from './utils';
19+
import { getEnhancedError, PLUGINS_BY_FILTER_ID } from './utils';
2520
import { type H5WasmWorkerAPI } from './worker';
2621

2722
export class H5WasmApi extends DataProviderApi {
@@ -48,8 +43,7 @@ export class H5WasmApi extends DataProviderApi {
4843
await this.processFilters(dataset);
4944

5045
try {
51-
const value = await this.remote.getValue(fileId, dataset.path, selection);
52-
return hasBigInts(dataset.type) ? sanitizeBigInts(value) : value;
46+
return await this.remote.getValue(fileId, dataset.path, selection);
5347
} catch (error) {
5448
throw getEnhancedError(error);
5549
}

packages/h5wasm/src/utils.ts

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { type DType, DTypeClass } from '@h5web/shared/hdf5-models';
2-
31
import { type HDF5Diag, Plugin } from './models';
42

53
// https://support.hdfgroup.org/services/contributions.html
@@ -15,38 +13,6 @@ export const PLUGINS_BY_FILTER_ID: Record<number, Plugin> = {
1513
32_026: Plugin.Blosc2,
1614
};
1715

18-
export function hasBigInts(type: DType): boolean {
19-
if (
20-
type.class === DTypeClass.Enum ||
21-
type.class === DTypeClass.Array ||
22-
type.class === DTypeClass.VLen
23-
) {
24-
return hasBigInts(type.base);
25-
}
26-
27-
if (type.class === DTypeClass.Compound) {
28-
return Object.values(type.fields).some(hasBigInts);
29-
}
30-
31-
return type.class === DTypeClass.Integer && type.size === 64;
32-
}
33-
34-
export function sanitizeBigInts(value: unknown): unknown {
35-
if (Array.isArray(value)) {
36-
return value.map(sanitizeBigInts);
37-
}
38-
39-
if (value instanceof BigInt64Array || value instanceof BigUint64Array) {
40-
return [...value].map(Number);
41-
}
42-
43-
if (typeof value === 'bigint') {
44-
return Number(value);
45-
}
46-
47-
return value;
48-
}
49-
5016
const DIAG_PREDICATES: ((diag: HDF5Diag) => boolean)[] = [
5117
(diag: HDF5Diag) => {
5218
return (

packages/h5wasm/src/worker-utils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
enumOrBoolType,
2222
floatType,
2323
getNameFromPath,
24-
intType,
24+
intOrBigintType,
2525
opaqueType,
2626
referenceType,
2727
strType,
@@ -216,12 +216,13 @@ function parseDType(metadata: Metadata): DType {
216216

217217
if (h5tClass === H5T_CLASS.INTEGER) {
218218
const { signed, littleEndian } = metadata;
219-
return intType(
219+
return intOrBigintType(
220220
signed,
221221
size * 8,
222222
littleEndian ? H5T_ORDER.LE : H5T_ORDER.BE,
223223
);
224224
}
225+
225226
if (h5tClass === H5T_CLASS.FLOAT) {
226227
const { littleEndian } = metadata;
227228
return floatType(size * 8, littleEndian ? H5T_ORDER.LE : H5T_ORDER.BE);

packages/shared/src/guards.ts

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type Data, type NdArray, type TypedArray } from 'ndarray';
22

33
import {
44
type ArrayShape,
5+
type BigIntegerType,
56
type BooleanType,
67
type ComplexArray,
78
type ComplexType,
@@ -30,20 +31,12 @@ import {
3031
import {
3132
type AnyNumArray,
3233
type AxisScaleType,
34+
type BigIntTypedArray,
3335
type ColorScaleType,
3436
type NumArray,
3537
} from './vis-models';
3638
import { AXIS_SCALE_TYPES, COLOR_SCALE_TYPES, getValues } from './vis-utils';
3739

38-
const PRINTABLE_DTYPES = new Set([
39-
DTypeClass.Integer,
40-
DTypeClass.Float,
41-
DTypeClass.String,
42-
DTypeClass.Bool,
43-
DTypeClass.Enum,
44-
DTypeClass.Complex,
45-
]);
46-
4740
export function isAbsolutePath(path: string): boolean {
4841
return path.startsWith('/');
4942
}
@@ -86,6 +79,12 @@ function assertNum(val: unknown): asserts val is number {
8679
}
8780
}
8881

82+
function assertNumOrBigint(val: unknown): asserts val is number {
83+
if (typeof val !== 'number' && typeof val !== 'bigint') {
84+
throw new TypeError('Expected number');
85+
}
86+
}
87+
8988
function assertNumOrBool(val: unknown): asserts val is number | boolean {
9089
if (typeof val !== 'number' && typeof val !== 'boolean') {
9190
throw new TypeError('Expected boolean or number');
@@ -142,6 +141,10 @@ export function isTypedArray(val: unknown): val is TypedArray {
142141
);
143142
}
144143

144+
export function isBigIntTypeArray(val: unknown): val is BigIntTypedArray {
145+
return val instanceof BigInt64Array || val instanceof BigUint64Array;
146+
}
147+
145148
export function isGroup(entity: Entity): entity is Group {
146149
return entity.kind === EntityKind.Group;
147150
}
@@ -313,11 +316,23 @@ export function assertNumericType<S extends Shape>(
313316
}
314317
}
315318

319+
export function isBigIntegerType(type: DType): type is BigIntegerType {
320+
return type.class === DTypeClass.BigInteger;
321+
}
322+
323+
export function isNumericLikeType(type: DType): type is NumericLikeType {
324+
return (
325+
isNumericType(type) ||
326+
isBigIntegerType(type) ||
327+
isBoolType(type) ||
328+
isEnumType(type)
329+
);
330+
}
331+
316332
export function hasNumericLikeType<S extends Shape>(
317333
dataset: Dataset<S>,
318334
): dataset is Dataset<S, NumericLikeType> {
319-
const { type } = dataset;
320-
return isNumericType(type) || isBoolType(type) || isEnumType(type);
335+
return isNumericLikeType(dataset.type);
321336
}
322337

323338
export function assertNumericLikeType<S extends Shape>(
@@ -360,22 +375,20 @@ export function assertNumericLikeOrComplexType<S extends Shape>(
360375
}
361376
}
362377

378+
export function isPrintableType(type: DType): type is PrintableType {
379+
return isStringType(type) || isNumericLikeType(type) || isComplexType(type);
380+
}
381+
363382
export function hasPrintableType<S extends Shape>(
364-
entity: Dataset<S>,
365-
): entity is Dataset<S, PrintableType> {
366-
return PRINTABLE_DTYPES.has(entity.type.class);
383+
dataset: Dataset<S>,
384+
): dataset is Dataset<S, PrintableType> {
385+
return isPrintableType(dataset.type);
367386
}
368387

369388
export function assertPrintableType<S extends Shape>(
370389
dataset: Dataset<S>,
371390
): asserts dataset is Dataset<S, PrintableType> {
372-
if (
373-
!hasStringType(dataset) &&
374-
!hasNumericType(dataset) &&
375-
!hasBoolType(dataset) &&
376-
!hasEnumType(dataset) &&
377-
!hasComplexType(dataset)
378-
) {
391+
if (!hasPrintableType(dataset)) {
379392
throw new Error('Expected dataset to have displayable type');
380393
}
381394
}
@@ -402,7 +415,7 @@ export function hasPrintableCompoundType<S extends Shape>(
402415
dataset: Dataset<S, CompoundType>,
403416
): dataset is Dataset<S, CompoundType<PrintableType>> {
404417
const { fields } = dataset.type;
405-
return Object.values(fields).every((f) => PRINTABLE_DTYPES.has(f.class));
418+
return Object.values(fields).every(isPrintableType);
406419
}
407420

408421
export function assertPrintableCompoundType<S extends Shape>(
@@ -420,22 +433,24 @@ export function isComplexValue(
420433
return type.class === DTypeClass.Complex;
421434
}
422435

423-
function assertPrimitiveValue(
436+
function assertScalarValue(
424437
type: DType,
425438
value: unknown,
426439
): asserts value is ScalarValue {
427440
if (isNumericType(type)) {
428441
assertNum(value);
429442
} else if (isStringType(type)) {
430443
assertStr(value);
444+
} else if (isBigIntegerType(type)) {
445+
assertNumOrBigint(value);
431446
} else if (isBoolType(type)) {
432447
assertNumOrBool(value);
433448
} else if (isComplexType(type)) {
434449
assertComplex(value);
435450
} else if (isCompoundType(type)) {
436451
assertArray(value);
437452
Object.values(type.fields).forEach((fieldType, index) => {
438-
assertPrimitiveValue(fieldType, value[index]);
453+
assertScalarValue(fieldType, value[index]);
439454
});
440455
}
441456
}
@@ -447,14 +462,18 @@ export function assertDatasetValue<D extends Dataset<ScalarShape | ArrayShape>>(
447462
const { type } = dataset;
448463

449464
if (hasScalarShape(dataset)) {
450-
assertPrimitiveValue(type, value);
465+
assertScalarValue(type, value);
451466
} else {
452-
if (!Array.isArray(value) && !isTypedArray(value)) {
467+
if (
468+
!Array.isArray(value) &&
469+
!isTypedArray(value) &&
470+
!isBigIntTypeArray(value)
471+
) {
453472
throw new TypeError('Expected array or typed array');
454473
}
455474

456475
if (value.length > 0) {
457-
assertPrimitiveValue(type, value[0]);
476+
assertScalarValue(type, value[0]);
458477
}
459478
}
460479
}

0 commit comments

Comments
 (0)