1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5define("mojo/public/js/codec", [
6  "mojo/public/js/buffer",
7  "mojo/public/js/interface_types",
8  "mojo/public/js/unicode",
9], function(buffer, types, unicode) {
10
11  var kErrorUnsigned = "Passing negative value to unsigned";
12  var kErrorArray = "Passing non Array for array type";
13  var kErrorString = "Passing non String for string type";
14  var kErrorMap = "Passing non Map for map type";
15
16  // Memory -------------------------------------------------------------------
17
18  var kAlignment = 8;
19
20  function align(size) {
21    return size + (kAlignment - (size % kAlignment)) % kAlignment;
22  }
23
24  function isAligned(offset) {
25    return offset >= 0 && (offset % kAlignment) === 0;
26  }
27
28  // Constants ----------------------------------------------------------------
29
30  var kArrayHeaderSize = 8;
31  var kStructHeaderSize = 8;
32  var kMessageHeaderSize = 24;
33  var kMessageWithRequestIDHeaderSize = 32;
34  var kMapStructPayloadSize = 16;
35
36  var kStructHeaderNumBytesOffset = 0;
37  var kStructHeaderVersionOffset = 4;
38
39  var kEncodedInvalidHandleValue = 0xFFFFFFFF;
40
41  // Decoder ------------------------------------------------------------------
42
43  function Decoder(buffer, handles, base) {
44    this.buffer = buffer;
45    this.handles = handles;
46    this.base = base;
47    this.next = base;
48  }
49
50  Decoder.prototype.align = function() {
51    this.next = align(this.next);
52  };
53
54  Decoder.prototype.skip = function(offset) {
55    this.next += offset;
56  };
57
58  Decoder.prototype.readInt8 = function() {
59    var result = this.buffer.getInt8(this.next);
60    this.next += 1;
61    return result;
62  };
63
64  Decoder.prototype.readUint8 = function() {
65    var result = this.buffer.getUint8(this.next);
66    this.next += 1;
67    return result;
68  };
69
70  Decoder.prototype.readInt16 = function() {
71    var result = this.buffer.getInt16(this.next);
72    this.next += 2;
73    return result;
74  };
75
76  Decoder.prototype.readUint16 = function() {
77    var result = this.buffer.getUint16(this.next);
78    this.next += 2;
79    return result;
80  };
81
82  Decoder.prototype.readInt32 = function() {
83    var result = this.buffer.getInt32(this.next);
84    this.next += 4;
85    return result;
86  };
87
88  Decoder.prototype.readUint32 = function() {
89    var result = this.buffer.getUint32(this.next);
90    this.next += 4;
91    return result;
92  };
93
94  Decoder.prototype.readInt64 = function() {
95    var result = this.buffer.getInt64(this.next);
96    this.next += 8;
97    return result;
98  };
99
100  Decoder.prototype.readUint64 = function() {
101    var result = this.buffer.getUint64(this.next);
102    this.next += 8;
103    return result;
104  };
105
106  Decoder.prototype.readFloat = function() {
107    var result = this.buffer.getFloat32(this.next);
108    this.next += 4;
109    return result;
110  };
111
112  Decoder.prototype.readDouble = function() {
113    var result = this.buffer.getFloat64(this.next);
114    this.next += 8;
115    return result;
116  };
117
118  Decoder.prototype.decodePointer = function() {
119    // TODO(abarth): To correctly decode a pointer, we need to know the real
120    // base address of the array buffer.
121    var offsetPointer = this.next;
122    var offset = this.readUint64();
123    if (!offset)
124      return 0;
125    return offsetPointer + offset;
126  };
127
128  Decoder.prototype.decodeAndCreateDecoder = function(pointer) {
129    return new Decoder(this.buffer, this.handles, pointer);
130  };
131
132  Decoder.prototype.decodeHandle = function() {
133    return this.handles[this.readUint32()] || null;
134  };
135
136  Decoder.prototype.decodeString = function() {
137    var numberOfBytes = this.readUint32();
138    var numberOfElements = this.readUint32();
139    var base = this.next;
140    this.next += numberOfElements;
141    return unicode.decodeUtf8String(
142        new Uint8Array(this.buffer.arrayBuffer, base, numberOfElements));
143  };
144
145  Decoder.prototype.decodeArray = function(cls) {
146    var numberOfBytes = this.readUint32();
147    var numberOfElements = this.readUint32();
148    var val = new Array(numberOfElements);
149    if (cls === PackedBool) {
150      var byte;
151      for (var i = 0; i < numberOfElements; ++i) {
152        if (i % 8 === 0)
153          byte = this.readUint8();
154        val[i] = (byte & (1 << i % 8)) ? true : false;
155      }
156    } else {
157      for (var i = 0; i < numberOfElements; ++i) {
158        val[i] = cls.decode(this);
159      }
160    }
161    return val;
162  };
163
164  Decoder.prototype.decodeStruct = function(cls) {
165    return cls.decode(this);
166  };
167
168  Decoder.prototype.decodeStructPointer = function(cls) {
169    var pointer = this.decodePointer();
170    if (!pointer) {
171      return null;
172    }
173    return cls.decode(this.decodeAndCreateDecoder(pointer));
174  };
175
176  Decoder.prototype.decodeArrayPointer = function(cls) {
177    var pointer = this.decodePointer();
178    if (!pointer) {
179      return null;
180    }
181    return this.decodeAndCreateDecoder(pointer).decodeArray(cls);
182  };
183
184  Decoder.prototype.decodeStringPointer = function() {
185    var pointer = this.decodePointer();
186    if (!pointer) {
187      return null;
188    }
189    return this.decodeAndCreateDecoder(pointer).decodeString();
190  };
191
192  Decoder.prototype.decodeMap = function(keyClass, valueClass) {
193    this.skip(4); // numberOfBytes
194    this.skip(4); // version
195    var keys = this.decodeArrayPointer(keyClass);
196    var values = this.decodeArrayPointer(valueClass);
197    var val = new Map();
198    for (var i = 0; i < keys.length; i++)
199      val.set(keys[i], values[i]);
200    return val;
201  };
202
203  Decoder.prototype.decodeMapPointer = function(keyClass, valueClass) {
204    var pointer = this.decodePointer();
205    if (!pointer) {
206      return null;
207    }
208    var decoder = this.decodeAndCreateDecoder(pointer);
209    return decoder.decodeMap(keyClass, valueClass);
210  };
211
212  // Encoder ------------------------------------------------------------------
213
214  function Encoder(buffer, handles, base) {
215    this.buffer = buffer;
216    this.handles = handles;
217    this.base = base;
218    this.next = base;
219  }
220
221  Encoder.prototype.align = function() {
222    this.next = align(this.next);
223  };
224
225  Encoder.prototype.skip = function(offset) {
226    this.next += offset;
227  };
228
229  Encoder.prototype.writeInt8 = function(val) {
230    this.buffer.setInt8(this.next, val);
231    this.next += 1;
232  };
233
234  Encoder.prototype.writeUint8 = function(val) {
235    if (val < 0) {
236      throw new Error(kErrorUnsigned);
237    }
238    this.buffer.setUint8(this.next, val);
239    this.next += 1;
240  };
241
242  Encoder.prototype.writeInt16 = function(val) {
243    this.buffer.setInt16(this.next, val);
244    this.next += 2;
245  };
246
247  Encoder.prototype.writeUint16 = function(val) {
248    if (val < 0) {
249      throw new Error(kErrorUnsigned);
250    }
251    this.buffer.setUint16(this.next, val);
252    this.next += 2;
253  };
254
255  Encoder.prototype.writeInt32 = function(val) {
256    this.buffer.setInt32(this.next, val);
257    this.next += 4;
258  };
259
260  Encoder.prototype.writeUint32 = function(val) {
261    if (val < 0) {
262      throw new Error(kErrorUnsigned);
263    }
264    this.buffer.setUint32(this.next, val);
265    this.next += 4;
266  };
267
268  Encoder.prototype.writeInt64 = function(val) {
269    this.buffer.setInt64(this.next, val);
270    this.next += 8;
271  };
272
273  Encoder.prototype.writeUint64 = function(val) {
274    if (val < 0) {
275      throw new Error(kErrorUnsigned);
276    }
277    this.buffer.setUint64(this.next, val);
278    this.next += 8;
279  };
280
281  Encoder.prototype.writeFloat = function(val) {
282    this.buffer.setFloat32(this.next, val);
283    this.next += 4;
284  };
285
286  Encoder.prototype.writeDouble = function(val) {
287    this.buffer.setFloat64(this.next, val);
288    this.next += 8;
289  };
290
291  Encoder.prototype.encodePointer = function(pointer) {
292    if (!pointer)
293      return this.writeUint64(0);
294    // TODO(abarth): To correctly encode a pointer, we need to know the real
295    // base address of the array buffer.
296    var offset = pointer - this.next;
297    this.writeUint64(offset);
298  };
299
300  Encoder.prototype.createAndEncodeEncoder = function(size) {
301    var pointer = this.buffer.alloc(align(size));
302    this.encodePointer(pointer);
303    return new Encoder(this.buffer, this.handles, pointer);
304  };
305
306  Encoder.prototype.encodeHandle = function(handle) {
307    if (handle) {
308      this.handles.push(handle);
309      this.writeUint32(this.handles.length - 1);
310    } else {
311      this.writeUint32(kEncodedInvalidHandleValue);
312    }
313  };
314
315  Encoder.prototype.encodeString = function(val) {
316    var base = this.next + kArrayHeaderSize;
317    var numberOfElements = unicode.encodeUtf8String(
318        val, new Uint8Array(this.buffer.arrayBuffer, base));
319    var numberOfBytes = kArrayHeaderSize + numberOfElements;
320    this.writeUint32(numberOfBytes);
321    this.writeUint32(numberOfElements);
322    this.next += numberOfElements;
323  };
324
325  Encoder.prototype.encodeArray =
326      function(cls, val, numberOfElements, encodedSize) {
327    if (numberOfElements === undefined)
328      numberOfElements = val.length;
329    if (encodedSize === undefined)
330      encodedSize = kArrayHeaderSize + cls.encodedSize * numberOfElements;
331
332    this.writeUint32(encodedSize);
333    this.writeUint32(numberOfElements);
334
335    if (cls === PackedBool) {
336      var byte = 0;
337      for (i = 0; i < numberOfElements; ++i) {
338        if (val[i])
339          byte |= (1 << i % 8);
340        if (i % 8 === 7 || i == numberOfElements - 1) {
341          Uint8.encode(this, byte);
342          byte = 0;
343        }
344      }
345    } else {
346      for (var i = 0; i < numberOfElements; ++i)
347        cls.encode(this, val[i]);
348    }
349  };
350
351  Encoder.prototype.encodeStruct = function(cls, val) {
352    return cls.encode(this, val);
353  };
354
355  Encoder.prototype.encodeStructPointer = function(cls, val) {
356    if (val == null) {
357      // Also handles undefined, since undefined == null.
358      this.encodePointer(val);
359      return;
360    }
361    var encoder = this.createAndEncodeEncoder(cls.encodedSize);
362    cls.encode(encoder, val);
363  };
364
365  Encoder.prototype.encodeArrayPointer = function(cls, val) {
366    if (val == null) {
367      // Also handles undefined, since undefined == null.
368      this.encodePointer(val);
369      return;
370    }
371
372    var numberOfElements = val.length;
373    if (!Number.isSafeInteger(numberOfElements) || numberOfElements < 0)
374      throw new Error(kErrorArray);
375
376    var encodedSize = kArrayHeaderSize + ((cls === PackedBool) ?
377        Math.ceil(numberOfElements / 8) : cls.encodedSize * numberOfElements);
378    var encoder = this.createAndEncodeEncoder(encodedSize);
379    encoder.encodeArray(cls, val, numberOfElements, encodedSize);
380  };
381
382  Encoder.prototype.encodeStringPointer = function(val) {
383    if (val == null) {
384      // Also handles undefined, since undefined == null.
385      this.encodePointer(val);
386      return;
387    }
388    // Only accepts string primivites, not String Objects like new String("foo")
389    if (typeof(val) !== "string") {
390      throw new Error(kErrorString);
391    }
392    var encodedSize = kArrayHeaderSize + unicode.utf8Length(val);
393    var encoder = this.createAndEncodeEncoder(encodedSize);
394    encoder.encodeString(val);
395  };
396
397  Encoder.prototype.encodeMap = function(keyClass, valueClass, val) {
398    var keys = new Array(val.size);
399    var values = new Array(val.size);
400    var i = 0;
401    val.forEach(function(value, key) {
402      values[i] = value;
403      keys[i++] = key;
404    });
405    this.writeUint32(kStructHeaderSize + kMapStructPayloadSize);
406    this.writeUint32(0);  // version
407    this.encodeArrayPointer(keyClass, keys);
408    this.encodeArrayPointer(valueClass, values);
409  }
410
411  Encoder.prototype.encodeMapPointer = function(keyClass, valueClass, val) {
412    if (val == null) {
413      // Also handles undefined, since undefined == null.
414      this.encodePointer(val);
415      return;
416    }
417    if (!(val instanceof Map)) {
418      throw new Error(kErrorMap);
419    }
420    var encodedSize = kStructHeaderSize + kMapStructPayloadSize;
421    var encoder = this.createAndEncodeEncoder(encodedSize);
422    encoder.encodeMap(keyClass, valueClass, val);
423  };
424
425  // Message ------------------------------------------------------------------
426
427  var kMessageInterfaceIdOffset = kStructHeaderSize;
428  var kMessageNameOffset = kMessageInterfaceIdOffset + 4;
429  var kMessageFlagsOffset = kMessageNameOffset + 4;
430  var kMessageRequestIDOffset = kMessageFlagsOffset + 8;
431
432  var kMessageExpectsResponse = 1 << 0;
433  var kMessageIsResponse      = 1 << 1;
434
435  function Message(buffer, handles) {
436    this.buffer = buffer;
437    this.handles = handles;
438  }
439
440  Message.prototype.getHeaderNumBytes = function() {
441    return this.buffer.getUint32(kStructHeaderNumBytesOffset);
442  };
443
444  Message.prototype.getHeaderVersion = function() {
445    return this.buffer.getUint32(kStructHeaderVersionOffset);
446  };
447
448  Message.prototype.getName = function() {
449    return this.buffer.getUint32(kMessageNameOffset);
450  };
451
452  Message.prototype.getFlags = function() {
453    return this.buffer.getUint32(kMessageFlagsOffset);
454  };
455
456  Message.prototype.isResponse = function() {
457    return (this.getFlags() & kMessageIsResponse) != 0;
458  };
459
460  Message.prototype.expectsResponse = function() {
461    return (this.getFlags() & kMessageExpectsResponse) != 0;
462  };
463
464  Message.prototype.setRequestID = function(requestID) {
465    // TODO(darin): Verify that space was reserved for this field!
466    this.buffer.setUint64(kMessageRequestIDOffset, requestID);
467  };
468
469
470  // MessageBuilder -----------------------------------------------------------
471
472  function MessageBuilder(messageName, payloadSize) {
473    // Currently, we don't compute the payload size correctly ahead of time.
474    // Instead, we resize the buffer at the end.
475    var numberOfBytes = kMessageHeaderSize + payloadSize;
476    this.buffer = new buffer.Buffer(numberOfBytes);
477    this.handles = [];
478    var encoder = this.createEncoder(kMessageHeaderSize);
479    encoder.writeUint32(kMessageHeaderSize);
480    encoder.writeUint32(0);  // version.
481    encoder.writeUint32(0);  // interface ID.
482    encoder.writeUint32(messageName);
483    encoder.writeUint32(0);  // flags.
484    encoder.writeUint32(0);  // padding.
485  }
486
487  MessageBuilder.prototype.createEncoder = function(size) {
488    var pointer = this.buffer.alloc(size);
489    return new Encoder(this.buffer, this.handles, pointer);
490  };
491
492  MessageBuilder.prototype.encodeStruct = function(cls, val) {
493    cls.encode(this.createEncoder(cls.encodedSize), val);
494  };
495
496  MessageBuilder.prototype.finish = function() {
497    // TODO(abarth): Rather than resizing the buffer at the end, we could
498    // compute the size we need ahead of time, like we do in C++.
499    this.buffer.trim();
500    var message = new Message(this.buffer, this.handles);
501    this.buffer = null;
502    this.handles = null;
503    this.encoder = null;
504    return message;
505  };
506
507  // MessageWithRequestIDBuilder -----------------------------------------------
508
509  function MessageWithRequestIDBuilder(messageName, payloadSize, flags,
510                                       requestID) {
511    // Currently, we don't compute the payload size correctly ahead of time.
512    // Instead, we resize the buffer at the end.
513    var numberOfBytes = kMessageWithRequestIDHeaderSize + payloadSize;
514    this.buffer = new buffer.Buffer(numberOfBytes);
515    this.handles = [];
516    var encoder = this.createEncoder(kMessageWithRequestIDHeaderSize);
517    encoder.writeUint32(kMessageWithRequestIDHeaderSize);
518    encoder.writeUint32(1);  // version.
519    encoder.writeUint32(0);  // interface ID.
520    encoder.writeUint32(messageName);
521    encoder.writeUint32(flags);
522    encoder.writeUint32(0);  // padding.
523    encoder.writeUint64(requestID);
524  }
525
526  MessageWithRequestIDBuilder.prototype =
527      Object.create(MessageBuilder.prototype);
528
529  MessageWithRequestIDBuilder.prototype.constructor =
530      MessageWithRequestIDBuilder;
531
532  // MessageReader ------------------------------------------------------------
533
534  function MessageReader(message) {
535    this.decoder = new Decoder(message.buffer, message.handles, 0);
536    var messageHeaderSize = this.decoder.readUint32();
537    this.payloadSize = message.buffer.byteLength - messageHeaderSize;
538    var version = this.decoder.readUint32();
539    var interface_id = this.decoder.readUint32();
540    if (interface_id != 0) {
541      throw new Error("Receiving non-zero interface ID. Associated interfaces " +
542                      "are not yet supported.");
543    }
544    this.messageName = this.decoder.readUint32();
545    this.flags = this.decoder.readUint32();
546    // Skip the padding.
547    this.decoder.skip(4);
548    if (version >= 1)
549      this.requestID = this.decoder.readUint64();
550    this.decoder.skip(messageHeaderSize - this.decoder.next);
551  }
552
553  MessageReader.prototype.decodeStruct = function(cls) {
554    return cls.decode(this.decoder);
555  };
556
557  // Built-in types -----------------------------------------------------------
558
559  // This type is only used with ArrayOf(PackedBool).
560  function PackedBool() {
561  }
562
563  function Int8() {
564  }
565
566  Int8.encodedSize = 1;
567
568  Int8.decode = function(decoder) {
569    return decoder.readInt8();
570  };
571
572  Int8.encode = function(encoder, val) {
573    encoder.writeInt8(val);
574  };
575
576  Uint8.encode = function(encoder, val) {
577    encoder.writeUint8(val);
578  };
579
580  function Uint8() {
581  }
582
583  Uint8.encodedSize = 1;
584
585  Uint8.decode = function(decoder) {
586    return decoder.readUint8();
587  };
588
589  Uint8.encode = function(encoder, val) {
590    encoder.writeUint8(val);
591  };
592
593  function Int16() {
594  }
595
596  Int16.encodedSize = 2;
597
598  Int16.decode = function(decoder) {
599    return decoder.readInt16();
600  };
601
602  Int16.encode = function(encoder, val) {
603    encoder.writeInt16(val);
604  };
605
606  function Uint16() {
607  }
608
609  Uint16.encodedSize = 2;
610
611  Uint16.decode = function(decoder) {
612    return decoder.readUint16();
613  };
614
615  Uint16.encode = function(encoder, val) {
616    encoder.writeUint16(val);
617  };
618
619  function Int32() {
620  }
621
622  Int32.encodedSize = 4;
623
624  Int32.decode = function(decoder) {
625    return decoder.readInt32();
626  };
627
628  Int32.encode = function(encoder, val) {
629    encoder.writeInt32(val);
630  };
631
632  function Uint32() {
633  }
634
635  Uint32.encodedSize = 4;
636
637  Uint32.decode = function(decoder) {
638    return decoder.readUint32();
639  };
640
641  Uint32.encode = function(encoder, val) {
642    encoder.writeUint32(val);
643  };
644
645  function Int64() {
646  }
647
648  Int64.encodedSize = 8;
649
650  Int64.decode = function(decoder) {
651    return decoder.readInt64();
652  };
653
654  Int64.encode = function(encoder, val) {
655    encoder.writeInt64(val);
656  };
657
658  function Uint64() {
659  }
660
661  Uint64.encodedSize = 8;
662
663  Uint64.decode = function(decoder) {
664    return decoder.readUint64();
665  };
666
667  Uint64.encode = function(encoder, val) {
668    encoder.writeUint64(val);
669  };
670
671  function String() {
672  };
673
674  String.encodedSize = 8;
675
676  String.decode = function(decoder) {
677    return decoder.decodeStringPointer();
678  };
679
680  String.encode = function(encoder, val) {
681    encoder.encodeStringPointer(val);
682  };
683
684  function NullableString() {
685  }
686
687  NullableString.encodedSize = String.encodedSize;
688
689  NullableString.decode = String.decode;
690
691  NullableString.encode = String.encode;
692
693  function Float() {
694  }
695
696  Float.encodedSize = 4;
697
698  Float.decode = function(decoder) {
699    return decoder.readFloat();
700  };
701
702  Float.encode = function(encoder, val) {
703    encoder.writeFloat(val);
704  };
705
706  function Double() {
707  }
708
709  Double.encodedSize = 8;
710
711  Double.decode = function(decoder) {
712    return decoder.readDouble();
713  };
714
715  Double.encode = function(encoder, val) {
716    encoder.writeDouble(val);
717  };
718
719  function Enum(cls) {
720    this.cls = cls;
721  }
722
723  Enum.prototype.encodedSize = 4;
724
725  Enum.prototype.decode = function(decoder) {
726    return decoder.readInt32();
727  };
728
729  Enum.prototype.encode = function(encoder, val) {
730    encoder.writeInt32(val);
731  };
732
733  function PointerTo(cls) {
734    this.cls = cls;
735  }
736
737  PointerTo.prototype.encodedSize = 8;
738
739  PointerTo.prototype.decode = function(decoder) {
740    var pointer = decoder.decodePointer();
741    if (!pointer) {
742      return null;
743    }
744    return this.cls.decode(decoder.decodeAndCreateDecoder(pointer));
745  };
746
747  PointerTo.prototype.encode = function(encoder, val) {
748    if (!val) {
749      encoder.encodePointer(val);
750      return;
751    }
752    var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize);
753    this.cls.encode(objectEncoder, val);
754  };
755
756  function NullablePointerTo(cls) {
757    PointerTo.call(this, cls);
758  }
759
760  NullablePointerTo.prototype = Object.create(PointerTo.prototype);
761
762  function ArrayOf(cls, length) {
763    this.cls = cls;
764    this.length = length || 0;
765  }
766
767  ArrayOf.prototype.encodedSize = 8;
768
769  ArrayOf.prototype.dimensions = function() {
770    return [this.length].concat(
771      (this.cls instanceof ArrayOf) ? this.cls.dimensions() : []);
772  }
773
774  ArrayOf.prototype.decode = function(decoder) {
775    return decoder.decodeArrayPointer(this.cls);
776  };
777
778  ArrayOf.prototype.encode = function(encoder, val) {
779    encoder.encodeArrayPointer(this.cls, val);
780  };
781
782  function NullableArrayOf(cls) {
783    ArrayOf.call(this, cls);
784  }
785
786  NullableArrayOf.prototype = Object.create(ArrayOf.prototype);
787
788  function Handle() {
789  }
790
791  Handle.encodedSize = 4;
792
793  Handle.decode = function(decoder) {
794    return decoder.decodeHandle();
795  };
796
797  Handle.encode = function(encoder, val) {
798    encoder.encodeHandle(val);
799  };
800
801  function NullableHandle() {
802  }
803
804  NullableHandle.encodedSize = Handle.encodedSize;
805
806  NullableHandle.decode = Handle.decode;
807
808  NullableHandle.encode = Handle.encode;
809
810  function Interface(cls) {
811    this.cls = cls;
812  }
813
814  Interface.prototype.encodedSize = 8;
815
816  Interface.prototype.decode = function(decoder) {
817    var interfacePtrInfo = new types.InterfacePtrInfo(
818        decoder.decodeHandle(), decoder.readUint32());
819    var interfacePtr = new this.cls();
820    interfacePtr.ptr.bind(interfacePtrInfo);
821    return interfacePtr;
822  };
823
824  Interface.prototype.encode = function(encoder, val) {
825    var interfacePtrInfo =
826        val ? val.ptr.passInterface() : new types.InterfacePtrInfo(null, 0);
827    encoder.encodeHandle(interfacePtrInfo.handle);
828    encoder.writeUint32(interfacePtrInfo.version);
829  };
830
831  function NullableInterface(cls) {
832    Interface.call(this, cls);
833  }
834
835  NullableInterface.prototype = Object.create(Interface.prototype);
836
837  function InterfaceRequest() {
838  }
839
840  InterfaceRequest.encodedSize = 4;
841
842  InterfaceRequest.decode = function(decoder) {
843    return new types.InterfaceRequest(decoder.decodeHandle());
844  };
845
846  InterfaceRequest.encode = function(encoder, val) {
847    encoder.encodeHandle(val ? val.handle : null);
848  };
849
850  function NullableInterfaceRequest() {
851  }
852
853  NullableInterfaceRequest.encodedSize = InterfaceRequest.encodedSize;
854
855  NullableInterfaceRequest.decode = InterfaceRequest.decode;
856
857  NullableInterfaceRequest.encode = InterfaceRequest.encode;
858
859  function MapOf(keyClass, valueClass) {
860    this.keyClass = keyClass;
861    this.valueClass = valueClass;
862  }
863
864  MapOf.prototype.encodedSize = 8;
865
866  MapOf.prototype.decode = function(decoder) {
867    return decoder.decodeMapPointer(this.keyClass, this.valueClass);
868  };
869
870  MapOf.prototype.encode = function(encoder, val) {
871    encoder.encodeMapPointer(this.keyClass, this.valueClass, val);
872  };
873
874  function NullableMapOf(keyClass, valueClass) {
875    MapOf.call(this, keyClass, valueClass);
876  }
877
878  NullableMapOf.prototype = Object.create(MapOf.prototype);
879
880  var exports = {};
881  exports.align = align;
882  exports.isAligned = isAligned;
883  exports.Message = Message;
884  exports.MessageBuilder = MessageBuilder;
885  exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder;
886  exports.MessageReader = MessageReader;
887  exports.kArrayHeaderSize = kArrayHeaderSize;
888  exports.kMapStructPayloadSize = kMapStructPayloadSize;
889  exports.kStructHeaderSize = kStructHeaderSize;
890  exports.kEncodedInvalidHandleValue = kEncodedInvalidHandleValue;
891  exports.kMessageHeaderSize = kMessageHeaderSize;
892  exports.kMessageWithRequestIDHeaderSize = kMessageWithRequestIDHeaderSize;
893  exports.kMessageExpectsResponse = kMessageExpectsResponse;
894  exports.kMessageIsResponse = kMessageIsResponse;
895  exports.Int8 = Int8;
896  exports.Uint8 = Uint8;
897  exports.Int16 = Int16;
898  exports.Uint16 = Uint16;
899  exports.Int32 = Int32;
900  exports.Uint32 = Uint32;
901  exports.Int64 = Int64;
902  exports.Uint64 = Uint64;
903  exports.Float = Float;
904  exports.Double = Double;
905  exports.String = String;
906  exports.Enum = Enum;
907  exports.NullableString = NullableString;
908  exports.PointerTo = PointerTo;
909  exports.NullablePointerTo = NullablePointerTo;
910  exports.ArrayOf = ArrayOf;
911  exports.NullableArrayOf = NullableArrayOf;
912  exports.PackedBool = PackedBool;
913  exports.Handle = Handle;
914  exports.NullableHandle = NullableHandle;
915  exports.Interface = Interface;
916  exports.NullableInterface = NullableInterface;
917  exports.InterfaceRequest = InterfaceRequest;
918  exports.NullableInterfaceRequest = NullableInterfaceRequest;
919  exports.MapOf = MapOf;
920  exports.NullableMapOf = NullableMapOf;
921  return exports;
922});
923