docs: def readinto(self, b):
          """
          Read bytes into a pre-allocated, writable bytes-like object b,
          and return the number of bytes read.
  
          Args:
              b (bytes-like object): buffer.
  
          Returns:
              int: number of bytes read
          """
          if not self._readable:
              raise UnsupportedOperation('read')
  
          with self._seek_lock:
              # Gets seek
              seek = self._seek
  
              # Initializes queue
              queue = self._read_queue
              if seek == 0:
                  # Starts initial preloading on first call
                  self._preload_range()
  
              # Initializes read data buffer
              size = len(b)
              if size:
                  # Preallocated buffer:
                  # Use memory view to avoid copies
                  b_view = memoryview(b)
                  size_left = size
              else:
                  # Dynamic buffer:
                  # Can't avoid copy, read until EOF
                  b_view = b
                  size_left = -1
              b_end = 0
  
              # Starts reading
              buffer_size = self._buffer_size
              while size_left > 0 or size_left == -1:
  
                  # Finds buffer position in queue and buffer seek
                  start = seek % buffer_size
                  queue_index = seek - start
  
                  # Gets preloaded buffer
                  try:
                      buffer = queue[queue_index]
                  except KeyError:
                      # EOF
                      break
  
                  # Get buffer from future
                  with handle_os_exceptions():
                      try:
                          queue[queue_index] = buffer = buffer.result()
  
                      # Already evaluated
                      except AttributeError:
                          pass
                  buffer_view = memoryview(buffer)
                  data_size = len(buffer)
  
                  # Checks if end of file reached
                  if not data_size:
                      break
  
                  # Gets theoretical range to copy
                  if size_left != -1:
                      end = start + size_left
                  else:
                      end = data_size - start
  
                  # Checks for end of buffer
                  if end >= data_size:
                      # Adjusts range to copy
                      end = data_size
  
                      # Removes consumed buffer from queue
                      del queue[queue_index]
  
                      # Append another buffer preload at end of queue
                      index = queue_index + buffer_size * self._max_buffers
                      if index < self._size:
                          queue[index] = self._workers.submit(
                              self._read_range, index, index + buffer_size)
  
                  # Gets read size, updates seek and updates size left
                  read_size = end - start
                  if size_left != -1:
                      size_left -= read_size
                  seek += read_size
  
                  # Defines read buffer range
                  b_start = b_end
                  b_end = b_start + read_size
  
                  # Copy data from preload buffer to read buffer
                  b_view[b_start:b_end] = buffer_view[start:end]
  
              # Updates seek and sync raw
              self._seek = seek
              self._raw.seek(seek)
  
          # Returns read size
          return b_end