Skip to content

Commit d4705ad

Browse files
Copilotstreamich
andcommitted
refactor: implement feedback on XDR encoder - remove schema validation, add XdrUnion, fix quadruple
Co-authored-by: streamich <9773803+streamich@users.noreply.github.com>
1 parent 55c1c4a commit d4705ad

File tree

8 files changed

+557
-458
lines changed

8 files changed

+557
-458
lines changed

src/xdr/XdrEncoder.ts

Lines changed: 35 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {BinaryJsonEncoder} from '../types';
44
/**
55
* XDR (External Data Representation) binary encoder for basic value encoding.
66
* Implements XDR binary encoding according to RFC 4506.
7-
*
7+
*
88
* Key XDR encoding principles:
99
* - All data types are aligned to 4-byte boundaries
1010
* - Multi-byte quantities are transmitted in big-endian byte order
@@ -40,10 +40,6 @@ export class XdrEncoder implements BinaryJsonEncoder {
4040
if (value === null) return this.writeVoid();
4141
const constructor = value.constructor;
4242
switch (constructor) {
43-
case Object:
44-
return this.writeObj(value as Record<string, unknown>);
45-
case Array:
46-
return this.writeArr(value as unknown[]);
4743
case Uint8Array:
4844
return this.writeBin(value as Uint8Array);
4945
default:
@@ -106,11 +102,11 @@ export class XdrEncoder implements BinaryJsonEncoder {
106102
public writeHyper(hyper: number | bigint): void {
107103
const writer = this.writer;
108104
writer.ensureCapacity(8);
109-
105+
110106
if (typeof hyper === 'bigint') {
111107
// Convert bigint to two 32-bit values for big-endian encoding
112-
const high = Number((hyper >> BigInt(32)) & BigInt(0xFFFFFFFF));
113-
const low = Number(hyper & BigInt(0xFFFFFFFF));
108+
const high = Number((hyper >> BigInt(32)) & BigInt(0xffffffff));
109+
const low = Number(hyper & BigInt(0xffffffff));
114110
writer.view.setInt32(writer.x, high, false); // high 32 bits
115111
writer.view.setUint32(writer.x + 4, low, false); // low 32 bits
116112
} else {
@@ -129,11 +125,11 @@ export class XdrEncoder implements BinaryJsonEncoder {
129125
public writeUnsignedHyper(uhyper: number | bigint): void {
130126
const writer = this.writer;
131127
writer.ensureCapacity(8);
132-
128+
133129
if (typeof uhyper === 'bigint') {
134130
// Convert bigint to two 32-bit values for big-endian encoding
135-
const high = Number((uhyper >> BigInt(32)) & BigInt(0xFFFFFFFF));
136-
const low = Number(uhyper & BigInt(0xFFFFFFFF));
131+
const high = Number((uhyper >> BigInt(32)) & BigInt(0xffffffff));
132+
const low = Number(uhyper & BigInt(0xffffffff));
137133
writer.view.setUint32(writer.x, high, false); // high 32 bits
138134
writer.view.setUint32(writer.x + 4, low, false); // low 32 bits
139135
} else {
@@ -168,12 +164,10 @@ export class XdrEncoder implements BinaryJsonEncoder {
168164

169165
/**
170166
* Writes an XDR quadruple value (128-bit float).
171-
* Note: JavaScript doesn't have native 128-bit float support, so this is a placeholder.
167+
* Note: JavaScript doesn't have native 128-bit float support.
172168
*/
173169
public writeQuadruple(quad: number): void {
174-
// Write as two doubles for now (this is not standard XDR)
175-
this.writeDouble(quad);
176-
this.writeDouble(0); // padding
170+
throw new Error('not implemented');
177171
}
178172

179173
/**
@@ -184,14 +178,14 @@ export class XdrEncoder implements BinaryJsonEncoder {
184178
if (data.length !== size) {
185179
throw new Error(`Opaque data length ${data.length} does not match expected size ${size}`);
186180
}
187-
181+
188182
const writer = this.writer;
189-
const paddedSize = this.getPaddedSize(size);
183+
const paddedSize = Math.ceil(size / 4) * 4;
190184
writer.ensureCapacity(paddedSize);
191-
185+
192186
// Write data
193187
writer.buf(data, size);
194-
188+
195189
// Write padding bytes
196190
const padding = paddedSize - size;
197191
for (let i = 0; i < padding; i++) {
@@ -205,14 +199,14 @@ export class XdrEncoder implements BinaryJsonEncoder {
205199
*/
206200
public writeVarlenOpaque(data: Uint8Array): void {
207201
this.writeUnsignedInt(data.length);
208-
202+
209203
const writer = this.writer;
210-
const paddedSize = this.getPaddedSize(data.length);
204+
const paddedSize = Math.ceil(data.length / 4) * 4;
211205
writer.ensureCapacity(paddedSize);
212-
206+
213207
// Write data
214208
writer.buf(data, data.length);
215-
209+
216210
// Write padding bytes
217211
const padding = paddedSize - data.length;
218212
for (let i = 0; i < padding; i++) {
@@ -226,47 +220,32 @@ export class XdrEncoder implements BinaryJsonEncoder {
226220
*/
227221
public writeStr(str: string): void {
228222
const writer = this.writer;
229-
const encoder = new TextEncoder();
230-
const utf8Bytes = encoder.encode(str);
231-
232-
// Write length
233-
this.writeUnsignedInt(utf8Bytes.length);
234-
235-
// Write string data with padding
236-
const paddedSize = this.getPaddedSize(utf8Bytes.length);
237-
writer.ensureCapacity(paddedSize);
238-
239-
// Write UTF-8 bytes
240-
writer.buf(utf8Bytes, utf8Bytes.length);
241-
242-
// Write padding bytes
243-
const padding = paddedSize - utf8Bytes.length;
223+
224+
// Write string using writer's UTF-8 method and get actual byte count
225+
const lengthOffset = writer.x;
226+
writer.x += 4; // Reserve space for length
227+
const bytesWritten = writer.utf8(str);
228+
229+
// Calculate and write padding
230+
const paddedSize = Math.ceil(bytesWritten / 4) * 4;
231+
const padding = paddedSize - bytesWritten;
244232
for (let i = 0; i < padding; i++) {
245233
writer.u8(0);
246234
}
235+
236+
// Go back and write the actual byte length
237+
const currentPos = writer.x;
238+
writer.x = lengthOffset;
239+
this.writeUnsignedInt(bytesWritten);
240+
writer.x = currentPos;
247241
}
248242

249-
/**
250-
* Writes XDR variable-length array.
251-
* Length is written first, followed by array elements.
252-
*/
253243
public writeArr(arr: unknown[]): void {
254-
this.writeUnsignedInt(arr.length);
255-
for (const item of arr) {
256-
this.writeAny(item);
257-
}
244+
throw new Error('writeArr not implemented in XDR encoder');
258245
}
259246

260-
/**
261-
* Writes XDR structure as a simple mapping (not standard XDR, for compatibility).
262-
*/
263247
public writeObj(obj: Record<string, unknown>): void {
264-
const entries = Object.entries(obj);
265-
this.writeUnsignedInt(entries.length);
266-
for (const [key, value] of entries) {
267-
this.writeStr(key);
268-
this.writeAny(value);
269-
}
248+
throw new Error('writeObj not implemented in XDR encoder');
270249
}
271250

272251
// BinaryJsonEncoder interface methods
@@ -313,11 +292,4 @@ export class XdrEncoder implements BinaryJsonEncoder {
313292
public writeAsciiStr(str: string): void {
314293
this.writeStr(str);
315294
}
316-
317-
/**
318-
* Calculates the padded size for 4-byte alignment.
319-
*/
320-
private getPaddedSize(size: number): number {
321-
return Math.ceil(size / 4) * 4;
322-
}
323-
}
295+
}

0 commit comments

Comments
 (0)