|
| 1 | +# XDR Quick Reference Guide |
| 2 | + |
| 3 | +## RFC Version Support |
| 4 | + |
| 5 | +| Feature | RFC 1014 | RFC 1832 | RFC 4506 | Status | |
| 6 | +| ----------------------------- | -------- | -------- | -------- | -------------- | |
| 7 | +| Basic types (int, bool, enum) | ✅ | ✅ | ✅ | ✅ Implemented | |
| 8 | +| Hyper integers (64-bit) | ✅ | ✅ | ✅ | ✅ Implemented | |
| 9 | +| Float, Double | ✅ | ✅ | ✅ | ✅ Implemented | |
| 10 | +| Quadruple (128-bit float) | ❌ | ✅ | ✅ | ⚠️ Type only | |
| 11 | +| Opaque, String | ✅ | ✅ | ✅ | ✅ Implemented | |
| 12 | +| Array, Struct, Union | ✅ | ✅ | ✅ | ✅ Implemented | |
| 13 | +| Optional-data | ❌ | ✅ | ✅ | ✅ Implemented | |
| 14 | +| Security guidelines | ❌ | ❌ | ✅ | ✅ Implemented | |
| 15 | + |
| 16 | +## Data Type Quick Reference |
| 17 | + |
| 18 | +### Primitive Types |
| 19 | + |
| 20 | +```typescript |
| 21 | +// Integer types |
| 22 | +{ type: 'int' } // 32-bit signed: -2^31 to 2^31-1 |
| 23 | +{ type: 'unsigned_int' } // 32-bit unsigned: 0 to 2^32-1 |
| 24 | +{ type: 'hyper' } // 64-bit signed |
| 25 | +{ type: 'unsigned_hyper' } // 64-bit unsigned |
| 26 | +{ type: 'boolean' } // Encoded as int (0/1) |
| 27 | + |
| 28 | +// Floating-point types |
| 29 | +{ type: 'float' } // IEEE 754 single-precision (32-bit) |
| 30 | +{ type: 'double' } // IEEE 754 double-precision (64-bit) |
| 31 | +{ type: 'quadruple' } // IEEE 754 quad-precision (128-bit) - not implemented |
| 32 | + |
| 33 | +// Special types |
| 34 | +{ type: 'void' } // No data |
| 35 | +{ type: 'enum', values: { RED: 0, GREEN: 1 } } |
| 36 | +``` |
| 37 | + |
| 38 | +### Composite Types |
| 39 | + |
| 40 | +```typescript |
| 41 | +// Fixed-length opaque data |
| 42 | +{ type: 'opaque', size: 16 } |
| 43 | + |
| 44 | +// Variable-length opaque data (max size optional) |
| 45 | +{ type: 'vopaque' } |
| 46 | +{ type: 'vopaque', size: 1024 } |
| 47 | + |
| 48 | +// String (max size optional) |
| 49 | +{ type: 'string' } |
| 50 | +{ type: 'string', size: 255 } |
| 51 | + |
| 52 | +// Fixed-length array |
| 53 | +{ type: 'array', elements: { type: 'int' }, size: 10 } |
| 54 | + |
| 55 | +// Variable-length array (max size optional) |
| 56 | +{ type: 'varray', elements: { type: 'int' } } |
| 57 | +{ type: 'varray', elements: { type: 'int' }, size: 100 } |
| 58 | + |
| 59 | +// Struct |
| 60 | +{ |
| 61 | + type: 'struct', |
| 62 | + fields: [ |
| 63 | + [{ type: 'int' }, 'id'], |
| 64 | + [{ type: 'string' }, 'name'] |
| 65 | + ] |
| 66 | +} |
| 67 | + |
| 68 | +// Union |
| 69 | +{ |
| 70 | + type: 'union', |
| 71 | + arms: [ |
| 72 | + [0, { type: 'int' }], |
| 73 | + [1, { type: 'string' }] |
| 74 | + ], |
| 75 | + default?: { type: 'void' } |
| 76 | +} |
| 77 | + |
| 78 | +// Optional-data (NEW in RFC 1832) |
| 79 | +{ type: 'optional', element: { type: 'int' } } |
| 80 | +``` |
| 81 | + |
| 82 | +## Usage Examples |
| 83 | + |
| 84 | +### Basic Encoding/Decoding |
| 85 | + |
| 86 | +```typescript |
| 87 | +import {XdrEncoder, XdrDecoder, Writer, Reader} from '@jsonjoy.com/json-pack'; |
| 88 | + |
| 89 | +// Encode |
| 90 | +const writer = new Writer(); |
| 91 | +const encoder = new XdrEncoder(writer); |
| 92 | +encoder.writeInt(42); |
| 93 | +encoder.writeString('hello'); |
| 94 | +const encoded = writer.flush(); |
| 95 | + |
| 96 | +// Decode |
| 97 | +const reader = new Reader(); |
| 98 | +const decoder = new XdrDecoder(reader); |
| 99 | +reader.reset(encoded); |
| 100 | +const num = decoder.readInt(); // 42 |
| 101 | +const str = decoder.readString(); // "hello" |
| 102 | +``` |
| 103 | + |
| 104 | +### Schema-Based Encoding/Decoding |
| 105 | + |
| 106 | +```typescript |
| 107 | +import {XdrSchemaEncoder, XdrSchemaDecoder, Writer, Reader} from '@jsonjoy.com/json-pack'; |
| 108 | + |
| 109 | +const schema = { |
| 110 | + type: 'struct', |
| 111 | + fields: [ |
| 112 | + [{type: 'int'}, 'id'], |
| 113 | + [{type: 'string', size: 100}, 'name'], |
| 114 | + [{type: 'boolean'}, 'active'], |
| 115 | + ], |
| 116 | +}; |
| 117 | + |
| 118 | +// Encode |
| 119 | +const writer = new Writer(); |
| 120 | +const encoder = new XdrSchemaEncoder(writer); |
| 121 | +const data = {id: 1, name: 'Alice', active: true}; |
| 122 | +const encoded = encoder.encode(data, schema); |
| 123 | + |
| 124 | +// Decode |
| 125 | +const reader = new Reader(); |
| 126 | +const decoder = new XdrSchemaDecoder(reader); |
| 127 | +const decoded = decoder.decode(encoded, schema); |
| 128 | +// { id: 1, name: 'Alice', active: true } |
| 129 | +``` |
| 130 | + |
| 131 | +### Optional-Data (RFC 1832) |
| 132 | + |
| 133 | +```typescript |
| 134 | +const schema = { |
| 135 | + type: 'optional', |
| 136 | + element: {type: 'int'}, |
| 137 | +}; |
| 138 | + |
| 139 | +// Encode optional value |
| 140 | +encoder.writeOptional(42, schema); // Encodes: TRUE + 42 |
| 141 | +encoder.writeOptional(null, schema); // Encodes: FALSE |
| 142 | +encoder.writeOptional(undefined, schema); // Encodes: FALSE |
| 143 | + |
| 144 | +// Decode optional value |
| 145 | +const value = decoder.readOptional(schema); // number | null |
| 146 | +``` |
| 147 | + |
| 148 | +### Union Types |
| 149 | + |
| 150 | +```typescript |
| 151 | +import {XdrUnion} from '@jsonjoy.com/json-pack'; |
| 152 | + |
| 153 | +const schema = { |
| 154 | + type: 'union', |
| 155 | + arms: [ |
| 156 | + [0, {type: 'int'}], |
| 157 | + [1, {type: 'string'}], |
| 158 | + ], |
| 159 | +}; |
| 160 | + |
| 161 | +// Encode union |
| 162 | +const intValue = new XdrUnion(0, 42); |
| 163 | +const strValue = new XdrUnion(1, 'hello'); |
| 164 | +encoder.encode(intValue, schema); |
| 165 | + |
| 166 | +// Decode union |
| 167 | +const decoded = decoder.decode(data, schema); // XdrUnion instance |
| 168 | +console.log(decoded.discriminant); // 0 or 1 |
| 169 | +console.log(decoded.value); // 42 or "hello" |
| 170 | +``` |
| 171 | + |
| 172 | +### Schema Validation |
| 173 | + |
| 174 | +```typescript |
| 175 | +import {XdrSchemaValidator} from '@jsonjoy.com/json-pack'; |
| 176 | + |
| 177 | +const validator = new XdrSchemaValidator(); |
| 178 | + |
| 179 | +// Validate schema structure |
| 180 | +const isValidSchema = validator.validateSchema(schema); // boolean |
| 181 | + |
| 182 | +// Validate value against schema |
| 183 | +const isValidValue = validator.validateValue(data, schema); // boolean |
| 184 | +``` |
| 185 | + |
| 186 | +## Security Best Practices |
| 187 | + |
| 188 | +### Always Use Size Limits |
| 189 | + |
| 190 | +```typescript |
| 191 | +// ❌ Bad - no maximum size |
| 192 | +{ type: 'string' } |
| 193 | +{ type: 'varray', elements: { type: 'int' } } |
| 194 | + |
| 195 | +// ✅ Good - explicit maximum size |
| 196 | +{ type: 'string', size: 1024 } |
| 197 | +{ type: 'varray', elements: { type: 'int' }, size: 100 } |
| 198 | +``` |
| 199 | + |
| 200 | +### Validate Before Encoding |
| 201 | + |
| 202 | +```typescript |
| 203 | +const validator = new XdrSchemaValidator(); |
| 204 | +if (!validator.validateValue(data, schema)) { |
| 205 | + throw new Error('Invalid data for schema'); |
| 206 | +} |
| 207 | +encoder.encode(data, schema); |
| 208 | +``` |
| 209 | + |
| 210 | +### Implement Depth Limits |
| 211 | + |
| 212 | +```typescript |
| 213 | +class SafeDecoder extends XdrSchemaDecoder { |
| 214 | + private depth = 0; |
| 215 | + private maxDepth = 100; |
| 216 | + |
| 217 | + decode(data: Uint8Array, schema: XdrSchema): unknown { |
| 218 | + if (++this.depth > this.maxDepth) { |
| 219 | + throw new Error('Max depth exceeded'); |
| 220 | + } |
| 221 | + try { |
| 222 | + return super.decode(data, schema); |
| 223 | + } finally { |
| 224 | + this.depth--; |
| 225 | + } |
| 226 | + } |
| 227 | +} |
| 228 | +``` |
| 229 | + |
| 230 | +## Common Patterns |
| 231 | + |
| 232 | +### Enum Pattern |
| 233 | + |
| 234 | +```typescript |
| 235 | +const ColorEnum = { |
| 236 | + type: 'enum', |
| 237 | + values: { |
| 238 | + RED: 0, |
| 239 | + GREEN: 1, |
| 240 | + BLUE: 2, |
| 241 | + }, |
| 242 | +} as const; |
| 243 | + |
| 244 | +encoder.writeEnum('RED', ColorEnum); |
| 245 | +const color = decoder.readEnum(ColorEnum); // 'RED' | 0 |
| 246 | +``` |
| 247 | + |
| 248 | +### Struct Pattern |
| 249 | + |
| 250 | +```typescript |
| 251 | +interface User { |
| 252 | + id: number; |
| 253 | + name: string; |
| 254 | + email: string; |
| 255 | + active: boolean; |
| 256 | +} |
| 257 | + |
| 258 | +const UserSchema = { |
| 259 | + type: 'struct', |
| 260 | + fields: [ |
| 261 | + [{type: 'int'}, 'id'], |
| 262 | + [{type: 'string', size: 100}, 'name'], |
| 263 | + [{type: 'string', size: 255}, 'email'], |
| 264 | + [{type: 'boolean'}, 'active'], |
| 265 | + ], |
| 266 | +} as const; |
| 267 | +``` |
| 268 | + |
| 269 | +### Variable-Length Array Pattern |
| 270 | + |
| 271 | +```typescript |
| 272 | +const ListSchema = { |
| 273 | + type: 'varray', |
| 274 | + elements: {type: 'int'}, |
| 275 | + size: 1000, // max 1000 elements |
| 276 | +}; |
| 277 | + |
| 278 | +encoder.encode([1, 2, 3, 4, 5], ListSchema); |
| 279 | +``` |
| 280 | + |
| 281 | +## Performance Tips |
| 282 | + |
| 283 | +1. **Reuse encoder/decoder instances** - avoid creating new ones per operation |
| 284 | +2. **Use fixed-size types** when possible - faster than variable-length |
| 285 | +3. **Preallocate buffers** for known sizes |
| 286 | +4. **Batch operations** - encode multiple values before flushing |
| 287 | +5. **Use schema validation** only in development/testing |
| 288 | + |
| 289 | +## Interoperability |
| 290 | + |
| 291 | +This implementation is wire-compatible with: |
| 292 | + |
| 293 | +- Sun RPC (ONC RPC) |
| 294 | +- NFS (Network File System) |
| 295 | +- Other RFC 4506-compliant libraries in any language |
| 296 | + |
| 297 | +## Further Reading |
| 298 | + |
| 299 | +- [SECURITY.md](./SECURITY.md) - Security considerations and best practices |
| 300 | +- [RFC_COMPLIANCE.md](./RFC_COMPLIANCE.md) - Detailed RFC compliance information |
| 301 | +- [CHANGELOG.md](./CHANGELOG.md) - Recent changes and additions |
| 302 | +- [RFC 4506](https://datatracker.ietf.org/doc/html/rfc4506) - Current XDR standard |
0 commit comments