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