1 // Copyright 2013 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(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 function load64(memory, pointer) { 48 var low = load32(memory, pointer); 49 var high = load32(memory, pointer + 4); 50 return low + high * 0x10000; 51 } 52 53 var kAlignment = 8; 54 55 function align(size) { 56 return size + (kAlignment - (size % kAlignment)) % kAlignment; 57 } 58 59 // Buffer ------------------------------------------------------------------- 60 61 function Buffer(size) { 62 this.memory = new Uint8Array(size); 63 this.next = 0; 64 } 65 66 Buffer.prototype.alloc = function(size) { 67 var pointer = this.next; 68 this.next += size; 69 if (this.next > this.memory.length) { 70 var newSize = (1.5 * (this.memory.length + size)) | 0; 71 this.grow(newSize); 72 } 73 return pointer; 74 }; 75 76 Buffer.prototype.grow = function(size) { 77 var newMemory = new Uint8Array(size); 78 var oldMemory = this.memory; 79 for (var i = 0; i < oldMemory.length; ++i) 80 newMemory[i] = oldMemory[i]; 81 this.memory = newMemory; 82 }; 83 84 Buffer.prototype.createViewOfAllocatedMemory = function() { 85 return new Uint8Array(this.memory.buffer, 0, this.next); 86 }; 87 88 // Constants ---------------------------------------------------------------- 89 90 var kArrayHeaderSize = 8; 91 var kStructHeaderSize = 8; 92 var kMessageHeaderSize = 8; 93 94 // Decoder ------------------------------------------------------------------ 95 96 function Decoder(memory, handles, base) { 97 this.memory = memory; 98 this.handles = handles; 99 this.base = base; 100 this.next = base; 101 } 102 103 Decoder.prototype.skip = function(offset) { 104 this.next += offset; 105 }; 106 107 Decoder.prototype.read8 = function() { 108 var result = load8(this.memory, this.next); 109 this.next += 1; 110 return result; 111 }; 112 113 Decoder.prototype.read32 = function() { 114 var result = load32(this.memory, this.next); 115 this.next += 4; 116 return result; 117 }; 118 119 Decoder.prototype.read64 = function() { 120 var result = load64(this.memory, this.next); 121 this.next += 8; 122 return result; 123 }; 124 125 Decoder.prototype.decodePointer = function() { 126 // TODO(abarth): To correctly decode a pointer, we need to know the real 127 // base address of the array buffer. 128 var offsetPointer = this.next; 129 var offset = this.read64(); 130 if (!offset) 131 return 0; 132 return offsetPointer + offset; 133 }; 134 135 Decoder.prototype.decodeAndCreateDecoder = function() { 136 return new Decoder(this.memory, this.handles, this.decodePointer()); 137 }; 138 139 Decoder.prototype.decodeHandle = function() { 140 return this.handles[this.read32()]; 141 }; 142 143 Decoder.prototype.decodeString = function() { 144 // TODO(abarth): We should really support UTF-8. We might want to 145 // jump out of the VM to decode the string directly from the array 146 // buffer using v8::String::NewFromUtf8. 147 var numberOfBytes = this.read32(); 148 var numberOfElements = this.read32(); 149 var val = new Array(numberOfElements); 150 var memory = this.memory; 151 var base = this.next; 152 for (var i = 0; i < numberOfElements; ++i) { 153 val[i] = String.fromCharCode(memory[base + i] & 0x7F); 154 } 155 this.next += numberOfElements; 156 return val.join(''); 157 }; 158 159 Decoder.prototype.decodeArray = function(cls) { 160 var numberOfBytes = this.read32(); 161 var numberOfElements = this.read32(); 162 var val = new Array(numberOfElements); 163 for (var i = 0; i < numberOfElements; ++i) { 164 val[i] = cls.decode(this); 165 } 166 return val; 167 }; 168 169 Decoder.prototype.decodeStructPointer = function(cls) { 170 return cls.decode(this.decodeAndCreateDecoder()); 171 }; 172 173 Decoder.prototype.decodeArrayPointer = function(cls) { 174 return this.decodeAndCreateDecoder().decodeArray(cls); 175 }; 176 177 Decoder.prototype.decodeStringPointer = function() { 178 return this.decodeAndCreateDecoder().decodeString(); 179 }; 180 181 // Encoder ------------------------------------------------------------------ 182 183 function Encoder(buffer, handles, base) { 184 this.buffer = buffer; 185 this.handles = handles; 186 this.base = base; 187 this.next = base; 188 } 189 190 Encoder.prototype.skip = function(offset) { 191 this.next += offset; 192 }; 193 194 Encoder.prototype.write8 = function(val) { 195 store8(this.buffer.memory, this.next, val); 196 this.next += 1; 197 }; 198 199 Encoder.prototype.write32 = function(val) { 200 store32(this.buffer.memory, this.next, val); 201 this.next += 4; 202 }; 203 204 Encoder.prototype.write64 = function(val) { 205 store64(this.buffer.memory, this.next, val); 206 this.next += 8; 207 }; 208 209 Encoder.prototype.encodePointer = function(pointer) { 210 if (!pointer) 211 return this.write64(0); 212 // TODO(abarth): To correctly encode a pointer, we need to know the real 213 // base address of the array buffer. 214 var offset = pointer - this.next; 215 this.write64(offset); 216 }; 217 218 Encoder.prototype.createAndEncodeEncoder = function(size) { 219 var pointer = this.buffer.alloc(align(size)); 220 this.encodePointer(pointer); 221 return new Encoder(this.buffer, this.handles, pointer); 222 }; 223 224 Encoder.prototype.encodeHandle = function(handle) { 225 this.handles.push(handle); 226 this.write32(this.handles.length - 1); 227 }; 228 229 Encoder.prototype.encodeString = function(val) { 230 var numberOfElements = val.length; 231 var numberOfBytes = kArrayHeaderSize + numberOfElements; 232 this.write32(numberOfBytes); 233 this.write32(numberOfElements); 234 // TODO(abarth): We should really support UTF-8. We might want to 235 // jump out of the VM to encode the string directly from the array 236 // buffer using v8::String::WriteUtf8. 237 var memory = this.buffer.memory; 238 var base = this.next; 239 var len = val.length; 240 for (var i = 0; i < len; ++i) { 241 memory[base + i] = val.charCodeAt(i) & 0x7F; 242 } 243 this.next += len; 244 }; 245 246 Encoder.prototype.encodeArray = function(cls, val) { 247 var numberOfElements = val.length; 248 var numberOfBytes = kArrayHeaderSize + cls.encodedSize * numberOfElements; 249 this.write32(numberOfBytes); 250 this.write32(numberOfElements); 251 for (var i = 0; i < numberOfElements; ++i) { 252 cls.encode(this, val[i]); 253 } 254 }; 255 256 Encoder.prototype.encodeStructPointer = function(cls, val) { 257 var encoder = this.createAndEncodeEncoder(cls.encodedSize); 258 cls.encode(encoder, val); 259 }; 260 261 Encoder.prototype.encodeArrayPointer = function(cls, val) { 262 var encodedSize = kArrayHeaderSize + cls.encodedSize * val.length; 263 var encoder = this.createAndEncodeEncoder(encodedSize); 264 encoder.encodeArray(cls, val); 265 }; 266 267 Encoder.prototype.encodeStringPointer = function(val) { 268 // TODO(abarth): This won't be right once we support UTF-8. 269 var encodedSize = kArrayHeaderSize + val.length; 270 var encoder = this.createAndEncodeEncoder(encodedSize); 271 encoder.encodeString(val); 272 }; 273 274 // Message ------------------------------------------------------------------ 275 276 function Message(memory, handles) { 277 this.memory = memory; 278 this.handles = handles; 279 } 280 281 // MessageBuilder ----------------------------------------------------------- 282 283 function MessageBuilder(messageName, payloadSize) { 284 // Currently, we don't compute the payload size correctly ahead of time. 285 // Instead, we overwrite this field at the end. 286 var numberOfBytes = kMessageHeaderSize + payloadSize; 287 this.buffer = new Buffer(numberOfBytes); 288 this.handles = []; 289 var encoder = this.createEncoder(kMessageHeaderSize); 290 encoder.write32(numberOfBytes); 291 encoder.write32(messageName); 292 } 293 294 MessageBuilder.prototype.createEncoder = function(size) { 295 var pointer = this.buffer.alloc(size); 296 return new Encoder(this.buffer, this.handles, pointer); 297 } 298 299 MessageBuilder.prototype.encodeStruct = function(cls, val) { 300 cls.encode(this.createEncoder(cls.encodedSize), val); 301 }; 302 303 MessageBuilder.prototype.finish = function() { 304 // TODO(abarth): Rather than resizing the buffer at the end, we could 305 // compute the size we need ahead of time, like we do in C++. 306 var memory = this.buffer.createViewOfAllocatedMemory(); 307 store32(memory, 0, memory.length); 308 var message = new Message(memory, this.handles); 309 this.buffer = null; 310 this.handles = null; 311 this.encoder = null; 312 return message; 313 }; 314 315 // MessageReader ------------------------------------------------------------ 316 317 function MessageReader(message) { 318 this.decoder = new Decoder(message.memory, message.handles, 0); 319 this.payloadSize = this.decoder.read32() - kMessageHeaderSize; 320 this.messageName = this.decoder.read32(); 321 } 322 323 MessageReader.prototype.decodeStruct = function(cls) { 324 return cls.decode(this.decoder); 325 }; 326 327 // Built-in types ----------------------------------------------------------- 328 329 function Uint8() { 330 } 331 332 Uint8.encodedSize = 1; 333 334 Uint8.decode = function(decoder) { 335 return decoder.read8(); 336 }; 337 338 Uint8.encode = function(encoder, val) { 339 encoder.write8(val); 340 }; 341 342 function Uint16() { 343 } 344 345 Uint16.encodedSize = 2; 346 347 Uint16.decode = function(decoder) { 348 return decoder.read16(); 349 }; 350 351 Uint16.encode = function(encoder, val) { 352 encoder.write16(val); 353 }; 354 355 function Uint32() { 356 } 357 358 Uint32.encodedSize = 4; 359 360 Uint32.decode = function(decoder) { 361 return decoder.read32(); 362 }; 363 364 Uint32.encode = function(encoder, val) { 365 encoder.write32(val); 366 }; 367 368 function Uint64() { 369 }; 370 371 Uint64.encodedSize = 8; 372 373 Uint64.decode = function(decoder) { 374 return decoder.read64(); 375 }; 376 377 Uint64.encode = function(encoder, val) { 378 encoder.write64(val); 379 }; 380 381 function PointerTo(cls) { 382 this.cls = cls; 383 }; 384 385 // TODO(abarth): Add missing types: 386 // * String 387 // * Float 388 // * Double 389 // * Signed integers 390 391 PointerTo.prototype.encodedSize = 8; 392 393 PointerTo.prototype.decode = function(decoder) { 394 return this.cls.decode(decoder.decodeAndCreateDecoder()); 395 }; 396 397 PointerTo.prototype.encode = function(encoder, val) { 398 var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize); 399 this.cls.encode(objectEncoder, val); 400 }; 401 402 function ArrayOf(cls) { 403 this.cls = cls; 404 }; 405 406 ArrayOf.prototype.encodedSize = 8; 407 408 ArrayOf.prototype.decode = function(decoder) { 409 return decoder.decodeArrayPointer(self.cls); 410 }; 411 412 ArrayOf.prototype.encode = function(encoder, val) { 413 encoder.encodeArrayPointer(self.cls, val); 414 }; 415 416 function Handle() { 417 } 418 419 Handle.encodedSize = 4; 420 421 Handle.decode = function(decoder) { 422 return decoder.decodeHandle(); 423 }; 424 425 Handle.encode = function(encoder, val) { 426 encoder.encodeHandle(val); 427 }; 428 429 var exports = {}; 430 exports.align = align; 431 exports.Message = Message; 432 exports.MessageBuilder = MessageBuilder; 433 exports.MessageReader = MessageReader; 434 exports.kArrayHeaderSize = kArrayHeaderSize; 435 exports.kStructHeaderSize = kStructHeaderSize; 436 exports.kMessageHeaderSize = kMessageHeaderSize; 437 exports.Uint8 = Uint8; 438 exports.Uint16 = Uint16; 439 exports.Uint32 = Uint32; 440 exports.Uint64 = Uint64; 441 exports.PointerTo = PointerTo; 442 exports.ArrayOf = ArrayOf; 443 exports.Handle = Handle; 444 return exports; 445 }); 446