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",
7 "mojo/public/js/bindings/buffer"
8 ], function(unicode, buffer) {
10 var kErrorUnsigned = "Passing negative value to unsigned";
12 // Memory -------------------------------------------------------------------
16 function align(size) {
17 return size + (kAlignment - (size % kAlignment)) % kAlignment;
20 function isAligned(offset) {
21 return offset >= 0 && (offset % kAlignment) === 0;
24 // Constants ----------------------------------------------------------------
26 var kArrayHeaderSize = 8;
27 var kStructHeaderSize = 8;
28 var kMessageHeaderSize = 16;
29 var kMessageWithRequestIDHeaderSize = 24;
31 var kStructHeaderNumBytesOffset = 0;
32 var kStructHeaderNumFieldsOffset = 4;
34 var kEncodedInvalidHandleValue = 0xFFFFFFFF;
36 // Decoder ------------------------------------------------------------------
38 function Decoder(buffer, handles, base) {
40 this.handles = handles;
45 Decoder.prototype.skip = function(offset) {
49 Decoder.prototype.readInt8 = function() {
50 var result = this.buffer.getInt8(this.next);
55 Decoder.prototype.readUint8 = function() {
56 var result = this.buffer.getUint8(this.next);
61 Decoder.prototype.readInt16 = function() {
62 var result = this.buffer.getInt16(this.next);
67 Decoder.prototype.readUint16 = function() {
68 var result = this.buffer.getUint16(this.next);
73 Decoder.prototype.readInt32 = function() {
74 var result = this.buffer.getInt32(this.next);
79 Decoder.prototype.readUint32 = function() {
80 var result = this.buffer.getUint32(this.next);
85 Decoder.prototype.readInt64 = function() {
86 var result = this.buffer.getInt64(this.next);
91 Decoder.prototype.readUint64 = function() {
92 var result = this.buffer.getUint64(this.next);
97 Decoder.prototype.readFloat = function() {
98 var result = this.buffer.getFloat32(this.next);
103 Decoder.prototype.readDouble = function() {
104 var result = this.buffer.getFloat64(this.next);
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();
116 return offsetPointer + offset;
119 Decoder.prototype.decodeAndCreateDecoder = function(pointer) {
120 return new Decoder(this.buffer, this.handles, pointer);
123 Decoder.prototype.decodeHandle = function() {
124 return this.handles[this.readUint32()];
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));
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) {
142 for (var i = 0; i < numberOfElements; ++i) {
144 byte = this.readUint8();
145 val[i] = (byte & (1 << i % 8)) ? true : false;
148 for (var i = 0; i < numberOfElements; ++i) {
149 val[i] = cls.decode(this);
155 Decoder.prototype.decodeStruct = function(cls) {
156 return cls.decode(this);
159 Decoder.prototype.decodeStructPointer = function(cls) {
160 var pointer = this.decodePointer();
164 return cls.decode(this.decodeAndCreateDecoder(pointer));
167 Decoder.prototype.decodeArrayPointer = function(cls) {
168 var pointer = this.decodePointer();
172 return this.decodeAndCreateDecoder(pointer).decodeArray(cls);
175 Decoder.prototype.decodeStringPointer = function() {
176 var pointer = this.decodePointer();
180 return this.decodeAndCreateDecoder(pointer).decodeString();
183 // Encoder ------------------------------------------------------------------
185 function Encoder(buffer, handles, base) {
186 this.buffer = buffer;
187 this.handles = handles;
192 Encoder.prototype.skip = function(offset) {
196 Encoder.prototype.writeInt8 = function(val) {
197 this.buffer.setInt8(this.next, val);
201 Encoder.prototype.writeUint8 = function(val) {
203 throw new Error(kErrorUnsigned);
205 this.buffer.setUint8(this.next, val);
209 Encoder.prototype.writeInt16 = function(val) {
210 this.buffer.setInt16(this.next, val);
214 Encoder.prototype.writeUint16 = function(val) {
216 throw new Error(kErrorUnsigned);
218 this.buffer.setUint16(this.next, val);
222 Encoder.prototype.writeInt32 = function(val) {
223 this.buffer.setInt32(this.next, val);
227 Encoder.prototype.writeUint32 = function(val) {
229 throw new Error(kErrorUnsigned);
231 this.buffer.setUint32(this.next, val);
235 Encoder.prototype.writeInt64 = function(val) {
236 this.buffer.setInt64(this.next, val);
240 Encoder.prototype.writeUint64 = function(val) {
242 throw new Error(kErrorUnsigned);
244 this.buffer.setUint64(this.next, val);
248 Encoder.prototype.writeFloat = function(val) {
249 this.buffer.setFloat32(this.next, val);
253 Encoder.prototype.writeDouble = function(val) {
254 this.buffer.setFloat64(this.next, val);
258 Encoder.prototype.encodePointer = function(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);
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);
273 Encoder.prototype.encodeHandle = function(handle) {
274 this.handles.push(handle);
275 this.writeUint32(this.handles.length - 1);
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;
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;
295 this.writeUint32(encodedSize);
296 this.writeUint32(numberOfElements);
298 if (cls === PackedBool) {
300 for (i = 0; i < numberOfElements; ++i) {
302 byte |= (1 << i % 8);
303 if (i % 8 === 7 || i == numberOfElements - 1) {
304 Uint8.encode(this, byte);
309 for (var i = 0; i < numberOfElements; ++i)
310 cls.encode(this, val[i]);
314 Encoder.prototype.encodeStruct = function(cls, val) {
315 return cls.encode(this, val);
318 Encoder.prototype.encodeStructPointer = function(cls, val) {
320 // Also handles undefined, since undefined == null.
321 this.encodePointer(val);
324 var encoder = this.createAndEncodeEncoder(cls.encodedSize);
325 cls.encode(encoder, val);
328 Encoder.prototype.encodeArrayPointer = function(cls, val) {
330 // Also handles undefined, since undefined == null.
331 this.encodePointer(val);
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);
341 Encoder.prototype.encodeStringPointer = function(val) {
343 // Also handles undefined, since undefined == null.
344 this.encodePointer(val);
347 var encodedSize = kArrayHeaderSize + unicode.utf8Length(val);
348 var encoder = this.createAndEncodeEncoder(encodedSize);
349 encoder.encodeString(val);
352 // Message ------------------------------------------------------------------
354 var kMessageNameOffset = kStructHeaderSize;
355 var kMessageFlagsOffset = kMessageNameOffset + 4;
356 var kMessageRequestIDOffset = kMessageFlagsOffset + 4;
358 var kMessageExpectsResponse = 1 << 0;
359 var kMessageIsResponse = 1 << 1;
361 function Message(buffer, handles) {
362 this.buffer = buffer;
363 this.handles = handles;
366 Message.prototype.getHeaderNumBytes = function() {
367 return this.buffer.getUint32(kStructHeaderNumBytesOffset);
370 Message.prototype.getHeaderNumFields = function() {
371 return this.buffer.getUint32(kStructHeaderNumFieldsOffset);
374 Message.prototype.getName = function() {
375 return this.buffer.getUint32(kMessageNameOffset);
378 Message.prototype.getFlags = function() {
379 return this.buffer.getUint32(kMessageFlagsOffset);
382 Message.prototype.isResponse = function() {
383 return (this.getFlags() & kMessageIsResponse) != 0;
386 Message.prototype.expectsResponse = function() {
387 return (this.getFlags() & kMessageExpectsResponse) != 0;
390 Message.prototype.setRequestID = function(requestID) {
391 // TODO(darin): Verify that space was reserved for this field!
392 this.buffer.setUint64(kMessageRequestIDOffset, requestID);
396 // MessageBuilder -----------------------------------------------------------
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);
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.
411 MessageBuilder.prototype.createEncoder = function(size) {
412 var pointer = this.buffer.alloc(size);
413 return new Encoder(this.buffer, this.handles, pointer);
416 MessageBuilder.prototype.encodeStruct = function(cls, val) {
417 cls.encode(this.createEncoder(cls.encodedSize), val);
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++.
424 var message = new Message(this.buffer, this.handles);
431 // MessageWithRequestIDBuilder -----------------------------------------------
433 function MessageWithRequestIDBuilder(messageName, payloadSize, flags,
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);
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);
448 MessageWithRequestIDBuilder.prototype =
449 Object.create(MessageBuilder.prototype);
451 MessageWithRequestIDBuilder.prototype.constructor =
452 MessageWithRequestIDBuilder;
454 // MessageReader ------------------------------------------------------------
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();
464 this.requestID = this.decoder.readUint64();
465 this.decoder.skip(messageHeaderSize - this.decoder.next);
468 MessageReader.prototype.decodeStruct = function(cls) {
469 return cls.decode(this.decoder);
472 // Built-in types -----------------------------------------------------------
474 // This type is only used with ArrayOf(PackedBool).
475 function PackedBool() {
481 Int8.encodedSize = 1;
483 Int8.decode = function(decoder) {
484 return decoder.readInt8();
487 Int8.encode = function(encoder, val) {
488 encoder.writeInt8(val);
491 Uint8.encode = function(encoder, val) {
492 encoder.writeUint8(val);
498 Uint8.encodedSize = 1;
500 Uint8.decode = function(decoder) {
501 return decoder.readUint8();
504 Uint8.encode = function(encoder, val) {
505 encoder.writeUint8(val);
511 Int16.encodedSize = 2;
513 Int16.decode = function(decoder) {
514 return decoder.readInt16();
517 Int16.encode = function(encoder, val) {
518 encoder.writeInt16(val);
524 Uint16.encodedSize = 2;
526 Uint16.decode = function(decoder) {
527 return decoder.readUint16();
530 Uint16.encode = function(encoder, val) {
531 encoder.writeUint16(val);
537 Int32.encodedSize = 4;
539 Int32.decode = function(decoder) {
540 return decoder.readInt32();
543 Int32.encode = function(encoder, val) {
544 encoder.writeInt32(val);
550 Uint32.encodedSize = 4;
552 Uint32.decode = function(decoder) {
553 return decoder.readUint32();
556 Uint32.encode = function(encoder, val) {
557 encoder.writeUint32(val);
563 Int64.encodedSize = 8;
565 Int64.decode = function(decoder) {
566 return decoder.readInt64();
569 Int64.encode = function(encoder, val) {
570 encoder.writeInt64(val);
576 Uint64.encodedSize = 8;
578 Uint64.decode = function(decoder) {
579 return decoder.readUint64();
582 Uint64.encode = function(encoder, val) {
583 encoder.writeUint64(val);
589 String.encodedSize = 8;
591 String.decode = function(decoder) {
592 return decoder.decodeStringPointer();
595 String.encode = function(encoder, val) {
596 encoder.encodeStringPointer(val);
599 function NullableString() {
602 NullableString.encodedSize = String.encodedSize;
604 NullableString.decode = String.decode;
606 NullableString.encode = String.encode;
611 Float.encodedSize = 4;
613 Float.decode = function(decoder) {
614 return decoder.readFloat();
617 Float.encode = function(encoder, val) {
618 encoder.writeFloat(val);
624 Double.encodedSize = 8;
626 Double.decode = function(decoder) {
627 return decoder.readDouble();
630 Double.encode = function(encoder, val) {
631 encoder.writeDouble(val);
634 function PointerTo(cls) {
638 PointerTo.prototype.encodedSize = 8;
640 PointerTo.prototype.decode = function(decoder) {
641 var pointer = decoder.decodePointer();
645 return this.cls.decode(decoder.decodeAndCreateDecoder(pointer));
648 PointerTo.prototype.encode = function(encoder, val) {
650 encoder.encodePointer(val);
653 var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize);
654 this.cls.encode(objectEncoder, val);
657 function NullablePointerTo(cls) {
658 PointerTo.call(this, cls);
661 NullablePointerTo.prototype = Object.create(PointerTo.prototype);
663 function ArrayOf(cls) {
667 ArrayOf.prototype.encodedSize = 8;
669 ArrayOf.prototype.decode = function(decoder) {
670 return decoder.decodeArrayPointer(this.cls);
673 ArrayOf.prototype.encode = function(encoder, val) {
674 encoder.encodeArrayPointer(this.cls, val);
677 function NullableArrayOf(cls) {
678 ArrayOf.call(this, cls);
681 NullableArrayOf.prototype = Object.create(ArrayOf.prototype);
686 Handle.encodedSize = 4;
688 Handle.decode = function(decoder) {
689 return decoder.decodeHandle();
692 Handle.encode = function(encoder, val) {
693 encoder.encodeHandle(val);
696 function NullableHandle() {
699 NullableHandle.encodedSize = Handle.encodedSize;
701 NullableHandle.decode = Handle.decode;
703 NullableHandle.encode = Handle.encode;
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;
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;