class SlotWriter internal constructor(table: SlotTable) : SlotEditor(table) {
    private var insertCount = 0
    private var pendingClear = false
    fun close() = table.close(this)
    /**
     * Set the value of the next slot.
     */
    fun update(value: Any?): Any? {
        val result = skip()
        set(value)
        return result
    }
    /**
     * Set the value at the current slot.
     */
    fun set(value: Any?) {
        slots[effectiveIndex(current - 1)] = value
    }
    /**
     * Skip the current slot without updating
     */
    fun skip(): Any? {
        if (insertCount > 0) {
            insert(1)
        }
        val index = current++
        return slots[table.effectiveIndex(index)]
    }
    /**
     * Backup one slot. For example, we ran into a key of a keyed group we don't want, this backs up
     * current to be before the key.
     */
    fun previous() {
        assert(current > 0) { "Invalid call to previous" }
        current--
    }
    /**
     * Begin inserting at the current location. beginInsert() can be nested and must be called with
     * a balanced number of endInsert()
     */
    fun beginInsert() {
        insertCount++
    }
    /**
     * Ends inserting.
     */
    fun endInsert() {
        assert(insertCount > 0) { "Unbalenced begin/end insert" }
        insertCount--
    }
    /**
     * Start a group
     */
    fun startGroup() = startGroup(GROUP)
    private fun startGroup(kind: GroupKind) {
        val inserting = insertCount > 0
        recordStartGroup(kind, validate = !inserting)
        if (inserting) {
            skip() // Skip a slot for the GroupStart added by endGroup.
            currentEnd = current
        }
    }
    /**
     *  Skip a group. Must be called at the start of a group.
     */
    fun skipGroup(): Int {
        assert(insertCount == 0) { "Cannot skip while inserting" }
        return advanceToNextGroup()
    }
    /**
     * End the current group. Must be called after the corresponding startGroup().
     */
    fun endGroup(): Int =
        recordEndGroup(writing = true, inserting = insertCount > 0, uncertain = false)
    /**
     * Move the offset'th group after the current item to the current location. Must be called when
     * a keyed group is expected.
     */
    fun moveItem(offset: Int) {
        assert(insertCount == 0) { "Cannot move an item while inserting" }
        val oldCurrent = current
        val oldNodeCount = nodeCount
        // Find the item to move
        var count = offset
        while (count > 0) {
            advanceToNextItem()
            count--
        }
        // Move the current one here by first inserting room for it then copying it over the spot
        // then removing the old slot.
        val moveLocation = current
        advanceToNextItem()
        val moveLen = current - moveLocation
        current = oldCurrent
        insert(moveLen)
        // insert inserted moveLen slots which moved moveLocation
        val newMoveLocation = moveLocation + moveLen
        current = oldCurrent
        nodeCount = oldNodeCount
        System.arraycopy(
            slots,
            effectiveIndex(newMoveLocation),
            slots,
            effectiveIndex(current),
            moveLen
        )
        // Before we remove the old location, move any anchors
        table.moveAnchors(newMoveLocation, current, moveLen)
        // Remove the now duplicate entries
        val anchorsRemoved = remove(moveLocation + moveLen, moveLen)
        assert(!anchorsRemoved) { "Unexpectedly removed anchors" }
    }
    /**
     * Remove an item. Must be called at the startGroup of an item.
     */
    fun removeItem(): Boolean {
        assert(insertCount == 0) { "Cannot remove and item while inserting" }
        val oldCurrent = current
        val count = advanceToNextItem()
        val anchorsRemoved = remove(oldCurrent, current - oldCurrent)
        current = oldCurrent
        nodeCount -= count
        return anchorsRemoved
    }
    /**
     * Returns an iterator for the slots of the item.
     */
    fun itemSlots(): Iterator<Any?> {
        val start = current
        val oldCount = nodeCount
        advanceToNextItem()
        val end = current
        current = start
        nodeCount = oldCount
        return object : Iterator<Any?> {
            var current = start + 2
            override fun hasNext(): Boolean = current < end
            override fun next(): Any? = slots[effectiveIndex(current++)]
        }
    }
    /**
     * Start a node.
     */
    fun startNode() = startGroup(NODE)
    /**
     * End a node
     */
    fun endNode() = endGroup()
    /**
     * Skip a node
     */
    fun skipNode() = skipGroup()
    /**
     * Skip the current item
     */
    fun skipItem(): Int {
        assert(insertCount == 0) { "Cannot skip an item while inserting" }
        return advanceToNextItem()
    }
    /**
     * Allocate an anchor for a location. As content is inserted and removed from the slot table the
     * anchor is updated to reflect those changes. For example, if an anchor is requested for an
     * item, the anchor will report the location of that item even if the item is moved in the slot
     * table. If the position referenced by the anchor is removed, the anchor location is set to -1.
     */
    fun anchor(index: Int = current): Anchor = table.anchor(index)
    private fun moveGapTo(index: Int) {
        if (table.gapLen > 0 && table.gapStart != index) {
            pendingClear = false
            if (table.anchors.isNotEmpty()) table.updateAnchors(index)
            if (index < table.gapStart) {
                val len = table.gapStart - index
                System.arraycopy(slots, index, slots, index + table.gapLen, len)
            } else {
                val len = index - table.gapStart
                System.arraycopy(
                    slots,
                    table.gapStart + table.gapLen,
                    slots,
                    table.gapStart,
                    len)
            }
            table.gapStart = index
            pendingClear = true
        } else {
            table.gapStart = index
        }
    }
    private fun insert(size: Int) {
        if (size > 0) {
            moveGapTo(current)
            if (table.gapLen < size) {
                // Create a bigger gap
                val oldCapacity = slots.size
                val oldSize = slots.size - table.gapLen
                // Double the size of the array, but at least MIN_GROWTH_SIZE and >= size
                val newCapacity = Math.max(Math.max(oldCapacity*2, oldSize + size),
                    MIN_GROWTH_SIZE
                )
                val newSlots = arrayOfNulls<Any?>(newCapacity)
                val newGapLen = newCapacity - oldSize
                val oldGapEnd = table.gapStart + table.gapLen
                val newGapEnd = table.gapStart + newGapLen
                // Copy the old array into the new array
                System.arraycopy(slots, 0, newSlots, 0, table.gapStart)
                System.arraycopy(slots, oldGapEnd, newSlots, newGapEnd, oldCapacity - oldGapEnd)
                // Update the anchors
                if (table.anchors.isNotEmpty()) table.anchorGapResize(newGapLen - table.gapLen)
                // Update the gap and slots
                table.slots = newSlots
                table.gapLen = newGapLen
            }
            if (currentEnd >= table.gapStart) currentEnd += size
            table.gapStart += size
            table.gapLen -= size
            repeat(size) {
                slots[current + it] = SlotTable.EMPTY
            }
            pendingClear = true
        }
    }
    internal fun remove(start: Int, len: Int): Boolean {
        return if (len > 0) {
            pendingClear = false
            var anchorsRemoved = false
            if (table.gapLen == 0) {
                // If there is no current gap, just make the removed items the gap
                table.gapStart = start
                if (table.anchors.isNotEmpty()) anchorsRemoved = table.removeAnchors(start, len)
                table.gapLen = len
            } else {
                // Move the gap to the startGroup + len location and set the gap startGroup to
                // startGroup and gap len to len + gapLen
                val removeEnd = start + len
                moveGapTo(removeEnd)
                if (table.anchors.isNotEmpty()) anchorsRemoved = table.removeAnchors(start, len)
                table.gapStart = start
                table.gapLen += len
            }
            if (currentEnd >= table.gapStart) currentEnd -= len
            pendingClear = true
            anchorsRemoved
        } else false
    }
    override fun toString(): String {
        if (pendingClear) {
            pendingClear = false
            table.clearGap()
        }
        return "${javaClass.simpleName}(current=$current, size=${slots.size - table.gapLen}, gap=${
        if (table.gapLen > 0) "$table.gapStart-${table.gapStart + table.gapLen - 1}" else "none"}${
        if (insertCount > 0) ", inserting" else ""})"
    }
}

SlotTable.kt#385