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.
5 define("mojo/public/js/bindings/codec", [
6 "mojo/public/js/bindings/unicode"
9 var kErrorUnsigned
= "Passing negative value to unsigned";
11 // Memory -------------------------------------------------------------------
14 var kHighWordMultiplier
= 0x100000000;
15 var kHostIsLittleEndian
= (function () {
16 var endianArrayBuffer
= new ArrayBuffer(2);
17 var endianUint8Array
= new Uint8Array(endianArrayBuffer
);
18 var endianUint16Array
= new Uint16Array(endianArrayBuffer
);
19 endianUint16Array
[0] = 1;
20 return endianUint8Array
[0] == 1;
23 function align(size
) {
24 return size
+ (kAlignment
- (size
% kAlignment
)) % kAlignment
;
27 function getInt64(dataView
, byteOffset
, value
) {
29 if (kHostIsLittleEndian
) {
30 lo
= dataView
.getUint32(byteOffset
, kHostIsLittleEndian
);
31 hi
= dataView
.getInt32(byteOffset
+ 4, kHostIsLittleEndian
);
33 hi
= dataView
.getInt32(byteOffset
, kHostIsLittleEndian
);
34 lo
= dataView
.getUint32(byteOffset
+ 4, kHostIsLittleEndian
);
36 return lo
+ hi
* kHighWordMultiplier
;
39 function getUint64(dataView
, byteOffset
, value
) {
41 if (kHostIsLittleEndian
) {
42 lo
= dataView
.getUint32(byteOffset
, kHostIsLittleEndian
);
43 hi
= dataView
.getUint32(byteOffset
+ 4, kHostIsLittleEndian
);
45 hi
= dataView
.getUint32(byteOffset
, kHostIsLittleEndian
);
46 lo
= dataView
.getUint32(byteOffset
+ 4, kHostIsLittleEndian
);
48 return lo
+ hi
* kHighWordMultiplier
;
51 function setInt64(dataView
, byteOffset
, value
) {
52 var hi
= Math
.floor(value
/ kHighWordMultiplier
);
53 if (kHostIsLittleEndian
) {
54 dataView
.setInt32(byteOffset
, value
, kHostIsLittleEndian
);
55 dataView
.setInt32(byteOffset
+ 4, hi
, kHostIsLittleEndian
);
57 dataView
.setInt32(byteOffset
, hi
, kHostIsLittleEndian
);
58 dataView
.setInt32(byteOffset
+ 4, value
, kHostIsLittleEndian
);
62 function setUint64(dataView
, byteOffset
, value
) {
63 var hi
= (value
/ kHighWordMultiplier
) | 0;
64 if (kHostIsLittleEndian
) {
65 dataView
.setInt32(byteOffset
, value
, kHostIsLittleEndian
);
66 dataView
.setInt32(byteOffset
+ 4, hi
, kHostIsLittleEndian
);
68 dataView
.setInt32(byteOffset
, hi
, kHostIsLittleEndian
);
69 dataView
.setInt32(byteOffset
+ 4, value
, kHostIsLittleEndian
);
73 function copyArrayBuffer(dstArrayBuffer
, srcArrayBuffer
) {
74 (new Uint8Array(dstArrayBuffer
)).set(new Uint8Array(srcArrayBuffer
));
77 // Buffer -------------------------------------------------------------------
79 function Buffer(sizeOrArrayBuffer
) {
80 if (sizeOrArrayBuffer
instanceof ArrayBuffer
) {
81 this.arrayBuffer
= sizeOrArrayBuffer
;
83 this.arrayBuffer
= new ArrayBuffer(sizeOrArrayBuffer
);
86 this.dataView
= new DataView(this.arrayBuffer
);
90 Buffer
.prototype.alloc = function(size
) {
91 var pointer
= this.next
;
93 if (this.next
> this.arrayBuffer
.byteLength
) {
94 var newSize
= (1.5 * (this.arrayBuffer
.byteLength
+ size
)) | 0;
100 Buffer
.prototype.grow = function(size
) {
101 var newArrayBuffer
= new ArrayBuffer(size
);
102 copyArrayBuffer(newArrayBuffer
, this.arrayBuffer
);
103 this.arrayBuffer
= newArrayBuffer
;
104 this.dataView
= new DataView(this.arrayBuffer
);
107 Buffer
.prototype.trim = function() {
108 this.arrayBuffer
= this.arrayBuffer
.slice(0, this.next
);
109 this.dataView
= new DataView(this.arrayBuffer
);
112 // Constants ----------------------------------------------------------------
114 var kArrayHeaderSize
= 8;
115 var kStructHeaderSize
= 8;
116 var kMessageHeaderSize
= 16;
117 var kMessageWithRequestIDHeaderSize
= 24;
119 // Decoder ------------------------------------------------------------------
121 function Decoder(buffer
, handles
, base
) {
122 this.buffer
= buffer
;
123 this.handles
= handles
;
128 Decoder
.prototype.skip = function(offset
) {
132 Decoder
.prototype.readInt8 = function() {
133 var result
= this.buffer
.dataView
.getInt8(this.next
, kHostIsLittleEndian
);
138 Decoder
.prototype.readUint8 = function() {
139 var result
= this.buffer
.dataView
.getUint8(this.next
, kHostIsLittleEndian
);
144 Decoder
.prototype.readInt16 = function() {
145 var result
= this.buffer
.dataView
.getInt16(this.next
, kHostIsLittleEndian
);
150 Decoder
.prototype.readUint16 = function() {
151 var result
= this.buffer
.dataView
.getUint16(this.next
, kHostIsLittleEndian
);
156 Decoder
.prototype.readInt32 = function() {
157 var result
= this.buffer
.dataView
.getInt32(this.next
, kHostIsLittleEndian
);
162 Decoder
.prototype.readUint32 = function() {
163 var result
= this.buffer
.dataView
.getUint32(this.next
, kHostIsLittleEndian
);
168 Decoder
.prototype.readInt64 = function() {
169 var result
= getInt64(this.buffer
.dataView
, this.next
, kHostIsLittleEndian
);
174 Decoder
.prototype.readUint64 = function() {
175 var result
= getUint64(
176 this.buffer
.dataView
, this.next
, kHostIsLittleEndian
);
181 Decoder
.prototype.readFloat = function() {
182 var result
= this.buffer
.dataView
.getFloat32(
183 this.next
, kHostIsLittleEndian
);
188 Decoder
.prototype.readDouble = function() {
189 var result
= this.buffer
.dataView
.getFloat64(
190 this.next
, kHostIsLittleEndian
);
195 Decoder
.prototype.decodePointer = function() {
196 // TODO(abarth): To correctly decode a pointer, we need to know the real
197 // base address of the array buffer.
198 var offsetPointer
= this.next
;
199 var offset
= this.readUint64();
202 return offsetPointer
+ offset
;
205 Decoder
.prototype.decodeAndCreateDecoder = function() {
206 return new Decoder(this.buffer
, this.handles
, this.decodePointer());
209 Decoder
.prototype.decodeHandle = function() {
210 return this.handles
[this.readUint32()];
213 Decoder
.prototype.decodeString = function() {
214 var numberOfBytes
= this.readUint32();
215 var numberOfElements
= this.readUint32();
216 var base
= this.next
;
217 this.next
+= numberOfElements
;
218 return unicode
.decodeUtf8String(
219 new Uint8Array(this.buffer
.arrayBuffer
, base
, numberOfElements
));
222 Decoder
.prototype.decodeArray = function(cls
) {
223 var numberOfBytes
= this.readUint32();
224 var numberOfElements
= this.readUint32();
225 var val
= new Array(numberOfElements
);
226 for (var i
= 0; i
< numberOfElements
; ++i
) {
227 val
[i
] = cls
.decode(this);
232 Decoder
.prototype.decodeStruct = function(cls
) {
233 return cls
.decode(this);
236 Decoder
.prototype.decodeStructPointer = function(cls
) {
237 return cls
.decode(this.decodeAndCreateDecoder());
240 Decoder
.prototype.decodeArrayPointer = function(cls
) {
241 return this.decodeAndCreateDecoder().decodeArray(cls
);
244 Decoder
.prototype.decodeStringPointer = function() {
245 return this.decodeAndCreateDecoder().decodeString();
248 // Encoder ------------------------------------------------------------------
250 function Encoder(buffer
, handles
, base
) {
251 this.buffer
= buffer
;
252 this.handles
= handles
;
257 Encoder
.prototype.skip = function(offset
) {
261 Encoder
.prototype.writeInt8 = function(val
) {
262 // NOTE: Endianness doesn't come into play for single bytes.
263 this.buffer
.dataView
.setInt8(this.next
, val
);
267 Encoder
.prototype.writeUint8 = function(val
) {
269 throw new Error(kErrorUnsigned
);
271 // NOTE: Endianness doesn't come into play for single bytes.
272 this.buffer
.dataView
.setUint8(this.next
, val
);
276 Encoder
.prototype.writeInt16 = function(val
) {
277 this.buffer
.dataView
.setInt16(this.next
, val
, kHostIsLittleEndian
);
281 Encoder
.prototype.writeUint16 = function(val
) {
283 throw new Error(kErrorUnsigned
);
285 this.buffer
.dataView
.setUint16(this.next
, val
, kHostIsLittleEndian
);
289 Encoder
.prototype.writeInt32 = function(val
) {
290 this.buffer
.dataView
.setInt32(this.next
, val
, kHostIsLittleEndian
);
294 Encoder
.prototype.writeUint32 = function(val
) {
296 throw new Error(kErrorUnsigned
);
298 this.buffer
.dataView
.setUint32(this.next
, val
, kHostIsLittleEndian
);
302 Encoder
.prototype.writeInt64 = function(val
) {
303 setInt64(this.buffer
.dataView
, this.next
, val
);
307 Encoder
.prototype.writeUint64 = function(val
) {
309 throw new Error(kErrorUnsigned
);
311 setUint64(this.buffer
.dataView
, this.next
, val
);
315 Encoder
.prototype.writeFloat = function(val
) {
316 this.buffer
.dataView
.setFloat32(this.next
, val
, kHostIsLittleEndian
);
320 Encoder
.prototype.writeDouble = function(val
) {
321 this.buffer
.dataView
.setFloat64(this.next
, val
, kHostIsLittleEndian
);
325 Encoder
.prototype.encodePointer = function(pointer
) {
327 return this.writeUint64(0);
328 // TODO(abarth): To correctly encode a pointer, we need to know the real
329 // base address of the array buffer.
330 var offset
= pointer
- this.next
;
331 this.writeUint64(offset
);
334 Encoder
.prototype.createAndEncodeEncoder = function(size
) {
335 var pointer
= this.buffer
.alloc(align(size
));
336 this.encodePointer(pointer
);
337 return new Encoder(this.buffer
, this.handles
, pointer
);
340 Encoder
.prototype.encodeHandle = function(handle
) {
341 this.handles
.push(handle
);
342 this.writeUint32(this.handles
.length
- 1);
345 Encoder
.prototype.encodeString = function(val
) {
346 var base
= this.next
+ kArrayHeaderSize
;
347 var numberOfElements
= unicode
.encodeUtf8String(
348 val
, new Uint8Array(this.buffer
.arrayBuffer
, base
));
349 var numberOfBytes
= kArrayHeaderSize
+ numberOfElements
;
350 this.writeUint32(numberOfBytes
);
351 this.writeUint32(numberOfElements
);
352 this.next
+= numberOfElements
;
355 Encoder
.prototype.encodeArray = function(cls
, val
) {
356 var numberOfElements
= val
.length
;
357 var numberOfBytes
= kArrayHeaderSize
+ cls
.encodedSize
* numberOfElements
;
358 this.writeUint32(numberOfBytes
);
359 this.writeUint32(numberOfElements
);
360 for (var i
= 0; i
< numberOfElements
; ++i
) {
361 cls
.encode(this, val
[i
]);
365 Encoder
.prototype.encodeStruct = function(cls
, val
) {
366 return cls
.encode(this, val
);
369 Encoder
.prototype.encodeStructPointer = function(cls
, val
) {
370 var encoder
= this.createAndEncodeEncoder(cls
.encodedSize
);
371 cls
.encode(encoder
, val
);
374 Encoder
.prototype.encodeArrayPointer = function(cls
, val
) {
375 var encodedSize
= kArrayHeaderSize
+ cls
.encodedSize
* val
.length
;
376 var encoder
= this.createAndEncodeEncoder(encodedSize
);
377 encoder
.encodeArray(cls
, val
);
380 Encoder
.prototype.encodeStringPointer = function(val
) {
381 var encodedSize
= kArrayHeaderSize
+ unicode
.utf8Length(val
);
382 var encoder
= this.createAndEncodeEncoder(encodedSize
);
383 encoder
.encodeString(val
);
386 // Message ------------------------------------------------------------------
388 var kMessageExpectsResponse
= 1 << 0;
389 var kMessageIsResponse
= 1 << 1;
391 // Skip over num_bytes, num_fields, and message_name.
392 var kFlagsOffset
= 4 + 4 + 4;
394 // Skip over num_bytes, num_fields, message_name, and flags.
395 var kRequestIDOffset
= 4 + 4 + 4 + 4;
397 function Message(buffer
, handles
) {
398 this.buffer
= buffer
;
399 this.handles
= handles
;
402 Message
.prototype.setRequestID = function(requestID
) {
403 // TODO(darin): Verify that space was reserved for this field!
404 setUint64(this.buffer
.dataView
, kRequestIDOffset
, requestID
);
407 Message
.prototype.getFlags = function() {
408 return this.buffer
.dataView
.getUint32(kFlagsOffset
, kHostIsLittleEndian
);
411 // MessageBuilder -----------------------------------------------------------
413 function MessageBuilder(messageName
, payloadSize
) {
414 // Currently, we don't compute the payload size correctly ahead of time.
415 // Instead, we resize the buffer at the end.
416 var numberOfBytes
= kMessageHeaderSize
+ payloadSize
;
417 this.buffer
= new Buffer(numberOfBytes
);
419 var encoder
= this.createEncoder(kMessageHeaderSize
);
420 encoder
.writeUint32(kMessageHeaderSize
);
421 encoder
.writeUint32(2); // num_fields.
422 encoder
.writeUint32(messageName
);
423 encoder
.writeUint32(0); // flags.
426 MessageBuilder
.prototype.createEncoder = function(size
) {
427 var pointer
= this.buffer
.alloc(size
);
428 return new Encoder(this.buffer
, this.handles
, pointer
);
431 MessageBuilder
.prototype.encodeStruct = function(cls
, val
) {
432 cls
.encode(this.createEncoder(cls
.encodedSize
), val
);
435 MessageBuilder
.prototype.finish = function() {
436 // TODO(abarth): Rather than resizing the buffer at the end, we could
437 // compute the size we need ahead of time, like we do in C++.
439 var message
= new Message(this.buffer
, this.handles
);
446 // MessageWithRequestIDBuilder -----------------------------------------------
448 function MessageWithRequestIDBuilder(messageName
, payloadSize
, flags
,
450 // Currently, we don't compute the payload size correctly ahead of time.
451 // Instead, we resize the buffer at the end.
452 var numberOfBytes
= kMessageWithRequestIDHeaderSize
+ payloadSize
;
453 this.buffer
= new Buffer(numberOfBytes
);
455 var encoder
= this.createEncoder(kMessageWithRequestIDHeaderSize
);
456 encoder
.writeUint32(kMessageWithRequestIDHeaderSize
);
457 encoder
.writeUint32(3); // num_fields.
458 encoder
.writeUint32(messageName
);
459 encoder
.writeUint32(flags
);
460 encoder
.writeUint64(requestID
);
463 MessageWithRequestIDBuilder
.prototype =
464 Object
.create(MessageBuilder
.prototype);
466 MessageWithRequestIDBuilder
.prototype.constructor =
467 MessageWithRequestIDBuilder
;
469 // MessageReader ------------------------------------------------------------
471 function MessageReader(message
) {
472 this.decoder
= new Decoder(message
.buffer
, message
.handles
, 0);
473 var messageHeaderSize
= this.decoder
.readUint32();
475 message
.buffer
.arrayBuffer
.byteLength
- messageHeaderSize
;
476 var numFields
= this.decoder
.readUint32();
477 this.messageName
= this.decoder
.readUint32();
478 this.flags
= this.decoder
.readUint32();
480 this.requestID
= this.decoder
.readUint64();
481 this.decoder
.skip(messageHeaderSize
- this.decoder
.next
);
484 MessageReader
.prototype.decodeStruct = function(cls
) {
485 return cls
.decode(this.decoder
);
488 // Built-in types -----------------------------------------------------------
493 Int8
.encodedSize
= 1;
495 Int8
.decode = function(decoder
) {
496 return decoder
.readInt8();
499 Int8
.encode = function(encoder
, val
) {
500 encoder
.writeInt8(val
);
503 Uint8
.encode = function(encoder
, val
) {
504 encoder
.writeUint8(val
);
510 Uint8
.encodedSize
= 1;
512 Uint8
.decode = function(decoder
) {
513 return decoder
.readUint8();
516 Uint8
.encode = function(encoder
, val
) {
517 encoder
.writeUint8(val
);
523 Int16
.encodedSize
= 2;
525 Int16
.decode = function(decoder
) {
526 return decoder
.readInt16();
529 Int16
.encode = function(encoder
, val
) {
530 encoder
.writeInt16(val
);
536 Uint16
.encodedSize
= 2;
538 Uint16
.decode = function(decoder
) {
539 return decoder
.readUint16();
542 Uint16
.encode = function(encoder
, val
) {
543 encoder
.writeUint16(val
);
549 Int32
.encodedSize
= 4;
551 Int32
.decode = function(decoder
) {
552 return decoder
.readInt32();
555 Int32
.encode = function(encoder
, val
) {
556 encoder
.writeInt32(val
);
562 Uint32
.encodedSize
= 4;
564 Uint32
.decode = function(decoder
) {
565 return decoder
.readUint32();
568 Uint32
.encode = function(encoder
, val
) {
569 encoder
.writeUint32(val
);
575 Int64
.encodedSize
= 8;
577 Int64
.decode = function(decoder
) {
578 return decoder
.readInt64();
581 Int64
.encode = function(encoder
, val
) {
582 encoder
.writeInt64(val
);
588 Uint64
.encodedSize
= 8;
590 Uint64
.decode = function(decoder
) {
591 return decoder
.readUint64();
594 Uint64
.encode = function(encoder
, val
) {
595 encoder
.writeUint64(val
);
601 String
.encodedSize
= 8;
603 String
.decode = function(decoder
) {
604 return decoder
.decodeStringPointer();
607 String
.encode = function(encoder
, val
) {
608 encoder
.encodeStringPointer(val
);
615 Float
.encodedSize
= 4;
617 Float
.decode = function(decoder
) {
618 return decoder
.readFloat();
621 Float
.encode = function(encoder
, val
) {
622 encoder
.writeFloat(val
);
628 Double
.encodedSize
= 8;
630 Double
.decode = function(decoder
) {
631 return decoder
.readDouble();
634 Double
.encode = function(encoder
, val
) {
635 encoder
.writeDouble(val
);
638 function PointerTo(cls
) {
642 PointerTo
.prototype.encodedSize
= 8;
644 PointerTo
.prototype.decode = function(decoder
) {
645 return this.cls
.decode(decoder
.decodeAndCreateDecoder());
648 PointerTo
.prototype.encode = function(encoder
, val
) {
649 var objectEncoder
= encoder
.createAndEncodeEncoder(this.cls
.encodedSize
);
650 this.cls
.encode(objectEncoder
, val
);
653 function ArrayOf(cls
) {
657 ArrayOf
.prototype.encodedSize
= 8;
659 ArrayOf
.prototype.decode = function(decoder
) {
660 return decoder
.decodeArrayPointer(this.cls
);
663 ArrayOf
.prototype.encode = function(encoder
, val
) {
664 encoder
.encodeArrayPointer(this.cls
, val
);
670 Handle
.encodedSize
= 4;
672 Handle
.decode = function(decoder
) {
673 return decoder
.decodeHandle();
676 Handle
.encode = function(encoder
, val
) {
677 encoder
.encodeHandle(val
);
681 exports
.align
= align
;
682 exports
.Buffer
= Buffer
;
683 exports
.Message
= Message
;
684 exports
.MessageBuilder
= MessageBuilder
;
685 exports
.MessageWithRequestIDBuilder
= MessageWithRequestIDBuilder
;
686 exports
.MessageReader
= MessageReader
;
687 exports
.kArrayHeaderSize
= kArrayHeaderSize
;
688 exports
.kStructHeaderSize
= kStructHeaderSize
;
689 exports
.kMessageHeaderSize
= kMessageHeaderSize
;
690 exports
.kMessageExpectsResponse
= kMessageExpectsResponse
;
691 exports
.kMessageIsResponse
= kMessageIsResponse
;
693 exports
.Uint8
= Uint8
;
694 exports
.Int16
= Int16
;
695 exports
.Uint16
= Uint16
;
696 exports
.Int32
= Int32
;
697 exports
.Uint32
= Uint32
;
698 exports
.Int64
= Int64
;
699 exports
.Uint64
= Uint64
;
700 exports
.Float
= Float
;
701 exports
.Double
= Double
;
702 exports
.String
= String
;
703 exports
.PointerTo
= PointerTo
;
704 exports
.ArrayOf
= ArrayOf
;
705 exports
.Handle
= Handle
;