OLD | NEW |
| (Empty) |
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 }); | |
OLD | NEW |