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