mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-11-03 23:00:58 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			247 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			247 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: BSD-2-Clause
 | 
						|
 */
 | 
						|
 | 
						|
#include <LibSQL/BTree.h>
 | 
						|
 | 
						|
namespace SQL {
 | 
						|
 | 
						|
BTreeIterator::BTreeIterator(TreeNode* node, int index)
 | 
						|
    : m_current(node)
 | 
						|
    , m_index(index)
 | 
						|
{
 | 
						|
    if (!node) {
 | 
						|
        m_where = Where::End;
 | 
						|
    } else {
 | 
						|
        if (index < 0) {
 | 
						|
            while (!node->is_leaf() && (node->size() != 0)) {
 | 
						|
                node = node->down_node(0);
 | 
						|
            }
 | 
						|
            if (node->size() == 0) {
 | 
						|
                m_where = Where::End;
 | 
						|
                m_current = nullptr;
 | 
						|
                m_index = -1;
 | 
						|
            } else {
 | 
						|
                m_where = Where::Valid;
 | 
						|
                m_current = node;
 | 
						|
                m_index = 0;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            VERIFY(m_index < (int)m_current->size());
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int BTreeIterator::cmp(BTreeIterator const& other) const
 | 
						|
{
 | 
						|
    if (is_end())
 | 
						|
        return (other.is_end()) ? 0 : 1;
 | 
						|
    if (other.is_end())
 | 
						|
        return -1;
 | 
						|
    VERIFY(&other.m_current->tree() == &m_current->tree());
 | 
						|
    VERIFY((m_current->size() > 0) && (other.m_current->size() > 0));
 | 
						|
    if (&m_current != &other.m_current)
 | 
						|
        return (*m_current)[m_current->size() - 1].compare((*(other.m_current))[0]);
 | 
						|
    return (*m_current)[m_index].compare((*(other.m_current))[other.m_index]);
 | 
						|
}
 | 
						|
 | 
						|
int BTreeIterator::cmp(Key const& other) const
 | 
						|
{
 | 
						|
    if (is_end())
 | 
						|
        return 1;
 | 
						|
    if (other.is_null())
 | 
						|
        return -1;
 | 
						|
    return key().compare(other);
 | 
						|
}
 | 
						|
 | 
						|
BTreeIterator BTreeIterator::next() const
 | 
						|
{
 | 
						|
    if (is_end())
 | 
						|
        return end();
 | 
						|
 | 
						|
    auto ix = m_index;
 | 
						|
    auto node = m_current;
 | 
						|
    if (ix < (int)(node->size() - 1)) {
 | 
						|
        if (node->is_leaf()) {
 | 
						|
            // We're in the middle of a leaf node. Next entry is
 | 
						|
            // is the next entry of the node:
 | 
						|
            return BTreeIterator(node, ix + 1);
 | 
						|
        } else {
 | 
						|
            /*
 | 
						|
             * We're in the middle of a non-leaf node. The iterator's
 | 
						|
             * next value is all the way down to the right, first entry.
 | 
						|
             *
 | 
						|
             *                  |
 | 
						|
             *                  +--+--+--+--+
 | 
						|
             *                  |  |##|  |  |
 | 
						|
             *                  +--+--+--+--+
 | 
						|
             *                 /   |  |  |   \
 | 
						|
             *                        |
 | 
						|
             *               +--+--+--+--+
 | 
						|
             *               |  |  |  |  |
 | 
						|
             *               +--+--+--+--+
 | 
						|
             *              /
 | 
						|
             * +--+--+--+--+
 | 
						|
             * |++|  |  |  |
 | 
						|
             * +--+--+--+--+
 | 
						|
             */
 | 
						|
            ix++;
 | 
						|
            while (!node->is_leaf()) {
 | 
						|
                node = node->down_node(ix);
 | 
						|
                ix = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        VERIFY(node->is_leaf() && (ix < (int)node->size()));
 | 
						|
        return BTreeIterator(node, ix);
 | 
						|
    }
 | 
						|
 | 
						|
    if (node->is_leaf()) {
 | 
						|
        // We currently at the last entry of a leaf node. We need to check
 | 
						|
        // one or more levels up until we end up in the "middle" of a node.
 | 
						|
        // If one level up we're still at the end of the node, we need
 | 
						|
        // to keep going up until we hit the root node. If we're at the
 | 
						|
        // end of the root node, we reached the end of the btree.
 | 
						|
        for (auto up = node->up(); up; up = node->up()) {
 | 
						|
            for (size_t i = 0; i < up->size(); i++) {
 | 
						|
                // One level up, try to find the entry with the current
 | 
						|
                // node's pointer as the left pointer:
 | 
						|
                if (up->down_pointer(i) == node->block_index())
 | 
						|
                    // Found it. This is the iterator's next value:
 | 
						|
                    return BTreeIterator(up, (int)i);
 | 
						|
            }
 | 
						|
            // We didn't find the m_current's pointer as a left node. So
 | 
						|
            // it must be the right node all the way at the end and we need
 | 
						|
            // to go one more level up:
 | 
						|
            node = up;
 | 
						|
        }
 | 
						|
        // We reached the root node and we're still at the end of the node.
 | 
						|
        // That means we're at the end of the btree.
 | 
						|
        return end();
 | 
						|
    }
 | 
						|
 | 
						|
    // If we're at the end of a non-leaf node, we need to follow the
 | 
						|
    // right pointer down until we find a leaf:
 | 
						|
    TreeNode* down;
 | 
						|
    for (down = node->down_node(node->size()); !down->is_leaf(); down = down->down_node(0))
 | 
						|
        ;
 | 
						|
    return BTreeIterator(down, 0);
 | 
						|
}
 | 
						|
 | 
						|
// FIXME Reverse iterating doesn't quite work; we don't recognize the
 | 
						|
// end (which is really the beginning) of the tree.
 | 
						|
BTreeIterator BTreeIterator::previous() const
 | 
						|
{
 | 
						|
    if (is_end())
 | 
						|
        return end();
 | 
						|
 | 
						|
    auto node = m_current;
 | 
						|
    auto ix = m_index;
 | 
						|
    if (ix > 0) {
 | 
						|
        if (node->is_leaf()) {
 | 
						|
            // We're in the middle of a leaf node. Previous entry is
 | 
						|
            // is the previous entry of the node:
 | 
						|
            return BTreeIterator(node, ix - 1);
 | 
						|
        } else {
 | 
						|
            /*
 | 
						|
             * We're in the middle of a non-leaf node. The iterator's
 | 
						|
             * previous value is all the way down to the left, last entry.
 | 
						|
             *
 | 
						|
             *                  |
 | 
						|
             *                  +--+--+--+--+
 | 
						|
             *                  |  |  |##|  |
 | 
						|
             *                  +--+--+--+--+
 | 
						|
             *                 /   |  |  |   \
 | 
						|
             *                        |
 | 
						|
             *               +--+--+--+--+
 | 
						|
             *               |  |  |  |  |
 | 
						|
             *               +--+--+--+--+
 | 
						|
             *                            \
 | 
						|
             *                 +--+--+--+--+
 | 
						|
             *                 |  |  |  |++|
 | 
						|
             *                 +--+--+--+--+
 | 
						|
             */
 | 
						|
            while (!node->is_leaf()) {
 | 
						|
                node = node->down_node(ix);
 | 
						|
                ix = (int)node->size();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        VERIFY(node->is_leaf() && (ix <= (int)node->size()));
 | 
						|
        return BTreeIterator(node, ix);
 | 
						|
    }
 | 
						|
 | 
						|
    if (node->is_leaf()) {
 | 
						|
        // We currently at the first entry of a leaf node. We need to check one
 | 
						|
        // or more levels up until we end up in the "middle" of a node.
 | 
						|
        // If one level up we're still at the start of the node, we need
 | 
						|
        // to keep going up until we hit the root node. If we're at the
 | 
						|
        // start of the root node, we reached the start of the btree.
 | 
						|
        auto stash_current = node;
 | 
						|
        for (auto up = node->up(); up; up = node->up()) {
 | 
						|
            for (size_t i = up->size(); i > 0; i--) {
 | 
						|
                // One level up, try to find the entry with the current
 | 
						|
                // node's pointer as the right pointer:
 | 
						|
                if (up->down_pointer(i) == node->block_index()) {
 | 
						|
                    // Found it. This is the iterator's next value:
 | 
						|
                    node = up;
 | 
						|
                    ix = (int)i - 1;
 | 
						|
                    return BTreeIterator(node, ix);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // We didn't find the m_current's pointer as a right node. So
 | 
						|
            // it must be the left node all the way at the start and we need
 | 
						|
            // to go one more level up:
 | 
						|
            node = up;
 | 
						|
        }
 | 
						|
        // We reached the root node and we're still at the start of the node.
 | 
						|
        // That means we're at the start of the btree.
 | 
						|
        return BTreeIterator(stash_current, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    // If we're at the start of a non-leaf node, we need to follow the
 | 
						|
    // left pointer down until we find a leaf:
 | 
						|
    TreeNode* down = node->down_node(0);
 | 
						|
    while (!down->is_leaf())
 | 
						|
        down = down->down_node(down->size());
 | 
						|
    return BTreeIterator(down, down->size() - 1);
 | 
						|
}
 | 
						|
 | 
						|
Key BTreeIterator::key() const
 | 
						|
{
 | 
						|
    if (is_end())
 | 
						|
        return {};
 | 
						|
    return (*m_current)[m_index];
 | 
						|
}
 | 
						|
 | 
						|
bool BTreeIterator::update(Key const& new_value)
 | 
						|
{
 | 
						|
    if (is_end())
 | 
						|
        return false;
 | 
						|
    if ((cmp(new_value) == 0) && (key().block_index() == new_value.block_index()))
 | 
						|
        return true;
 | 
						|
    auto previous_iter = previous();
 | 
						|
    auto next_iter = next();
 | 
						|
    if (!m_current->tree().duplicates_allowed() && ((previous_iter == new_value) || (next_iter == new_value))) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    if ((previous_iter > new_value) || (next_iter < new_value))
 | 
						|
        return false;
 | 
						|
 | 
						|
    // We are friend of BTree and TreeNode. Don't know how I feel about that.
 | 
						|
    m_current->m_entries[m_index] = new_value;
 | 
						|
    m_current->tree().serializer().serialize_and_write(*m_current);
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
BTreeIterator& BTreeIterator::operator=(BTreeIterator const& other)
 | 
						|
{
 | 
						|
    if (&other != this) {
 | 
						|
        m_current = other.m_current;
 | 
						|
        m_index = other.m_index;
 | 
						|
        m_where = other.m_where;
 | 
						|
    }
 | 
						|
    return *this;
 | 
						|
}
 | 
						|
 | 
						|
}
 |