1+ import { Reader } from '@jsonjoy.com/buffers/lib/Reader' ;
2+ import type { IReader , IReaderResettable } from '@jsonjoy.com/buffers/lib' ;
3+ import type { BinaryJsonDecoder } from '../types' ;
4+
5+ /**
6+ * XDR (External Data Representation) binary decoder for basic value decoding.
7+ * Implements XDR binary decoding according to RFC 4506.
8+ *
9+ * Key XDR decoding principles:
10+ * - All data types are aligned to 4-byte boundaries
11+ * - Multi-byte quantities are transmitted in big-endian byte order
12+ * - Strings and opaque data are padded to 4-byte boundaries
13+ * - Variable-length arrays and strings are preceded by their length
14+ */
15+ export class XdrDecoder < R extends IReader & IReaderResettable = IReader & IReaderResettable >
16+ implements BinaryJsonDecoder
17+ {
18+ public constructor ( public reader : R = new Reader ( ) as any ) { }
19+
20+ public read ( uint8 : Uint8Array ) : unknown {
21+ this . reader . reset ( uint8 ) ;
22+ return this . readAny ( ) ;
23+ }
24+
25+ public decode ( uint8 : Uint8Array ) : unknown {
26+ this . reader . reset ( uint8 ) ;
27+ return this . readAny ( ) ;
28+ }
29+
30+ public readAny ( ) : unknown {
31+ // Basic implementation - in practice this would need schema info
32+ // For now, we'll throw as this should be used with schema decoder
33+ throw new Error ( 'XdrDecoder.readAny() requires explicit type methods or use XdrSchemaDecoder' ) ;
34+ }
35+
36+ /**
37+ * Reads an XDR void value (no data is actually read).
38+ */
39+ public readVoid ( ) : void {
40+ // Void values have no representation in XDR
41+ }
42+
43+ /**
44+ * Reads an XDR boolean value as a 4-byte integer.
45+ * Returns true for non-zero values, false for zero.
46+ */
47+ public readBoolean ( ) : boolean {
48+ return this . readInt ( ) !== 0 ;
49+ }
50+
51+ /**
52+ * Reads an XDR signed 32-bit integer in big-endian format.
53+ */
54+ public readInt ( ) : number {
55+ const reader = this . reader ;
56+ const value = reader . view . getInt32 ( reader . x , false ) ; // false = big-endian
57+ reader . x += 4 ;
58+ return value ;
59+ }
60+
61+ /**
62+ * Reads an XDR unsigned 32-bit integer in big-endian format.
63+ */
64+ public readUnsignedInt ( ) : number {
65+ const reader = this . reader ;
66+ const value = reader . view . getUint32 ( reader . x , false ) ; // false = big-endian
67+ reader . x += 4 ;
68+ return value ;
69+ }
70+
71+ /**
72+ * Reads an XDR signed 64-bit integer (hyper) in big-endian format.
73+ */
74+ public readHyper ( ) : bigint {
75+ const reader = this . reader ;
76+ const value = reader . view . getBigInt64 ( reader . x , false ) ; // false = big-endian
77+ reader . x += 8 ;
78+ return value ;
79+ }
80+
81+ /**
82+ * Reads an XDR unsigned 64-bit integer (unsigned hyper) in big-endian format.
83+ */
84+ public readUnsignedHyper ( ) : bigint {
85+ const reader = this . reader ;
86+ const value = reader . view . getBigUint64 ( reader . x , false ) ; // false = big-endian
87+ reader . x += 8 ;
88+ return value ;
89+ }
90+
91+ /**
92+ * Reads an XDR float value using IEEE 754 single-precision in big-endian format.
93+ */
94+ public readFloat ( ) : number {
95+ const reader = this . reader ;
96+ const value = reader . view . getFloat32 ( reader . x , false ) ; // false = big-endian
97+ reader . x += 4 ;
98+ return value ;
99+ }
100+
101+ /**
102+ * Reads an XDR double value using IEEE 754 double-precision in big-endian format.
103+ */
104+ public readDouble ( ) : number {
105+ const reader = this . reader ;
106+ const value = reader . view . getFloat64 ( reader . x , false ) ; // false = big-endian
107+ reader . x += 8 ;
108+ return value ;
109+ }
110+
111+ /**
112+ * Reads an XDR quadruple value (128-bit float).
113+ * Note: JavaScript doesn't have native 128-bit float support.
114+ */
115+ public readQuadruple ( ) : number {
116+ throw new Error ( 'not implemented' ) ;
117+ }
118+
119+ /**
120+ * Reads XDR opaque data with known fixed length.
121+ * Data is padded to 4-byte boundary but only the actual data is returned.
122+ */
123+ public readOpaque ( size : number ) : Uint8Array {
124+ const reader = this . reader ;
125+ const data = new Uint8Array ( size ) ;
126+
127+ // Read actual data
128+ for ( let i = 0 ; i < size ; i ++ ) {
129+ data [ i ] = reader . u8 ( ) ;
130+ }
131+
132+ // Skip padding bytes to reach 4-byte boundary
133+ const paddedSize = Math . ceil ( size / 4 ) * 4 ;
134+ const padding = paddedSize - size ;
135+ reader . skip ( padding ) ;
136+
137+ return data ;
138+ }
139+
140+ /**
141+ * Reads XDR variable-length opaque data.
142+ * Length is read first, followed by data padded to 4-byte boundary.
143+ */
144+ public readVarlenOpaque ( ) : Uint8Array {
145+ const size = this . readUnsignedInt ( ) ;
146+ return this . readOpaque ( size ) ;
147+ }
148+
149+ /**
150+ * Reads an XDR string with UTF-8 encoding.
151+ * Length is read first, followed by UTF-8 bytes padded to 4-byte boundary.
152+ */
153+ public readString ( ) : string {
154+ const size = this . readUnsignedInt ( ) ;
155+ const reader = this . reader ;
156+
157+ // Read UTF-8 bytes
158+ const utf8Bytes = new Uint8Array ( size ) ;
159+ for ( let i = 0 ; i < size ; i ++ ) {
160+ utf8Bytes [ i ] = reader . u8 ( ) ;
161+ }
162+
163+ // Skip padding bytes to reach 4-byte boundary
164+ const paddedSize = Math . ceil ( size / 4 ) * 4 ;
165+ const padding = paddedSize - size ;
166+ reader . skip ( padding ) ;
167+
168+ // Decode UTF-8 to string
169+ return new TextDecoder ( 'utf-8' ) . decode ( utf8Bytes ) ;
170+ }
171+
172+ /**
173+ * Reads an XDR enum value as an unsigned integer.
174+ */
175+ public readEnum ( ) : number {
176+ return this . readInt ( ) ;
177+ }
178+
179+ /**
180+ * Reads a fixed-size array of elements.
181+ * Caller must provide the decode function for each element.
182+ */
183+ public readArray < T > ( size : number , elementReader : ( ) => T ) : T [ ] {
184+ const array : T [ ] = [ ] ;
185+ for ( let i = 0 ; i < size ; i ++ ) {
186+ array . push ( elementReader ( ) ) ;
187+ }
188+ return array ;
189+ }
190+
191+ /**
192+ * Reads a variable-length array of elements.
193+ * Length is read first, followed by elements.
194+ */
195+ public readVarlenArray < T > ( elementReader : ( ) => T ) : T [ ] {
196+ const size = this . readUnsignedInt ( ) ;
197+ return this . readArray ( size , elementReader ) ;
198+ }
199+ }
0 commit comments