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/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}); 740