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 |