docs: private List<OrcDataOutput> bufferStripeData(long stripeStartOffset, FlushReason flushReason)
              throws IOException
      {
          if (stripeRowCount == 0) {
              verify(flushReason == CLOSED, "An empty stripe is not allowed");
              // column writers must be closed or the reset call will fail
              columnWriters.forEach(ColumnWriter::close);
              return ImmutableList.of();
          }
  
          if (rowGroupRowCount > 0) {
              finishRowGroup();
          }
  
          // convert any dictionary encoded column with a low compression ratio to direct
          dictionaryCompressionOptimizer.finalOptimize(bufferedBytes);
  
          columnWriters.forEach(ColumnWriter::close);
  
          List<OrcDataOutput> outputData = new ArrayList<>();
          List<Stream> allStreams = new ArrayList<>(columnWriters.size() * 3);
  
          // get index streams
          long indexLength = 0;
          for (ColumnWriter columnWriter : columnWriters) {
              for (StreamDataOutput indexStream : columnWriter.getIndexStreams(metadataWriter)) {
                  // The ordering is critical because the stream only contain a length with no offset.
                  outputData.add(indexStream);
                  allStreams.add(indexStream.getStream());
                  indexLength += indexStream.size();
              }
          }
  
          // data streams (sorted by size)
          long dataLength = 0;
          List<StreamDataOutput> dataStreams = new ArrayList<>(columnWriters.size() * 2);
          for (ColumnWriter columnWriter : columnWriters) {
              List<StreamDataOutput> streams = columnWriter.getDataStreams();
              dataStreams.addAll(streams);
              dataLength += streams.stream()
                      .mapToLong(StreamDataOutput::size)
                      .sum();
          }
          Collections.sort(dataStreams);
  
          // add data streams
          for (StreamDataOutput dataStream : dataStreams) {
              // The ordering is critical because the stream only contain a length with no offset.
              outputData.add(dataStream);
              allStreams.add(dataStream.getStream());
          }
  
          Map<Integer, ColumnEncoding> columnEncodings = new HashMap<>();
          columnWriters.forEach(columnWriter -> columnEncodings.putAll(columnWriter.getColumnEncodings()));
  
          Map<Integer, ColumnStatistics> columnStatistics = new HashMap<>();
          columnWriters.forEach(columnWriter -> columnStatistics.putAll(columnWriter.getColumnStripeStatistics()));
  
          // the 0th column is a struct column for the whole row
          columnEncodings.put(0, new ColumnEncoding(DIRECT, 0));
          columnStatistics.put(0, new ColumnStatistics((long) stripeRowCount, 0, null, null, null, null, null, null, null, null));
  
          // add footer
          StripeFooter stripeFooter = new StripeFooter(allStreams, toDenseList(columnEncodings, orcTypes.size()));
          Slice footer = metadataWriter.writeStripeFooter(stripeFooter);
          outputData.add(createDataOutput(footer));
  
          // create final stripe statistics
          StripeStatistics statistics = new StripeStatistics(toDenseList(columnStatistics, orcTypes.size()));
          recordValidation(validation -> validation.addStripeStatistics(stripeStartOffset, statistics));
          StripeInformation stripeInformation = new StripeInformation(stripeRowCount, stripeStartOffset, indexLength, dataLength, footer.length());
          ClosedStripe closedStripe = new ClosedStripe(stripeInformation, statistics);
          closedStripes.add(closedStripe);
          closedStripesRetainedBytes += closedStripe.getRetainedSizeInBytes();
          recordValidation(validation -> validation.addStripe(stripeInformation.getNumberOfRows()));
          stats.recordStripeWritten(flushReason, stripeInformation.getTotalLength(), stripeInformation.getNumberOfRows(), dictionaryCompressionOptimizer.getDictionaryMemoryBytes());
  
          return outputData;
      }