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