func (w *TermsWriter) pushTerm(text []byte) error {
	limit := w.lastTerm.Length()
	if len(text) < limit {
		limit = len(text)
	}
	pos := 0
	for pos < limit && w.lastTerm.At(pos) == text[pos] {
		pos++
	}
	for i := w.lastTerm.Length() - 1; i >= pos; i-- {
		if prefixTopSize := len(w.pending) - w.prefixStarts[i]; prefixTopSize >= w.owner.minItemsInBlock {
			if err := w.writeBlocks(i+1, prefixTopSize); err != nil {
				return err
			}
			w.prefixStarts[i] -= prefixTopSize - 1
		}
	}
	if len(w.prefixStarts) < len(text) {
		w.prefixStarts = util.GrowIntSlice(w.prefixStarts, len(text))
	}
	for i := pos; i < len(text); i++ {
		w.prefixStarts[i] = len(w.pending)
	}
	w.lastTerm.Copy(text)
	return nil
}
/* Pushes the new term to the top of the stack, and writes new blocks.
package blocktree
import (
	"fmt"
	"github.com/balzaczyy/golucene/core/codec"
	. "github.com/balzaczyy/golucene/core/codec/spi"
	. "github.com/balzaczyy/golucene/core/index/model"
	"github.com/balzaczyy/golucene/core/store"
	"github.com/balzaczyy/golucene/core/util"
	"github.com/balzaczyy/golucene/core/util/fst"
	"github.com/balzaczyy/golucene/core/util/packed"
	"io"
	"math"
	"strings"
)
type PostingsWriterBase interface {
	codec.PostingsConsumer
	io.Closer
	Init(store.IndexOutput) error
	NewTermState() *BlockTermState
	StartTerm() error
	FinishTerm(*BlockTermState) error
	EncodeTerm([]int64, util.DataOutput, *FieldInfo, *BlockTermState, bool) error
	SetField(fieldInfo *FieldInfo) int
}
const (
	DEFAULT_MIN_BLOCK_SIZE = 25
	DEFAULT_MAX_BLOCK_SIZE = 48
	TERMS_EXTENSION  = "tim"
	TERMS_CODEC_NAME = "BLOCK_TREE_TERMS_DICT"
	TERMS_VERSION_START = 0
	TERMS_VERSION_APPEND_ONLY   = 1
	TERMS_VERSION_META_ARRAY    = 2
	TERMS_VERSION_CHECKSUM      = 3
	TERMS_VERSION_MIN_MAX_TERMS = 4
	TERMS_VERSION_CURRENT = TERMS_VERSION_MIN_MAX_TERMS
	TERMS_INDEX_EXTENSION  = "tip"
	TERMS_INDEX_CODEC_NAME = "BLOCK_TREE_TERMS_INDEX"
)
type BlockTreeTermsWriterSPI interface {
	WriteHeader(store.IndexOutput) error
	WriteIndexHeader(store.IndexOutput) error
}
type FieldMetaData struct {
	fieldInfo        *FieldInfo
	rootCode         []byte
	numTerms         int64
	indexStartFP     int64
	sumTotalTermFreq int64
	sumDocFreq       int64
	docCount         int
	longsSize        int
	minTerm          []byte
	maxTerm          []byte
}
func newFieldMetaData(fieldInfo *FieldInfo,
	rootCode []byte, numTerms, indexStartFP, sumTotalTermFreq, sumDocFreq int64,
	docCount, longsSize int, minTerm, maxTerm []byte) *FieldMetaData {
	assert(numTerms > 0)
	assert2(rootCode != nil, "field=%v numTerms=%v", fieldInfo.Name, numTerms)
	return &FieldMetaData{
		fieldInfo,
		rootCode,
		numTerms,
		indexStartFP,
		sumTotalTermFreq,
		sumDocFreq,
		docCount,
		longsSize,
		minTerm,
		maxTerm,
	}
}
type BlockTreeTermsWriter struct {
	spi BlockTreeTermsWriterSPI
	out             store.IndexOutput
	indexOut        store.IndexOutput
	maxDoc          int
	minItemsInBlock int
	maxItemsInBlock int
	postingsWriter PostingsWriterBase
	fieldInfos     FieldInfos
	currentField   *FieldInfo
	fields  []*FieldMetaData
	segment string
	scratchBytes   *store.RAMOutputStream
	scratchIntsRef *util.IntsRefBuilder
}
func NewBlockTreeTermsWriter(state *SegmentWriteState,
	postingsWriter PostingsWriterBase,
	minItemsInBlock, maxItemsInBlock int) (*BlockTreeTermsWriter, error) {
	assert2(minItemsInBlock >= 2, "minItemsInBlock must be >= 2; got %v", minItemsInBlock)
	assert2(maxItemsInBlock >= 1, "maxItemsInBlock must be >= 1; got %v", maxItemsInBlock)
	assert2(minItemsInBlock <= maxItemsInBlock,
		"maxItemsInBlock must be >= minItemsInBlock; got maxItemsInBlock=%v minItemsInBlock=%v",
		maxItemsInBlock, minItemsInBlock)
	assert2(2*(minItemsInBlock-1) <= maxItemsInBlock,
		"maxItemsInBlock must be at least 2*(minItemsInBlock-1; got maxItemsInBlock=%v minItemsInBlock=%v",
		maxItemsInBlock, minItemsInBlock)
	ans := &BlockTreeTermsWriter{
		maxDoc:          state.SegmentInfo.DocCount(),
		fieldInfos:      state.FieldInfos,
		minItemsInBlock: minItemsInBlock,
		maxItemsInBlock: maxItemsInBlock,
		postingsWriter:  postingsWriter,
		segment:         state.SegmentInfo.Name,
		scratchBytes:    store.NewRAMOutputStreamBuffer(),
		scratchIntsRef:  util.NewIntsRefBuilder(),
	}
	ans.spi = ans
	var out, indexOut store.IndexOutput
	if err := func() error {
		var success = false
		defer func() {
			if !success {
				util.CloseWhileSuppressingError(out, indexOut)
			}
		}()
		var err error
		termsFileName := util.SegmentFileName(state.SegmentInfo.Name, state.SegmentSuffix, TERMS_EXTENSION)
		if out, err = state.Directory.CreateOutput(termsFileName, state.Context); err != nil {
			return err
		}
		if err = ans.spi.WriteHeader(out); err != nil {
			return err
		}
		termsIndexFileName := util.SegmentFileName(state.SegmentInfo.Name, state.SegmentSuffix, TERMS_INDEX_EXTENSION)
		if indexOut, err = state.Directory.CreateOutput(termsIndexFileName, state.Context); err != nil {
			return err
		}
		if err = ans.spi.WriteIndexHeader(indexOut); err != nil {
			return err
		}
		if err = postingsWriter.Init(out); err != nil {
			return err
		}
		success = true
		return nil
	}(); err != nil {
		return nil, err
	}
	ans.out = out
	ans.indexOut = indexOut
	return ans, nil
}
func (w *BlockTreeTermsWriter) WriteHeader(out store.IndexOutput) error {
	return codec.WriteHeader(out, TERMS_CODEC_NAME, TERMS_VERSION_CURRENT)
}
func (w *BlockTreeTermsWriter) WriteIndexHeader(out store.IndexOutput) error {
	return codec.WriteHeader(out, TERMS_INDEX_CODEC_NAME, TERMS_VERSION_CURRENT)
}
func (w *BlockTreeTermsWriter) writeTrailer(out store.IndexOutput, dirStart int64) error {
	return out.WriteLong(dirStart)
}
func (w *BlockTreeTermsWriter) writeIndexTrailer(indexOut store.IndexOutput, dirStart int64) error {
	return indexOut.WriteLong(dirStart)
}
func (w *BlockTreeTermsWriter) AddField(field *FieldInfo) (TermsConsumer, error) {
	assert(w.currentField == nil || w.currentField.Name < field.Name)
	w.currentField = field
	return newTermsWriter(w, field), nil
}
func encodeOutput(fp int64, hasTerms bool, isFloor bool) int64 {
	assert(fp < (1 << 62))
	ans := (fp << 2)
	if hasTerms {
		ans |= BTT_OUTPUT_FLAG_HAS_TERMS
	}
	if isFloor {
		ans |= BTT_OUTPUT_FLAG_IS_FLOOR
	}
	return ans
}
type PendingEntry interface {
	isTerm() bool
}
type PendingTerm struct {
	term []byte
	state *BlockTermState
}
func newPendingTerm(term []byte, state *BlockTermState) *PendingTerm {
	clone := make([]byte, len(term))
	copy(clone, term)
	return &PendingTerm{clone, state}
}
func (t *PendingTerm) isTerm() bool { return true }
func (t *PendingTerm) String() string { panic("not implemented yet") }
type PendingBlock struct {
	prefix        []byte
	fp            int64
	index         *fst.FST
	subIndices    []*fst.FST
	hasTerms      bool
	isFloor       bool
	floorLeadByte int
}
func newPendingBlock(prefix []byte, fp int64, hasTerms, isFloor bool,
	floorLeadByte int, subIndices []*fst.FST) *PendingBlock {
	return &PendingBlock{
		prefix:        prefix,
		fp:            fp,
		index:         nil,
		subIndices:    subIndices,
		hasTerms:      hasTerms,
		isFloor:       isFloor,
		floorLeadByte: floorLeadByte,
	}
}
func (b *PendingBlock) isTerm() bool { return false }
func (b *PendingBlock) String() string {
	return fmt.Sprintf("BLOCK: %v", utf8ToString(b.prefix))
}
func (b *PendingBlock) compileIndex(blocks []*PendingBlock,
	scratchBytes *store.RAMOutputStream,
	scratchIntsRef *util.IntsRefBuilder) (err error) {
	assert2(b.isFloor && len(blocks) > 1 || (!b.isFloor && len(blocks) == 1),
		"isFloor=%v blocks=%v", b.isFloor, blocks)
	assert(blocks[0] == b)
	assert(scratchBytes.FilePointer() == 0)
	if err = scratchBytes.WriteVLong(encodeOutput(b.fp, b.hasTerms, b.isFloor)); err != nil {
		return
	}
	if b.isFloor {
		if err = scratchBytes.WriteVInt(int32(len(blocks) - 1)); err != nil {
			return
		}
		for _, sub := range blocks[1:] {
			assert(sub.floorLeadByte != -1)
			if err = scratchBytes.WriteByte(byte(sub.floorLeadByte)); err != nil {
				return
			}
			assert(sub.fp > b.fp)
			if err = scratchBytes.WriteVLong((sub.fp-b.fp)<<1 |
				int64(map[bool]int{true: 1, false: 0}[sub.hasTerms])); err != nil {
				return
			}
		}
	}
	outputs := fst.ByteSequenceOutputsSingleton()
	indexBuilder := fst.NewBuilder(fst.INPUT_TYPE_BYTE1,
		0, 0, true, false, int(math.MaxInt32),
		outputs, false,
		packed.PackedInts.COMPACT, true, 15)
	bytes := make([]byte, scratchBytes.FilePointer())
	assert(len(bytes) > 0)
	err = scratchBytes.WriteToBytes(bytes)
	if err != nil {
		return err
	}
	err = indexBuilder.Add(fst.ToIntsRef(b.prefix, scratchIntsRef), bytes)
	if err != nil {
		return err
	}
	scratchBytes.Reset()
	for _, block := range blocks {
		if block.subIndices != nil {
			for _, subIndex := range block.subIndices {
				if err = b.append(indexBuilder, subIndex, scratchIntsRef); err != nil {
					return err
				}
			}
		}
		block.subIndices = nil
	}
	if b.index, err = indexBuilder.Finish(); err != nil {
		return err
	}
	assert(b.subIndices == nil)
	return nil
}
func (b *PendingBlock) append(
	builder *fst.Builder,
	subIndex *fst.FST,
	scratchIntsRef *util.IntsRefBuilder) error {
	subIndexEnum := fst.NewBytesRefFSTEnum(subIndex)
	indexEnt, err := subIndexEnum.Next()
	for err == nil && indexEnt != nil {
		err = builder.Add(fst.ToIntsRef(indexEnt.Input.ToBytes(), scratchIntsRef), indexEnt.Output)
		if err == nil {
			indexEnt, err = subIndexEnum.Next()
		}
	}
	return err
}
type TermsWriter struct {
	owner            *BlockTreeTermsWriter
	fieldInfo        *FieldInfo
	longsSize        int
	numTerms         int64
	docsSeen         *util.FixedBitSet
	sumTotalTermFreq int64
	sumDocFreq       int64
	docCount         int
	indexStartFP     int64
	lastTerm     *util.BytesRefBuilder
	prefixStarts []int
	longs []int64
	pending []PendingEntry
	newBlocks []*PendingBlock
	firstPendingTerm *PendingTerm
	lastPendingTerm  *PendingTerm
	suffixWriter *store.RAMOutputStream
	statsWriter  *store.RAMOutputStream
	metaWriter   *store.RAMOutputStream
	bytesWriter  *store.RAMOutputStream
}
func newTermsWriter(owner *BlockTreeTermsWriter,
	fieldInfo *FieldInfo) *TermsWriter {
	owner.postingsWriter.SetField(fieldInfo)
	ans := &TermsWriter{
		owner:        owner,
		fieldInfo:    fieldInfo,
		lastTerm:     util.NewBytesRefBuilder(),
		prefixStarts: make([]int, 8),
		suffixWriter: store.NewRAMOutputStreamBuffer(),
		statsWriter:  store.NewRAMOutputStreamBuffer(),
		metaWriter:   store.NewRAMOutputStreamBuffer(),
		bytesWriter:  store.NewRAMOutputStreamBuffer(),
	}
	ans.longsSize = owner.postingsWriter.SetField(fieldInfo)
	ans.longs = make([]int64, ans.longsSize)
	return ans
}
func (w *TermsWriter) writeBlocks(prefixLength, count int) (err error) {
	assert(count > 0)
	assert(prefixLength > 0 || count == len(w.pending))
	lastSuffixLeadLabel := -1
	hasTerms := false
	hasSubBlocks := false
	end := len(w.pending)
	start := end - count
	nextBlockStart := start
	nextFloorLeadLabel := -1
	for i, ent := range w.pending[start:] {
		var suffixLeadLabel int
		if ent.isTerm() {
			term := ent.(*PendingTerm)
			if len(term.term) == prefixLength {
				assert(lastSuffixLeadLabel == -1)
				suffixLeadLabel = -1
			} else {
				suffixLeadLabel = int(term.term[prefixLength])
			}
		} else {
			block := ent.(*PendingBlock)
			assert(len(block.prefix) > prefixLength)
			suffixLeadLabel = int(block.prefix[prefixLength])
		}
		if suffixLeadLabel != lastSuffixLeadLabel {
			if itemsInBlock := i + count - nextBlockStart; itemsInBlock >= w.owner.minItemsInBlock &&
				end-nextBlockStart > w.owner.maxItemsInBlock {
				isFloor := itemsInBlock < count
				var block *PendingBlock
				if block, err = w.writeBlock(prefixLength, isFloor,
					nextFloorLeadLabel, nextBlockStart, i+count, hasTerms,
					hasSubBlocks); err != nil {
					return
				}
				w.newBlocks = append(w.newBlocks, block)
				hasTerms = false
				hasSubBlocks = false
				nextFloorLeadLabel = suffixLeadLabel
				nextBlockStart = i + count
			}
			lastSuffixLeadLabel = suffixLeadLabel
		}
		if ent.isTerm() {
			hasTerms = true
		} else {
			hasSubBlocks = true
		}
	}
	if nextBlockStart < end {
		itemsInBlock := end - nextBlockStart
		isFloor := itemsInBlock < count
		var block *PendingBlock
		if block, err = w.writeBlock(prefixLength, isFloor,
			nextFloorLeadLabel, nextBlockStart, end, hasTerms,
			hasSubBlocks); err != nil {
			return
		}
		w.newBlocks = append(w.newBlocks, block)
	}
	assert(len(w.newBlocks) > 0)
	firstBlock := w.newBlocks[0]
	assert(firstBlock.isFloor || len(w.newBlocks) == 1)
	if err = firstBlock.compileIndex(w.newBlocks,
		w.owner.scratchBytes, w.owner.scratchIntsRef); err != nil {
		return
	}
	w.pending = w.pending[:start]
	w.pending = append(w.pending, firstBlock)
	w.newBlocks = nil
	return nil
}
func (w *TermsWriter) writeBlock(
	prefixLength int,
	isFloor bool,
	floorLeadLabel, start, end int,
	hasTerms, hasSubBlocks bool) (*PendingBlock, error) {
	assert(end > start)
	startFP := w.owner.out.FilePointer()
	hasFloorLeadLabel := isFloor && floorLeadLabel != -1
	prefix := make([]byte, prefixLength)
	copy(prefix, w.lastTerm.Bytes()[:prefixLength])
	numEntries := end - start
	code := numEntries << 1
	if end == len(w.pending) {  
		code |= 1
	}
	var err error
	if err = w.owner.out.WriteVInt(int32(code)); err != nil {
		return nil, err
	}
	isLeafBlock := !hasSubBlocks
	var subIndices []*fst.FST
	var absolute = true
	if isLeafBlock {  
		subIndices = nil
		for i, ent := range w.pending[start:end] {
			assert2(ent.isTerm(), "i=%v", i+start)
			term := ent.(*PendingTerm)
			assert2(strings.HasPrefix(string(term.term), string(prefix)), "term.term=%v prefix=%v", term.term, prefix)
			state := term.state
			suffix := len(term.term) - prefixLength
			if err = w.suffixWriter.WriteVInt(int32(suffix)); err != nil {
				return nil, err
			}
			if err = w.suffixWriter.WriteBytes(term.term[prefixLength : prefixLength+suffix]); err != nil {
				return nil, err
			}
			assert(floorLeadLabel == -1 || int(term.term[prefixLength]) >= floorLeadLabel)
			if err = w.statsWriter.WriteVInt(int32(state.DocFreq)); err != nil {
				return nil, err
			}
			if w.fieldInfo.IndexOptions() != INDEX_OPT_DOCS_ONLY {
				assert2(state.TotalTermFreq >= int64(state.DocFreq),
					"%v vs %v", state.TotalTermFreq, state.DocFreq)
				if err := w.statsWriter.WriteVLong(state.TotalTermFreq - int64(state.DocFreq)); err != nil {
					return nil, err
				}
			}
			if err = w.owner.postingsWriter.EncodeTerm(w.longs, w.bytesWriter, w.fieldInfo, state, absolute); err != nil {
				return nil, err
			}
			for _, v := range w.longs[:w.longsSize] {
				assert(v >= 0)
				if err = w.metaWriter.WriteVLong(v); err != nil {
					return nil, err
				}
			}
			if err = w.bytesWriter.WriteTo(w.metaWriter); err != nil {
				return nil, err
			}
			w.bytesWriter.Reset()
			absolute = false
		}
	} else {  
		subIndices = nil
		for _, ent := range w.pending[start:end] {
			if ent.isTerm() {
				term := ent.(*PendingTerm)
				assert2(strings.HasPrefix(string(term.term), string(prefix)), "term.term=%v prefix=%v", term.term, prefix)
				state := term.state
				suffix := len(term.term) - prefixLength
				if err = w.suffixWriter.WriteVInt(int32(suffix << 1)); err != nil {
					return nil, err
				}
				if err = w.suffixWriter.WriteBytes(term.term[prefixLength : prefixLength+suffix]); err != nil {
					return nil, err
				}
				assert(floorLeadLabel == -1 || int(term.term[prefixLength]) >= floorLeadLabel)
				if err = w.statsWriter.WriteVInt(int32(state.DocFreq)); err != nil {
					return nil, err
				}
				if w.fieldInfo.IndexOptions() != INDEX_OPT_DOCS_ONLY {
					assert(state.TotalTermFreq >= int64(state.DocFreq))
					if err = w.statsWriter.WriteVLong(state.TotalTermFreq - int64(state.DocFreq)); err != nil {
						return nil, err
					}
				}
				if err = w.owner.postingsWriter.EncodeTerm(w.longs, w.bytesWriter, w.fieldInfo, state, absolute); err != nil {
					return nil, err
				}
				for _, v := range w.longs[:w.longsSize] {
					assert(v >= 0)
					if err = w.metaWriter.WriteVLong(v); err != nil {
						return nil, err
					}
				}
				if err = w.bytesWriter.WriteTo(w.metaWriter); err != nil {
					return nil, err
				}
				w.bytesWriter.Reset()
				absolute = false
			} else {
				block := ent.(*PendingBlock)
				assert(strings.HasPrefix(string(block.prefix), string(prefix)))
				suffix := len(block.prefix) - prefixLength
				assert(suffix > 0)
				if err = w.suffixWriter.WriteVInt(int32((suffix << 1) | 1)); err != nil {
					return nil, err
				}
				if err = w.suffixWriter.WriteBytes(block.prefix[prefixLength : prefixLength+suffix]); err != nil {
					return nil, err
				}
				assert(floorLeadLabel == -1 || int(block.prefix[prefixLength]) >= floorLeadLabel)
				assert(block.fp < startFP)
				if err = w.suffixWriter.WriteVLong(startFP - block.fp); err != nil {
					return nil, err
				}
				subIndices = append(subIndices, block.index)
			}
		}
		assert(len(subIndices) != 0)
	}
	if err = w.owner.out.WriteVInt(
		int32(w.suffixWriter.FilePointer()<<1) |
			(map[bool]int32{true: 1, false: 0}[isLeafBlock])); err != nil {
		return nil, err
	}
	if err = w.suffixWriter.WriteTo(w.owner.out); err != nil {
		return nil, err
	}
	w.suffixWriter.Reset()
	if err = w.owner.out.WriteVInt(int32(w.statsWriter.FilePointer())); err != nil {
		return nil, err
	}
	if err = w.statsWriter.WriteTo(w.owner.out); err != nil {
		return nil, err
	}
	w.statsWriter.Reset()
	if err = w.owner.out.WriteVInt(int32(w.metaWriter.FilePointer())); err != nil {
		return nil, err
	}
	if err = w.metaWriter.WriteTo(w.owner.out); err != nil {
		return nil, err
	}
	w.metaWriter.Reset()
	if hasFloorLeadLabel {
		prefix = append(prefix, byte(floorLeadLabel))
	}
	return newPendingBlock(prefix, startFP, hasTerms, isFloor, floorLeadLabel, subIndices), nil
}
func (w *TermsWriter) Comparator() func(a, b []byte) bool {
	return util.UTF8SortedAsUnicodeLess
}
func (w *TermsWriter) StartTerm(text []byte) (codec.PostingsConsumer, error) {
	assert(w.owner != nil)
	assert(w.owner.postingsWriter != nil)
	err := w.owner.postingsWriter.StartTerm()
	return w.owner.postingsWriter, err
}
func (w *TermsWriter) FinishTerm(text []byte, stats *codec.TermStats) (err error) {
	assert(stats.DocFreq > 0)
	assert2(w.fieldInfo.IndexOptions() == INDEX_OPT_DOCS_ONLY ||
		stats.TotalTermFreq >= int64(stats.DocFreq),
		"postingsWriter=%v", w.owner.postingsWriter)
	state := w.owner.postingsWriter.NewTermState()
	state.DocFreq = stats.DocFreq
	state.TotalTermFreq = stats.TotalTermFreq
	if err = w.owner.postingsWriter.FinishTerm(state); err != nil {
		return
	}
	w.sumDocFreq += int64(state.DocFreq)
	w.sumTotalTermFreq += state.TotalTermFreq
	if err = w.pushTerm(text); err != nil {
		return
	}
	term := newPendingTerm(text, state)
	w.pending = append(w.pending, term)
	w.numTerms++
	if w.firstPendingTerm == nil {
		w.firstPendingTerm = term
	}
	w.lastPendingTerm = term
	return nil
}