void TermMatchTreeConverter::ProcessNGramBuffer(RowMatchNode::Builder& builder,
                                                    RingBuffer<Term, Term::c_log2MaxGramSize + 1>& gramBuffer)
    {
        const size_t count = gramBuffer.GetCount();
        LogAssertB(count > 0, "must have non-empty gram.");

        Term term(gramBuffer[0]);
        AppendTermRows(builder, term);
        for (size_t n = 1; n < count; ++n)
        {
            term.AddTerm(gramBuffer[n], m_index.GetConfiguration());
            AppendTermRows(builder, term);
        }
        gramBuffer.PopFront();
    }
    const RowMatchNode* TermMatchTreeConverter::BuildMatchTree(const TermMatchNode::Phrase& node)
    {
        RowMatchNode::Builder builder(RowMatchNode::AndMatch, m_allocator);
        RingBuffer<Term, Term::c_log2MaxGramSize + 1> termBuffer;

        StringVector const & stringVector = node.GetGrams();
        for (unsigned i = 0; i < stringVector.GetSize(); ++i)
        {
            *termBuffer.PushBack() = GetUnigramTerm(stringVector[i],
                                                    node.GetStreamId());

            if (termBuffer.GetCount() == Term::c_maxGramSize)
            {
                ProcessNGramBuffer(builder, termBuffer);
            }
        }

        while (!termBuffer.IsEmpty())
        {
            ProcessNGramBuffer(builder, termBuffer);
        }

        return builder.Complete();
    }