OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE.md file. | |
4 | |
5 /// This comment describes the Fletch Agent's request and reply message format. | |
6 /// | |
7 /// Message requests all start with the following header: | |
8 /// | |
9 /// ----------------------------------------- | |
10 /// | Command (16 bits) | Version (16 bits) | | |
11 /// ----------------------------------------- | |
12 /// | Msg ID (16 bits) | Unused (16 bits) | | |
13 /// ----------------------------------------- | |
14 /// | Payload Length (32 bits) | | |
15 /// ----------------------------------------- | |
16 /// | |
17 /// Field descriptions: | |
18 /// | |
19 /// Command: Identifies the requested action. | |
20 /// Version: used to determine if the server supports the client's request. | |
21 /// Msg ID: used to correlate a reply with the sender. | |
22 /// Unused: Reserved for future uses. | |
23 /// Payload Length: Length of request payload in bytes. | |
24 /// | |
25 /// The request header is immediately followed by the relevant payload data for | |
26 /// the given command. See details for payloads below. | |
27 /// | |
28 /// All requests have a related reply with the following reply header. | |
29 /// | |
30 /// --------------------------------------- | |
31 /// | Msg ID (16 bits) | Result (16 bits) | | |
32 /// --------------------------------------- | |
33 /// | Payload Length (32 bits) | | |
34 /// --------------------------------------- | |
35 /// | |
36 /// Field descriptions: | |
37 /// | |
38 /// Msg ID: used to correlate a reply with the sender. | |
39 /// Result: Can be success (0) or failure (a positive number). | |
40 /// Payload length: Length of reply payload in bytes. | |
41 /// | |
42 /// The reply header is immediately followed by the relevant payload data for | |
43 /// the related request's command. See details for payloads below. | |
44 /// | |
45 /// Command descriptions: | |
46 /// | |
47 /// Below follows a description of request/reply payloads for a given command. | |
48 /// Each payload is always preceded by the corresponding header. | |
49 /// | |
50 /// START_VM: | |
51 /// Start a new Fletch VM and return the vm's id and port on which it is | |
52 /// listening. | |
53 /// Request Payload: | |
54 /// None. | |
55 /// Reply Payload on success: | |
56 /// --------------------- | |
57 /// | VM ID (32 bits) | | |
58 /// --------------------- | |
59 /// | VM Port (32 bits) | | |
60 /// --------------------- | |
61 /// Reply Payload on failure: | |
62 /// None. | |
63 /// | |
64 /// STOP_VM: | |
65 /// Stop the VM specified by the given vm id. | |
66 /// Request Payload: | |
67 /// --------------------- | |
68 /// | VM ID (32 bits) | | |
69 /// --------------------- | |
70 /// Reply Payload on success: | |
71 /// None. | |
72 /// Reply Payload on failure: | |
73 /// None. | |
74 /// | |
75 /// LIST_VMS: | |
76 /// This command lists the currently running Fletch VMs. | |
77 /// Request Payload: | |
78 /// None. | |
79 /// Reply Payload on success: | |
80 /// --------------------- | |
81 /// | VM ID (32 bits) | | |
82 /// --------------------- | |
83 /// | VM ID (32 bits) | | |
84 /// --------------------- | |
85 /// | VM ID (32 bits) | | |
86 /// --------------------- | |
87 /// ... | |
88 /// | |
89 /// Reply Payload on failure: | |
90 /// None. | |
91 /// | |
92 /// UPGRADE_VM: | |
93 /// This command is used to update the Fletch VM binary on the device. | |
94 /// Request Payload: | |
95 /// ... the vm binary bytes | |
96 /// | |
97 /// Reply Payload on success: | |
98 /// None. | |
99 /// Reply Payload on failure: | |
100 /// None. | |
101 | |
102 library fletch_agent.messages; | |
103 | |
104 import 'dart:convert' show ASCII; | |
105 import 'dart:typed_data'; | |
106 | |
107 /// Current Fletch Agent version | |
108 const int AGENT_VERSION = 1; | |
109 | |
110 /// Default agent port | |
111 const int AGENT_DEFAULT_PORT = 12121; | |
112 | |
113 /// Temporary path for the agent package used during upgrade. | |
114 const String PACKAGE_FILE_NAME = '/tmp/fletch-agent.deb'; | |
115 | |
116 class RequestHeader { | |
117 static const int START_VM = 0; | |
118 static const int STOP_VM = 1; | |
119 static const int LIST_VMS = 2; | |
120 static const int UPGRADE_AGENT = 3; | |
121 static const int FLETCH_VERSION = 4; | |
122 static const int SIGNAL_VM = 5; | |
123 | |
124 // Wire size (bytes) of the RequestHeader. | |
125 static const int HEADER_SIZE = 12; | |
126 | |
127 static int _nextMessageId = 1; | |
128 static int get nextMessageId { | |
129 if ((_nextMessageId & 0xFFFF) == 0) { | |
130 _nextMessageId = 1; | |
131 } | |
132 return _nextMessageId++; | |
133 } | |
134 | |
135 final int command; | |
136 final int version; | |
137 final int id; | |
138 final int reserved; | |
139 final int payloadLength; | |
140 | |
141 RequestHeader( | |
142 this.command, | |
143 {this.version: AGENT_VERSION, | |
144 int id, | |
145 this.reserved: 0, | |
146 this.payloadLength: 0}) | |
147 : id = id != null ? id : nextMessageId; | |
148 | |
149 factory RequestHeader.fromBuffer(ByteBuffer buffer) { | |
150 if (buffer.lengthInBytes < HEADER_SIZE) { | |
151 throw new MessageDecodeException( | |
152 'Insufficient bytes (${buffer.lengthInBytes}) to decode header: ' | |
153 '${buffer.asUint8List()})'); | |
154 } | |
155 // The network byte order is big endian. | |
156 int cmd = readUint16(buffer, 0); | |
157 int version = readUint16(buffer, 2); | |
158 int id = readUint16(buffer, 4); | |
159 int reserved = readUint16(buffer, 6); | |
160 int payloadLength = readUint32(buffer, 8); | |
161 return new RequestHeader(cmd, version: version, id: id, reserved: reserved, | |
162 payloadLength: payloadLength); | |
163 } | |
164 | |
165 ByteBuffer toBuffer() { | |
166 var buffer = new Uint8List(HEADER_SIZE).buffer; | |
167 _writeHeader(buffer); | |
168 return buffer; | |
169 } | |
170 | |
171 void _writeHeader(ByteBuffer buffer) { | |
172 writeUint16(buffer, 0, command); | |
173 writeUint16(buffer, 2, version); | |
174 writeUint16(buffer, 4, id); | |
175 writeUint16(buffer, 6, reserved); | |
176 writeUint32(buffer, 8, payloadLength); | |
177 } | |
178 } | |
179 | |
180 class StartVmRequest extends RequestHeader { | |
181 StartVmRequest() : super(RequestHeader.START_VM); | |
182 | |
183 StartVmRequest.withHeader(RequestHeader header) | |
184 : super( | |
185 RequestHeader.START_VM, | |
186 version: header.version, | |
187 id: header.id, | |
188 reserved: header.reserved); | |
189 | |
190 factory StartVmRequest.fromBuffer(ByteBuffer buffer) { | |
191 var header = new RequestHeader.fromBuffer(buffer); | |
192 assert(header != null); | |
193 if (header.command != RequestHeader.START_VM || header.payloadLength != 0) { | |
194 throw new MessageDecodeException( | |
195 "Invalid StartVmRequest: ${buffer.asUint8List()}"); | |
196 } | |
197 return new StartVmRequest.withHeader(header); | |
198 } | |
199 | |
200 // A StartVmRequest has no payload so just use parent's toBuffer method. | |
201 } | |
202 | |
203 class StopVmRequest extends RequestHeader { | |
204 final int vmPid; | |
205 | |
206 StopVmRequest(this.vmPid) | |
207 : super(RequestHeader.STOP_VM, payloadLength: 4); | |
208 | |
209 StopVmRequest.withHeader(RequestHeader header, this.vmPid) | |
210 : super( | |
211 RequestHeader.STOP_VM, | |
212 version: header.version, | |
213 id: header.id, | |
214 reserved: header.reserved); | |
215 | |
216 factory StopVmRequest.fromBuffer(ByteBuffer buffer) { | |
217 if (buffer.lengthInBytes < RequestHeader.HEADER_SIZE + 4) { | |
218 throw new MessageDecodeException( | |
219 'Insufficient data for a StopVmRequest: ${buffer.asUint8List()}'); | |
220 } | |
221 var header = new RequestHeader.fromBuffer(buffer); | |
222 if (header.command != RequestHeader.STOP_VM || header.payloadLength != 4) { | |
223 throw new MessageDecodeException( | |
224 "Invalid StopVmRequest: ${buffer.asUint8List()}"); | |
225 } | |
226 var vmPid = readUint32(buffer, RequestHeader.HEADER_SIZE); | |
227 return new StopVmRequest.withHeader(header, vmPid); | |
228 } | |
229 | |
230 ByteBuffer toBuffer() { | |
231 var buffer = new Uint8List(RequestHeader.HEADER_SIZE + 4).buffer; | |
232 _writeHeader(buffer); | |
233 writeUint32(buffer, RequestHeader.HEADER_SIZE, vmPid); | |
234 return buffer; | |
235 } | |
236 } | |
237 | |
238 class ListVmsRequest extends RequestHeader { | |
239 ListVmsRequest() : super(RequestHeader.LIST_VMS); | |
240 | |
241 ListVmsRequest.withHeader(RequestHeader header) | |
242 : super( | |
243 RequestHeader.LIST_VMS, | |
244 version: header.version, | |
245 id: header.id, | |
246 reserved: header.reserved); | |
247 | |
248 factory ListVmsRequest.fromBuffer(ByteBuffer buffer) { | |
249 var header = new RequestHeader.fromBuffer(buffer); | |
250 if (header.command != RequestHeader.LIST_VMS || header.payloadLength != 0) { | |
251 throw new MessageDecodeException( | |
252 "Invalid ListVmsRequest: ${buffer.asUint8List()}"); | |
253 } | |
254 return new ListVmsRequest.withHeader(header); | |
255 } | |
256 | |
257 // A ListVmsRequest has no payload so just use parent's toBuffer method. | |
258 } | |
259 | |
260 class UpgradeAgentRequest extends RequestHeader { | |
261 final List<int> binary; | |
262 | |
263 UpgradeAgentRequest(List<int> binary) | |
264 : super(RequestHeader.UPGRADE_AGENT, payloadLength: binary.length), | |
265 binary = binary; | |
266 | |
267 UpgradeAgentRequest.withHeader(RequestHeader header, List<int> binary) | |
268 : super( | |
269 RequestHeader.UPGRADE_AGENT, | |
270 version: header.version, | |
271 id: header.id, | |
272 reserved: header.reserved, | |
273 payloadLength: binary.length), | |
274 binary = binary; | |
275 | |
276 factory UpgradeAgentRequest.fromBuffer(ByteBuffer buffer) { | |
277 var header = new RequestHeader.fromBuffer(buffer); | |
278 if (header.command != RequestHeader.UPGRADE_AGENT) { | |
279 throw new MessageDecodeException( | |
280 'Invalid UpgradeVmRequest: ${buffer.asUint8List()}'); | |
281 } | |
282 // TODO(wibling): figure out how to best represent the vm binary data. | |
283 // The below has issues since the list view is offset and hence using | |
284 // the underlying buffer requires the user to know the buffer is not the | |
285 // same length as the list. | |
286 var binary = buffer.asUint8List(RequestHeader.HEADER_SIZE); | |
287 return new UpgradeAgentRequest.withHeader(header, binary); | |
288 } | |
289 | |
290 ByteBuffer toBuffer() { | |
291 var bytes = | |
292 new Uint8List(RequestHeader.HEADER_SIZE + binary.length); | |
293 _writeHeader(bytes.buffer); | |
294 // TODO(wibling): This does a copy of the vm binary. Try to avoid that. | |
295 for (int i = 0; i < binary.length; ++i) { | |
296 bytes[RequestHeader.HEADER_SIZE + i] = binary[i]; | |
297 } | |
298 return bytes.buffer; | |
299 } | |
300 } | |
301 | |
302 class FletchVersionRequest extends RequestHeader { | |
303 FletchVersionRequest() | |
304 : super(RequestHeader.FLETCH_VERSION); | |
305 | |
306 FletchVersionRequest.withHeader(RequestHeader header) | |
307 : super( | |
308 RequestHeader.FLETCH_VERSION, | |
309 version: header.version, | |
310 id: header.id, | |
311 reserved: header.reserved); | |
312 | |
313 factory FletchVersionRequest.fromBuffer(ByteBuffer buffer) { | |
314 var header = new RequestHeader.fromBuffer(buffer); | |
315 if (header.command != RequestHeader.FLETCH_VERSION || | |
316 header.payloadLength != 0) { | |
317 throw new MessageDecodeException( | |
318 'Invalid FletchVersionRequest: ${buffer.asUint8List()}'); | |
319 } | |
320 return new FletchVersionRequest.withHeader(header); | |
321 } | |
322 | |
323 // A FletchVersionRequest has no payload so just use parent's toBuffer method. | |
324 } | |
325 | |
326 class SignalVmRequest extends RequestHeader { | |
327 final int vmPid; | |
328 final int signal; | |
329 | |
330 SignalVmRequest(this.vmPid, this.signal) | |
331 : super(RequestHeader.SIGNAL_VM, payloadLength: 8); | |
332 | |
333 SignalVmRequest.withHeader(RequestHeader header, this.vmPid, this.signal) | |
334 : super( | |
335 RequestHeader.SIGNAL_VM, | |
336 version: header.version, | |
337 id: header.id, | |
338 reserved: header.reserved); | |
339 | |
340 factory SignalVmRequest.fromBuffer(ByteBuffer buffer) { | |
341 if (buffer.lengthInBytes < RequestHeader.HEADER_SIZE + 8) { | |
342 throw new MessageDecodeException( | |
343 'Insufficient data for a SignalVmRequest: ${buffer.asUint8List()}'); | |
344 } | |
345 RequestHeader header = new RequestHeader.fromBuffer(buffer); | |
346 if (header.command != RequestHeader.SIGNAL_VM || | |
347 header.payloadLength != 8) { | |
348 throw new MessageDecodeException( | |
349 "Invalid SignalVmRequest: ${buffer.asUint8List()}"); | |
350 } | |
351 int vmPid = readUint32(buffer, RequestHeader.HEADER_SIZE); | |
352 int signal = readUint32(buffer, RequestHeader.HEADER_SIZE + 4); | |
353 return new SignalVmRequest.withHeader(header, vmPid, signal); | |
354 } | |
355 | |
356 ByteBuffer toBuffer() { | |
357 var buffer = new Uint8List(RequestHeader.HEADER_SIZE + 8).buffer; | |
358 _writeHeader(buffer); | |
359 writeUint32(buffer, RequestHeader.HEADER_SIZE, vmPid); | |
360 writeUint32(buffer, RequestHeader.HEADER_SIZE + 4, signal); | |
361 return buffer; | |
362 } | |
363 } | |
364 | |
365 class ReplyHeader { | |
366 /// Error codes. | |
367 static const int SUCCESS = 0; | |
368 static const int UNKNOWN_COMMAND = 1; | |
369 static const int INVALID_PAYLOAD = 2; | |
370 static const int UNSUPPORTED_VERSION = 3; | |
371 static const int START_VM_FAILED = 4; | |
372 static const int UNKNOWN_VM_ID = 5; | |
373 static const int UPGRADE_FAILED = 6; | |
374 | |
375 // Wire size (bytes) of the ReplyHeader. | |
376 static const int HEADER_SIZE = 8; | |
377 | |
378 final int id; | |
379 final int result; | |
380 final int payloadLength; | |
381 | |
382 ReplyHeader(this.id, this.result, {this.payloadLength: 0}); | |
383 | |
384 factory ReplyHeader.fromBuffer(ByteBuffer buffer) { | |
385 if (buffer == null || buffer.lengthInBytes < HEADER_SIZE) { | |
386 throw new MessageDecodeException( | |
387 'Insufficient data for a ReplyHeader: ${buffer.asUint8List()}'); | |
388 } | |
389 var id = readUint16(buffer, 0); | |
390 var result = readUint16(buffer, 2); | |
391 var payloadLength = readUint32(buffer, 4); | |
392 return new ReplyHeader(id, result, payloadLength: payloadLength); | |
393 } | |
394 | |
395 ByteBuffer toBuffer() { | |
396 var buffer = new Uint8List(HEADER_SIZE).buffer; | |
397 _writeHeader(buffer); | |
398 return buffer; | |
399 } | |
400 | |
401 void _writeHeader(ByteBuffer buffer) { | |
402 writeUint16(buffer, 0, id); | |
403 writeUint16(buffer, 2, result); | |
404 writeUint32(buffer, 4, payloadLength); | |
405 } | |
406 } | |
407 | |
408 class StartVmReply extends ReplyHeader { | |
409 final int vmId; | |
410 final int vmPort; | |
411 | |
412 StartVmReply(int id, int result, {this.vmId, this.vmPort}) | |
413 : super( | |
414 id, | |
415 result, | |
416 payloadLength: result == ReplyHeader.SUCCESS ? 8 : 0); | |
417 | |
418 factory StartVmReply.fromBuffer(ByteBuffer buffer) { | |
419 var header = new ReplyHeader.fromBuffer(buffer); | |
420 int vmId; | |
421 int vmPort; | |
422 if (header.result == ReplyHeader.SUCCESS) { | |
423 // There must be 8 bytes of payload. | |
424 if (buffer.lengthInBytes < ReplyHeader.HEADER_SIZE + 8 || | |
425 header.payloadLength != 8) { | |
426 throw new MessageDecodeException( | |
427 "Invalid StartVmReply: ${buffer.asUint8List()}"); | |
428 } | |
429 vmId = readUint32(buffer, ReplyHeader.HEADER_SIZE); | |
430 vmPort = readUint32(buffer, ReplyHeader.HEADER_SIZE + 4); | |
431 } | |
432 return new StartVmReply( | |
433 header.id, header.result, vmId: vmId, vmPort: vmPort); | |
434 } | |
435 | |
436 ByteBuffer toBuffer() { | |
437 ByteBuffer buffer; | |
438 if (result == ReplyHeader.SUCCESS) { | |
439 buffer = new Uint8List(ReplyHeader.HEADER_SIZE + 8).buffer; | |
440 _writeHeader(buffer); | |
441 writeUint32(buffer, ReplyHeader.HEADER_SIZE, vmId); | |
442 writeUint32(buffer, ReplyHeader.HEADER_SIZE + 4, vmPort); | |
443 } else { | |
444 buffer = new Uint8List(ReplyHeader.HEADER_SIZE).buffer; | |
445 _writeHeader(buffer); | |
446 } | |
447 return buffer; | |
448 } | |
449 } | |
450 | |
451 class StopVmReply extends ReplyHeader { | |
452 StopVmReply(int id, int result) : super(id, result); | |
453 | |
454 factory StopVmReply.fromBuffer(ByteBuffer buffer) { | |
455 ReplyHeader header = new ReplyHeader.fromBuffer(buffer); | |
456 if (header.payloadLength != 0) { | |
457 throw new MessageDecodeException( | |
458 "Invalid payload length in StopVmReply: ${buffer.asUint8List()}"); | |
459 } | |
460 return new StopVmReply(header.id, header.result); | |
461 } | |
462 | |
463 // The STOP_VM reply has no payload, so leverage parent's toBuffer method. | |
464 } | |
465 | |
466 class ListVmsReply extends ReplyHeader { | |
467 final List<int> vmIds; | |
468 | |
469 ListVmsReply(int id, int result, {List<int> vmIds}) | |
470 : super(id, result, payloadLength: vmIds != null ? vmIds.length * 4 : 0), | |
471 vmIds = vmIds; | |
472 | |
473 factory ListVmsReply.fromBuffer(ByteBuffer buffer) { | |
474 var header = new ReplyHeader.fromBuffer(buffer); | |
475 List<int> vmIds = []; | |
476 if (header.result == ReplyHeader.SUCCESS) { | |
477 for (int i = 0; i < header.payloadLength ~/ 4; ++i) { | |
478 vmIds.add(readUint32(buffer, ReplyHeader.HEADER_SIZE + (i * 4))); | |
479 } | |
480 } | |
481 return new ListVmsReply(header.id, header.result, vmIds: vmIds); | |
482 } | |
483 | |
484 ByteBuffer toBuffer() { | |
485 ByteBuffer buffer; | |
486 if (result == ReplyHeader.SUCCESS) { | |
487 int numVms = vmIds != null ? vmIds.length : 0; | |
488 buffer = new Uint8List(ReplyHeader.HEADER_SIZE + (numVms * 4)).buffer; | |
489 _writeHeader(buffer); | |
490 for (int i = 0; i < numVms; ++i) { | |
491 writeUint32(buffer, ReplyHeader.HEADER_SIZE + (i * 4), vmIds[i]); | |
492 } | |
493 } else { | |
494 buffer = new Uint8List(ReplyHeader.HEADER_SIZE).buffer; | |
495 _writeHeader(buffer); | |
496 } | |
497 return buffer; | |
498 } | |
499 } | |
500 | |
501 class UpgradeAgentReply extends ReplyHeader { | |
502 | |
503 UpgradeAgentReply(int id, int result) : super(id, result); | |
504 | |
505 factory UpgradeAgentReply.fromBuffer(ByteBuffer buffer) { | |
506 ReplyHeader header = new ReplyHeader.fromBuffer(buffer); | |
507 if (header.payloadLength != 0) { | |
508 throw new MessageDecodeException( | |
509 "Invalid payload length in UpgradeAgentReply: ${buffer.asUint8List()}"
); | |
510 } | |
511 return new UpgradeAgentReply(header.id, header.result); | |
512 } | |
513 | |
514 // The UPGRADE_AGENT reply has no payload, so leverage parent's toBuffer metho
d. | |
515 } | |
516 | |
517 class FletchVersionReply extends ReplyHeader { | |
518 final String fletchVersion; | |
519 | |
520 FletchVersionReply(int id, int result, {String version}) | |
521 : super(id, result, payloadLength: version != null ? version.length : 0), | |
522 fletchVersion = version { | |
523 if (result == ReplyHeader.SUCCESS && version == null) { | |
524 throw new MessageEncodeException( | |
525 "Missing version for FletchVersionReply."); | |
526 } | |
527 } | |
528 | |
529 factory FletchVersionReply.fromBuffer(ByteBuffer buffer) { | |
530 var header = new ReplyHeader.fromBuffer(buffer); | |
531 String version; | |
532 if (header.result == ReplyHeader.SUCCESS) { | |
533 int payloadLength = header.payloadLength; | |
534 if (payloadLength == 0 || | |
535 buffer.lengthInBytes < ReplyHeader.HEADER_SIZE + payloadLength) { | |
536 throw new MessageDecodeException( | |
537 "Invalid FletchVersionReply: ${buffer.asUint8List()}"); | |
538 } | |
539 version = ASCII.decode(buffer.asUint8List(ReplyHeader.HEADER_SIZE)); | |
540 } | |
541 return new FletchVersionReply(header.id, header.result, version: version); | |
542 } | |
543 | |
544 ByteBuffer toBuffer() { | |
545 ByteBuffer buffer; | |
546 if (result == ReplyHeader.SUCCESS) { | |
547 Uint8List message = new Uint8List( | |
548 ReplyHeader.HEADER_SIZE + fletchVersion.length); | |
549 _writeHeader(message.buffer); | |
550 List<int> encodedVersion = ASCII.encode(fletchVersion); | |
551 assert(encodedVersion != null); | |
552 assert(fletchVersion.length == encodedVersion.length); | |
553 message.setRange( | |
554 ReplyHeader.HEADER_SIZE, | |
555 ReplyHeader.HEADER_SIZE + encodedVersion.length, | |
556 encodedVersion); | |
557 buffer = message.buffer; | |
558 } else { | |
559 buffer = new Uint8List(ReplyHeader.HEADER_SIZE).buffer; | |
560 _writeHeader(buffer); | |
561 } | |
562 return buffer; | |
563 } | |
564 } | |
565 | |
566 class SignalVmReply extends ReplyHeader { | |
567 SignalVmReply(int id, int result) | |
568 : super(id, result); | |
569 | |
570 factory SignalVmReply.fromBuffer(ByteBuffer buffer) { | |
571 ReplyHeader header = new ReplyHeader.fromBuffer(buffer); | |
572 if (header.payloadLength != 0) { | |
573 throw new MessageDecodeException( | |
574 "Invalid payload length in SignalVmReply: ${buffer.asUint8List()}"); | |
575 } | |
576 return new SignalVmReply(header.id, header.result); | |
577 } | |
578 | |
579 // The SIGNAL_VM reply has no payload, so leverage parent's toBuffer method. | |
580 // TODO(wibling): Figure out how to return exitcode from vm on exit. | |
581 } | |
582 | |
583 // Utility methods to read and write 16 and 32 bit entities from/to big endian. | |
584 int readUint16(ByteBuffer buffer, int offset) { | |
585 // Goto right offset | |
586 var b = buffer.asUint8List(offset, 2); | |
587 return b[0] << 8 | b[1]; | |
588 } | |
589 | |
590 void writeUint16(ByteBuffer buffer, int offset, int value) { | |
591 // Goto right offset | |
592 var b = buffer.asUint8List(offset, 2); | |
593 b[0] = value >> 8 & 0xff; | |
594 b[1] = value & 0xff; | |
595 } | |
596 | |
597 int readUint32(ByteBuffer buffer, int offset) { | |
598 // Goto right offset | |
599 var b = buffer.asUint8List(offset, 4); | |
600 return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; | |
601 } | |
602 | |
603 void writeUint32(ByteBuffer buffer, int offset, int value) { | |
604 // Goto right offset | |
605 var b = buffer.asUint8List(offset, 4); | |
606 b[0] = value >> 24 & 0xff; | |
607 b[1] = value >> 16 & 0xff; | |
608 b[2] = value >> 8 & 0xff; | |
609 b[3] = value & 0xff; | |
610 } | |
611 | |
612 class MessageDecodeException implements Exception { | |
613 final String message; | |
614 MessageDecodeException(this.message); | |
615 String toString() => 'MessageDecodeException($message)'; | |
616 } | |
617 | |
618 class MessageEncodeException implements Exception { | |
619 final String message; | |
620 MessageEncodeException(this.message); | |
621 String toString() => 'MessageEncodeException($message)'; | |
622 } | |
OLD | NEW |