/*
* This file is part of MiniGUI, a mature cross-platform windowing
* and Graphics User Interface (GUI) support system for embedded systems
* and smart IoT devices.
*
* Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd.
* Copyright (C) 1998~2002, WEI Yongming
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Or,
*
* As this program is a library, any link to this program must follow
* GNU General Public License version 3 (GPLv3). If you cannot accept
* GPLv3, you need to be licensed from FMSoft.
*
* If you have got a commercial license of this program, please use it
* under the terms and conditions of the commercial license.
*
* For more information about the commercial license, please refer to
* .
*/
/*
* btree.c: btree implementation.
*
* Author: Yan Xiaowei
*
* Create date: 2004/03/01
*
*/
#include
#include
#include
#ifdef __MINIGUI_LIB__
#include "common.h"
#include "minigui.h"
#include "gdi.h"
#include "window.h"
#include "object.h"
#elif __MGNCS_LIB__
#include
#include
#include
#include
#include "mgncsconfig.h"
#include "mcommon.h"
#include "mobject.h"
#include "mcomponent.h"
#endif
#if defined(_MGCTRL_TEXTEDIT_USE_NEW_IMPL) || defined(__MGNCS_TEXTEDITOR)
#include "mbtree.h"
static mCommBTreeNode* mCommBTree_newLeaf(mCommBTree *self)
{
return NEW(mCommBTreeNode);
}
static mCommBTreeNode* mCommBTree_newNode(mCommBTree *self)
{
return NEW(mCommBTreeNode);
}
static BOOL mCommBTree_insertLeaf(mCommBTree *self,
mCommBTreeNode *parent, mCommBTreeNode *sibLeaf, mCommBTreeNode *leaf)
{
mCommBTreeNode *node = NULL;
if (!self || !leaf)
return FALSE;
if (parent && (!sibLeaf || sibLeaf->parent == parent))
node = parent;
else if (!parent && sibLeaf)
node = sibLeaf->parent;
//insert a leaf
if (node && node->level == 1) {
leaf->level = 0;
leaf->parent = node;
if (sibLeaf) {
//append to sibLeaf
leaf->next = sibLeaf->next;
sibLeaf->next = leaf;
}
else {
//append to last
mCommBTreeNode *child = parent->children;
if (child) {
while(child) {
if (child->next == NULL)
break;
child = child->next;
}
if (!child)
return FALSE;
child->next = leaf;
}
else
parent->children = leaf;
}
//TODO: numChildren, numLeaves
_c(leaf)->changeKey(leaf, NULL, 1);
if (node->numChildren > self->maxChildrenNum) {
_c(self)->rebalanceNode(self, node);
}
return TRUE;
}
return FALSE;
}
static void mCommBTree_deleteNode(mCommBTree *self, mCommBTreeNode *node)
{
mCommBTreeNode *parent;
if (!self || !node)
return;
parent = (mCommBTreeNode*)node->parent;
//root should have two children at least.
if (parent == self->rootNode && parent->numChildren <= 2 && parent->level == 1)
{
_c(node)->changeKey(node, (void*)-1, -1);
_c(node)->resetKey(node);
node->children = NULL;
node->level = 0;
node->numLeaves = 0;
_c(self)->recomputeNodeInfo(self, parent);
return;
}
if (parent) {
//delete node from children
mCommBTreeNode *child = parent->children;
if (child == node) {
parent->children = child->next;
}
else {
while(child) {
if (child->next == node)
break;
child = child->next;
}
if (!child) {
return;
}
child->next = node->next;
}
if (node->level > 0) {
_c(self)->recomputeNodeInfo(self, parent);
}
else {
_c(node)->changeKey(node, (void*)-1, -1);
}
if (parent->numChildren < self->minChildrenNum) {
_c(self)->rebalanceNode(self, parent);
}
}
DELETE(node);
}
static void mCommBTree_recomputeNodeInfo(mCommBTree *self, mCommBTreeNode *node)
{
mCommBTreeNode *leaf;
if (!self || !node)
return;
node->numChildren = 0;
node->numLeaves = 0;
_c(node)->resetKey(node);
leaf = node->children;
while (leaf) {
node->numChildren++;
if (node->level <= 1) {
node->numLeaves++;
}
else {
node->numLeaves += leaf->numLeaves;
}
_c(node)->recompute(node, leaf);
leaf = leaf->next;
}
}
static void mCommBTree_rebalanceNode(mCommBTree *self, mCommBTreeNode *node)
{
/*
* Loop over the entire ancestral chain of the mCommBTreeNode, working
* up through the self one mCommBTreeNode at a time until the root
* mCommBTreeNode has been processed.
*/
while (node) {
mCommBTreeNode *newNode, *child;
int i;
/*
* Check to see if the mCommBTreeNode has too many children. If it does,
* then split off all but the first MIN_CHILDREN into a separate
* mCommBTreeNode following the original one. Then repeat until the
* mCommBTreeNode has a decent size.
*/
if (node->numChildren > self->maxChildrenNum) {
while (1) {
/*
* If the mCommBTreeNode being split is the root
* mCommBTreeNode, then make a new root mCommBTreeNode above
* it first.
*/
if (node->parent == NULL) {
newNode = _c(self)->newNode(self);
newNode->level = node->level + 1;
newNode->children = (mCommBTreeNode *)node;
_c(self)->recomputeNodeInfo(self, newNode);
self->rootNode = newNode;
}
/* append a sibling node after node. */
newNode = _c(self)->newNode(self);
newNode->parent = node->parent;
newNode->next = node->next;
node->next = newNode;
newNode->level = node->level;
newNode->numChildren = node->numChildren - self->minChildrenNum;
//found child in specified position.
{
mCommBTreeNode *leaf;
for (i = self->minChildrenNum - 1,
leaf = node->children;
i > 0; i--, leaf = leaf->next)
{
/* Empty loop body. */
}
newNode->children = leaf->next;
leaf->next = NULL;
}
_c(self)->recomputeNodeInfo(self, node);
node->parent->numChildren++;
node = newNode;
if (node->numChildren <= self->maxChildrenNum) {
_c(self)->recomputeNodeInfo(self, node);
break;
}
} //end while(1)
} //end if (node->numChildren > self->maxChildrenNum)
while (node->numChildren < self->minChildrenNum) {
mCommBTreeNode *other, *halfwaynode = NULL;
int total, first, i;
/*
* Too few children for this mCommBTreeNode. If this is the root then,
* it's OK for it to have less than self->minChildrenNum children
* as lon as it's at least two. If it has only one (and isn't at
* level 1), then chop the root mCommBTreeNode out of the tree and
* use its child as the new root.
*/
if (node->parent == NULL) {
if ((node->numChildren == 1) && (node->level > 1)) {
self->rootNode = (mCommBTreeNode*) node->children;
self->rootNode->parent = NULL;
node->children = NULL;
DELETE(node);
}
return;
}
/* Not root node. Make sure that there are siblings to balance with.*/
if (node->parent->numChildren < 2) {
_c(self)->rebalanceNode(self, node->parent);
continue;
}
/* Find a sibling neighbour to borrow from, and arrange for
* node to be the earlier of the pair.*/
if (node->next == NULL) {
for (other = (mCommBTreeNode*)(((mCommBTreeNode*)(node->parent))->children);
other->next != node;
other = other->next)
{
/* Empty loop body. */
}
node = other;
}
other = node->next;
/*
* We're going to either merge the two siblings together
* into one mCommBTreeNode or redivide the children among them to
* balance their loads. As preparation, join their two
* child lists into a single list and remember the half-way
* point in the list.
*/
total = node->numChildren + other->numChildren;
first = total /2;
if (node->children == NULL) {
node->children = other->children;
other->children = NULL;
}
for (child = node->children, i = 1;
child->next != NULL;
child = child->next, i++) {
if (i == first)
halfwaynode = child;
}
child->next = other->children;
while (i <= first) {
halfwaynode = child;
child = child->next;
i++;
}
/* If the two siblings can simply be merged together, do it. */
if (total <= self->maxChildrenNum) {
_c(self)->recomputeNodeInfo(self, node);
node->next = other->next;
node->parent->numChildren--;
other->children = NULL;
DELETE(other);
continue;
}
/* The siblings can't be merged, so just divide their
* children evenly between them. */
other->children = (mCommBTreeNode*)halfwaynode->next;
halfwaynode->next = NULL;
_c(self)->recomputeNodeInfo(self, node);
_c(self)->recomputeNodeInfo(self, other);
}
node = node->parent;
}
}
static mCommBTreeNode* _search_leaf(mCommBTreeNode *node, void *searchInfo, int flags)
{
int ret;
if (!node || !searchInfo)
return NULL;
ret = _c(node)->compareNode(node, searchInfo);
if (ret == BTREE_ERRNO_SCHILD) {
if (node->level == 0)
return node;
return _search_leaf(node->children, searchInfo,flags);
}
else if (ret == BTREE_ERRNO_SNEXT) {
if(!node->next && (flags & BTSF_RETLAST_IF_OUTOFRANGE))
{
while(node->level>0)
node = node->children;
return node;
}
return _search_leaf(node->next, searchInfo, flags);
}
return NULL;
}
static void* mCommBTree_search(mCommBTree *self, void* searchInfo, int flags)
{
if (self) {
return _search_leaf(self->rootNode, searchInfo, flags);
}
return NULL;
}
static void mCommBTree_construct(mCommBTree* self, va_list va)
{
mCommBTreeNode *rootNode;
_SUPER(mObject, self, construct, 0);
self->refCount = 1;
self->minChildrenNum = 6;
self->maxChildrenNum = 12;
/*The tree will initially have two empty lines. The second
* line isn't actually part of the tree's contents, but its
* presence makes several operations easier.*/
/*Create the root node. */
rootNode = _c(self)->newNode(self);
if (rootNode) {
mCommBTreeNode *leaf, *leaf2;
leaf = _c(self)->newLeaf(self);
leaf2 = _c(self)->newLeaf(self);
rootNode->level = 1;
rootNode->numChildren = 2;
rootNode->numLeaves = 2;
rootNode->children = leaf;
leaf->parent = rootNode;
leaf->next = leaf2;
leaf2->parent = rootNode;
leaf2->next = NULL;
self->rootNode = rootNode;
}
}
static void mCommBTree_destroy(mCommBTree* self)
{
_c(self)->unref(self);
if (!self || self->refCount > 0)
return;
DELETE(self->rootNode);
_SUPER(mObject, self, destroy);
}
static void mCommBTree_ref(mCommBTree *self)
{
if (!self || self->refCount <= 0)
return;
self->refCount += 1;
}
static void mCommBTree_unref(mCommBTree *self)
{
if (!self || self->refCount <= 0)
return;
self->refCount -= 1;
}
static mCommBTreeNode* mCommBTree_getRoot(mCommBTree *self)
{
return self? self->rootNode : NULL;
}
BEGIN_MINI_CLASS(mCommBTree, mObject)
CLASS_METHOD_MAP(mCommBTree, construct);
CLASS_METHOD_MAP(mCommBTree, destroy);
CLASS_METHOD_MAP(mCommBTree, ref);
CLASS_METHOD_MAP(mCommBTree, unref);
CLASS_METHOD_MAP(mCommBTree, getRoot);
CLASS_METHOD_MAP(mCommBTree, newNode);
CLASS_METHOD_MAP(mCommBTree, deleteNode);
CLASS_METHOD_MAP(mCommBTree, newLeaf);
CLASS_METHOD_MAP(mCommBTree, insertLeaf);
CLASS_METHOD_MAP(mCommBTree, recomputeNodeInfo);
CLASS_METHOD_MAP(mCommBTree, rebalanceNode);
CLASS_METHOD_MAP(mCommBTree, search);
END_MINI_CLASS
static mCommBTreeNode* _get_left_leaf(mCommBTreeNode *node)
{
mCommBTreeNode *child;
if (!node)
return NULL;
child = node->children;
if (!child || child->level == 0) {
return child;
}
return _get_left_leaf(child);
}
static void mCommBTreeLeafIterator_construct(mCommBTreeLeafIterator* self, va_list va)
{
ncsParseConstructParams(va, "pp", &self->tree, &self->current);
_SUPER(mObject, self, construct, 0);
if(self->tree && !self->current)
self->current = _get_left_leaf(self->tree->rootNode);
}
static void mCommBTreeLeafIterator_remove(mCommBTreeLeafIterator* self)
{
mCommBTreeNode *delNode = self->current;
self->current = _c(self)->next(self);
_c(self->tree)->deleteNode(self->tree, delNode);
}
static void mCommBTreeLeafIterator_insert(mCommBTreeLeafIterator* self,
mCommBTreeNode *leaf)
{
if (!self || !self->tree || !self->current)
return;
if (_c(self->tree)->insertLeaf(self->tree, NULL, self->current, leaf))
self->current = leaf;
}
static mCommBTreeNode* _get_next_parent(mCommBTreeNode *node)
{
if (!node)
return NULL;
if (node->next)
return node->next;
return _get_next_parent(node->parent);
}
static mCommBTreeNode* mCommBTreeLeafIterator_getCurrent(mCommBTreeLeafIterator* self)
{
if (self && self->tree)
return self->current;
return NULL;
}
static BOOL mCommBTreeLeafIterator_hasNext(mCommBTreeLeafIterator* self)
{
if (self && self->tree && self->current) {
if (self->current->next
|| _get_left_leaf(_get_next_parent(self->current->parent)))
return TRUE;
}
return FALSE;
}
static mCommBTreeNode* mCommBTreeLeafIterator_next(mCommBTreeLeafIterator* self)
{
if (self && self->tree && self->current) {
if (self->current->next)
self->current = self->current->next;
else
self->current = _get_left_leaf(_get_next_parent(self->current->parent));
return self->current;
}
return NULL;
}
BEGIN_MINI_CLASS(mCommBTreeLeafIterator, mObject)
CLASS_METHOD_MAP(mCommBTreeLeafIterator, construct);
CLASS_METHOD_MAP(mCommBTreeLeafIterator, hasNext);
CLASS_METHOD_MAP(mCommBTreeLeafIterator, next);
CLASS_METHOD_MAP(mCommBTreeLeafIterator, remove);
CLASS_METHOD_MAP(mCommBTreeLeafIterator, insert);
CLASS_METHOD_MAP(mCommBTreeLeafIterator, getCurrent);
END_MINI_CLASS
///////////////////////////////////////////////////////////////////////////////
static void mCommBTreeNode_construct(mCommBTreeNode* self, va_list va)
{
_SUPER(mObject, self, construct, 0);
self->parent = NULL;
self->next = NULL;
self->children = NULL;
self->level = 0;
self->numChildren = 0;
self->numLeaves = 0;
}
static void mCommBTreeNode_destroy(mCommBTreeNode *self)
{
mCommBTreeNode *child;
while (self->children) {
child = self->children;
self->children = child->next;
DELETE(child);
}
_SUPER(mObject, self, destroy);
}
static void mCommBTreeNode_resetKey(mCommBTreeNode *self)
{
}
static int mCommBTreeNode_compareNode(mCommBTreeNode *self, void *cmpInfo)
{
return BTREE_ERRNO_SNULL;
}
static void mCommBTreeNode_recompute(mCommBTreeNode *self, mCommBTreeNode *child)
{
if (child->parent != self)
child->parent = self;
}
static void mCommBTreeNode_changeKey(mCommBTreeNode *self, void *diffInfo, int deltaChild)
{
if (!self || !INSTANCEOF(self, mCommBTreeNode))
return;
if (deltaChild) {
if (self->level == 0) {
if (self->parent) self->parent->numChildren += deltaChild;
}
else {
self->numLeaves += deltaChild;
}
}
if (self->parent)
_c(self->parent)->changeKey(self->parent, diffInfo, deltaChild);
}
BEGIN_MINI_CLASS(mCommBTreeNode, mObject)
CLASS_METHOD_MAP(mCommBTreeNode, construct);
CLASS_METHOD_MAP(mCommBTreeNode, destroy);
CLASS_METHOD_MAP(mCommBTreeNode, compareNode);
CLASS_METHOD_MAP(mCommBTreeNode, recompute);
CLASS_METHOD_MAP(mCommBTreeNode, resetKey);
CLASS_METHOD_MAP(mCommBTreeNode, changeKey);
END_MINI_CLASS
#endif /* defined(_MGCTRL_TEXTEDIT_USE_NEW_IMPL) || defined(__MGNCS_TEXTEDITOR) */