@Override
  public Block readBlock(Type type) throws IOException {
    if (!rowGroupOpen) {
      openRowGroup();
    }

    if (readOffset > 0) {
      if (presentStream != null) {
        // skip ahead the present bit reader, but count the set bits
        // and use this as the skip size for the field readers
        readOffset = presentStream.countBitsSet(readOffset);
      }
      for (StreamReader structField : structFields) {
        structField.prepareNextRead(readOffset);
      }
    }

    List<Type> typeParameters = type.getTypeParameters();

    boolean[] nullVector = new boolean[nextBatchSize];
    Block[] blocks = new Block[typeParameters.size()];
    if (presentStream == null) {
      for (int i = 0; i < typeParameters.size(); i++) {
        StreamReader structField = structFields[i];
        structField.prepareNextRead(nextBatchSize);
        blocks[i] = structField.readBlock(typeParameters.get(i));
      }
    } else {
      int nullValues = presentStream.getUnsetBits(nextBatchSize, nullVector);
      if (nullValues != nextBatchSize) {
        for (int i = 0; i < typeParameters.size(); i++) {
          StreamReader structField = structFields[i];
          structField.prepareNextRead(nextBatchSize - nullValues);
          blocks[i] = structField.readBlock(typeParameters.get(i));
        }
      } else {
        for (int i = 0; i < typeParameters.size(); i++) {
          blocks[i] = typeParameters.get(i).createBlockBuilder(new BlockBuilderStatus(), 0).build();
        }
      }
    }

    // Build offsets for array block (null valued have no positions)
    int[] offsets = new int[nextBatchSize];
    offsets[0] = (nullVector[0] ? 0 : typeParameters.size());
    for (int i = 1; i < nextBatchSize; i++) {
      offsets[i] = offsets[i - 1] + (nullVector[i] ? 0 : typeParameters.size());
    }

    // Struct is represented as an array block holding an interleaved block
    InterleavedBlock interleavedBlock = new InterleavedBlock(blocks);
    ArrayBlock arrayBlock =
        new ArrayBlock(
            interleavedBlock,
            Slices.wrappedIntArray(offsets),
            0,
            Slices.wrappedBooleanArray(nullVector));

    readOffset = 0;
    nextBatchSize = 0;

    return arrayBlock;
  }