parse static method

StompFrame parse(
  1. Uint8List data
)

Parses a STOMP frame from bytes

Implementation

static StompFrame parse(Uint8List data) {
  if (data.isEmpty) {
    throw const StompFrameException('Empty frame data');
  }

  var position = 0;

  // Parse command
  final commandEnd = _findLineEnd(data, position);
  if (commandEnd == -1) {
    throw const StompFrameException('No command line found');
  }

  final command = _extractLine(data, position, commandEnd);
  if (command.isEmpty) {
    throw const StompFrameException('Empty command');
  }

  position = commandEnd + 1;
  if (position < data.length && data[position - 1] == StompConstants.carriageReturn) {
    position++; // Skip LF after CR
  }

  // Parse headers
  final headers = <String, String>{};
  while (position < data.length) {
    final lineEnd = _findLineEnd(data, position);
    if (lineEnd == -1) {
      throw const StompFrameException('Malformed headers section');
    }

    final line = _extractLine(data, position, lineEnd);

    // Empty line indicates end of headers
    if (line.isEmpty) {
      position = lineEnd + 1;
      if (position < data.length && data[position - 1] == StompConstants.carriageReturn) {
        position++; // Skip LF after CR
      }
      break;
    }

    // Parse header
    final colonIndex = line.indexOf(':');
    if (colonIndex == -1) {
      throw StompFrameException('Invalid header format: $line');
    }

    final name = line.substring(0, colonIndex);
    final value = line.substring(colonIndex + 1);

    // Handle repeated headers (use first occurrence)
    if (!headers.containsKey(name)) {
      headers[name] = _shouldEscapeHeaders(command) ? StompEscaping.unescape(value) : value;
    }

    position = lineEnd + 1;
    if (position < data.length && data[position - 1] == StompConstants.carriageReturn) {
      position++; // Skip LF after CR
    }
  }

  // Parse body
  Uint8List? body;
  if (position < data.length) {
    // Find NULL terminator
    var bodyEnd = data.length;
    for (int i = position; i < data.length; i++) {
      if (data[i] == StompConstants.nullByte) {
        bodyEnd = i;
        break;
      }
    }

    if (bodyEnd == data.length) {
      throw const StompFrameException('Frame not terminated with NULL byte');
    }

    // Check content-length if specified
    final contentLengthStr = headers[StompHeaders.contentLength];
    if (contentLengthStr != null) {
      final contentLength = int.tryParse(contentLengthStr);
      if (contentLength == null || contentLength < 0) {
        throw StompFrameException('Invalid content-length: $contentLengthStr');
      }

      if (position + contentLength > bodyEnd) {
        throw const StompFrameException('Content-length exceeds available body data');
      }

      bodyEnd = position + contentLength;
    }

    if (bodyEnd > position) {
      body = Uint8List.fromList(data.sublist(position, bodyEnd));
    }
  }

  final frame = StompFrame(
    command: command,
    headers: headers,
    body: body,
  );

  frame.validate();
  return frame;
}