Skip to content

Commit c01171b

Browse files
committed
0.0.3 - feat: Implement automatic byteOffset alignment to prevent TypedArray view creation errors
1 parent 53c4496 commit c01171b

File tree

5 files changed

+82
-49
lines changed

5 files changed

+82
-49
lines changed

CHANGELOG.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,29 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
## [0.0.1] - 2024-10-26
7+
## [0.0.3] - 2024-10-26
88

9-
- Initial release
9+
### Added
10+
11+
- **Alignment Adjustment for `byteOffset`**: Implemented automatic alignment of each `TypedArray` view’s `byteOffset` to prevent errors during view creation.
12+
- **README.md Update**:
13+
- Added a note in the `Overview` section that `byteOffset` alignment is essential for preventing errors when creating `TypedArray` views.
14+
15+
### Changed
16+
17+
- **Function Adjustment**: Modified the `createArrayBufferViews` function to automatically align the `byteOffset` of each `TypedArray` view, preventing errors across different data types.
18+
19+
### Fixed
20+
21+
- **Error Prevention**: Enforced correct alignment during `TypedArray` view creation to resolve potential errors.
1022

1123
## [0.0.2] - 2024-10-26
1224

1325
### Changed
1426

1527
- Updated README to replace the "Documentation" badge with a "CI" badge to reflect the status of continuous integration workflows.
1628
- Updated the badge URL and link to point to the correct GitHub Actions workflow.
29+
30+
## [0.0.1] - 2024-10-26
31+
32+
- Initial release

README.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# @ain1084/array-buffer-partitioner
1+
# Array Buffer Partitioner
22

33
[![npm version](https://badge.fury.io/js/@ain1084%2Farray-buffer-partitioner.svg)](https://badge.fury.io/js/@ain1084%2Farray-buffer-partitioner)
44
[![CI](https://github.com/ain1084/array-buffer-partitioner/actions/workflows/ci.yml/badge.svg)](https://github.com/ain1084/array-buffer-partitioner/actions?query=workflow%3Aci)
@@ -9,20 +9,19 @@ Partition an ArrayBuffer into multiple TypedArray views efficiently, while handl
99

1010
## Overview
1111

12-
`@ain1084/array-buffer-partitioner` is a utility library designed to create multiple `TypedArray` views on a single `ArrayBuffer` or `SharedArrayBuffer`. This can be particularly useful when dealing with various data types that need to coexist within a single buffer, without manually calculating offsets and ensuring correct alignment.
12+
`@ain1084/array-buffer-partitioner` is a utility library for creating multiple TypedArray views on a single `ArrayBuffer` or `SharedArrayBuffer`. This allows various data types to be efficiently placed within a single buffer. The library automatically adjusts each TypedArray’s byteOffset to prevent errors that can occur during view creation.
1313

1414
### Features
1515

1616
- Create multiple `TypedArray` views from a single `ArrayBuffer` or `SharedArrayBuffer`.
1717
- Efficient memory partitioning without manual offset calculations.
18-
- Future-proof: planned support for alignment adjustment to optimize memory access performance.
1918

2019
## Installation
2120

2221
Install the library via npm:
2322

2423
```sh
25-
npm install @ain1084/array-buffer-partitioner
24+
npm i @ain1084/array-buffer-partitioner
2625
```
2726

2827
## Usage
@@ -63,10 +62,6 @@ Creates multiple `TypedArray` views on a single `ArrayBuffer` or `SharedArrayBuf
6362

6463
An object containing the views, with each key corresponding to the provided configuration.
6564

66-
## Future Plans
67-
68-
- **Alignment Support**: Future versions will include alignment support to ensure efficient access patterns, especially when working with mixed data types like `Float32Array` and `Uint8Array`. This will improve the performance of memory access and help avoid inefficient alignments.
69-
7065
## Contributing
7166

7267
Contributions are welcome! If you have ideas, suggestions, or issues, please create an issue on the [GitHub repository](https://github.com/ain1084/array-buffer-partitioner).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ain1084/array-buffer-partitioner",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"description": "Partition an ArrayBuffer into multiple TypedArray views efficiently.",
55
"module": "dist/index.js",
66
"types": "dist/index.d.ts",

src/index.ts

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,50 +30,54 @@
3030
* ```
3131
*/
3232
export function createArrayBufferViews<T extends Record<string, [ArrayTypeConstructor<SupportedArrays>, number]>>(
33-
BufferType: { new(size: number): ArrayBuffer },
34-
config: T
35-
): {
36-
[K in keyof T]: InstanceType<T[K][0]>
37-
} {
38-
const totalSize = (Object.values(config) as ArrayView[])
39-
.reduce((sum, [type, size]) =>
40-
sum + (type.BYTES_PER_ELEMENT * size),
41-
0
42-
);
33+
BufferType: { new(size: number): ArrayBuffer },
34+
config: T
35+
): {
36+
[K in keyof T]: InstanceType<T[K][0]>
37+
} {
38+
const alignTo = (value: number, alignment: number) =>
39+
(value + alignment - 1) & ~(alignment - 1);
4340

44-
// Create an instance of the specified buffer type with the calculated total size
45-
const buffer = new BufferType(totalSize);
41+
let totalSize = 0;
4642

47-
const result = {} as { [K in keyof T]: InstanceType<T[K][0]> };
43+
for (const [, [type, size]] of Object.entries(config) as Array<[string, ArrayView]>) {
44+
const alignment = type.BYTES_PER_ELEMENT;
45+
totalSize = alignTo(totalSize, alignment) + (alignment * size);
46+
}
4847

49-
let offset = 0;
48+
const buffer = new BufferType(totalSize);
49+
const result = {} as { [K in keyof T]: InstanceType<T[K][0]> };
5050

51-
for (const key in config) {
52-
const [type, size] = config[key];
53-
result[key] = new type(buffer, offset, size) as InstanceType<T[typeof key][0]>;
54-
offset += type.BYTES_PER_ELEMENT * size;
55-
}
51+
let offset = 0;
5652

57-
return result;
53+
for (const key in config) {
54+
const [type, size] = config[key];
55+
const alignment = type.BYTES_PER_ELEMENT;
56+
offset = alignTo(offset, alignment);
57+
result[key] = new type(buffer, offset, size) as InstanceType<T[typeof key][0]>;
58+
offset += alignment * size;
59+
}
60+
61+
return result;
5862
}
5963

6064
/**
6165
* Represents the constructor for supported TypedArray types.
6266
* @internal
6367
*/
64-
type ArrayTypeConstructor<R> = {
65-
new(buffer: ArrayBuffer, byteOffset: number, length: number): R;
66-
BYTES_PER_ELEMENT: number;
68+
type ArrayTypeConstructor<R> = {
69+
new(buffer: ArrayBuffer, byteOffset: number, length: number): R;
70+
BYTES_PER_ELEMENT: number;
6771
};
6872

6973
/**
7074
* Lists the supported TypedArray types.
7175
* @internal
7276
*/
73-
type SupportedArrays =
74-
Float32Array | Uint32Array | Uint8Array |
75-
Int32Array | Int16Array | Uint16Array |
76-
Float64Array | BigInt64Array | BigUint64Array;
77+
type SupportedArrays =
78+
Float32Array | Uint32Array | Uint8Array |
79+
Int32Array | Int16Array | Uint16Array |
80+
Float64Array | BigInt64Array | BigUint64Array;
7781

7882
/**
7983
* Represents the configuration for creating TypedArray views.

tests/createArrayBufferViews.test.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,50 @@
22

33
import { createArrayBufferViews } from '../src';
44

5+
const alignTo = (offset: number, alignment: number): number => {
6+
return (offset + alignment - 1) & ~(alignment - 1)
7+
}
8+
59
describe('createArrayBufferViews', () => {
610
it('should create the correct TypedArray views with ArrayBuffer', () => {
711
const views = createArrayBufferViews(ArrayBuffer, {
812
data: [Float32Array, 1024],
913
index: [Uint32Array, 1],
10-
flag: [Uint8Array, 1]
14+
flag: [Uint8Array, 1],
15+
u32: [Uint32Array, 1],
16+
u64: [BigUint64Array, 1],
1117
});
1218

19+
expect(views.data.byteOffset).toBe(0);
1320
expect(views.data.length).toBe(1024);
21+
expect(views.index.byteOffset).toBe(1024 * Float32Array.BYTES_PER_ELEMENT);
1422
expect(views.index.length).toBe(1);
23+
expect(views.flag.byteOffset).toBe(alignTo(views.index.byteOffset + Uint32Array.BYTES_PER_ELEMENT, Uint8Array.BYTES_PER_ELEMENT));
1524
expect(views.flag.length).toBe(1);
16-
expect(views.data.byteOffset).toBe(0);
17-
expect(views.index.byteOffset).toBe(1024 * Float32Array.BYTES_PER_ELEMENT);
18-
expect(views.flag.byteOffset).toBe(1024 * Float32Array.BYTES_PER_ELEMENT + Uint32Array.BYTES_PER_ELEMENT);
25+
expect(views.u32.byteOffset).toBe(alignTo(views.flag.byteOffset + Uint8Array.BYTES_PER_ELEMENT, Uint32Array.BYTES_PER_ELEMENT));
26+
expect(views.u32.length).toBe(1);
27+
expect(views.u64.byteOffset).toBe(alignTo(views.u32.byteOffset + Uint32Array.BYTES_PER_ELEMENT, BigUint64Array.BYTES_PER_ELEMENT));
28+
expect(views.u64.length).toBe(1);
1929
});
2030

2131
it('should create the correct TypedArray views with SharedArrayBuffer', () => {
2232
const views = createArrayBufferViews(SharedArrayBuffer, {
23-
buffer1: [Int16Array, 512],
24-
buffer2: [Uint8Array, 256]
33+
data: [Float32Array, 1024],
34+
index: [Uint32Array, 1],
35+
flag: [Uint8Array, 1],
36+
u32: [Uint32Array, 1],
37+
u64: [BigUint64Array, 1],
2538
});
26-
27-
expect(views.buffer1.length).toBe(512);
28-
expect(views.buffer2.length).toBe(256);
29-
expect(views.buffer1.byteOffset).toBe(0);
30-
expect(views.buffer2.byteOffset).toBe(512 * Int16Array.BYTES_PER_ELEMENT);
39+
expect(views.data.byteOffset).toBe(0);
40+
expect(views.data.length).toBe(1024);
41+
expect(views.index.byteOffset).toBe(1024 * Float32Array.BYTES_PER_ELEMENT);
42+
expect(views.index.length).toBe(1);
43+
expect(views.flag.byteOffset).toBe(alignTo(views.index.byteOffset + Uint32Array.BYTES_PER_ELEMENT, Uint8Array.BYTES_PER_ELEMENT));
44+
expect(views.flag.length).toBe(1);
45+
expect(views.u32.byteOffset).toBe(alignTo(views.flag.byteOffset + Uint8Array.BYTES_PER_ELEMENT, Uint32Array.BYTES_PER_ELEMENT));
46+
expect(views.u32.length).toBe(1);
47+
expect(views.u64.byteOffset).toBe(alignTo(views.u32.byteOffset + Uint32Array.BYTES_PER_ELEMENT, BigUint64Array.BYTES_PER_ELEMENT));
48+
expect(views.u64.length).toBe(1);
3149
});
3250

3351
it('should throw an error if the buffer size is insufficient', () => {

0 commit comments

Comments
 (0)