| OLD | NEW |
| (Empty) |
| 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/bindings/js/codec", function() { | |
| 6 | |
| 7 // Memory ------------------------------------------------------------------- | |
| 8 | |
| 9 function store8(memory, pointer, val) { | |
| 10 memory[pointer] = val; | |
| 11 } | |
| 12 | |
| 13 function store16(memory, pointer, val) { | |
| 14 memory[pointer + 0] = val >> 0; | |
| 15 memory[pointer + 1] = val >> 8; | |
| 16 } | |
| 17 | |
| 18 function store32(memory, pointer, val) { | |
| 19 memory[pointer + 0] = val >> 0; | |
| 20 memory[pointer + 1] = val >> 8; | |
| 21 memory[pointer + 2] = val >> 16; | |
| 22 memory[pointer + 3] = val >> 24; | |
| 23 } | |
| 24 | |
| 25 function store64(memory, pointer, val) { | |
| 26 store32(memory, pointer, val); | |
| 27 var high = (val / 0x10000) | 0; | |
| 28 store32(memory, pointer + 4, high); | |
| 29 } | |
| 30 | |
| 31 function load8(memory, pointer) { | |
| 32 return memory[pointer]; | |
| 33 } | |
| 34 | |
| 35 function load16(memory, pointer) { | |
| 36 return (memory[pointer + 0] << 0) + | |
| 37 (memory[pointer + 1] << 8); | |
| 38 } | |
| 39 | |
| 40 function load32(memory, pointer) { | |
| 41 return (memory[pointer + 0] << 0) + | |
| 42 (memory[pointer + 1] << 8) + | |
| 43 (memory[pointer + 2] << 16) + | |
| 44 (memory[pointer + 3] << 24); | |
| 45 } | |
| 46 | |
| 47 var kAlignment = 8; | |
| 48 | |
| 49 function align(size) { | |
| 50 return size + (kAlignment - (size % kAlignment)) % kAlignment; | |
| 51 } | |
| 52 | |
| 53 // Buffer ------------------------------------------------------------------- | |
| 54 | |
| 55 function Buffer(size) { | |
| 56 this.memory = new Uint8Array(size); | |
| 57 this.next = 0; | |
| 58 } | |
| 59 | |
| 60 Buffer.prototype.alloc = function(size) { | |
| 61 var pointer = this.next; | |
| 62 this.next += size; | |
| 63 if (this.next > this.memory.length) { | |
| 64 var newSize = (1.5 * (this.memory.length + size)) | 0; | |
| 65 this.grow(newSize); | |
| 66 } | |
| 67 return pointer; | |
| 68 }; | |
| 69 | |
| 70 Buffer.prototype.grow = function(size) { | |
| 71 var newMemory = new Uint8Array(size); | |
| 72 var oldMemory = this.memory; | |
| 73 for (var i = 0; i < oldMemory.length; ++i) | |
| 74 newMemory[i] = oldMemory[i]; | |
| 75 this.memory = newMemory; | |
| 76 }; | |
| 77 | |
| 78 Buffer.prototype.createViewOfAllocatedMemory = function() { | |
| 79 return new Uint8Array(this.memory.buffer, 0, this.next); | |
| 80 }; | |
| 81 | |
| 82 // Constants ---------------------------------------------------------------- | |
| 83 | |
| 84 var kArrayHeaderSize = 8; | |
| 85 var kStructHeaderSize = 8; | |
| 86 var kMessageHeaderSize = 16; | |
| 87 var kMessageWithRequestIDHeaderSize = 24; | |
| 88 | |
| 89 // Decoder ------------------------------------------------------------------ | |
| 90 | |
| 91 function Decoder(memory, handles, base) { | |
| 92 this.memory = memory; | |
| 93 this.handles = handles; | |
| 94 this.base = base; | |
| 95 this.next = base; | |
| 96 this.viewU32 = new Uint32Array( | |
| 97 this.memory.buffer, 0, | |
| 98 Math.floor(this.memory.length / Uint32Array.BYTES_PER_ELEMENT)); | |
| 99 this.viewFloat = new Float32Array( | |
| 100 this.memory.buffer, 0, | |
| 101 Math.floor(this.memory.length / Float32Array.BYTES_PER_ELEMENT)); | |
| 102 } | |
| 103 | |
| 104 Decoder.prototype.skip = function(offset) { | |
| 105 this.next += offset; | |
| 106 }; | |
| 107 | |
| 108 Decoder.prototype.read8 = function() { | |
| 109 var result = load8(this.memory, this.next); | |
| 110 this.next += 1; | |
| 111 return result; | |
| 112 }; | |
| 113 | |
| 114 Decoder.prototype.read32 = function() { | |
| 115 var result = this.viewU32[this.next / this.viewU32.BYTES_PER_ELEMENT]; | |
| 116 this.next += this.viewU32.BYTES_PER_ELEMENT; | |
| 117 return result; | |
| 118 }; | |
| 119 | |
| 120 Decoder.prototype.read64 = function() { | |
| 121 var low = this.read32(); | |
| 122 var high = this.read32(); | |
| 123 return low + high * 0x100000000; | |
| 124 }; | |
| 125 | |
| 126 Decoder.prototype.decodeFloat = function() { | |
| 127 var result = this.viewFloat[this.next / this.viewFloat.BYTES_PER_ELEMENT]; | |
| 128 this.next += this.viewFloat.BYTES_PER_ELEMENT; | |
| 129 return result; | |
| 130 }; | |
| 131 | |
| 132 Decoder.prototype.decodePointer = function() { | |
| 133 // TODO(abarth): To correctly decode a pointer, we need to know the real | |
| 134 // base address of the array buffer. | |
| 135 var offsetPointer = this.next; | |
| 136 var offset = this.read64(); | |
| 137 if (!offset) | |
| 138 return 0; | |
| 139 return offsetPointer + offset; | |
| 140 }; | |
| 141 | |
| 142 Decoder.prototype.decodeAndCreateDecoder = function() { | |
| 143 return new Decoder(this.memory, this.handles, this.decodePointer()); | |
| 144 }; | |
| 145 | |
| 146 Decoder.prototype.decodeHandle = function() { | |
| 147 return this.handles[this.read32()]; | |
| 148 }; | |
| 149 | |
| 150 Decoder.prototype.decodeString = function() { | |
| 151 // TODO(abarth): We should really support UTF-8. We might want to | |
| 152 // jump out of the VM to decode the string directly from the array | |
| 153 // buffer using v8::String::NewFromUtf8. | |
| 154 var numberOfBytes = this.read32(); | |
| 155 var numberOfElements = this.read32(); | |
| 156 var val = new Array(numberOfElements); | |
| 157 var memory = this.memory; | |
| 158 var base = this.next; | |
| 159 for (var i = 0; i < numberOfElements; ++i) { | |
| 160 val[i] = String.fromCharCode(memory[base + i] & 0x7F); | |
| 161 } | |
| 162 this.next += numberOfElements; | |
| 163 return val.join(''); | |
| 164 }; | |
| 165 | |
| 166 Decoder.prototype.decodeArray = function(cls) { | |
| 167 var numberOfBytes = this.read32(); | |
| 168 var numberOfElements = this.read32(); | |
| 169 var val = new Array(numberOfElements); | |
| 170 for (var i = 0; i < numberOfElements; ++i) { | |
| 171 val[i] = cls.decode(this); | |
| 172 } | |
| 173 return val; | |
| 174 }; | |
| 175 | |
| 176 Decoder.prototype.decodeStructPointer = function(cls) { | |
| 177 return cls.decode(this.decodeAndCreateDecoder()); | |
| 178 }; | |
| 179 | |
| 180 Decoder.prototype.decodeArrayPointer = function(cls) { | |
| 181 return this.decodeAndCreateDecoder().decodeArray(cls); | |
| 182 }; | |
| 183 | |
| 184 Decoder.prototype.decodeStringPointer = function() { | |
| 185 return this.decodeAndCreateDecoder().decodeString(); | |
| 186 }; | |
| 187 | |
| 188 // Encoder ------------------------------------------------------------------ | |
| 189 | |
| 190 function Encoder(buffer, handles, base) { | |
| 191 this.buffer = buffer; | |
| 192 this.handles = handles; | |
| 193 this.base = base; | |
| 194 this.next = base; | |
| 195 } | |
| 196 | |
| 197 Encoder.prototype.skip = function(offset) { | |
| 198 this.next += offset; | |
| 199 }; | |
| 200 | |
| 201 Encoder.prototype.write8 = function(val) { | |
| 202 store8(this.buffer.memory, this.next, val); | |
| 203 this.next += 1; | |
| 204 }; | |
| 205 | |
| 206 Encoder.prototype.write32 = function(val) { | |
| 207 store32(this.buffer.memory, this.next, val); | |
| 208 this.next += 4; | |
| 209 }; | |
| 210 | |
| 211 Encoder.prototype.write64 = function(val) { | |
| 212 store64(this.buffer.memory, this.next, val); | |
| 213 this.next += 8; | |
| 214 }; | |
| 215 | |
| 216 Encoder.prototype.encodeFloat = function(val) { | |
| 217 var floatBuffer = new Float32Array(1); | |
| 218 floatBuffer[0] = val; | |
| 219 var buffer = new Uint8Array(floatBuffer.buffer, 0); | |
| 220 for (var i = 0; i < buffer.length; ++i) | |
| 221 this.buffer.memory[this.next++] = buffer[i]; | |
| 222 }; | |
| 223 | |
| 224 Encoder.prototype.encodePointer = function(pointer) { | |
| 225 if (!pointer) | |
| 226 return this.write64(0); | |
| 227 // TODO(abarth): To correctly encode a pointer, we need to know the real | |
| 228 // base address of the array buffer. | |
| 229 var offset = pointer - this.next; | |
| 230 this.write64(offset); | |
| 231 }; | |
| 232 | |
| 233 Encoder.prototype.createAndEncodeEncoder = function(size) { | |
| 234 var pointer = this.buffer.alloc(align(size)); | |
| 235 this.encodePointer(pointer); | |
| 236 return new Encoder(this.buffer, this.handles, pointer); | |
| 237 }; | |
| 238 | |
| 239 Encoder.prototype.encodeHandle = function(handle) { | |
| 240 this.handles.push(handle); | |
| 241 this.write32(this.handles.length - 1); | |
| 242 }; | |
| 243 | |
| 244 Encoder.prototype.encodeString = function(val) { | |
| 245 var numberOfElements = val.length; | |
| 246 var numberOfBytes = kArrayHeaderSize + numberOfElements; | |
| 247 this.write32(numberOfBytes); | |
| 248 this.write32(numberOfElements); | |
| 249 // TODO(abarth): We should really support UTF-8. We might want to | |
| 250 // jump out of the VM to encode the string directly from the array | |
| 251 // buffer using v8::String::WriteUtf8. | |
| 252 var memory = this.buffer.memory; | |
| 253 var base = this.next; | |
| 254 var len = val.length; | |
| 255 for (var i = 0; i < len; ++i) { | |
| 256 memory[base + i] = val.charCodeAt(i) & 0x7F; | |
| 257 } | |
| 258 this.next += len; | |
| 259 }; | |
| 260 | |
| 261 Encoder.prototype.encodeArray = function(cls, val) { | |
| 262 var numberOfElements = val.length; | |
| 263 var numberOfBytes = kArrayHeaderSize + cls.encodedSize * numberOfElements; | |
| 264 this.write32(numberOfBytes); | |
| 265 this.write32(numberOfElements); | |
| 266 for (var i = 0; i < numberOfElements; ++i) { | |
| 267 cls.encode(this, val[i]); | |
| 268 } | |
| 269 }; | |
| 270 | |
| 271 Encoder.prototype.encodeStructPointer = function(cls, val) { | |
| 272 var encoder = this.createAndEncodeEncoder(cls.encodedSize); | |
| 273 cls.encode(encoder, val); | |
| 274 }; | |
| 275 | |
| 276 Encoder.prototype.encodeArrayPointer = function(cls, val) { | |
| 277 var encodedSize = kArrayHeaderSize + cls.encodedSize * val.length; | |
| 278 var encoder = this.createAndEncodeEncoder(encodedSize); | |
| 279 encoder.encodeArray(cls, val); | |
| 280 }; | |
| 281 | |
| 282 Encoder.prototype.encodeStringPointer = function(val) { | |
| 283 // TODO(abarth): This won't be right once we support UTF-8. | |
| 284 var encodedSize = kArrayHeaderSize + val.length; | |
| 285 var encoder = this.createAndEncodeEncoder(encodedSize); | |
| 286 encoder.encodeString(val); | |
| 287 }; | |
| 288 | |
| 289 // Message ------------------------------------------------------------------ | |
| 290 | |
| 291 var kMessageExpectsResponse = 1 << 0; | |
| 292 var kMessageIsResponse = 1 << 1; | |
| 293 | |
| 294 function Message(memory, handles) { | |
| 295 this.memory = memory; | |
| 296 this.handles = handles; | |
| 297 } | |
| 298 | |
| 299 Message.prototype.setRequestID = function(requestID) { | |
| 300 // TODO(darin): Verify that space was reserved for this field! | |
| 301 store64(this.memory, 4 + 4 + 4 + 4, requestID); | |
| 302 }; | |
| 303 | |
| 304 Message.prototype.getFlags = function() { | |
| 305 // Skip over num_bytes, num_fields, and message_name. | |
| 306 return load32(this.memory, 4 + 4 + 4); | |
| 307 }; | |
| 308 | |
| 309 // MessageBuilder ----------------------------------------------------------- | |
| 310 | |
| 311 function MessageBuilder(messageName, payloadSize) { | |
| 312 // Currently, we don't compute the payload size correctly ahead of time. | |
| 313 // Instead, we resize the buffer at the end. | |
| 314 var numberOfBytes = kMessageHeaderSize + payloadSize; | |
| 315 this.buffer = new Buffer(numberOfBytes); | |
| 316 this.handles = []; | |
| 317 var encoder = this.createEncoder(kMessageHeaderSize); | |
| 318 encoder.write32(kMessageHeaderSize); | |
| 319 encoder.write32(2); // num_fields. | |
| 320 encoder.write32(messageName); | |
| 321 encoder.write32(0); // flags. | |
| 322 } | |
| 323 | |
| 324 MessageBuilder.prototype.createEncoder = function(size) { | |
| 325 var pointer = this.buffer.alloc(size); | |
| 326 return new Encoder(this.buffer, this.handles, pointer); | |
| 327 }; | |
| 328 | |
| 329 MessageBuilder.prototype.encodeStruct = function(cls, val) { | |
| 330 cls.encode(this.createEncoder(cls.encodedSize), val); | |
| 331 }; | |
| 332 | |
| 333 MessageBuilder.prototype.finish = function() { | |
| 334 // TODO(abarth): Rather than resizing the buffer at the end, we could | |
| 335 // compute the size we need ahead of time, like we do in C++. | |
| 336 var memory = this.buffer.createViewOfAllocatedMemory(); | |
| 337 var message = new Message(memory, this.handles); | |
| 338 this.buffer = null; | |
| 339 this.handles = null; | |
| 340 this.encoder = null; | |
| 341 return message; | |
| 342 }; | |
| 343 | |
| 344 // MessageWithRequestIDBuilder ----------------------------------------------- | |
| 345 | |
| 346 function MessageWithRequestIDBuilder(messageName, payloadSize, flags, | |
| 347 requestID) { | |
| 348 // Currently, we don't compute the payload size correctly ahead of time. | |
| 349 // Instead, we resize the buffer at the end. | |
| 350 var numberOfBytes = kMessageWithRequestIDHeaderSize + payloadSize; | |
| 351 this.buffer = new Buffer(numberOfBytes); | |
| 352 this.handles = []; | |
| 353 var encoder = this.createEncoder(kMessageWithRequestIDHeaderSize); | |
| 354 encoder.write32(kMessageWithRequestIDHeaderSize); | |
| 355 encoder.write32(3); // num_fields. | |
| 356 encoder.write32(messageName); | |
| 357 encoder.write32(flags); | |
| 358 encoder.write64(requestID); | |
| 359 } | |
| 360 | |
| 361 MessageWithRequestIDBuilder.prototype = | |
| 362 Object.create(MessageBuilder.prototype); | |
| 363 MessageWithRequestIDBuilder.prototype.constructor = | |
| 364 MessageWithRequestIDBuilder; | |
| 365 | |
| 366 // MessageReader ------------------------------------------------------------ | |
| 367 | |
| 368 function MessageReader(message) { | |
| 369 this.decoder = new Decoder(message.memory, message.handles, 0); | |
| 370 var messageHeaderSize = this.decoder.read32(); | |
| 371 this.payloadSize = message.memory.length - messageHeaderSize; | |
| 372 var numFields = this.decoder.read32(); | |
| 373 this.messageName = this.decoder.read32(); | |
| 374 this.flags = this.decoder.read32(); | |
| 375 if (numFields >= 3) | |
| 376 this.requestID = this.decoder.read64(); | |
| 377 this.decoder.skip(messageHeaderSize - this.decoder.next); | |
| 378 } | |
| 379 | |
| 380 MessageReader.prototype.decodeStruct = function(cls) { | |
| 381 return cls.decode(this.decoder); | |
| 382 }; | |
| 383 | |
| 384 // Built-in types ----------------------------------------------------------- | |
| 385 | |
| 386 function Uint8() { | |
| 387 } | |
| 388 | |
| 389 Uint8.encodedSize = 1; | |
| 390 | |
| 391 Uint8.decode = function(decoder) { | |
| 392 return decoder.read8(); | |
| 393 }; | |
| 394 | |
| 395 Uint8.encode = function(encoder, val) { | |
| 396 encoder.write8(val); | |
| 397 }; | |
| 398 | |
| 399 function Uint16() { | |
| 400 } | |
| 401 | |
| 402 Uint16.encodedSize = 2; | |
| 403 | |
| 404 Uint16.decode = function(decoder) { | |
| 405 return decoder.read16(); | |
| 406 }; | |
| 407 | |
| 408 Uint16.encode = function(encoder, val) { | |
| 409 encoder.write16(val); | |
| 410 }; | |
| 411 | |
| 412 function Uint32() { | |
| 413 } | |
| 414 | |
| 415 Uint32.encodedSize = 4; | |
| 416 | |
| 417 Uint32.decode = function(decoder) { | |
| 418 return decoder.read32(); | |
| 419 }; | |
| 420 | |
| 421 Uint32.encode = function(encoder, val) { | |
| 422 encoder.write32(val); | |
| 423 }; | |
| 424 | |
| 425 function Uint64() { | |
| 426 }; | |
| 427 | |
| 428 Uint64.encodedSize = 8; | |
| 429 | |
| 430 Uint64.decode = function(decoder) { | |
| 431 return decoder.read64(); | |
| 432 }; | |
| 433 | |
| 434 Uint64.encode = function(encoder, val) { | |
| 435 encoder.write64(val); | |
| 436 }; | |
| 437 | |
| 438 function PointerTo(cls) { | |
| 439 this.cls = cls; | |
| 440 }; | |
| 441 | |
| 442 // TODO(abarth): Add missing types: | |
| 443 // * String | |
| 444 // * Float | |
| 445 // * Double | |
| 446 // * Signed integers | |
| 447 | |
| 448 PointerTo.prototype.encodedSize = 8; | |
| 449 | |
| 450 PointerTo.prototype.decode = function(decoder) { | |
| 451 return this.cls.decode(decoder.decodeAndCreateDecoder()); | |
| 452 }; | |
| 453 | |
| 454 PointerTo.prototype.encode = function(encoder, val) { | |
| 455 var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize); | |
| 456 this.cls.encode(objectEncoder, val); | |
| 457 }; | |
| 458 | |
| 459 function ArrayOf(cls) { | |
| 460 this.cls = cls; | |
| 461 }; | |
| 462 | |
| 463 ArrayOf.prototype.encodedSize = 8; | |
| 464 | |
| 465 ArrayOf.prototype.decode = function(decoder) { | |
| 466 return decoder.decodeArrayPointer(self.cls); | |
| 467 }; | |
| 468 | |
| 469 ArrayOf.prototype.encode = function(encoder, val) { | |
| 470 encoder.encodeArrayPointer(self.cls, val); | |
| 471 }; | |
| 472 | |
| 473 function Handle() { | |
| 474 } | |
| 475 | |
| 476 Handle.encodedSize = 4; | |
| 477 | |
| 478 Handle.decode = function(decoder) { | |
| 479 return decoder.decodeHandle(); | |
| 480 }; | |
| 481 | |
| 482 Handle.encode = function(encoder, val) { | |
| 483 encoder.encodeHandle(val); | |
| 484 }; | |
| 485 | |
| 486 var exports = {}; | |
| 487 exports.align = align; | |
| 488 exports.Message = Message; | |
| 489 exports.MessageBuilder = MessageBuilder; | |
| 490 exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder; | |
| 491 exports.MessageReader = MessageReader; | |
| 492 exports.kArrayHeaderSize = kArrayHeaderSize; | |
| 493 exports.kStructHeaderSize = kStructHeaderSize; | |
| 494 exports.kMessageHeaderSize = kMessageHeaderSize; | |
| 495 exports.kMessageExpectsResponse = kMessageExpectsResponse; | |
| 496 exports.kMessageIsResponse = kMessageIsResponse; | |
| 497 exports.Uint8 = Uint8; | |
| 498 exports.Uint16 = Uint16; | |
| 499 exports.Uint32 = Uint32; | |
| 500 exports.Uint64 = Uint64; | |
| 501 exports.PointerTo = PointerTo; | |
| 502 exports.ArrayOf = ArrayOf; | |
| 503 exports.Handle = Handle; | |
| 504 return exports; | |
| 505 }); | |
| OLD | NEW |