Beispiel #1
0
int insert_rec_2(long node_idx, rec_t *rec, long *parents, int parent_cnt, int *parent_is, int parent_i_cnt) {
	int i;

	node_t node;
	node.idx = node_idx;
	load_node(&node);

	for (i=0;i<node.rec_cnt;i++) {
		int cmped = rec_cmp(&node.recs[i], rec);

		if (cmped > 0) break;
		else if (cmped == 0) return -1;
	}

	int cur_i = i;

	if (node.child_cnt) {
		fseek(fp, node.idx*sizeof(node_t), SEEK_SET);
		fwrite(&node, sizeof(node), 1, fp);

		parents[-1] = node.idx;
		parent_is[-1] = cur_i;
		return insert_rec_2(node.childs[i], rec, parents-1, parent_cnt+1, parent_is-1, parent_i_cnt+1);
	} else {
		for (i=node.rec_cnt-1;i>=cur_i;i--) node.recs[i+1] = node.recs[i];
		node.rec_cnt++;
		node.recs[cur_i] = *rec;

		fseek(fp, node.idx*sizeof(node_t), SEEK_SET);
		fwrite(&node, sizeof(node), 1, fp);

		insert_rec_adjust(node_idx, parents, parent_cnt, parent_is, parent_i_cnt);
		return 0;
	}
}
Beispiel #2
0
/*----------------------------------------------------------------------------
 * rumavl_node_find
 *
 * Returns a pointer to the node that matches "record".
 *--------------------------------------------------------------------------*/
RUMAVL_NODE *rumavl_node_find (RUMAVL *tree, const void *find, void **record)
{
    RUMAVL_NODE *node;
    int ln;
    
    if (find == NULL || tree->root == NULL)
	goto fail;

    node = tree->root;
    for (;;){
	if ((ln = rec_cmp(tree, find, NODE_REC(node))) == 0){
	    if (record != NULL)
		*record = NODE_REC(node);
	    return node;
	}

	ln = LINK_NO(ln);
	if (node->thread[ln] > 0)
	    break;

	node = node->link[ln];
    }
    /* we didn't find the desired node */

fail:
    if (record != NULL)
	*record = NULL;
    
    return NULL; 
}
Beispiel #3
0
int search_rec(long node_idx, rec_t *rec) {
	int i;

	node_t node;
	node.idx = node_idx;
	load_node(&node);

	for (i=0;i<node.rec_cnt;i++) {
		int cmped = rec_cmp(&node.recs[i], rec);

		if (cmped > 0) {
			if (node.child_cnt) return search_rec(node.childs[i], rec);
			else return -1;
		} else if (cmped == 0) {
			*rec = node.recs[i];
			return 0;
		}
	}

	if (node.child_cnt) return search_rec(node.childs[node.child_cnt-1], rec);
	else return -1;
}
Beispiel #4
0
int remove_rec_2(long node_idx, rec_t *rec, long *parents, int parent_cnt, int *parent_is, int parent_i_cnt) {
	int i, j;

	node_t node;
	node.idx = node_idx;
	load_node(&node);

	for (i=0;i<node.rec_cnt;i++) {
		int cmped = rec_cmp(&node.recs[i], rec);

		if (cmped > 0) {
			if (node.child_cnt) {
				parents[-1] = node.idx;
				parent_is[-1] = i;
				return remove_rec_2(node.childs[i], rec, parents-1, parent_cnt+1, parent_is-1, parent_i_cnt+1);
			} else {
				return -1;
			}
		} else if (cmped == 0) {
			if (!node.child_cnt) {
				for (j=i+1;j<node.rec_cnt;j++) node.recs[j-1] = node.recs[j];
				node.rec_cnt--;

				save_node(&node);

				remove_rec_adjust(node.idx, parents, parent_cnt, parent_is, parent_i_cnt);
				return 0;
			} else {
				int prev_i = i+1;
				long prev_suc = node.idx;

				node_t suc;
				suc.idx = node.childs[prev_i];
				load_node(&suc);

				int new_parent_cnt = 0;
				int new_parent_i_cnt = 0;

				parents[--new_parent_cnt] = prev_suc;
				parent_is[--new_parent_i_cnt] = prev_i;

				while (suc.child_cnt) {
					prev_suc = suc.idx;
					prev_i = 0;

					suc.idx = suc.childs[prev_i];
					load_node(&suc);

					parents[--new_parent_cnt] = prev_suc;
					parent_is[--new_parent_i_cnt] = prev_i;
				}

				rec_t _rec = suc.recs[0];

				node.recs[i] = _rec;
				save_node(&node);

				return remove_rec_2(suc.idx, &_rec, parents+new_parent_cnt, parent_cnt-new_parent_cnt, parent_is+new_parent_i_cnt, parent_i_cnt-new_parent_i_cnt);
			}
		}
	}

	if (node.child_cnt) {
		parents[-1] = node.idx;
		parent_is[-1] = node.child_cnt-1;
		return remove_rec_2(node.childs[node.child_cnt-1], rec, parents-1, parent_cnt+1, parent_is-1, parent_i_cnt+1);
	} else return -1;
}
Beispiel #5
0
int 
sweep (
    int opcode,							/* The operation to perform. */
    char *file1,							/* The search/source file. */
    char *file2,							/* The scratch file.  (Required.) */
    int recsep,							/* Record seperator. */
    int fldsep,							/* Field seperator. */
    int maxlen,							/* Maximum length of record. */
    char *fmatch[],						/* Fields to match. */
    char *usrbuf,						/* User's copy of matching record. */
    char *usrmatch[],					/* Pointers to arguments. */
    int (*chop)(char *, int, char **),			/* Routine to seperate the fields. */
    int (*compare)(char **, char **, int)		/* Routine to compare records. */
)
{
	char	**arg = NULL,						/* Pointers to the fields. */
			*realrec = NULL,				/* A copy of what was actually read. */
			*hackrec = NULL;				/* A place to keep the fields. */

	int		cmpstat,					/* Comparison status. */
			retcode,					/* Return code. */
			action = 0,					/* Record of what we have done. */
			rectype,					/* FIXED or VARIED record size. */
			i, j;						/* Looping variable. */

	FILE	*fp1, *fp2 = NULL;					/* Pointers to file1 and file2. */

	TR("Sweep: opcode=%d file1=(%s) file2=(%s)\n", opcode, file1, file2);
	TR(".. recsep=(%c) fldsep=(%c) maxlen=%d ", recsep, fldsep, maxlen);
	TR("usrbuf=%d usrmatch=%d chop=%d\n", usrbuf, usrmatch, chop);
	TR(".. usrbuf=(%s) compare=%d\n", usrbuf, compare, EMPTY);
#ifdef TRACE
	TR(".. fmatch ..\n", EMPTY, EMPTY, EMPTY);
	for (i = 0; fmatch[i] != EMPTY; i++)
		TR("%d (%s)\n", i, fmatch[i], EMPTY);
#endif

	if ((fp1 = fopen (file1, READ)) == NULL) {
		if (opcode == PUTNOW || (opcode & INSERT) > 0) {
			if ((fp1 = fopen (file1, APP)) == NULL)
				return (NOSOURCE);
			if (fclose (fp1) == ERROR)
				return (NOSOURCE);
			if ((fp1 = fopen (file1, READ)) == NULL)
				return (NOSOURCE);
			TR("Sweep: made a blank file1\n", EMPTY, EMPTY, EMPTY);
			opcode = PUTNOW;			/* Since buffers are unneeded. */
			}
		else
			return (NOSOURCE);
		}

	if ((opcode & VERIFY) == 0) {
		if ((fp2 = fopen (file2, READ)) != NULL) {
			if (fclose (fp2) == ERROR || unlink (file2) == ERROR) {
				if (fclose (fp1) == ERROR)
					return (RECURSE + LIVEDEST);
				return (LIVEDEST);
				}
			}
		if ((fp2 = fopen (file2, APP)) == NULL) {
			if (fclose (fp1) == ERROR)
				return (RECURSE + NODEST);
			return (NODEST);
			}
		action += OFILE2;				/* Flag file2 as open. */
		}

	action += OFILE1;					/* Flag file1 as open. */

	TR("Sweep: file1%s open\n", ((action & OFILE2) > 0) ? " and file2" : "",
	  EMPTY, EMPTY);

	if (opcode == PUTNOW)
		goto begin;						/* Because we need no allocations. */

	/* At this point, we begin allocating the three areas that  this routine
	 * needs.  realrec will contain a copy of what was really read.  hackrec
	 * will provide space to store its individual fields.  arg will  provide
	 * space for the pointers the those fields.  The general scheme used for
	 * allocation and use of these areas is as follows:
	 *
	 *			Given:	usrbuf		--			usrbuf		--
	 *					usrmatch	usrmatch	--			--
	 *			---------------------------------------------------
	 *			arg		usrmatch/1	usrmatch/2	ALLOC3		ALLOC3
	 *			---------------------------------------------------
	 *			realrec	ALLOC1		ALLOC1		usrbuf/3	ALLOC1
	 *			---------------------------------------------------
	 *			hackrec	usrbuf/4	ALLOC2		ALLOC2		ALLOC2
	 *			---------------------------------------------------
	 *
	 * Note 1: On DELETE this  array will hold pointers to the actual fields
	 *  of the DELETED record.  On INSERT and REPLACE, it will hold pointers
	 *  into usrbuf which will be MEANINGLESS.  On VERIFY, it will also hold
	 *	pointers, but they are to the matched record.  On PUTNOW, this array
	 *	is unaffected.
	 *
	 * Note 2: On all operations, the pointers in this array will be useless
	 *	upon return.
	 *
	 * Note 3: On DELETE and VERIFY, this array will hold the actual  record
	 *	deleted or matched.  On INSERT or REPLACE, this record will hold the
	 *	actual  record  inserted  or  replaced (and is not used internally).
	 *	PUTNOW has no effect.
	 *
	 * Note 4: On DELETE and VERIFY, this array will hold the actual  record
	 *	deleted or matched.  On INSERT and REPLACE, this array will hold the
	 *	actual record inserted or replaced (in which case, the specification
	 *	of usrmatch will put only junk into that array, as in note 1).  This
	 *	area is not used internally for an INSERT or REPLACE.  This array is
	 *	not affected by PUTNOW.
	 */

	if (maxlen <= 0) {
		retcode = BADSIZE;
		goto bye;
		}

	retcode = NOSPACE;					/* All errors will be for this. */

	if (usrbuf == EMPTY) {				/* We must allocate a buffer. */
		TR("Sweep: no usrbuf\n", EMPTY, EMPTY, EMPTY);
		if ((realrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
			goto bye;
		action += ALLOC1;				/* A buffer was allocated. */
		TR("Sweep: allocated (1) realrec=%d\n", realrec, EMPTY, EMPTY);
		if ((hackrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
			goto bye;
		action += ALLOC2;
		TR("Sweep: allocated (2) hackrec=%d\n", hackrec, EMPTY, EMPTY);
		if (usrmatch == (char**) NULL) { /* User provided no field pointers. */
			j = (maxlen / 2 + 1) * sizeof (char*);
			if ((arg = (char**) malloc ((unsigned) j)) == (char**) NULL)
				goto bye;
			action += ALLOC3;			/* Pointer space was allocated. */
			TR("Sweep: allocated (3) arg=%d\n", arg, EMPTY, EMPTY);
			}
		else
			arg = usrmatch;
		}
	
	else {								/* A usrbuf area was specified */
		TR("Sweep: usrbuf given\n", EMPTY, EMPTY, EMPTY);
		if (usrmatch == (char**) NULL) {
			TR("Sweep: no usrmatch\n", EMPTY, EMPTY, EMPTY);
			if ((opcode & REPLACE) > 0 || (opcode & INSERT) > 0) {
				/* Alas, we cannot use the area. */
				if ((realrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
					goto bye;
				action += ALLOC1;
				TR("Sweep: allocated (4) realrec=%d\n", realrec, EMPTY, EMPTY);
				}
			else
				realrec = usrbuf;
			if ((hackrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
				goto bye;
			action += ALLOC2;
			TR("Sweep: allocated (5) hackrec=%d\n", hackrec, EMPTY, EMPTY);
			j = (maxlen / 2 + 1) * sizeof (char*);
			if ((arg = (char**) malloc ((unsigned) j)) == (char**) NULL)
				goto bye;
			action += ALLOC3;
			TR("Sweep: allocated (6) arg=%d\n", arg, EMPTY, EMPTY);
			}
		else {							/* We have usrbuf and usrmatch. */
			if ((opcode & REPLACE) > 0 || (opcode & INSERT) > 0) {
				if ((hackrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
					goto bye;
				action += ALLOC2;
				TR("Sweep: allocated (7) hackrec=%d\n", hackrec, EMPTY, EMPTY);
				}
			else
				hackrec = usrbuf;
			arg = usrmatch;
			if ((realrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
				goto bye;
			action += ALLOC1;
			TR("Sweep: allocated (8) realrec=%d\n", realrec, EMPTY, EMPTY);
			}
		}

begin:

	if (opcode == PUTNOW) {				/* Insert a message at beginning. */
		if ((action & OFILE2) > 0) {
			fprintf (fp2, "%s%c", usrbuf, recsep);
			retcode = DONE;
			goto bye;
			}
		else {
			retcode = NOTPUT;
			goto bye;
			}
		}

	if (recsep == 0)
		rectype = FIXED;
	else
		rectype = VARIED;

	while ((i = getrec (fp1, realrec, recsep, rectype, maxlen)) == DONE) {

		/* Copy the buffer */
		strncpy (hackrec, realrec, (unsigned) maxlen);

		/* Seperate the command into arguments. */
		if (chop == (int (*)(char *, int, char **)) NULL)
			i = argchop (hackrec, fldsep, arg);
		else
			i = (*chop) (hackrec, fldsep, arg);
		if (i != DONE)
			break;						/* Something funny in record. */

		/* Now, determine the alphabetic status of this record. */
		if (compare == (int (*)(char **, char **, int)) NULL)
			cmpstat = rec_cmp (arg, fmatch, recsep);
		else
			cmpstat = (*compare) (arg, fmatch, recsep);

		/* Now, decide to continue or not. */
		if ((opcode & SEQUENTIAL) == 0 && cmpstat != SAME)
			continue;
			/* We continue if the search is not sequential and the record
			 * did not match. */

		if (cmpstat == SAME) {
			TR("Sweep: Enter SAME section\n", EMPTY, EMPTY, EMPTY);
			if ((opcode & INSERT) > 0)
				retcode = NOTNEW;
			else if ((opcode & VERIFY) > 0)
				retcode = FOUND;
			else {
				if ((opcode & REPLACE) > 0 && usrbuf != EMPTY)
					fprintf (fp2, "%s%c", usrbuf, recsep);
					/* And if this was a DELETE, nothing is printed. */
				retcode = DONE;
				}
			goto bye;
			}

		else if (cmpstat == BEFORE) {
			TR("Sweep: Enter BEFORE section\n", EMPTY, EMPTY, EMPTY);
			/* Put the record into the "scratch" file. */
			if ((opcode & VERIFY) == 0)
				fprintf (fp2, "%s%c", realrec, recsep);
			}

		else {							/* cmpstat == AFTER. */
			TR("Sweep: enter AFTER section\n", EMPTY, EMPTY, EMPTY);
			/* Match goes (or should have come) before this one. */
			if (opcode == SEQINSERT) {
				if (usrbuf != EMPTY)
					fprintf (fp2, "%s%c", usrbuf, recsep);
				/* Put in the INSERTed one, then replace the old one. */
				fprintf (fp2, "%s%c", realrec, recsep);
				retcode = DONE;
				}
			else
				/* We never get here on non-sequential searches. */
				retcode = NOTFOUND;
			goto bye;
			}
		}								/* End of while loop. */
	TR("Sweep: end of record loop\n", EMPTY, EMPTY, EMPTY);

	if (i != FILE1EOF) {
		TR("Sweep: funny death\n", EMPTY, EMPTY, EMPTY);
		retcode = i;					/* So user knows about bad data. */
		goto bye;
		}

	if (fclose (fp1) == ERROR)
		retcode = NOCLOSE;
	action -= OFILE1;

	if (((opcode & VERIFY) | (opcode & REPLACE) | (opcode & DELETE)) > 0) {
		TR("Sweep: not there\n", EMPTY, EMPTY, EMPTY);
		if ((opcode & VERIFY) == 0) {
			if (fclose (fp2) == ERROR)
				retcode = RESETERR;
			action -= OFILE2;
			if (unlink (file2) == ERROR) {
				retcode = RESETERR;		/* Should never happen? */
				goto bye;
				}
			}
		retcode = NOTFOUND;
		goto bye;
		}

	if ((opcode & INSERT) > 0) {
		TR("Sweep: insert tail record\n", EMPTY, EMPTY, EMPTY);
		if (usrbuf != EMPTY)
			fprintf (fp2, "%s%c", usrbuf, recsep);
		retcode = DONE;
		}
	else
		retcode = ABEND;				/* Should never happen? */

bye:

	TR("Sweep: closing retcode=%d action=%d\n", retcode, action, EMPTY);
	if (retcode == DONE) {				/* Successful on a file update. */
		if ((action & OFILE1) > 0 && (action & OFILE2) > 0)
			retcode = copyrest (fp1, fp2, EMPTY, BUFSIZE);
		}

	if ((action & OFILE1) > 0) {
		if (fclose (fp1) == ERROR)
			retcode = NOCLOSE;
		TR("Sweep: file1 closed\n", EMPTY, EMPTY, EMPTY);
		}
	if ((action & OFILE2) > 0) {
		if (fclose (fp2) == ERROR)
			retcode = NOCLOSE;
		TR("Sweep: file2 closed\n", EMPTY, EMPTY, EMPTY);
		}

	/* With the files closed, we now replace file1 with file2 (if needed). */
	if (retcode == DONE) {
		if ((retcode = ftrans (MOVE, file2, file1, EMPTY, BUFSIZE)) != DONE)
			retcode += RECURSE;
		TR("Sweep: final transfer\n", EMPTY, EMPTY, EMPTY);
		}
	if ((action & ALLOC1) > 0) {
		TR("Sweep: free realrec\n", EMPTY, EMPTY, EMPTY);
		free (realrec);
		}
	if ((action & ALLOC2) > 0) {
		TR("Sweep: free hackrec\n", EMPTY, EMPTY, EMPTY);
		free (hackrec);
		}
	if ((action & ALLOC3) > 0) {
		TR("Sweep: free arg\n", EMPTY, EMPTY, EMPTY);
		free ((char*) arg);
		}
	return (retcode);
}
Beispiel #6
0
/*----------------------------------------------------------------------------
 * rumavl_delete - deletes a node. Beware! this function is the worst part of
 * the library. Think (and draw pictures) when you edit this function.
 *--------------------------------------------------------------------------*/
int rumavl_delete (RUMAVL *tree, const void *record)
{
    RUMAVL_NODE **node, *tmpnode;
    RUMAVL_STACK *stack;
    int dir, ln;

    if (tree->root == NULL)	/* tree is empty */
	return RUMAVL_ERR_NOENT;

    stack = NULL;
    node = &tree->root;

    /* Find desired node */
    while ((dir = rec_cmp(tree, record, NODE_REC(*node))) != 0){
	if (stack_push(tree, &stack, node, dir) != 0)
	    goto nomemout;

	if ((*node)->thread[LINK_NO(dir)] > 0){
	    /* desired node does not exist */
	    stack_destroy(tree, stack);
	    return RUMAVL_ERR_NOENT;
	}
	node = &(*node)->link[LINK_NO(dir)];
    }

    /* OK, we got the node to be deleted, now get confirmation from user */
    if (tree->delcb != NULL &&
	    (ln = tree->delcb(tree, *node, NODE_REC(*node), tree->udata)) 
	    != 0){
	stack_destroy(tree, stack);
	return ln;
    }

    if ((*node)->thread[LEFT] > 0){
	if ((*node)->thread[RIGHT] > 0){
	    /* ooh look, we're a leaf */
	    tmpnode = *node;
	    if (stack != NULL){
		/* This node has a parent, which will need to take over a
		 * thread from the node being deleted. First we work out
		 * which (left/right) child we are of parent, then give
		 * parent the respective thread. If the thread destination
		 * points back to us (edge of tree thread), update it to
		 * point to our parent. */
		ln = LINK_NO(stack->dir);
		(*stack->node)->link[ln] = tmpnode->link[ln];
		(*stack->node)->thread[ln] = tmpnode->thread[ln];
		if ((*stack->node)->thread[ln] == 2)
		    (*stack->node)->link[ln]->link[OTHER_LINK(ln)] =
			*stack->node;
	    }else{
		/* 
		 * the only time stack will == NULL is when we are
		 * deleting the root of the tree. We already know that
		 * this is a leaf, so we will be leaving the tree empty.
		 */
		tree->root = NULL;
	    }
	    node_destroy(tree, tmpnode);
	}else{
	    /* *node has only one child, and can be pruned by replacing
	     * *node with its only child. This block of code and the next
	     * should be identical, except that all directions and link
	     * numbers are opposite.
	     *
	     * Let node being deleted = DELNODE for this comment.
	     * DELNODE only has one child (the right child). The left
	     * most descendant of DELNODE will have a thread (left thread)
	     * pointing to DELNODE. This thread must be updated to point
	     * to the node currently pointed to by DELNODE's left thread.
	     *
	     * DELNODE's left thread may point to the opposite edge of the
	     * BST. In this case, the destination of the thread will have
	     * a thread back to DELNODE. This will need to be updated to
	     * point back to the leftmost descendant of DELNODE.
	     */
	    tmpnode = *node;		    /* node being deleted */
	    *node = (*node)->link[RIGHT];   /* right child */
	    /* find left most descendant */
	    while ((*node)->thread[LEFT] == 0) 
		node = &(*node)->link[LEFT];
	    /* inherit thread from node being deleted */
	    (*node)->link[LEFT] = tmpnode->link[LEFT];
	    (*node)->thread[LEFT] = tmpnode->thread[LEFT];
	    /* update reverse thread if necessary */
	    if ((*node)->thread[LEFT] == 2)
		(*node)->link[LEFT]->link[RIGHT] = *node;
	    node_destroy(tree, tmpnode);
	}
    }else if ((*node)->thread[RIGHT] > 0){
	/* see above */
	tmpnode = *node;
	*node = (*node)->link[LEFT];
	while ((*node)->thread[RIGHT] == 0)
	    node = &(*node)->link[RIGHT];
	(*node)->link[RIGHT] = tmpnode->link[RIGHT];
	(*node)->thread[RIGHT] = tmpnode->thread[RIGHT];
	if ((*node)->thread[RIGHT] == 2)
	    (*node)->link[RIGHT]->link[LEFT] = *node;
	node_destroy(tree, tmpnode);
    }else{
	/* Delete a node with children on both sides. We do this by replacing
	 * the node to be deleted (delnode) with its inner most child
	 * on the heavier side (repnode). This in place replacement is quicker
	 * than the previously used method of rotating delnode until it is a
	 * (semi) leaf.
	 *
	 * At this point node points to delnode's parent's link to delnode. */
	RUMAVL_NODE *repnode, *parent;
	int outdir, outln;

	/* find heaviest subtree */
	if ((*node)->balance > 0){
	    outdir = +1;    /* outter direction */
	    dir = -1;	    /* inner direction */
	    outln = 1;	    /* outer link number */
	    ln = 0;	    /* inner link number */
	}else{
	    outdir = -1;    /* same as above, but opposite subtree */
	    dir = +1;
	    outln = 0;
	    ln = 1;
	}
	
	/* Add node to be deleted to the list of nodes to be rebalanced.
	 * Rememer that the replacement node will actually be acted apon,
	 * and that the replacement node should feel the effect of its own
	 * move */
	if (stack_push(tree, &stack, node, outdir) != 0)
	    goto nomemout;
	
	parent = *node;
	repnode = parent->link[outln];

	if (repnode->thread[ln] != 0){
	    /* repnode inherits delnode's lighter tree, and balance, and gets
	     * balance readjusted below */
	    repnode->link[ln] = (*node)->link[ln];
	    repnode->thread[ln] = (*node)->thread[ln];
	    repnode->balance = (*node)->balance;
	}else{
	    /* Now we add delnodes direct child to the list of "to update".
	     * We pass a pointer to delnode's link to its direct child to 
	     * stack_push(), but that pointer is invalid, because when
	     * stack_update() tries to access the link, delnode would have
	     * been destroyed. So, we remember the stack position at which
	     * we passed the faulty pointer to stack_push, and update its
	     * node pointer when we find repnode to point to repnodes 
	     * link on the same side */
	    RUMAVL_STACK *tmpstack;

	    if (stack_push(tree, &stack, &parent->link[outln], dir) != 0)
		goto nomemout;

	    tmpstack = stack;

	    parent = repnode;
	    repnode = repnode->link[ln];

	    /* move towards the innermost child of delnode */		
	    while (repnode->thread[ln] == 0){
		if (stack_push(tree, &stack, &parent->link[ln], dir) != 0)
		    goto nomemout;
		parent = repnode;
		repnode = repnode->link[ln];
	    }

	    if (repnode->thread[outln] == 0){
		/* repnode's parent inherits repnodes only child */
		parent->link[ln] = repnode->link[outln];
	    }else{
		/* parent already has a link to repnode, but it must now be
		 * marked as a thread */
		parent->thread[ln] = 1;
	    }

	    repnode->link[0] = (*node)->link[0];
	    repnode->thread[0] = (*node)->thread[0];
	    repnode->link[1] = (*node)->link[1];
	    repnode->thread[1] = (*node)->thread[1];
	    repnode->balance = (*node)->balance;

	    /* see comment above */
	    tmpstack->node = &repnode->link[outln];
	}
	node_destroy(tree, *node);
	*node = repnode;

	/* innermost child in lighter tree has an invalid thread to delnode,
	 * update it to point to repnode */
	repnode = seq_next(repnode, dir);
	repnode->link[outln] = *node;
    }

    /* update parents' balances */
    stack_update(tree, stack, -1);
    return 0;

nomemout:
    stack_destroy(tree, stack);
    return RUMAVL_ERR_NOMEM;
}
Beispiel #7
0
/*----------------------------------------------------------------------------
 * rumavl_set - set a node, overwriting if necessary, or creating if the node
 * does not exist
 *--------------------------------------------------------------------------*/
int rumavl_set (RUMAVL *tree, const void *record)
{
    RUMAVL_NODE **node, *tmp;
    RUMAVL_STACK *stack;
    int ln;
    
    if (tree->root == NULL){
	/* This is the first node in the tree */
	if ((tree->root = node_new(tree, record)) == NULL)
	    return RUMAVL_ERR_NOMEM;
	tree->root->link[LEFT] = tree->root;
	tree->root->link[RIGHT] = tree->root;
	tree->root->thread[LEFT] = 2;
	tree->root->thread[RIGHT] = 2;
	return 0;
    }

    /* Since the tree is not empty, we must descend towards the nodes ideal
     * possition, and we may even find an existing node with the same record.
     * We keep a list parents for the eventual node position, because these
     * parents may become inbalanced by a new insertion. */

    stack = NULL;
    node = &tree->root;
    for (;;){
	if ((ln = rec_cmp(tree, record, NODE_REC(*node))) == 0){
	    /* OK, we found the exact node we wish to set, and we now
	     * overwrite it. No change happens to the tree structure */
	    stack_destroy(tree, stack);
	    
	    if (tree->owcb != NULL &&
		    (ln = tree->owcb(tree, *node, NODE_REC(*node), 
				      record, tree->udata)) != 0){
		return ln;
	    }
	    
	    memcpy(NODE_REC(*node), record, tree->reclen);
	    return 0;
	}
	
	/* *node is not the node we seek */
	
	if (stack_push(tree, &stack, node, ln)){
	    stack_destroy(tree, stack);
	    return RUMAVL_ERR_NOMEM;
	}
	
	ln = LINK_NO(ln);
	if ((*node)->thread[ln] > 0){
	    /* This is as close to the correct node as we can get. We will
	     * now break and add the new node as a leaf */
	    break;
	}
	
	node = &(*node)->link[ln];
    }
	    
    /* we have reached a leaf, add new node here */
    if ((tmp = node_new(tree, record)) == NULL){
	stack_destroy(tree, stack);
	return RUMAVL_ERR_NOMEM;
    }
    /* new child inherits parent thread */
    tmp->link[ln] = (*node)->link[ln];
    tmp->thread[ln] = (*node)->thread[ln];
    if (tmp->thread[ln] == 2)
	tmp->link[ln]->link[OTHER_LINK(ln)] = tmp;
    
    tmp->link[OTHER_LINK(ln)] = *node;
    tmp->thread[OTHER_LINK(ln)] = 1;
    (*node)->link[ln] = tmp;
    (*node)->thread[ln] = 0;

    /* all parentage is now one level heavier - balance where necessary */
    stack_update(tree, stack, +1);
    
    return 0;
}