Skip to content

feat: remove use of 'buffer' polyfill for webhid and Buffer types #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/src/__tests__/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as fs from 'fs'
import * as path from 'path'

export function readFixtureJSON(fileName: string): Buffer {
export function readFixtureJSON(fileName: string): Uint8Array {
const filePath = path.resolve(__dirname, '../../../../fixtures', fileName)
const fileData = fs.readFileSync(filePath)
return Buffer.from(JSON.parse(fileData.toString()) as Array<number>)
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/__tests__/hid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export class DummyHID extends EventEmitter implements HIDDevice {
expect(typeof path).toEqual('string')
}

public async sendFeatureReport(_data: Buffer): Promise<void> {
public async sendFeatureReport(_data: Uint8Array): Promise<void> {
throw new Error('Method not implemented.')
}
public async getFeatureReport(_reportId: number, _reportLength: number): Promise<Buffer> {
public async getFeatureReport(_reportId: number, _reportLength: number): Promise<Uint8Array> {
throw new Error('Method not implemented.')
}
public async sendReports(_data: Buffer[]): Promise<void> {
public async sendReports(_data: Uint8Array[]): Promise<void> {
throw new Error('Method not implemented.')
}
public async close(): Promise<void> {
Expand Down
18 changes: 9 additions & 9 deletions packages/core/src/__tests__/test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { DummyHID } from './hid'
import { EncodeJPEGHelper } from '../models/base'

function openStreamDeck(path: string, deviceModel: DeviceModelId, userOptions?: OpenStreamDeckOptions): StreamDeck {
const encodeJpegMock: jest.MockedFunction<EncodeJPEGHelper> = jest.fn((_b: Buffer, _w: number, _h: number) => {
const encodeJpegMock: jest.MockedFunction<EncodeJPEGHelper> = jest.fn((_b: Uint8Array, _w: number, _h: number) => {
throw new Error('Not implemented')
})
const options: Required<OpenStreamDeckOptions> = {
Expand Down Expand Up @@ -115,7 +115,7 @@ function runForDevice(path: string, model: DeviceModelId): void {

test('firmwareVersion', async () => {
const device = getDevice()
device.getFeatureReport = async (): Promise<Buffer> => {
device.getFeatureReport = async (): Promise<Uint8Array> => {
return Buffer.from([4, 85, 170, 212, 4, 49, 46, 48, 46, 49, 55, 48, 49, 51, 51, 0, 0])
}

Expand All @@ -125,7 +125,7 @@ function runForDevice(path: string, model: DeviceModelId): void {

test('serialNumber', async () => {
const device = getDevice()
device.getFeatureReport = async (): Promise<Buffer> => {
device.getFeatureReport = async (): Promise<Uint8Array> => {
return Buffer.from([3, 85, 170, 211, 3, 65, 76, 51, 55, 71, 49, 65, 48, 50, 56, 52, 48])
}

Expand Down Expand Up @@ -167,7 +167,7 @@ function runForDevice(path: string, model: DeviceModelId): void {

test('firmwareVersion-jpeg', async () => {
const device = getDevice()
device.getFeatureReport = async (): Promise<Buffer> => {
device.getFeatureReport = async (): Promise<Uint8Array> => {
// prettier-ignore
return Buffer.from([ 5, 12, 254, 90, 239, 250, 49, 46, 48, 48, 46, 48, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
}
Expand All @@ -178,7 +178,7 @@ function runForDevice(path: string, model: DeviceModelId): void {

test('serialNumber-jpeg', async () => {
const device = getDevice()
device.getFeatureReport = async (): Promise<Buffer> => {
device.getFeatureReport = async (): Promise<Uint8Array> => {
// prettier-ignore
return Buffer.from([ 6, 12, 67, 76, 49, 56, 73, 49, 65, 48, 48, 57, 49, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
}
Expand Down Expand Up @@ -589,7 +589,7 @@ describe('StreamDeck XL', () => {

test('firmwareVersion', async () => {
const device = getDevice()
device.getFeatureReport = async (): Promise<Buffer> => {
device.getFeatureReport = async (): Promise<Uint8Array> => {
// prettier-ignore
return Buffer.from([ 5, 12, 254, 90, 239, 250, 49, 46, 48, 48, 46, 48, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
}
Expand All @@ -600,7 +600,7 @@ describe('StreamDeck XL', () => {

test('serialNumber', async () => {
const device = getDevice()
device.getFeatureReport = async (): Promise<Buffer> => {
device.getFeatureReport = async (): Promise<Uint8Array> => {
// prettier-ignore
return Buffer.from([ 6, 12, 67, 76, 49, 56, 73, 49, 65, 48, 48, 57, 49, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
}
Expand All @@ -611,7 +611,7 @@ describe('StreamDeck XL', () => {

test('fillImage', async () => {
const device = getDevice()
device.encodeJPEG.mockImplementationOnce(async (buffer: Buffer) => {
device.encodeJPEG.mockImplementationOnce(async (buffer: Uint8Array) => {
const start = buffer.length / 8
return buffer.subarray(start, start * 2)
})
Expand Down Expand Up @@ -664,7 +664,7 @@ describe('StreamDeck Original V2', () => {

test('fillImage', async () => {
const device = getDevice()
device.encodeJPEG.mockImplementationOnce(async (buffer: Buffer) => {
device.encodeJPEG.mockImplementationOnce(async (buffer: Uint8Array) => {
const start = buffer.length / 8
return buffer.subarray(start, start * 2)
})
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/__tests__/util.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { transformImageBuffer } from '../util'

function getSimpleBuffer(dim: number, components: 3 | 4): Buffer {
function getSimpleBuffer(dim: number, components: 3 | 4): Uint8Array {
const buf = Buffer.alloc(dim * dim * components)
for (let i = 0; i < buf.length; i++) {
buf[i] = i
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ export interface HIDDevice {

close(): Promise<void>

sendFeatureReport(data: Buffer): Promise<void>
getFeatureReport(reportId: number, reportLength: number): Promise<Buffer>
sendFeatureReport(data: Uint8Array): Promise<void>
getFeatureReport(reportId: number, reportLength: number): Promise<Uint8Array>

sendReports(buffers: Buffer[]): Promise<void>
sendReports(buffers: Uint8Array[]): Promise<void>

getDeviceInfo(): Promise<HIDDeviceInfo>
}
Expand Down
70 changes: 39 additions & 31 deletions packages/core/src/imageWriter/headerGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { uint8ArrayToDataView } from '../util'
import type { StreamdeckImageHeaderGenerator, StreamdeckImageWriterProps } from './types'

export class StreamdeckGen1ImageHeaderGenerator implements StreamdeckImageHeaderGenerator {
Expand All @@ -6,18 +7,19 @@ export class StreamdeckGen1ImageHeaderGenerator implements StreamdeckImageHeader
}

writeFillImageCommandHeader(
buffer: Buffer,
buffer: Uint8Array,
props: StreamdeckImageWriterProps,
partIndex: number,
isLast: boolean,
_bodyLength: number
): void {
buffer.writeUInt8(0x02, 0)
buffer.writeUInt8(0x01, 1)
buffer.writeUInt16LE(partIndex, 2)
// 3 = 0x00
buffer.writeUInt8(isLast ? 1 : 0, 4)
buffer.writeUInt8(props.keyIndex + 1, 5)
const bufferView = uint8ArrayToDataView(buffer)

bufferView.setUint8(0, 0x02)
bufferView.setUint8(2, 0x01)
bufferView.setUint16(2, partIndex, true)
bufferView.setUint8(4, isLast ? 1 : 0)
bufferView.setUint8(5, props.keyIndex + 1)
}
}

Expand All @@ -27,18 +29,20 @@ export class StreamdeckGen2ImageHeaderGenerator implements StreamdeckImageHeader
}

writeFillImageCommandHeader(
buffer: Buffer,
buffer: Uint8Array,
props: StreamdeckImageWriterProps,
partIndex: number,
isLast: boolean,
bodyLength: number
): void {
buffer.writeUInt8(0x02, 0)
buffer.writeUInt8(0x07, 1)
buffer.writeUInt8(props.keyIndex, 2)
buffer.writeUInt8(isLast ? 1 : 0, 3)
buffer.writeUInt16LE(bodyLength, 4)
buffer.writeUInt16LE(partIndex++, 6)
const bufferView = uint8ArrayToDataView(buffer)

bufferView.setUint8(0, 0x02)
bufferView.setUint8(1, 0x07)
bufferView.setUint8(2, props.keyIndex)
bufferView.setUint8(3, isLast ? 1 : 0)
bufferView.setUint16(4, bodyLength, true)
bufferView.setUint16(4, partIndex++, true)
}
}

Expand All @@ -56,21 +60,23 @@ export class StreamdeckPlusLcdImageHeaderGenerator
}

writeFillImageCommandHeader(
buffer: Buffer,
buffer: Uint8Array,
props: StreamdeckPlusHeaderProps,
partIndex: number,
isLast: boolean,
bodyLength: number
): void {
buffer.writeUInt8(0x02, 0)
buffer.writeUInt8(0x0c, 1)
buffer.writeUInt16LE(props.x, 2)
buffer.writeUInt16LE(props.y, 4)
buffer.writeUInt16LE(props.width, 6)
buffer.writeUInt16LE(props.height, 8)
buffer.writeUInt8(isLast ? 1 : 0, 10) // Is last
buffer.writeUInt16LE(partIndex, 11)
buffer.writeUInt16LE(bodyLength, 13)
const bufferView = uint8ArrayToDataView(buffer)

bufferView.setUint8(0, 0x02)
bufferView.setUint8(1, 0x0c)
bufferView.setUint16(2, props.x, true)
bufferView.setUint16(4, props.y, true)
bufferView.setUint16(6, props.width, true)
bufferView.setUint16(8, props.height, true)
bufferView.setUint8(10, isLast ? 1 : 0)
bufferView.setUint16(11, partIndex, true)
bufferView.setUint16(13, bodyLength, true)
}
}

Expand All @@ -80,17 +86,19 @@ export class StreamdeckNeoLcdImageHeaderGenerator implements StreamdeckImageHead
}

writeFillImageCommandHeader(
buffer: Buffer,
buffer: Uint8Array,
_props: never,
partIndex: number,
isLast: boolean,
bodyLength: number
): void {
buffer.writeUInt8(0x02, 0)
buffer.writeUInt8(0x0b, 1)
buffer.writeUInt8(0, 2)
buffer.writeUInt8(isLast ? 1 : 0, 3)
buffer.writeUInt16LE(bodyLength, 4)
buffer.writeUInt16LE(partIndex++, 6)
const bufferView = uint8ArrayToDataView(buffer)

bufferView.setUint8(0, 0x02)
bufferView.setUint8(1, 0x0b)
bufferView.setUint8(2, 0)
bufferView.setUint8(3, isLast ? 1 : 0)
bufferView.setUint16(4, bodyLength, true)
bufferView.setUint16(6, partIndex, true)
}
}
18 changes: 9 additions & 9 deletions packages/core/src/imageWriter/imageWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import { StreamdeckImageHeaderGenerator, StreamdeckImageWriter, StreamdeckImageW
export class StreamdeckOriginalImageWriter implements StreamdeckImageWriter {
private readonly headerGenerator = new StreamdeckGen1ImageHeaderGenerator()

public generateFillImageWrites(props: StreamdeckImageWriterProps, byteBuffer: Buffer): Buffer[] {
public generateFillImageWrites(props: StreamdeckImageWriterProps, byteBuffer: Uint8Array): Uint8Array[] {
const MAX_PACKET_SIZE = 8191
const PACKET_HEADER_LENGTH = this.headerGenerator.getFillImageCommandHeaderLength()

// The original uses larger packets, and splits the payload equally across 2

const packet1Bytes = byteBuffer.length / 2

const packet1 = Buffer.alloc(MAX_PACKET_SIZE)
const packet1 = new Uint8Array(MAX_PACKET_SIZE)
this.headerGenerator.writeFillImageCommandHeader(packet1, props, 0x01, false, packet1Bytes)
byteBuffer.copy(packet1, PACKET_HEADER_LENGTH, 0, packet1Bytes)
packet1.set(byteBuffer.subarray(0, packet1Bytes), PACKET_HEADER_LENGTH)

const packet2 = Buffer.alloc(MAX_PACKET_SIZE)
const packet2 = new Uint8Array(MAX_PACKET_SIZE)
this.headerGenerator.writeFillImageCommandHeader(packet2, props, 0x02, true, packet1Bytes)
byteBuffer.copy(packet2, PACKET_HEADER_LENGTH, packet1Bytes)
packet2.set(byteBuffer.subarray(packet1Bytes), PACKET_HEADER_LENGTH)

return [packet1, packet2]
}
Expand All @@ -33,16 +33,16 @@ export class StreamdeckDefaultImageWriter<TProps = StreamdeckImageWriterProps>
this.headerGenerator = headerGenerator
}

public generateFillImageWrites(props: TProps, byteBuffer: Buffer): Buffer[] {
public generateFillImageWrites(props: TProps, byteBuffer: Uint8Array): Uint8Array[] {
const MAX_PACKET_SIZE = 1024
const PACKET_HEADER_LENGTH = this.headerGenerator.getFillImageCommandHeaderLength()
const MAX_PAYLOAD_SIZE = MAX_PACKET_SIZE - PACKET_HEADER_LENGTH

const result: Buffer[] = []
const result: Uint8Array[] = []

let remainingBytes = byteBuffer.length
for (let part = 0; remainingBytes > 0; part++) {
const packet = Buffer.alloc(MAX_PACKET_SIZE)
const packet = new Uint8Array(MAX_PACKET_SIZE)

const byteCount = Math.min(remainingBytes, MAX_PAYLOAD_SIZE)
this.headerGenerator.writeFillImageCommandHeader(
Expand All @@ -55,7 +55,7 @@ export class StreamdeckDefaultImageWriter<TProps = StreamdeckImageWriterProps>

const byteOffset = byteBuffer.length - remainingBytes
remainingBytes -= byteCount
byteBuffer.copy(packet, PACKET_HEADER_LENGTH, byteOffset, byteOffset + byteCount)
packet.set(byteBuffer.subarray(byteOffset, byteOffset + byteCount), PACKET_HEADER_LENGTH)

result.push(packet)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/imageWriter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ export interface StreamdeckImageWriterProps {
}

export interface StreamdeckImageWriter<TProps = StreamdeckImageWriterProps> {
generateFillImageWrites(props: TProps, byteBuffer: Buffer): Buffer[]
generateFillImageWrites(props: TProps, byteBuffer: Uint8Array): Uint8Array[]
}

export interface StreamdeckImageHeaderGenerator<TProps = StreamdeckImageWriterProps> {
getFillImageCommandHeaderLength(): number
writeFillImageCommandHeader(
buffer: Buffer,
buffer: Uint8Array,
props: TProps,
partIndex: number,
isLast: boolean,
Expand Down
12 changes: 6 additions & 6 deletions packages/core/src/models/base-gen1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export abstract class StreamDeckGen1Base extends StreamDeckBase {
}

// prettier-ignore
const brightnessCommandBuffer = Buffer.from([
const brightnessCommandBuffer = Uint8Array.from([
0x05,
0x55, 0xaa, 0xd1, 0x01, percentage, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
Expand All @@ -43,7 +43,7 @@ export abstract class StreamDeckGen1Base extends StreamDeckBase {

public async resetToLogo(): Promise<void> {
// prettier-ignore
const resetCommandBuffer = Buffer.from([
const resetCommandBuffer = Uint8Array.from([
0x0b,
0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
Expand All @@ -52,25 +52,25 @@ export abstract class StreamDeckGen1Base extends StreamDeckBase {
}

public async getFirmwareVersion(): Promise<string> {
let val: Buffer
let val: Uint8Array
try {
val = await this.device.getFeatureReport(4, 32)
} catch (e) {
// In case some devices can't handle the different report length
val = await this.device.getFeatureReport(4, 17)
}
const end = val.indexOf(0, 5)
return val.toString('ascii', 5, end === -1 ? undefined : end)
return new TextDecoder('ascii').decode(val.subarray(5, end === -1 ? undefined : end))
}

public async getSerialNumber(): Promise<string> {
let val: Buffer
let val: Uint8Array
try {
val = await this.device.getFeatureReport(3, 32)
} catch (e) {
// In case some devices can't handle the different report length
val = await this.device.getFeatureReport(3, 17)
}
return val.toString('ascii', 5, 17)
return new TextDecoder('ascii').decode(val.subarray(5, 17))
}
}
13 changes: 8 additions & 5 deletions packages/core/src/models/base-gen2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,20 @@ export abstract class StreamDeckGen2Base extends StreamDeckBase {

public async getFirmwareVersion(): Promise<string> {
const val = await this.device.getFeatureReport(5, 32)
const end = val.readUInt8(1) + 2
return val.toString('ascii', 6, end)
const end = val[1] + 2
return new TextDecoder('ascii').decode(val.subarray(6, end))
}

public async getSerialNumber(): Promise<string> {
const val = await this.device.getFeatureReport(6, 32)
const end = val.readUInt8(1) + 2
return val.toString('ascii', 2, end)
const end = val[1] + 2
return new TextDecoder('ascii').decode(val.subarray(2, end))
}

protected async convertFillImage(sourceBuffer: Buffer, sourceOptions: InternalFillImageOptions): Promise<Buffer> {
protected async convertFillImage(
sourceBuffer: Uint8Array,
sourceOptions: InternalFillImageOptions
): Promise<Uint8Array> {
const byteBuffer = transformImageBuffer(
sourceBuffer,
sourceOptions,
Expand Down
Loading
Loading