Effectively streams data from multiple opened AFF4ImageBase objects.

    Args:
      fds: A list of opened AFF4Stream (or AFF4Stream descendants) objects.

    Yields:
      Tuples (chunk, fd, exception) where chunk is a binary blob of data and fd
      is an object from the fds argument.

      If one or more chunks are missing, exception will be a MissingChunksError
      while chunk will be None. _MultiStream does its best to skip the file
      entirely if one of its chunks is missing, but in case of very large files
      it's still possible to yield a truncated file.

def _MultiStream(cls, fds):
    """Effectively streams data from multiple opened AFF4ImageBase objects.

    Args:
      fds: A list of opened AFF4Stream (or AFF4Stream descendants) objects.

    Yields:
      Tuples (chunk, fd, exception) where chunk is a binary blob of data and fd
      is an object from the fds argument.

      If one or more chunks are missing, exception will be a MissingChunksError
      while chunk will be None. _MultiStream does its best to skip the file
      entirely if one of its chunks is missing, but in case of very large files
      it's still possible to yield a truncated file.
    """

    missing_chunks_by_fd = {}
    for chunk_fd_pairs in collection.Batch(
        cls._GenerateChunkPaths(fds), cls.MULTI_STREAM_CHUNKS_READ_AHEAD):

      chunks_map = dict(chunk_fd_pairs)
      contents_map = {}
      for chunk_fd in FACTORY.MultiOpen(
          chunks_map, mode="r", token=fds[0].token):
        if isinstance(chunk_fd, AFF4Stream):
          fd = chunks_map[chunk_fd.urn]
          contents_map[chunk_fd.urn] = chunk_fd.read()

      for chunk_urn, fd in chunk_fd_pairs:
        if chunk_urn not in contents_map or not contents_map[chunk_urn]:
          missing_chunks_by_fd.setdefault(fd, []).append(chunk_urn)

      for chunk_urn, fd in chunk_fd_pairs:
        if fd in missing_chunks_by_fd:
          continue

        yield fd, contents_map[chunk_urn], None

    for fd, missing_chunks in iteritems(missing_chunks_by_fd):
      e = MissingChunksError(
          "%d missing chunks (multi-stream)." % len(missing_chunks),
          missing_chunks=missing_chunks)
      yield fd, None, e