read method

  1. @override
Future<Uint8List> read([
  1. int? length
])
override

Reads data from the connection. If length is provided, reads exactly that many bytes. Otherwise, reads whatever is available.

Implementation

@override
Future<Uint8List> read([int? length]) async {
  final readId = Uuid().v4().substring(0, 8);
  _log.finer('TCPConnection($id) - Read($readId) START. Requested: $length. Buffer: ${_receiveBuffer.length} bytes.');
  _assertNotClosed();

  if (length != null && length < 0) throw ArgumentError('Length cannot be negative');
  if (length == 0) {
     _log.finer('TCPConnection($id) - Read($readId) END (requested 0 bytes). Returning 0 bytes.');
    return Uint8List(0);
  }

  while (true) { // Loop until enough data is available or EOF/error
    if (length != null) { // Specific length requested
      if (_receiveBuffer.length >= length) {
        final result = Uint8List.fromList(_receiveBuffer.toBytes().sublist(0, length));
        final remainingBytes = _receiveBuffer.toBytes().sublist(length);
        _receiveBuffer.clear();
        _receiveBuffer.add(remainingBytes);
        _log.finer('TCPConnection($id) - Read($readId) END (from buffer). Returning ${result.length} bytes: ${hex.encode(result)}. Buffer after: ${_receiveBuffer.length} bytes: ${hex.encode(remainingBytes)}');
        return result;
      }
    } else { // length is null, read any available data
      if (_receiveBuffer.isNotEmpty) {
        final result = _receiveBuffer.toBytes();
        _receiveBuffer.clear();
        _log.finer('TCPConnection($id) - Read($readId) END (all from buffer, length null). Returning ${result.length} bytes: ${hex.encode(result)}. Buffer after: 0');
        return result;
      }
    }

    // If not enough data, check for EOF or closed state
    if (_socketIsDone) {
      if (_receiveBuffer.isEmpty) {
        _log.finer('TCPConnection($id) - Read($readId) END (socket done, buffer empty -> EOF). Returning 0 bytes.');
        return Uint8List(0); // Clean EOF if buffer is empty
      }
      // Socket is done, buffer is NOT empty. Return what's available from buffer.
      Uint8List resultToReturn;
      if (length == null || _receiveBuffer.length <= length) {
        // Requested all, or specific length but buffer has less than or equal to what's requested.
        resultToReturn = _receiveBuffer.toBytes();
        _receiveBuffer.clear();
        _log.finer('TCPConnection($id) - Read($readId) END (socket done, returning all/partial from buffer: ${resultToReturn.length} bytes).');
      } else { // length != null && _receiveBuffer.length > length
        // Buffer has more than the specific length requested.
        resultToReturn = Uint8List.fromList(_receiveBuffer.toBytes().sublist(0, length));
        final remainingBytesInInternalBuffer = _receiveBuffer.toBytes().sublist(length);
        _receiveBuffer.clear();
        _receiveBuffer.add(remainingBytesInInternalBuffer);
        _log.finer('TCPConnection($id) - Read($readId) END (socket done, returning specific length from buffer: ${resultToReturn.length} bytes, remaining in buffer: ${_receiveBuffer.length}).');
      }
      return resultToReturn;
    }

    if (_closed) { // Connection explicitly closed via this.close() by the application
        _log.warning('TCPConnection($id) - Read($readId) ERROR: Connection explicitly closed by API call.');
        throw StateError('Connection closed.');
    }

    // Not enough data, socket not done, connection not closed: wait for more data
    _log.finer('TCPConnection($id) - Read($readId) ASYNC WAIT. Requested: $length. Buffer: ${_receiveBuffer.length}');
    _pendingReadCompleter = Completer<void>();
    Timer? timeoutTimer;

    try {
      if (_currentReadTimeout != null && _currentReadTimeout! > Duration.zero) {
        timeoutTimer = Timer(_currentReadTimeout!, () {
          if (_pendingReadCompleter != null && !_pendingReadCompleter!.isCompleted) {
            _log.warning('TCPConnection($id) - Read($readId) TIMEOUT after $_currentReadTimeout.');
            _pendingReadCompleter!.completeError(TimeoutException('Raw read timed out after $_currentReadTimeout'));
          }
        });
      }
      await _pendingReadCompleter!.future;
    } finally {
      timeoutTimer?.cancel();
      // _pendingReadCompleter = null; // Do not nullify here, a new one is made if loop continues
    }
    _log.finer('TCPConnection($id) - Read($readId) ASYNC AWOKE. Re-checking buffer.');
    // Loop continues
  }
}