Skip to content

Commit

Permalink
0.0.3 - feat: Implement automatic byteOffset alignment to prevent Typ…
Browse files Browse the repository at this point in the history
…edArray view creation errors
  • Loading branch information
ain1084 committed Oct 26, 2024
1 parent 53c4496 commit c01171b
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 49 deletions.
20 changes: 18 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,29 @@ All notable changes to this project will be documented in this file.

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).

## [0.0.1] - 2024-10-26
## [0.0.3] - 2024-10-26

- Initial release
### Added

- **Alignment Adjustment for `byteOffset`**: Implemented automatic alignment of each `TypedArray` view’s `byteOffset` to prevent errors during view creation.
- **README.md Update**:
- Added a note in the `Overview` section that `byteOffset` alignment is essential for preventing errors when creating `TypedArray` views.

### Changed

- **Function Adjustment**: Modified the `createArrayBufferViews` function to automatically align the `byteOffset` of each `TypedArray` view, preventing errors across different data types.

### Fixed

- **Error Prevention**: Enforced correct alignment during `TypedArray` view creation to resolve potential errors.

## [0.0.2] - 2024-10-26

### Changed

- Updated README to replace the "Documentation" badge with a "CI" badge to reflect the status of continuous integration workflows.
- Updated the badge URL and link to point to the correct GitHub Actions workflow.

## [0.0.1] - 2024-10-26

- Initial release
11 changes: 3 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# @ain1084/array-buffer-partitioner
# Array Buffer Partitioner

[![npm version](https://badge.fury.io/js/@ain1084%2Farray-buffer-partitioner.svg)](https://badge.fury.io/js/@ain1084%2Farray-buffer-partitioner)
[![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)
Expand All @@ -9,20 +9,19 @@ Partition an ArrayBuffer into multiple TypedArray views efficiently, while handl

## Overview

`@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.
`@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.

### Features

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

## Installation

Install the library via npm:

```sh
npm install @ain1084/array-buffer-partitioner
npm i @ain1084/array-buffer-partitioner
```

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

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

## Future Plans

- **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.

## Contributing

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).
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ain1084/array-buffer-partitioner",
"version": "0.0.2",
"version": "0.0.3",
"description": "Partition an ArrayBuffer into multiple TypedArray views efficiently.",
"module": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
58 changes: 31 additions & 27 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,50 +30,54 @@
* ```
*/
export function createArrayBufferViews<T extends Record<string, [ArrayTypeConstructor<SupportedArrays>, number]>>(
BufferType: { new(size: number): ArrayBuffer },
config: T
): {
[K in keyof T]: InstanceType<T[K][0]>
} {
const totalSize = (Object.values(config) as ArrayView[])
.reduce((sum, [type, size]) =>
sum + (type.BYTES_PER_ELEMENT * size),
0
);
BufferType: { new(size: number): ArrayBuffer },
config: T
): {
[K in keyof T]: InstanceType<T[K][0]>
} {
const alignTo = (value: number, alignment: number) =>
(value + alignment - 1) & ~(alignment - 1);

// Create an instance of the specified buffer type with the calculated total size
const buffer = new BufferType(totalSize);
let totalSize = 0;

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

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

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

return result;
for (const key in config) {
const [type, size] = config[key];
const alignment = type.BYTES_PER_ELEMENT;
offset = alignTo(offset, alignment);
result[key] = new type(buffer, offset, size) as InstanceType<T[typeof key][0]>;
offset += alignment * size;
}

return result;
}

/**
* Represents the constructor for supported TypedArray types.
* @internal
*/
type ArrayTypeConstructor<R> = {
new(buffer: ArrayBuffer, byteOffset: number, length: number): R;
BYTES_PER_ELEMENT: number;
type ArrayTypeConstructor<R> = {
new(buffer: ArrayBuffer, byteOffset: number, length: number): R;
BYTES_PER_ELEMENT: number;
};

/**
* Lists the supported TypedArray types.
* @internal
*/
type SupportedArrays =
Float32Array | Uint32Array | Uint8Array |
Int32Array | Int16Array | Uint16Array |
Float64Array | BigInt64Array | BigUint64Array;
type SupportedArrays =
Float32Array | Uint32Array | Uint8Array |
Int32Array | Int16Array | Uint16Array |
Float64Array | BigInt64Array | BigUint64Array;

/**
* Represents the configuration for creating TypedArray views.
Expand Down
40 changes: 29 additions & 11 deletions tests/createArrayBufferViews.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,50 @@

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

const alignTo = (offset: number, alignment: number): number => {
return (offset + alignment - 1) & ~(alignment - 1)
}

describe('createArrayBufferViews', () => {
it('should create the correct TypedArray views with ArrayBuffer', () => {
const views = createArrayBufferViews(ArrayBuffer, {
data: [Float32Array, 1024],
index: [Uint32Array, 1],
flag: [Uint8Array, 1]
flag: [Uint8Array, 1],
u32: [Uint32Array, 1],
u64: [BigUint64Array, 1],
});

expect(views.data.byteOffset).toBe(0);
expect(views.data.length).toBe(1024);
expect(views.index.byteOffset).toBe(1024 * Float32Array.BYTES_PER_ELEMENT);
expect(views.index.length).toBe(1);
expect(views.flag.byteOffset).toBe(alignTo(views.index.byteOffset + Uint32Array.BYTES_PER_ELEMENT, Uint8Array.BYTES_PER_ELEMENT));
expect(views.flag.length).toBe(1);
expect(views.data.byteOffset).toBe(0);
expect(views.index.byteOffset).toBe(1024 * Float32Array.BYTES_PER_ELEMENT);
expect(views.flag.byteOffset).toBe(1024 * Float32Array.BYTES_PER_ELEMENT + Uint32Array.BYTES_PER_ELEMENT);
expect(views.u32.byteOffset).toBe(alignTo(views.flag.byteOffset + Uint8Array.BYTES_PER_ELEMENT, Uint32Array.BYTES_PER_ELEMENT));
expect(views.u32.length).toBe(1);
expect(views.u64.byteOffset).toBe(alignTo(views.u32.byteOffset + Uint32Array.BYTES_PER_ELEMENT, BigUint64Array.BYTES_PER_ELEMENT));
expect(views.u64.length).toBe(1);
});

it('should create the correct TypedArray views with SharedArrayBuffer', () => {
const views = createArrayBufferViews(SharedArrayBuffer, {
buffer1: [Int16Array, 512],
buffer2: [Uint8Array, 256]
data: [Float32Array, 1024],
index: [Uint32Array, 1],
flag: [Uint8Array, 1],
u32: [Uint32Array, 1],
u64: [BigUint64Array, 1],
});

expect(views.buffer1.length).toBe(512);
expect(views.buffer2.length).toBe(256);
expect(views.buffer1.byteOffset).toBe(0);
expect(views.buffer2.byteOffset).toBe(512 * Int16Array.BYTES_PER_ELEMENT);
expect(views.data.byteOffset).toBe(0);
expect(views.data.length).toBe(1024);
expect(views.index.byteOffset).toBe(1024 * Float32Array.BYTES_PER_ELEMENT);
expect(views.index.length).toBe(1);
expect(views.flag.byteOffset).toBe(alignTo(views.index.byteOffset + Uint32Array.BYTES_PER_ELEMENT, Uint8Array.BYTES_PER_ELEMENT));
expect(views.flag.length).toBe(1);
expect(views.u32.byteOffset).toBe(alignTo(views.flag.byteOffset + Uint8Array.BYTES_PER_ELEMENT, Uint32Array.BYTES_PER_ELEMENT));
expect(views.u32.length).toBe(1);
expect(views.u64.byteOffset).toBe(alignTo(views.u32.byteOffset + Uint32Array.BYTES_PER_ELEMENT, BigUint64Array.BYTES_PER_ELEMENT));
expect(views.u64.length).toBe(1);
});

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

0 comments on commit c01171b

Please sign in to comment.