@Override
    public SegmentMeta write(StorageSegment segment, ByteBufferWriter.PredictSizeOpener writerOpener) throws IOException {
        List<StorageColumn> columns = segment.columns();
        int version = segment.version();
        SegmentMode mode = segment.mode();
        SegmentMetaV1 sectionInfo = new SegmentMetaV1(version, segment.rowCount(), columns.size(), mode.id);
        sectionInfo.sectionOffset = Version.INDEXR_SEG_FILE_FLAG_SIZE; // The first section, and the only one.

        OuterIndex.Cache[] outerIndexCaches = new OuterIndex.Cache[columns.size()];
        for (int colId = 0; colId < columns.size(); colId++) {
            ColumnNode columnNode = segment.columnNode(colId);
            sectionInfo.columnNodeInfos[colId] = new ColumnNodeMeta(columnNode.getMinNumValue(), columnNode.getMaxNumValue());

            StorageColumn column = columns.get(colId);
            DataPackNode[] dpns = column.getDPNs();
            int dpnSize = mode.versionAdapter.dpnSize(version, mode) * dpns.length;
            int indexSize = 0;
            int extIndexSize = 0;
            long packSize = 0;
            for (DataPackNode dpn : dpns) {
                indexSize += dpn.indexSize();
                extIndexSize += dpn.extIndexSize();
                packSize += dpn.packSize();
            }

            // Generate outer index here.
            OuterIndex.Cache outerIndexCache = mode.versionAdapter.createOuterIndex(version, mode, column);
            long outerIndexSize = outerIndexCache.size();
            outerIndexCaches[colId] = outerIndexCache;

            // Hack!
            boolean isIndexed = (version >= Version.VERSION_7_ID && column.isIndexed());
            ColumnMetaV1 info = new ColumnMetaV1(
                    column.name(),
                    column.sqlType().id,
                    isIndexed,
                    dpnSize,
                    indexSize,
                    extIndexSize,
                    outerIndexSize,
                    packSize);
            sectionInfo.columnInfos[colId] = info;
        }

        // Calculate the whole size of this section.
        long dataSize = 0;
        for (ColumnMeta ci : sectionInfo.columnInfos) {
            dataSize += ((ColumnMetaV1) ci).dataSize();
        }

        // Set the dataOffset.
        long offset = sectionInfo.sectionOffset + 4 + sectionInfo.metaSize();
        for (int colId = 0; colId < columns.size(); colId++) {
            ColumnMetaV1 columnInfo = (ColumnMetaV1) sectionInfo.columnInfos[colId];
            columnInfo.setDataOffset(version, offset);
            offset += columnInfo.dataSize();
        }

        // Set outer index offset.
        long totalOuterIndexSize = 0;
        for (int colId = 0; colId < columns.size(); colId++) {
            ColumnMetaV1 columnInfo = (ColumnMetaV1) sectionInfo.columnInfos[colId];
            columnInfo.outerIndexOffset = offset;
            totalOuterIndexSize += columnInfo.outerIndexSize;

            offset += columnInfo.outerIndexSize;
        }

        // First 4 bytes are the size of info. See ObjSaver#save() for detail.
        sectionInfo.sectionSize = 4 + sectionInfo.metaSize() + dataSize + totalOuterIndexSize;
        assert offset - sectionInfo.sectionOffset == sectionInfo.sectionSize;

        long totalSize = offset;

        // Now everything is ready, let's move data into new segment.
        try (ByteBufferWriter writer = writerOpener.open(totalSize)) {
            // First write version message.
            writer.write(Version.fromId(version).flag);

            // Write info.
            ObjectSaver.save(writer, sectionInfo, sectionInfo.metaSize(), SegmentMetaV1::write);

            // Write data.
            for (StorageColumn column : columns) {
                DataPackNode[] dpns = column.getDPNs();
                for (DataPackNode dpn : dpns) {
                    dpn.write(writer);
                }

                RSIndex index = column.loadIndex();
                if (index != null) {
                    index.write(writer);
                    index.free();
                }

                for (DataPackNode dpn : dpns) {
                    PackExtIndex extIndex = column.loadExtIndex(dpn);
                    extIndex.write(writer);
                    extIndex.free();
                }

                for (DataPackNode dpn : dpns) {
                    ByteSlice pack = column.loadPack(dpn);
                    writer.write(pack.byteBuffer());
                    pack.free();
                }
            }

            for (OuterIndex.Cache oc : outerIndexCaches) {
                oc.write(writer);
                IOUtil.closeQuietly(oc);
            }

            writer.flush();

            return sectionInfo;
        }
    }
Store segment column by column.

<pre>
Segment storage structure:
|  IXRSEGxx | sementMeta | column0 | column1 ... | outerIndex0 | outerIndex0 ... |
            | <---                      sectionSize                        ----> |

Column structure:
|  dpn | rsIndex | extIndex | dataPack |
</pre>
indexr-segment/src/main/java/io/indexr/segment/storage/itg/IntegrateV1.java