Home | History | Annotate | Download | only in bindings
      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