class SegIterator implements Iterator<Seg> {
        final SegmentBuilderBase<?> builder;
        int nextIndex;

        public SegIterator(SegmentBuilderBase<?> builder) {
            this.builder = builder;
        }

        @Override
        public boolean hasNext() {
            return nextIndex < builder.size();
        }

        @Override
        public Seg next() {
            return builder.getSegPart(nextIndex++);
        }
    }

    @Override
    public int getOptions() {
        return options;
    }

    @Override
    public boolean isIncludeAnchors() {
        return (options & F_INCLUDE_ANCHORS) != 0;
    }

    /**
     * Span for offsets of this list
     *
     * @return -ve if no information in the list, or span of offsets
     */
    @Override
    public int getSpan() {
        return startOffset > endOffset ? -1 : endOffset - startOffset;
    }

    @Nullable
    private Seg getSegOrNull(int index) {
        int i = index * 2;
        return i + 1 >= parts.length ? null : Seg.segOf(parts[i], parts[i + 1]);
    }

    @NotNull
    private Seg getSeg(int index) {
        int i = index * 2;
        return i + 1 >= parts.length ? Seg.NULL : Seg.segOf(parts[i], parts[i + 1]);
    }

    @NotNull
    public Object getPart(int index) {
        if (index == partsSize && haveDanglingText()) {
            // return dangling text
            return text.subSequence(immutableOffset, text.length());
        } else {
            int i = index * 2;
            Seg seg = i + 1 >= parts.length ? Seg.NULL : Seg.segOf(parts[i], parts[i + 1]);
            return seg.isBase() ? seg.getRange() : seg.isText() ? text.subSequence(seg.getTextStart(), seg.getTextEnd()) : Range.NULL;
        }
    }

    @NotNull
    Seg getSegPart(int index) {
        if (index == partsSize && haveDanglingText()) {
            // return dangling text
            return Seg.textOf(immutableOffset, text.length(), textStats.isTextFirst256(), textStats.isRepeatedText());
        } else {
            int i = index * 2;
            return i + 1 >= parts.length ? Seg.NULL : Seg.segOf(parts[i], parts[i + 1]);
        }
    }

    private void setSegEnd(int index, int endOffset) {
        int i = index * 2;
        assert i + 1 < parts.length;

//        parts[i] = startOffset;
        // adjust anchor count
        if (parts[i] == endOffset) {
            if (parts[i] != parts[i + 1]) anchorsSize++;
        } else if (parts[i] == parts[i + 1]) anchorsSize--;

        parts[i + 1] = endOffset;
    }

    private void addSeg(int startOffset, int endOffset) {
        ensureCapacity(partsSize);
        int i = partsSize * 2;
        parts[i] = startOffset;
        parts[i + 1] = endOffset;
        partsSize++;
        if (startOffset == endOffset) anchorsSize++;
    }

    @Nullable
    private Seg lastSegOrNull() {
        return partsSize == 0 ? null : getSegOrNull(partsSize - 1);
    }

    protected boolean haveDanglingText() {
        return text.length() > immutableOffset;
    }

    protected Object[] optimizeText(@NotNull Object[] parts) {
        return parts;
    }

    protected Object[] handleOverlap(@NotNull Object[] parts) {<FILL_FUNCTION_BODY>

        // range overlaps with last segment in the list
        Range lastSeg = (Range) parts[0];
        CharSequence text = (CharSequence) parts[1];
        Range range = (Range) parts[2];

        assert !lastSeg.isNull() && lastSeg.getEnd() > range.getStart();

        if (lastSeg.getEnd() < range.getEnd()) {
            // there is a part of the overlap outside the last seg range
            if (text.length() > 0) {
                // append the chopped off base part
                parts[2] = Range.of(lastSeg.getEnd(), range.getEnd());
            } else {
                // extend the last base seg to include this range
                parts[0] = lastSeg.withEnd(range.getEnd());
                parts[2] = Range.NULL;
            }
        } else {
            parts[2] = Range.NULL;
        }
        return parts;
    
<no_super_class>