| 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 |