Skip to content

Commit 2796aef

Browse files
committed
Support data alignment in the custom extensions
1 parent 12046e7 commit 2796aef

File tree

5 files changed

+53
-3
lines changed

5 files changed

+53
-3
lines changed

src/Decoder.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -687,8 +687,16 @@ export class Decoder<ContextType = undefined> {
687687
throw new DecodeError(`Max length exceeded: ext length (${size}) > maxExtLength (${this.maxExtLength})`);
688688
}
689689

690-
const extType = this.view.getInt8(this.pos + headOffset);
691-
const data = this.decodeBinary(size, headOffset + 1 /* extType */);
690+
let padding = 0;
691+
let extType = this.view.getInt8(this.pos + headOffset);
692+
693+
// 0xc1 => -63 (Int8) (noop byte)
694+
while (extType === -63) {
695+
padding++;
696+
extType = this.view.getInt8(this.pos + headOffset + padding);
697+
}
698+
699+
const data = this.decodeBinary(size, headOffset + padding + 1 /* extType */);
692700
return this.extensionCodec.decode(data, extType, this.context);
693701
}
694702

src/Encoder.ts

+8
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,14 @@ export class Encoder<ContextType = undefined> {
427427
} else {
428428
throw new Error(`Too large extension object: ${size}`);
429429
}
430+
if (ext.align && Number.isInteger(ext.align)) {
431+
const align = ext.align;
432+
const dataPos = this.pos + 1; // + extType size
433+
const padding = (align - (dataPos % align)) % align;
434+
for (let i = 0; i < padding; i++) {
435+
this.writeU8(0xc1); // noop byte
436+
}
437+
}
430438
this.writeI8(ext.type);
431439
this.writeU8a(ext.data);
432440
}

src/ExtData.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ export class ExtData {
55
constructor(
66
readonly type: number,
77
readonly data: Uint8Array,
8+
readonly align: number | undefined | null = null,
89
) {}
910
}

src/ExtensionCodec.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,28 @@ export class ExtensionCodec<ContextType = undefined> implements ExtensionCodecTy
3434
// custom extensions
3535
private readonly encoders: Array<ExtensionEncoderType<ContextType> | undefined | null> = [];
3636
private readonly decoders: Array<ExtensionDecoderType<ContextType> | undefined | null> = [];
37+
private readonly aligns: Array<number | undefined | null> = [];
3738

3839
public constructor() {
3940
this.register(timestampExtension);
4041
}
4142

4243
public register({
4344
type,
45+
align,
4446
encode,
4547
decode,
4648
}: {
4749
type: number;
50+
align?: number;
4851
encode: ExtensionEncoderType<ContextType>;
4952
decode: ExtensionDecoderType<ContextType>;
5053
}): void {
5154
if (type >= 0) {
5255
// custom extensions
5356
this.encoders[type] = encode;
5457
this.decoders[type] = decode;
58+
this.aligns[type] = align;
5559
} else {
5660
// built-in extensions
5761
const index = 1 + type;
@@ -80,7 +84,8 @@ export class ExtensionCodec<ContextType = undefined> implements ExtensionCodecTy
8084
const data = encodeExt(object, context);
8185
if (data != null) {
8286
const type = i;
83-
return new ExtData(type, data);
87+
const align = this.aligns[type];
88+
return new ExtData(type, data, align);
8489
}
8590
}
8691
}

test/ExtensionCodec.test.ts

+28
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,32 @@ describe("ExtensionCodec", () => {
196196
]);
197197
});
198198
});
199+
200+
context("custom extensions with alignment", () => {
201+
const extensionCodec = new ExtensionCodec();
202+
203+
extensionCodec.register({
204+
type: 0x01,
205+
align: 4,
206+
encode: (object: unknown): Uint8Array | null => {
207+
if (object instanceof Float32Array) {
208+
return new Uint8Array(object.buffer);
209+
}
210+
return null;
211+
},
212+
decode: (data: Uint8Array) => {
213+
return new Float32Array(data.buffer, data.byteOffset, data.byteLength / Float32Array.BYTES_PER_ELEMENT);
214+
},
215+
});
216+
217+
it("encodes and decodes Float32Array type with zero-copy", () => {
218+
const data = {
219+
position: new Float32Array([1.1, 2.2, 3.3, 4.4, 5.5]),
220+
};
221+
const encoded = encode(data, { extensionCodec });
222+
const decoded = decode(encoded, { extensionCodec });
223+
assert.deepStrictEqual(decoded, data);
224+
assert.strictEqual(decoded.position.buffer, encoded.buffer);
225+
});
226+
});
199227
});

0 commit comments

Comments
 (0)