source: branches/1.7/src/objects/RpTree.c @ 6694

Last change on this file since 6694 was 5679, checked in by ldelgass, 9 years ago

Full merge 1.3 branch to uq branch to sync. Fixed partial subdirectory merge
by removing mergeinfo from lang/python/Rappture directory.

  • Property svn:eol-style set to native
File size: 87.7 KB
Line 
1
2/*
3 * bltTree.c --
4 *
5 * Copyright 1998-1999 Lucent Technologies, Inc.
6 *
7 * Permission to use, copy, modify, and distribute this software and
8 * its documentation for any purpose and without fee is hereby
9 * granted, provided that the above copyright notice appear in all
10 * copies and that both that the copyright notice and warranty
11 * disclaimer appear in supporting documentation, and that the names
12 * of Lucent Technologies or any of their entities not be used in
13 * advertising or publicity pertaining to distribution of the software
14 * without specific, written prior permission.
15 *
16 * Lucent Technologies disclaims all warranties with regard to this
17 * software, including all implied warranties of merchantability and
18 * fitness.  In no event shall Lucent Technologies be liable for any
19 * special, indirect or consequential damages or any damages
20 * whatsoever resulting from loss of use, data or profits, whether in
21 * an action of contract, negligence or other tortuous action, arising
22 * out of or in connection with the use or performance of this
23 * software.
24 *
25 * The "tree" data object was created by George A. Howlett.
26 */
27
28#include "RpInt.h"
29
30/* TODO:
31 *  traces and notifiers should be in one list in tree object.
32 *  notifier is always fired.
33 *  incorporate first/next tag routines ?
34 */
35
36
37#ifndef NO_TREE
38
39#include "RpTree.h"
40
41//static Tcl_InterpDeleteProc TreeInterpDeleteProc;
42static Rp_TreeApplyProc SizeApplyProc;
43//static Tcl_IdleProc NotifyIdleProc;
44
45#define TREE_THREAD_KEY     "BLT Tree Data"
46#define TREE_MAGIC          ((unsigned int) 0x46170277)
47#define TREE_DESTROYED      (1<<0)
48
49typedef struct Rp_TreeNodeStruct Node;
50typedef struct Rp_TreeClientStruct TreeClient;
51typedef struct Rp_TreeObjectStruct TreeObject;
52typedef struct Rp_TreeValueStruct Value;
53
54/*
55 * Rp_TreeValue --
56 *
57 *  Tree nodes contain heterogeneous data fields, represented as a
58 *  chain of these structures.  Each field contains the key of the
59 *  field (Rp_TreeKey) and the value (Tcl_Obj) containing the
60 *  actual data representations.
61 *
62 */
63struct Rp_TreeValueStruct {
64    Rp_TreeKey key;     /* String identifying the data field */
65    void *objPtr;       /* Data representation. */
66    Rp_Tree owner;      /* Non-NULL if privately owned. */
67    Rp_TreeValue next;  /* Next value in the chain. */
68};
69
70#include <stdio.h>
71#include <string.h>
72/* The following header is required for LP64 compilation */
73#include <stdlib.h>
74
75#include "RpHash.h"
76
77static void TreeDestroyValues _ANSI_ARGS_((Rp_TreeNode node));
78
79static Value *TreeFindValue _ANSI_ARGS_((Rp_TreeNode node,
80    Rp_TreeKey key));
81static Value *TreeCreateValue _ANSI_ARGS_((Rp_TreeNode node,
82    Rp_TreeKey key, int *newPtr));
83
84static int TreeDeleteValue _ANSI_ARGS_((Rp_TreeNode node,
85    Rp_TreeValue value));
86
87static Value *TreeFirstValue _ANSI_ARGS_((Rp_TreeNode,
88    Rp_TreeKeySearch *searchPtr));
89
90static Value *TreeNextValue _ANSI_ARGS_((Rp_TreeKeySearch *srchPtr));
91
92/*
93 * When there are this many entries per bucket, on average, rebuild
94 * the hash table to make it larger.
95 */
96
97#define REBUILD_MULTIPLIER  3
98#define START_LOGSIZE       5  /* Initial hash table size is 32. */
99#define MAX_LIST_VALUES     20 /* Convert to hash table when node
100                                * value list gets bigger than this
101                                * many values. */
102
103#if (SIZEOF_VOID_P == 8)
104#define RANDOM_INDEX(i)     HashOneWord(mask, downshift, i)
105#define BITSPERWORD         64
106#else
107
108/*
109 * The following macro takes a preliminary integer hash value and
110 * produces an index into a hash tables bucket list.  The idea is
111 * to make it so that preliminary values that are arbitrarily similar
112 * will end up in different buckets.  The hash function was taken
113 * from a random-number generator.
114 */
115#define RANDOM_INDEX(i) \
116    (((((long) (i))*1103515245) >> downshift) & mask)
117#define BITSPERWORD         32
118#endif
119
120#define DOWNSHIFT_START     (BITSPERWORD - 2)
121
122/*
123 * Procedure prototypes for static procedures in this file:
124 */
125
126
127#if (SIZEOF_VOID_P == 8)
128static Rp_Hash HashOneWord _ANSI_ARGS_((uint64_t mask, unsigned int downshift,
129    CONST void *key));
130
131#endif /* SIZEOF_VOID_P == 8 */
132
133/*
134 * The hash table below is used to keep track of all the Rp_TreeKeys
135 * created so far.
136 */
137static Rp_HashTable keyTable;
138static int keyTableInitialized = 0;
139
140typedef struct {
141    Rp_HashTable treeTable; /* Table of trees. */
142    unsigned int nextId;
143//    Tcl_Interp *interp;
144} TreeInterpData;
145
146typedef struct {
147//    Tcl_Interp *interp;
148    ClientData clientData;
149    Rp_TreeKey key;
150    unsigned int mask;
151    Rp_TreeNotifyEventProc *proc;
152    Rp_TreeNotifyEvent event;
153    int notifyPending;
154} EventHandler;
155
156typedef struct {
157    ClientData clientData;
158    char *keyPattern;
159    char *withTag;
160    Node *nodePtr;
161    unsigned int mask;
162    Rp_TreeTraceProc *proc;
163    TreeClient *clientPtr;
164    Rp_ChainLink *linkPtr;
165} TraceHandler;
166
167/*
168 * --------------------------------------------------------------
169 *
170 * GetTreeInterpData --
171 *
172 *  Creates or retrieves data associated with tree data objects
173 *  for a particular thread.  We're using Tcl_GetAssocData rather
174 *  than the Tcl thread routines so BLT can work with pre-8.0
175 *  Tcl versions that don't have thread support.
176 *
177 * Results:
178 *  Returns a pointer to the tree interpreter data.
179 *
180 * --------------------------------------------------------------
181 */
182/*
183static TreeInterpData *
184GetTreeInterpData(Tcl_Interp *interp)
185{
186    Tcl_InterpDeleteProc *proc;
187    TreeInterpData *dataPtr;
188
189    dataPtr = (TreeInterpData *)
190        Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc);
191    if (dataPtr == NULL) {
192        dataPtr = Rp_Malloc(sizeof(TreeInterpData));
193        assert(dataPtr);
194        dataPtr->interp = interp;
195        Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc,
196                 dataPtr);
197        Rp_InitHashTable(&dataPtr->treeTable, BLT_STRING_KEYS);
198    }
199    return dataPtr;
200}
201*/
202
203/*
204 * --------------------------------------------------------------
205 *
206 * NewNode --
207 *
208 *  Creates a new node in the tree without installing it.  The
209 *  number of nodes in the tree is incremented and a unique serial
210 *  number is generated for the node.
211 *
212 *  Also, all nodes have a label.  If no label was provided (name
213 *  is NULL) then automatically generate one in the form "nodeN"
214 *  where N is the serial number of the node.
215 *
216 * Results:
217 *  Returns a pointer to the new node.
218 *
219 * --------------------------------------------------------------
220 */
221static Node *
222NewNode(TreeObject *treeObjPtr, CONST char *name, size_t inode)
223{
224    Node *nodePtr;
225
226    /* Create the node structure */
227    nodePtr = Rp_PoolAllocItem(treeObjPtr->nodePool, sizeof(Node));
228    nodePtr->inode = inode;
229    nodePtr->treeObject = treeObjPtr;
230    nodePtr->parent = NULL;
231    nodePtr->depth = 0;
232    nodePtr->flags = 0;
233    nodePtr->next = nodePtr->prev = NULL;
234    nodePtr->first = nodePtr->last = NULL;
235    nodePtr->nChildren = 0;
236    nodePtr->values = NULL;
237    nodePtr->logSize = 0;
238    nodePtr->nValues = 0;
239    nodePtr->label = NULL;
240    if (name != NULL) {
241        nodePtr->label = Rp_TreeGetKey(name);
242    }
243    treeObjPtr->nNodes++;
244    return nodePtr;
245}
246
247/*
248 *----------------------------------------------------------------------
249 *
250 * ReleaseTagTable --
251 *
252 *----------------------------------------------------------------------
253 */
254static void
255ReleaseTagTable(Rp_TreeTagTable *tablePtr)
256{
257    tablePtr->refCount--;
258    if (tablePtr->refCount <= 0) {
259        Rp_HashEntry *hPtr;
260        Rp_HashSearch cursor;
261
262        for(hPtr = Rp_FirstHashEntry(&tablePtr->tagTable, &cursor);
263            hPtr != NULL; hPtr = Rp_NextHashEntry(&cursor)) {
264
265            Rp_TreeTagEntry *tPtr;
266
267            tPtr = Rp_GetHashValue(hPtr);
268            Rp_DeleteHashTable(&tPtr->nodeTable);
269            Rp_Free(tPtr);
270        }
271        Rp_DeleteHashTable(&tablePtr->tagTable);
272        Rp_Free(tablePtr);
273    }
274}
275
276/*
277 * ----------------------------------------------------------------------
278 *
279 * ResetDepths --
280 *
281 *  Called after moving a node, resets the depths of each node
282 *  for the entire branch (node and it's decendants).
283 *
284 * Results:
285 *  None.
286 *
287 * ----------------------------------------------------------------------
288 */
289static void
290ResetDepths(
291    Node *nodePtr,      /* Root node. */
292    int depth)          /* Depth of the node. */
293{
294    nodePtr->depth = depth;
295    /* Also reset the depth for each descendant node. */
296    for (nodePtr = nodePtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
297        ResetDepths(nodePtr, depth + 1);
298    }
299}
300
301/*
302 *----------------------------------------------------------------------
303 *
304 * LinkBefore --
305 *
306 *  Inserts a link preceding a given link.
307 *
308 * Results:
309 *  None.
310 *
311 *----------------------------------------------------------------------
312 */
313static void
314LinkBefore(
315    Node *parentPtr,    /* Parent to hold the new entry. */
316    Node *nodePtr,      /* New node to be inserted. */
317    Node *beforePtr)    /* Node to link before. */
318{
319    if (parentPtr->first == NULL) {
320        parentPtr->last = parentPtr->first = nodePtr;
321    } else if (beforePtr == NULL) { /* Append onto the end of the chain */
322        nodePtr->next = NULL;
323        nodePtr->prev = parentPtr->last;
324        parentPtr->last->next = nodePtr;
325        parentPtr->last = nodePtr;
326    } else {
327        nodePtr->prev = beforePtr->prev;
328        nodePtr->next = beforePtr;
329        if (beforePtr == parentPtr->first) {
330            parentPtr->first = nodePtr;
331        } else {
332            beforePtr->prev->next = nodePtr;
333        }
334        beforePtr->prev = nodePtr;
335    }
336    parentPtr->nChildren++;
337    nodePtr->parent = parentPtr;
338}
339
340
341/*
342 *----------------------------------------------------------------------
343 *
344 * UnlinkNode --
345 *
346 *  Unlinks a link from the chain. The link is not deallocated,
347 *  but only removed from the chain.
348 *
349 * Results:
350 *  None.
351 *
352 *----------------------------------------------------------------------
353 */
354static void
355UnlinkNode(Node *nodePtr)
356{
357    Node *parentPtr;
358    int unlinked;       /* Indicates if the link is actually
359                         * removed from the chain. */
360    parentPtr = nodePtr->parent;
361    unlinked = FALSE;
362    if (parentPtr->first == nodePtr) {
363        parentPtr->first = nodePtr->next;
364        unlinked = TRUE;
365    }
366    if (parentPtr->last == nodePtr) {
367        parentPtr->last = nodePtr->prev;
368        unlinked = TRUE;
369    }
370    if (nodePtr->next != NULL) {
371        nodePtr->next->prev = nodePtr->prev;
372        unlinked = TRUE;
373    }
374    if (nodePtr->prev != NULL) {
375        nodePtr->prev->next = nodePtr->next;
376        unlinked = TRUE;
377    }
378    if (unlinked) {
379        parentPtr->nChildren--;
380    }
381    nodePtr->prev = nodePtr->next = NULL;
382}
383
384/*
385 * --------------------------------------------------------------
386 *
387 * FreeNode --
388 *
389 *  Unlinks a given node from the tree, removes its data, and
390 *  frees memory allocated to the node.
391 *
392 * Results:
393 *  None.
394 *
395 * --------------------------------------------------------------
396 */
397static void
398FreeNode(TreeObject *treeObjPtr, Node *nodePtr)
399{
400    Rp_HashEntry *hPtr;
401
402    /*
403     * Destroy any data fields associated with this node.
404     */
405    TreeDestroyValues(nodePtr);
406    UnlinkNode(nodePtr);
407    treeObjPtr->nNodes--;
408    hPtr = Rp_FindHashEntry(&treeObjPtr->nodeTable, (char *)nodePtr->inode);
409    assert(hPtr);
410    Rp_DeleteHashEntry(&treeObjPtr->nodeTable, hPtr);
411    Rp_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
412}
413
414/*
415 * --------------------------------------------------------------
416 *
417 * NewTreeObject --
418 *
419 *  Creates and initializes a new tree object. Trees always
420 *  contain a root node, so one is allocated here.
421 *
422 * Results:
423 *  Returns a pointer to the new tree object is successful, NULL
424 *  otherwise.  If a tree can't be generated, interp->result will
425 *  contain an error message.
426 *
427 * -------------------------------------------------------------- */
428static TreeObject *
429// NewTreeObject(TreeInterpData *dataPtr, CONST char *treeName)
430NewTreeObject(CONST char *treeName)
431{
432    TreeObject *treeObjPtr;
433    int isNew;
434    Rp_HashEntry *hPtr;
435
436    treeObjPtr = Rp_Calloc(1, sizeof(TreeObject));
437    if (treeObjPtr == NULL) {
438        fprintf(stderr, "can't allocate tree");
439        return NULL;
440    }
441    treeObjPtr->name = Rp_Strdup(treeName);
442    //treeObjPtr->interp = interp;
443    treeObjPtr->valuePool = Rp_PoolCreate(RP_FIXED_SIZE_ITEMS);
444    treeObjPtr->nodePool = Rp_PoolCreate(RP_FIXED_SIZE_ITEMS);
445    treeObjPtr->clients = Rp_ChainCreate();
446    treeObjPtr->depth = 1;
447    treeObjPtr->notifyFlags = 0;
448    Rp_InitHashTableWithPool(&treeObjPtr->nodeTable, RP_ONE_WORD_KEYS);
449
450    hPtr = Rp_CreateHashEntry(&treeObjPtr->nodeTable, (char *)0, &isNew);
451    treeObjPtr->root = NewNode(treeObjPtr, treeName, 0);
452    Rp_SetHashValue(hPtr, treeObjPtr->root);
453
454    // treeObjPtr->tablePtr = &dataPtr->treeTable;
455    // treeObjPtr->hashPtr = Rp_CreateHashEntry(treeObjPtr->tablePtr, treeName,
456    //    &isNew);
457    // Rp_SetHashValue(treeObjPtr->hashPtr, treeObjPtr);
458    treeObjPtr->tablePtr = NULL;
459    treeObjPtr->hashPtr = NULL;
460
461    return treeObjPtr;
462}
463
464/*
465static TreeObject *
466FindTreeInNamespace(
467*/
468//    TreeInterpData *dataPtr,    /* Interpreter-specific data. */
469/*    Tcl_Namespace *nsPtr,
470    CONST char *treeName)
471{
472    Tcl_DString dString;
473    char *name;
474    Rp_HashEntry *hPtr;
475
476    name = Rp_GetQualifiedName(nsPtr, treeName, &dString);
477    hPtr = Rp_FindHashEntry(&dataPtr->treeTable, name);
478    Tcl_DStringFree(&dString);
479    if (hPtr != NULL) {
480        return Rp_GetHashValue(hPtr);
481    }
482    return NULL;
483}
484*/
485
486/*
487 * ----------------------------------------------------------------------
488 *
489 * GetTreeObject --
490 *
491 *  Searches for the tree object associated by the name given.
492 *
493 * Results:
494 *  Returns a pointer to the tree if found, otherwise NULL.
495 *
496 * ----------------------------------------------------------------------
497 */
498//static TreeObject *
499//GetTreeObject(Tcl_Interp *interp, CONST char *name, int flags)
500//{
501//    CONST char *treeName;
502//    Tcl_Namespace *nsPtr;   /* Namespace associated with the tree object.
503//                             * If NULL, indicates to look in first the
504//                             * current namespace and then the global
505//                             * for the tree. */
506//    TreeInterpData *dataPtr;    /* Interpreter-specific data. */
507//    TreeObject *treeObjPtr;
508//
509//    treeObjPtr = NULL;
510//    if (Rp_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
511//        Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
512//            (char *)NULL);
513//        return NULL;
514//    }
515//    dataPtr = GetTreeInterpData(interp);
516//    if (nsPtr != NULL) {
517//        treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
518//    } else {
519//        if (flags & NS_SEARCH_CURRENT) {
520//            /* Look first in the current namespace. */
521//            nsPtr = Tcl_GetCurrentNamespace(interp);
522//            treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
523//        }
524//        if ((treeObjPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
525//            nsPtr = Tcl_GetGlobalNamespace(interp);
526//            treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
527//        }
528//    }
529//    return treeObjPtr;
530//}
531
532/*
533 * ----------------------------------------------------------------------
534 *
535 * TeardownTree --
536 *
537 *  Destroys an entire branch.  This is a special purpose routine
538 *  used to speed up the final clean up of the tree.
539 *
540 * Results:
541 *  None.
542 *
543 * ----------------------------------------------------------------------
544 */
545static void
546TeardownTree(TreeObject *treeObjPtr, Node *nodePtr)
547{
548    if (nodePtr->first != NULL) {
549        Node *childPtr, *nextPtr;
550
551        for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
552            nextPtr = childPtr->next;
553            TeardownTree(treeObjPtr, childPtr);
554        }
555    }
556    if (nodePtr->values != NULL) {
557        TreeDestroyValues(nodePtr);
558    }
559    Rp_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
560}
561
562static void
563DestroyTreeObject(TreeObject *treeObjPtr)
564{
565    Rp_ChainLink *linkPtr;
566    TreeClient *clientPtr;
567
568    treeObjPtr->flags |= TREE_DESTROYED;
569    treeObjPtr->nNodes = 0;
570
571    /* Remove the remaining clients. */
572    for (linkPtr = Rp_ChainFirstLink(treeObjPtr->clients); linkPtr != NULL;
573        linkPtr = Rp_ChainNextLink(linkPtr)) {
574
575        clientPtr = Rp_ChainGetValue(linkPtr);
576        Rp_ChainDestroy(clientPtr->events);
577        Rp_ChainDestroy(clientPtr->traces);
578        Rp_Free(clientPtr);
579    }
580    Rp_ChainDestroy(treeObjPtr->clients);
581
582    TeardownTree(treeObjPtr, treeObjPtr->root);
583    Rp_PoolDestroy(treeObjPtr->nodePool);
584    Rp_PoolDestroy(treeObjPtr->valuePool);
585    Rp_DeleteHashTable(&treeObjPtr->nodeTable);
586
587    if (treeObjPtr->hashPtr != NULL) {
588        /* Remove the entry from the global tree table. */
589        //Rp_DeleteHashEntry(treeObjPtr->tablePtr, treeObjPtr->hashPtr);
590        //if ((treeObjPtr->tablePtr->numEntries == 0) && (keyTableInitialized)) {
591        //    keyTableInitialized = FALSE;
592        //    Rp_DeleteHashTable(&keyTable);
593        //}
594    }
595    if (treeObjPtr->name != NULL) {
596        Rp_Free(treeObjPtr->name);
597    }
598    Rp_Free(treeObjPtr);
599}
600
601/*
602 * -----------------------------------------------------------------------
603 *
604 * TreeInterpDeleteProc --
605 *
606 *  This is called when the interpreter hosting the tree object
607 *  is deleted from the interpreter.
608 *
609 * Results:
610 *  None.
611 *
612 * Side effects:
613 *  Destroys all remaining trees and removes the hash table
614 *  used to register tree names.
615 *
616 * ------------------------------------------------------------------------
617 */
618/* ARGSUSED */
619//static void
620//TreeInterpDeleteProc(
621//    ClientData clientData,  /* Interpreter-specific data. */
622//    )
623//    Tcl_Interp *interp)
624//{
625//    TreeInterpData *dataPtr = clientData;
626//    Rp_HashEntry *hPtr;
627//    Rp_HashSearch cursor;
628//    TreeObject *treeObjPtr;
629//
630//    for (hPtr = Rp_FirstHashEntry(&dataPtr->treeTable, &cursor);
631//        hPtr != NULL; hPtr = Rp_NextHashEntry(&cursor)) {
632//
633//        treeObjPtr = (TreeObject *)Rp_GetHashValue(hPtr);
634//        treeObjPtr->hashPtr = NULL;
635//        DestroyTreeObject(treeObjPtr);
636//    }
637//    if (keyTableInitialized) {
638//        keyTableInitialized = FALSE;
639//        Rp_DeleteHashTable(&keyTable);
640//    }
641//    Rp_DeleteHashTable(&dataPtr->treeTable);
642//    // Tcl_DeleteAssocData(interp, TREE_THREAD_KEY);
643//    Rp_Free(dataPtr);
644//}
645
646/*
647 *----------------------------------------------------------------------
648 *
649 * NotifyIdleProc --
650 *
651 *  Used to invoke event handler routines at some idle point.
652 *  This routine is called from the Tcl event loop.  Errors
653 *  generated by the event handler routines are backgrounded.
654 *
655 * Results:
656 *  None.
657 *
658 *----------------------------------------------------------------------
659 */
660//static void
661//NotifyIdleProc(ClientData clientData)
662//{
663//    EventHandler *notifyPtr = clientData;
664//    int result;
665//
666//    notifyPtr->notifyPending = FALSE;
667//    notifyPtr->mask |= TREE_NOTIFY_ACTIVE;
668//    result = (*notifyPtr->proc)(notifyPtr->clientData, &notifyPtr->event);
669//    notifyPtr->mask &= ~TREE_NOTIFY_ACTIVE;
670//    if (result != TCL_OK) {
671//        Tcl_BackgroundError(notifyPtr->interp);
672//    }
673//}
674
675/*
676 *----------------------------------------------------------------------
677 *
678 * CheckEventHandlers --
679 *
680 *  Traverses the list of client event callbacks and checks
681 *  if one matches the given event.  A client may trigger an
682 *  action that causes the tree to notify it.  The can be
683 *  prevented by setting the TREE_NOTIFY_FOREIGN_ONLY bit in
684 *  the event handler.
685 *
686 *  If a matching handler is found, a callback may be called either
687 *  immediately or at the next idle time depending upon the
688 *  TREE_NOTIFY_WHENIDLE bit.
689 *
690 *  Since a handler routine may trigger yet another call to
691 *  itself, callbacks are ignored while the event handler is
692 *  executing.
693 *
694 * Results:
695 *  None.
696 *
697 *----------------------------------------------------------------------
698 */
699//static void
700//CheckEventHandlers(
701//    TreeClient *clientPtr,
702//    int isSource,       /* Indicates if the client is the source
703//                         * of the event. */
704//    Rp_TreeNotifyEvent *eventPtr)
705//{
706//    Rp_ChainLink *linkPtr, *nextPtr;
707//    EventHandler *notifyPtr;
708//
709//    eventPtr->tree = clientPtr;
710//    for (linkPtr = Rp_ChainFirstLink(clientPtr->events);
711//        linkPtr != NULL; linkPtr = nextPtr) {
712//        nextPtr = Rp_ChainNextLink(linkPtr);
713//        notifyPtr = Rp_ChainGetValue(linkPtr);
714//        if ((notifyPtr->mask & TREE_NOTIFY_ACTIVE) ||
715//            (notifyPtr->mask & eventPtr->type) == 0) {
716//            continue;       /* Ignore callbacks that are generated
717//                             * inside of a notify handler routine. */
718//        }
719//        if ((isSource) && (notifyPtr->mask & TREE_NOTIFY_FOREIGN_ONLY)) {
720//            continue;       /* Don't notify yourself. */
721//        }
722//        if (notifyPtr->mask & TREE_NOTIFY_WHENIDLE) {
723//            if (!notifyPtr->notifyPending) {
724//                notifyPtr->notifyPending = TRUE;
725//                notifyPtr->event = *eventPtr;
726//                Tcl_DoWhenIdle(NotifyIdleProc, notifyPtr);
727//            }
728//        } else {
729//            int result;
730//
731//            notifyPtr->mask |= TREE_NOTIFY_ACTIVE;
732//            result = (*notifyPtr->proc) (notifyPtr->clientData, eventPtr);
733//            notifyPtr->mask &= ~TREE_NOTIFY_ACTIVE;
734//            if (result != TCL_OK) {
735//                Tcl_BackgroundError(notifyPtr->interp);
736//            }
737//        }
738//    }
739//}
740
741/*
742 *----------------------------------------------------------------------
743 *
744 * NotifyClients --
745 *
746 *  Traverses the list of clients for a particular tree and
747 *  notifies each client that an event occurred.  Clients
748 *  indicate interest in a particular event through a bit
749 *  flag.
750 *
751 *----------------------------------------------------------------------
752 */
753//static void
754//NotifyClients(
755//    TreeClient *sourcePtr,
756//    TreeObject *treeObjPtr,
757//    Node *nodePtr,
758//    int eventFlag)
759//{
760//    Rp_ChainLink *linkPtr;
761//    Rp_TreeNotifyEvent event;
762//    TreeClient *clientPtr;
763//    int isSource;
764//
765//    event.type = eventFlag;
766//    event.inode = nodePtr->inode;
767//
768//    /*
769//     * Issue callbacks to each client indicating that a new node has
770//     * been created.
771//     */
772//    for (linkPtr = Rp_ChainFirstLink(treeObjPtr->clients);
773//        linkPtr != NULL; linkPtr = Rp_ChainNextLink(linkPtr)) {
774//        clientPtr = Rp_ChainGetValue(linkPtr);
775//        isSource = (clientPtr == sourcePtr);
776//        CheckEventHandlers(clientPtr, isSource, &event);
777//    }
778//}
779
780static void
781FreeValue(Node *nodePtr, Value *valuePtr)
782{
783    if (valuePtr->objPtr != NULL) {
784        // Tcl_DecrRefCount(valuePtr->objPtr);
785    }
786    Rp_PoolFreeItem(nodePtr->treeObject->valuePool, valuePtr);
787}
788
789
790/* Public Routines */
791/*
792 *----------------------------------------------------------------------
793 *
794 * Rp_TreeGetKey --
795 *
796 *  Given a string, returns a unique identifier for the string.
797 *
798 *----------------------------------------------------------------------
799 */
800Rp_TreeKey
801Rp_TreeGetKey(string)
802    CONST char *string;     /* String to convert. */
803{
804    Rp_HashEntry *hPtr;
805    int isNew;
806
807    if (!keyTableInitialized) {
808        Rp_InitHashTable(&keyTable, RP_STRING_KEYS);
809        keyTableInitialized = 1;
810    }
811    hPtr = Rp_CreateHashEntry(&keyTable, string, &isNew);
812    return (Rp_TreeKey)Rp_GetHashKey(&keyTable, hPtr);
813}
814
815
816/*
817 *----------------------------------------------------------------------
818 *
819 * Rp_TreeCreateNode --
820 *
821 *  Creates a new node in the given parent node.  The name and
822 *  position in the parent are also provided.
823 *
824 *----------------------------------------------------------------------
825 */
826Rp_TreeNode
827Rp_TreeCreateNode(
828    TreeClient *clientPtr,  /* The tree client that is creating
829                             * this node.  If NULL, indicates to
830                             * trigger notify events on behalf of
831                             * the initiating client also. */
832    Node *parentPtr,        /* Parent node where the new node will
833                             * be inserted. */
834    CONST char *name,       /* Name of node. */
835    int pos)                /* Position in the parent's list of children
836                             * where to insert the new node. */
837{
838    Rp_HashEntry *hPtr;
839    Node *beforePtr;
840    Node *nodePtr;  /* Node to be inserted. */
841    TreeObject *treeObjPtr;
842    size_t inode;
843    int isNew;
844
845    treeObjPtr = parentPtr->treeObject;
846
847    /* Generate an unique serial number for this node.  */
848    do {
849        inode = treeObjPtr->nextInode++;
850        hPtr = Rp_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode,
851                   &isNew);
852    } while (!isNew);
853    nodePtr = NewNode(treeObjPtr, name, inode);
854    Rp_SetHashValue(hPtr, nodePtr);
855
856    if ((pos == -1) || (pos >= (int)parentPtr->nChildren)) {
857        beforePtr = NULL;
858    } else {
859        beforePtr = parentPtr->first;
860        while ((pos > 0) && (beforePtr != NULL)) {
861            pos--;
862            beforePtr = beforePtr->next;
863        }
864    }
865    LinkBefore(parentPtr, nodePtr, beforePtr);
866    nodePtr->depth = parentPtr->depth + 1;
867    /*
868     * Issue callbacks to each client indicating that a new node has
869     * been created.
870     */
871    // NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
872    return nodePtr;
873}
874
875/*
876 *----------------------------------------------------------------------
877 *
878 * Rp_TreeCreateNodeWithId --
879 *
880 *  Like Rp_TreeCreateNode, but provides a specific id to use
881 *  for the node.  If the tree already contains a node by that
882 *  id, NULL is returned.
883 *
884 *----------------------------------------------------------------------
885 */
886Rp_TreeNode
887Rp_TreeCreateNodeWithId(
888    TreeClient *clientPtr,
889    Node *parentPtr,        /* Parent node where the new node will
890                             * be inserted. */
891    CONST char *name,       /* Name of node. */
892    size_t inode,           /* Requested id of the new node. If a
893                             * node by this id already exists in the
894                             * tree, no node is created. */
895    int position)           /* Position in the parent's list of children
896                             * where to insert the new node. */
897{
898    Rp_HashEntry *hPtr;
899    Node *beforePtr;
900    Node *nodePtr;  /* Node to be inserted. */
901    TreeObject *treeObjPtr;
902    int isNew;
903
904    treeObjPtr = parentPtr->treeObject;
905    hPtr = Rp_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode, &isNew);
906    if (!isNew) {
907        return NULL;
908    }
909    nodePtr = NewNode(treeObjPtr, name, inode);
910    Rp_SetHashValue(hPtr, nodePtr);
911
912    if ((position == -1) || (position >= (int)parentPtr->nChildren)) {
913        beforePtr = NULL;
914    } else {
915        beforePtr = parentPtr->first;
916        while ((position > 0) && (beforePtr != NULL)) {
917            position--;
918            beforePtr = beforePtr->next;
919        }
920    }
921    LinkBefore(parentPtr, nodePtr, beforePtr);
922    nodePtr->depth = parentPtr->depth + 1;
923    /*
924     * Issue callbacks to each client indicating that a new node has
925     * been created.
926     */
927    // NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
928    return nodePtr;
929}
930
931/*
932 *----------------------------------------------------------------------
933 *
934 * Rp_TreeMoveNode --
935 *
936 *  Move an entry into a new location in the hierarchy.
937 *
938 *----------------------------------------------------------------------
939 */
940/*ARGSUSED*/
941int
942Rp_TreeMoveNode(
943    TreeClient *clientPtr,
944    Node *nodePtr,
945    Node *parentPtr,
946    Node *beforePtr)
947{
948    // TreeObject *treeObjPtr = nodePtr->treeObject;
949    int newDepth;
950
951    if (nodePtr == beforePtr) {
952        return RP_ERROR;
953    }
954    if ((beforePtr != NULL) && (beforePtr->parent != parentPtr)) {
955        return RP_ERROR;
956    }
957    if (nodePtr->parent == NULL) {
958        return RP_ERROR;   /* Can't move root. */
959    }
960    /* Verify that the node isn't an ancestor of the new parent. */
961    if (Rp_TreeIsAncestor(nodePtr, parentPtr)) {
962        return RP_ERROR;
963    }
964    UnlinkNode(nodePtr);
965    /*
966     * Relink the node as a child of the new parent.
967     */
968    LinkBefore(parentPtr, nodePtr, beforePtr);
969    newDepth = parentPtr->depth + 1;
970    if (nodePtr->depth != newDepth) {
971        /* Reset the depths of all descendant nodes. */
972        ResetDepths(nodePtr, newDepth);
973    }
974
975    /*
976     * Issue callbacks to each client indicating that a node has
977     * been moved.
978     */
979    // NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_MOVE);
980    return RP_OK;
981}
982
983int
984Rp_TreeDeleteNode(TreeClient *clientPtr, Node *nodePtr)
985{
986    TreeObject *treeObjPtr = nodePtr->treeObject;
987    Node *childPtr, *nextPtr;
988
989    /* In depth-first order, delete each descendant node. */
990    for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
991        nextPtr = childPtr->next;
992        Rp_TreeDeleteNode(clientPtr, childPtr);
993    }
994    /*
995     * Issue callbacks to each client indicating that the node can
996     * no longer be used.
997     */
998    // NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_DELETE);
999
1000    /* Now remove the actual node. */
1001    FreeNode(treeObjPtr, nodePtr);
1002    return RP_OK;
1003}
1004
1005Rp_TreeNode
1006Rp_TreeGetNode(TreeClient *clientPtr, size_t inode)
1007{
1008    TreeObject *treeObjPtr = clientPtr->treeObject;
1009    Rp_HashEntry *hPtr;
1010
1011    hPtr = Rp_FindHashEntry(&treeObjPtr->nodeTable, (char *)inode);
1012    if (hPtr != NULL) {
1013        return (Rp_TreeNode)Rp_GetHashValue(hPtr);
1014    }
1015    return NULL;
1016}
1017
1018Rp_TreeTrace
1019Rp_TreeCreateTrace(
1020    TreeClient *clientPtr,
1021    Node *nodePtr,
1022    CONST char *keyPattern,
1023    CONST char *tagName,
1024    unsigned int mask,
1025    Rp_TreeTraceProc *proc,
1026    ClientData clientData)
1027{
1028    TraceHandler *tracePtr;
1029
1030    tracePtr = Rp_Calloc(1, sizeof (TraceHandler));
1031    assert(tracePtr);
1032    tracePtr->linkPtr = Rp_ChainAppend(clientPtr->traces, tracePtr);
1033    if (keyPattern != NULL) {
1034        tracePtr->keyPattern = Rp_Strdup(keyPattern);
1035    }
1036    if (tagName != NULL) {
1037        tracePtr->withTag = Rp_Strdup(tagName);
1038    }
1039    tracePtr->clientPtr = clientPtr;
1040    tracePtr->proc = proc;
1041    tracePtr->clientData = clientData;
1042    tracePtr->mask = mask;
1043    tracePtr->nodePtr = nodePtr;
1044    return (Rp_TreeTrace)tracePtr;
1045}
1046
1047void
1048Rp_TreeDeleteTrace(Rp_TreeTrace trace)
1049{
1050    TraceHandler *tracePtr = (TraceHandler *)trace;
1051
1052    Rp_ChainDeleteLink(tracePtr->clientPtr->traces, tracePtr->linkPtr);
1053    if (tracePtr->keyPattern != NULL) {
1054        Rp_Free(tracePtr->keyPattern);
1055    }
1056    if (tracePtr->withTag != NULL) {
1057        Rp_Free(tracePtr->withTag);
1058    }
1059    Rp_Free(tracePtr);
1060}
1061
1062void
1063Rp_TreeRelabelNode(TreeClient *clientPtr, Node *nodePtr, CONST char *string)
1064{
1065    nodePtr->label = Rp_TreeGetKey(string);
1066    /*
1067     * Issue callbacks to each client indicating that a new node has
1068     * been created.
1069     */
1070    // NotifyClients(clientPtr, clientPtr->treeObject, nodePtr,
1071    //               TREE_NOTIFY_RELABEL);
1072}
1073
1074void
1075Rp_TreeRelabelNode2(Node *nodePtr, CONST char *string)
1076{
1077    nodePtr->label = Rp_TreeGetKey(string);
1078}
1079
1080/*
1081 *----------------------------------------------------------------------
1082 *
1083 * Rp_TreeFindChild --
1084 *
1085 *  Searches for the named node in a parent's chain of siblings.
1086 *
1087 *
1088 * Results:
1089 *  If found, the child node is returned, otherwise NULL.
1090 *
1091 *----------------------------------------------------------------------
1092 */
1093Rp_TreeNode
1094Rp_TreeFindChild(Node *parentPtr, CONST char *string)
1095{
1096    Rp_TreeKey label;
1097    register Node *nodePtr;
1098
1099    label = Rp_TreeGetKey(string);
1100    for (nodePtr = parentPtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
1101        if (label == nodePtr->label) {
1102            return nodePtr;
1103        }
1104    }
1105    return NULL;
1106}
1107
1108/*
1109 *----------------------------------------------------------------------
1110 *
1111 * Rp_TreeFindChildNext --
1112 *
1113 *  Searches for the next named node in a parent's chain of siblings.
1114 *
1115 *
1116 * Results:
1117 *  If found, the child node is returned, otherwise NULL.
1118 *
1119 *----------------------------------------------------------------------
1120 */
1121Rp_TreeNode
1122Rp_TreeFindChildNext(Node *childPtr, CONST char *string)
1123{
1124    Rp_TreeKey label;
1125    register Node *nodePtr;
1126
1127    label = Rp_TreeGetKey(string);
1128    for (nodePtr = childPtr->next; nodePtr != NULL; nodePtr = nodePtr->next) {
1129        if (label == nodePtr->label) {
1130            return nodePtr;
1131        }
1132    }
1133    return NULL;
1134}
1135
1136/*
1137 *----------------------------------------------------------------------
1138 *
1139 * Rp_TreeNodePosition --
1140 *
1141 *  Returns the position of the node in its parent's list of
1142 *  children.  The root's position is 0.
1143 *
1144 *----------------------------------------------------------------------
1145 */
1146int
1147Rp_TreeNodePosition(Node *nodePtr)
1148{
1149    Node *parentPtr;
1150    int count;
1151
1152    count = 0;
1153    parentPtr = nodePtr->parent;
1154    if (parentPtr != NULL) {
1155        Node *childPtr;
1156
1157        for (childPtr = parentPtr->first; childPtr != NULL;
1158             childPtr = childPtr->next) {
1159            if (nodePtr == childPtr) {
1160                break;
1161            }
1162            count++;
1163        }
1164    }
1165    return count;
1166}
1167
1168
1169/*
1170 *----------------------------------------------------------------------
1171 *
1172 * Rp_TreePrevNode --
1173 *
1174 *  Returns the "previous" node in the tree.  This node (in
1175 *  depth-first order) is its parent, if the node has no siblings
1176 *  that are previous to it.  Otherwise it is the last descendant
1177 *  of the last sibling.  In this case, descend the sibling's
1178 *  hierarchy, using the last child at any ancestor, with we
1179 *  we find a leaf.
1180 *
1181 *----------------------------------------------------------------------
1182 */
1183Rp_TreeNode
1184Rp_TreePrevNode(Node *rootPtr, Node *nodePtr)
1185{
1186    Node *prevPtr;
1187
1188    if (nodePtr == rootPtr) {
1189        return NULL;        /* The root is the first node. */
1190    }
1191    prevPtr = nodePtr->prev;
1192    if (prevPtr == NULL) {
1193        /* There are no siblings previous to this one, so pick the parent. */
1194        return nodePtr->parent;
1195    }
1196    /*
1197     * Traverse down the right-most thread, in order to select the
1198     * next entry.  Stop when we reach a leaf.
1199     */
1200    nodePtr = prevPtr;
1201    while ((prevPtr = nodePtr->last) != NULL) {
1202        nodePtr = prevPtr;
1203    }
1204    return nodePtr;
1205}
1206
1207/*
1208 *----------------------------------------------------------------------
1209 *
1210 * Rp_TreeNextNode --
1211 *
1212 *  Returns the "next" node in relation to the given node.
1213 *  The next node (in depth-first order) is either the first
1214 *  child of the given node the next sibling if the node has
1215 *  no children (the node is a leaf).  If the given node is the
1216 *  last sibling, then try it's parent next sibling.  Continue
1217 *  until we either find a next sibling for some ancestor or
1218 *  we reach the root node.  In this case the current node is
1219 *  the last node in the tree.
1220 *
1221 *----------------------------------------------------------------------
1222 */
1223Rp_TreeNode
1224Rp_TreeNextNode(Node *rootPtr, Node *nodePtr)
1225{
1226    Node *nextPtr;
1227
1228    /* Pick the first sub-node. */
1229    nextPtr = nodePtr->first;
1230    if (nextPtr != NULL) {
1231        return nextPtr;
1232    }
1233    /*
1234     * Back up until we can find a level where we can pick a
1235     * "next sibling".  For the last entry we'll thread our
1236     * way back to the root.
1237     */
1238    while (nodePtr != rootPtr) {
1239        nextPtr = nodePtr->next;
1240        if (nextPtr != NULL) {
1241            return nextPtr;
1242        }
1243        nodePtr = nodePtr->parent;
1244    }
1245    return NULL;        /* At root, no next node. */
1246}
1247
1248
1249int
1250Rp_TreeIsBefore(Node *n1Ptr, Node *n2Ptr)
1251{
1252    int depth;
1253    register int i;
1254    Node *nodePtr;
1255
1256    if (n1Ptr == n2Ptr) {
1257        return FALSE;
1258    }
1259    depth = MIN(n1Ptr->depth, n2Ptr->depth);
1260    if (depth == 0) {       /* One of the nodes is root. */
1261        return (n1Ptr->parent == NULL);
1262    }
1263    /*
1264     * Traverse back from the deepest node, until both nodes are at
1265     * the same depth.  Check if this ancestor node is the same for
1266     * both nodes.
1267     */
1268    for (i = n1Ptr->depth; i > depth; i--) {
1269        n1Ptr = n1Ptr->parent;
1270    }
1271    if (n1Ptr == n2Ptr) {
1272        return FALSE;
1273    }
1274    for (i = n2Ptr->depth; i > depth; i--) {
1275        n2Ptr = n2Ptr->parent;
1276    }
1277    if (n2Ptr == n1Ptr) {
1278        return TRUE;
1279    }
1280
1281    /*
1282     * First find the mutual ancestor of both nodes.  Look at each
1283     * preceding ancestor level-by-level for both nodes.  Eventually
1284     * we'll find a node that's the parent of both ancestors.  Then
1285     * find the first ancestor in the parent's list of subnodes.
1286     */
1287    for (i = depth; i > 0; i--) {
1288        if (n1Ptr->parent == n2Ptr->parent) {
1289            break;
1290        }
1291        n1Ptr = n1Ptr->parent;
1292        n2Ptr = n2Ptr->parent;
1293    }
1294    for (nodePtr = n1Ptr->parent->first; nodePtr != NULL;
1295         nodePtr = nodePtr->next) {
1296        if (nodePtr == n1Ptr) {
1297            return TRUE;
1298        } else if (nodePtr == n2Ptr) {
1299            return FALSE;
1300        }
1301    }
1302    return FALSE;
1303}
1304
1305//static void
1306//CallTraces(
1307//    Tcl_Interp *interp,
1308//    TreeClient *sourcePtr,  /* Client holding a reference to the
1309//                             * tree.  If NULL, indicates to
1310//                             * execute all handlers, including
1311//                             * those of the caller. */
1312//    TreeObject *treeObjPtr, /* Tree that was changed. */
1313//    Node *nodePtr,          /* Node that received the event. */
1314//    Rp_TreeKey key,
1315//    unsigned int flags)
1316//{
1317//    Rp_ChainLink *l1Ptr, *l2Ptr;
1318//    TreeClient *clientPtr;
1319//    TraceHandler *tracePtr;
1320//
1321//    for(l1Ptr = Rp_ChainFirstLink(treeObjPtr->clients);
1322//        l1Ptr != NULL; l1Ptr = Rp_ChainNextLink(l1Ptr)) {
1323//        clientPtr = Rp_ChainGetValue(l1Ptr);
1324//        for(l2Ptr = Rp_ChainFirstLink(clientPtr->traces);
1325//            l2Ptr != NULL; l2Ptr = Rp_ChainNextLink(l2Ptr)) {
1326//            tracePtr = Rp_ChainGetValue(l2Ptr);
1327//            if ((tracePtr->keyPattern != NULL) &&
1328//                (!Tcl_StringMatch(key, tracePtr->keyPattern))) {
1329//                continue;   /* Key pattern doesn't match. */
1330//            }
1331//            if ((tracePtr->withTag != NULL) &&
1332//                (!Rp_TreeHasTag(clientPtr, nodePtr, tracePtr->withTag))) {
1333//                continue;   /* Doesn't have the tag. */
1334//            }
1335//            if ((tracePtr->mask & flags) == 0) {
1336//                continue;   /* Flags don't match. */
1337//            }
1338//            if ((clientPtr == sourcePtr) &&
1339//                (tracePtr->mask & TREE_TRACE_FOREIGN_ONLY)) {
1340//                continue;   /* This client initiated the trace. */
1341//            }
1342//            if ((tracePtr->nodePtr != NULL) && (tracePtr->nodePtr != nodePtr)) {
1343//                continue;   /* Nodes don't match. */
1344//            }
1345//            nodePtr->flags |= TREE_TRACE_ACTIVE;
1346//            if ((*tracePtr->proc) (tracePtr->clientData, treeObjPtr->interp,
1347//                nodePtr, key, flags) != TCL_OK) {
1348//                if (interp != NULL) {
1349//                    Tcl_BackgroundError(interp);
1350//                }
1351//            }
1352//            nodePtr->flags &= ~TREE_TRACE_ACTIVE;
1353//        }
1354//    }
1355//}
1356
1357static Value *
1358GetTreeValue(
1359    TreeClient *clientPtr,
1360    Node *nodePtr,
1361    Rp_TreeKey key)
1362{
1363    register Value *valuePtr;
1364
1365    valuePtr = TreeFindValue(nodePtr, key);
1366    if (valuePtr == NULL) {
1367        // fprintf(stderr, "can't find field \"%s\"", key);
1368        return NULL;
1369    }
1370    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1371        // fprintf(stderr, "can't access private field \"%s\"", key);
1372        return NULL;
1373    }
1374    return valuePtr;
1375}
1376
1377int
1378Rp_TreePrivateValue(
1379    TreeClient *clientPtr,
1380    Node *nodePtr,
1381    Rp_TreeKey key)
1382{
1383    Value *valuePtr;
1384
1385    valuePtr = TreeFindValue(nodePtr, key);
1386    if (valuePtr == NULL) {
1387        // fprintf(stderr, "can't find field \"%s\"", key);
1388        return RP_ERROR;
1389    }
1390    valuePtr->owner = clientPtr;
1391    return RP_OK;
1392}
1393
1394int
1395Rp_TreePublicValue(
1396    TreeClient *clientPtr,
1397    Node *nodePtr,
1398    Rp_TreeKey key)
1399{
1400    Value *valuePtr;
1401
1402    valuePtr = TreeFindValue(nodePtr, key);
1403    if (valuePtr == NULL) {
1404        // fprintf(stderr, "can't find field \"%s\"", key,);
1405        return RP_ERROR;
1406    }
1407    if (valuePtr->owner != clientPtr) {
1408        // fprintf(interp, "not the owner of \"%s\"", key);
1409        return RP_ERROR;
1410    }
1411    valuePtr->owner = NULL;
1412    return RP_OK;
1413}
1414
1415int
1416Rp_TreeValueExistsByKey(clientPtr, nodePtr, key)
1417    TreeClient *clientPtr;
1418    Node *nodePtr;
1419    Rp_TreeKey key;
1420{
1421    register Value *valuePtr;
1422
1423    valuePtr = GetTreeValue(clientPtr, nodePtr, key);
1424    if (valuePtr == NULL) {
1425        return FALSE;
1426    }
1427    return TRUE;
1428}
1429
1430int
1431Rp_TreeGetValueByKey(
1432    TreeClient *clientPtr,
1433    Node *nodePtr,
1434    Rp_TreeKey key,
1435    void **objPtrPtr)
1436{
1437    register Value *valuePtr;
1438    // TreeObject *treeObjPtr = nodePtr->treeObject;
1439
1440    valuePtr = GetTreeValue(clientPtr, nodePtr, key);
1441    if (valuePtr == NULL) {
1442        return RP_ERROR;
1443    }
1444    *objPtrPtr = valuePtr->objPtr;
1445    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1446//        CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
1447//               TREE_TRACE_READ);
1448    }
1449    return RP_OK;
1450}
1451
1452int
1453Rp_TreeSetValueByKey(
1454    TreeClient *clientPtr,
1455    Node *nodePtr,          /* Node to be updated. */
1456    Rp_TreeKey key,         /* Identifies the field key. */
1457    void *objPtr)           /* New value of field. */
1458{
1459    // TreeObject *treeObjPtr = nodePtr->treeObject;
1460    Value *valuePtr;
1461    unsigned int flags;
1462    int isNew;
1463
1464    assert(objPtr != NULL);
1465    valuePtr = TreeCreateValue(nodePtr, key, &isNew);
1466    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1467//        fprintf(stderr,"can't set private field \"%s\"",key);
1468        return RP_ERROR;
1469    }
1470    if (objPtr != valuePtr->objPtr) {
1471//        Tcl_IncrRefCount(objPtr);
1472//        if (valuePtr->objPtr != NULL) {
1473//            Tcl_DecrRefCount(valuePtr->objPtr);
1474//        }
1475        valuePtr->objPtr = objPtr;
1476    }
1477    flags = TREE_TRACE_WRITE;
1478    if (isNew) {
1479        flags |= TREE_TRACE_CREATE;
1480    }
1481    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1482//        CallTraces(interp, clientPtr, treeObjPtr, nodePtr, valuePtr->key,
1483//                flags);
1484    }
1485    return RP_OK;
1486}
1487
1488int
1489Rp_TreeUnsetValueByKey(
1490    TreeClient *clientPtr,
1491    Node *nodePtr,      /* Node to be updated. */
1492    Rp_TreeKey key)     /* Name of field in node. */
1493{
1494    // TreeObject *treeObjPtr = nodePtr->treeObject;
1495    Value *valuePtr;
1496
1497    valuePtr = TreeFindValue(nodePtr, key);
1498    if (valuePtr == NULL) {
1499        return RP_OK;      /* It's okay to unset values that don't
1500                             * exist in the node. */
1501    }
1502    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1503        // fprintf(stderr, "can't unset private field \"%s\"", key);
1504        return RP_ERROR;
1505    }
1506    TreeDeleteValue(nodePtr, valuePtr);
1507//    CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key, TREE_TRACE_UNSET);
1508    return RP_OK;
1509}
1510
1511/*
1512 * commented out to avoid compiler warnings about function not being used
1513 */
1514/*
1515static int
1516ParseParentheses(
1517    CONST char *string,
1518    char **leftPtr,
1519    char **rightPtr)
1520{
1521    register char *p = NULL;
1522    char *left = NULL;
1523    char *right = NULL;
1524
1525    left = right = NULL;
1526    for (p = (char *)string; *p != '\0'; p++) {
1527        if (*p == '(') {
1528            left = p;
1529        } else if (*p == ')') {
1530            right = p;
1531        }
1532    }
1533    if (left != right) {
1534        if (((left != NULL) && (right == NULL)) ||
1535            ((left == NULL) && (right != NULL)) ||
1536            (left > right) || (right != (p - 1))) {
1537            fprintf(stderr, "bad array specification \"%s\"", string);
1538            return RP_ERROR;
1539        }
1540    }
1541    *leftPtr = left;
1542    *rightPtr = right;
1543    return RP_OK;
1544}
1545*/
1546
1547//FIXME: commented out because it calls Rp_TreeGetArrayValue
1548int
1549Rp_TreeGetValue(
1550    TreeClient *clientPtr,
1551    Node *nodePtr,
1552    CONST char *string,     /* String identifying the field in node. */
1553    void **objPtrPtr)
1554{
1555    char *left = NULL;
1556    char *right = NULL;
1557    int result;
1558
1559// uncomment after Rp_TreeGetArrayValue is fixed
1560//    if (ParseParentheses(string, &left, &right) != TCL_OK) {
1561//        return TCL_ERROR;
1562//    }
1563    if (left != NULL) {
1564        *left = *right = '\0';
1565        // result = Rp_TreeGetArrayValue(clientPtr, nodePtr, string, left + 1, objPtrPtr);
1566        *left = '(', *right = ')';
1567    } else {
1568        result = Rp_TreeGetValueByKey(clientPtr, nodePtr,
1569                    Rp_TreeGetKey(string), objPtrPtr);
1570    }
1571    return result;
1572}
1573
1574//FIXME: commented out because it calls Rp_TreeSetArrayValue
1575int
1576Rp_TreeSetValue(
1577    TreeClient *clientPtr,
1578    Node *nodePtr,          /* Node to be updated. */
1579    CONST char *string,     /* String identifying the field in node. */
1580    void *valueObjPtr)      /* New value of field. If NULL, field
1581                             * is deleted. */
1582{
1583    char *left = NULL;
1584    char *right = NULL;
1585    int result;
1586
1587// uncomment after Rp_TreeSetArrayValue is fixed
1588//    if (ParseParentheses(string, &left, &right) != RP_OK) {
1589//        return RP_ERROR;
1590//    }
1591    if (left != NULL) {
1592        *left = *right = '\0';
1593        //result = Rp_TreeSetArrayValue(clientPtr, nodePtr, string, left + 1, valueObjPtr);
1594        *left = '(', *right = ')';
1595    } else {
1596        result = Rp_TreeSetValueByKey(clientPtr, nodePtr,
1597                    Rp_TreeGetKey(string), valueObjPtr);
1598    }
1599    return result;
1600}
1601
1602//FIXME: commented out because it calls Rp_TreeUnsetArrayValue
1603int
1604Rp_TreeUnsetValue(
1605    TreeClient *clientPtr,
1606    Node *nodePtr,          /* Node to be updated. */
1607    CONST char *string)     /* String identifying the field in node. */
1608{
1609    char *left = NULL;
1610    char *right = NULL;
1611    int result;
1612
1613// uncomment after Rp_TreeUnsetArrayValue is fixed
1614//    if (ParseParentheses(string, &left, &right) != RP_OK) {
1615//        return RP_ERROR;
1616//    }
1617    if (left != NULL) {
1618        *left = *right = '\0';
1619        // result = Rp_TreeUnsetArrayValue(clientPtr, nodePtr, string, left + 1);
1620        *left = '(', *right = ')';
1621    } else {
1622        result = Rp_TreeUnsetValueByKey(clientPtr, nodePtr, Rp_TreeGetKey(string));
1623    }
1624    return result;
1625}
1626
1627//FIXME: commented out because it calls Rp_TreeArrayValueExists
1628int
1629Rp_TreeValueExists(TreeClient *clientPtr, Node *nodePtr, CONST char *string)
1630{
1631    char *left = NULL;
1632    char *right = NULL;
1633    int result;
1634
1635// uncomment after Rp_TreeArrayValueExists is fixed
1636//    if (ParseParentheses(string, &left, &right) != RP_OK) {
1637//        return FALSE;
1638//    }
1639    if (left != NULL) {
1640        *left = *right = '\0';
1641        // result = Rp_TreeArrayValueExists(clientPtr, nodePtr, string, left + 1);
1642        *left = '(', *right = ')';
1643    } else {
1644        result = Rp_TreeValueExistsByKey(clientPtr, nodePtr,
1645                Rp_TreeGetKey(string));
1646    }
1647    return result;
1648}
1649
1650Rp_TreeKey
1651Rp_TreeFirstKey(
1652    TreeClient *clientPtr,
1653    Node *nodePtr,
1654    Rp_TreeKeySearch *iterPtr)
1655{
1656    Value *valuePtr;
1657
1658    valuePtr = TreeFirstValue(nodePtr, iterPtr);
1659    if (valuePtr == NULL) {
1660        return NULL;
1661    }
1662    while ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1663        valuePtr = TreeNextValue(iterPtr);
1664        if (valuePtr == NULL) {
1665            return NULL;
1666        }
1667    }
1668    return valuePtr->key;
1669}
1670
1671Rp_TreeKey
1672Rp_TreeNextKey(TreeClient *clientPtr, Rp_TreeKeySearch *iterPtr)
1673{
1674    Value *valuePtr;
1675
1676    valuePtr = TreeNextValue(iterPtr);
1677    if (valuePtr == NULL) {
1678        return NULL;
1679    }
1680    while ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1681        valuePtr = TreeNextValue(iterPtr);
1682        if (valuePtr == NULL) {
1683            return NULL;
1684        }
1685    }
1686    return valuePtr->key;
1687}
1688
1689int
1690Rp_TreeIsAncestor(Node *n1Ptr, Node *n2Ptr)
1691{
1692    if (n2Ptr != NULL) {
1693        n2Ptr = n2Ptr->parent;
1694        while (n2Ptr != NULL) {
1695            if (n2Ptr == n1Ptr) {
1696                return TRUE;
1697            }
1698            n2Ptr = n2Ptr->parent;
1699        }
1700    }
1701    return FALSE;
1702}
1703
1704/*
1705 *----------------------------------------------------------------------
1706 *
1707 * Rp_TreeSortNode --
1708 *
1709 *  Sorts the subnodes at a given node.
1710 *
1711 * Results:
1712 *  Always returns TCL_OK.
1713 *
1714 *----------------------------------------------------------------------
1715 */
1716int
1717Rp_TreeSortNode(
1718    TreeClient *clientPtr,
1719    Node *nodePtr,
1720    Rp_TreeCompareNodesProc *proc)
1721{
1722    Node **nodeArr;
1723    Node *childPtr;
1724    int nNodes;
1725    register Node **p;
1726
1727    nNodes = nodePtr->nChildren;
1728    if (nNodes < 2) {
1729        return RP_OK;
1730    }
1731    nodeArr = Rp_Malloc((nNodes + 1) * sizeof(Node *));
1732    if (nodeArr == NULL) {
1733        return RP_ERROR;   /* Out of memory. */
1734    }
1735    for (p = nodeArr, childPtr = nodePtr->first; childPtr != NULL;
1736         childPtr = childPtr->next, p++) {
1737        *p = childPtr;
1738    }
1739    *p = NULL;
1740
1741    qsort((char *)nodeArr, nNodes, sizeof(Node *), (QSortCompareProc *)proc);
1742    for (p = nodeArr; *p != NULL; p++) {
1743        UnlinkNode(*p);
1744        LinkBefore(nodePtr, *p, (Rp_TreeNode)NULL);
1745    }
1746    Rp_Free(nodeArr);
1747    // NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_SORT);
1748    return RP_OK;
1749}
1750
1751#define TEST_RESULT(result) \
1752    switch (result) { \
1753    case RP_CONTINUE: \
1754        return RP_OK; \
1755    case RP_OK: \
1756        break; \
1757    default: \
1758        return (result); \
1759    }
1760
1761int
1762Rp_TreeApply(
1763    Node *nodePtr,          /* Root node of subtree. */
1764    Rp_TreeApplyProc *proc, /* Procedure to call for each node. */
1765    ClientData clientData)  /* One-word of data passed when calling
1766                             * proc. */
1767{
1768    Node *childPtr, *nextPtr;
1769    int result;
1770
1771    for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
1772
1773        /*
1774         * Get the next link in the chain before calling Rp_TreeApply
1775         * recursively.  This is because the apply callback may delete
1776         * the node and its link.
1777         */
1778
1779        nextPtr = childPtr->next;
1780        result = Rp_TreeApply(childPtr, proc, clientData);
1781        TEST_RESULT(result);
1782    }
1783    return (*proc) (nodePtr, clientData, TREE_POSTORDER);
1784}
1785
1786int
1787Rp_TreeApplyDFS(
1788    Node *nodePtr,          /* Root node of subtree. */
1789    Rp_TreeApplyProc *proc, /* Procedure to call for each node. */
1790    ClientData clientData,  /* One-word of data passed when calling
1791                             * proc. */
1792    int order)              /* Order of traversal. */
1793{
1794    Node *childPtr, *nextPtr;
1795    int result;
1796
1797    if (order & TREE_PREORDER) {
1798        result = (*proc) (nodePtr, clientData, TREE_PREORDER);
1799        TEST_RESULT(result);
1800    }
1801    childPtr = nodePtr->first;
1802    if (order & TREE_INORDER) {
1803        if (childPtr != NULL) {
1804            result = Rp_TreeApplyDFS(childPtr, proc, clientData, order);
1805            TEST_RESULT(result);
1806            childPtr = childPtr->next;
1807        }
1808        result = (*proc) (nodePtr, clientData, TREE_INORDER);
1809        TEST_RESULT(result);
1810    }
1811    for (/* empty */; childPtr != NULL; childPtr = nextPtr) {
1812        /*
1813         * Get the next link in the chain before calling
1814         * Rp_TreeApply recursively.  This is because the
1815         * apply callback may delete the node and its link.
1816         */
1817        nextPtr = childPtr->next;
1818        result = Rp_TreeApplyDFS(childPtr, proc, clientData, order);
1819        TEST_RESULT(result);
1820    }
1821    if (order & TREE_POSTORDER) {
1822        return (*proc) (nodePtr, clientData, TREE_POSTORDER);
1823    }
1824    return RP_OK;
1825}
1826
1827int
1828Rp_TreeApplyBFS(nodePtr, proc, clientData)
1829    Node *nodePtr;          /* Root node of subtree. */
1830    Rp_TreeApplyProc *proc; /* Procedure to call for each node. */
1831    ClientData clientData;  /* One-word of data passed when calling
1832                             * proc. */
1833{
1834    Rp_Chain *queuePtr;
1835    Rp_ChainLink *linkPtr, *nextPtr;
1836    Node *childPtr;
1837    int result;
1838
1839    queuePtr = Rp_ChainCreate();
1840    linkPtr = Rp_ChainAppend(queuePtr, nodePtr);
1841    while (linkPtr != NULL) {
1842        nodePtr = Rp_ChainGetValue(linkPtr);
1843        /* Add the children to the queue. */
1844        for (childPtr = nodePtr->first; childPtr != NULL;
1845             childPtr = childPtr->next) {
1846            Rp_ChainAppend(queuePtr, childPtr);
1847        }
1848        /* Process the node. */
1849        result = (*proc) (nodePtr, clientData, TREE_BREADTHFIRST);
1850        switch (result) {
1851        case RP_CONTINUE:
1852            Rp_ChainDestroy(queuePtr);
1853            return RP_OK;
1854        case RP_OK:
1855            break;
1856        default:
1857            Rp_ChainDestroy(queuePtr);
1858            return result;
1859        }
1860        /* Remove the node from the queue. */
1861        nextPtr = Rp_ChainNextLink(linkPtr);
1862        Rp_ChainDeleteLink(queuePtr, linkPtr);
1863        linkPtr = nextPtr;
1864    }
1865    Rp_ChainDestroy(queuePtr);
1866    return RP_OK;
1867}
1868
1869static TreeClient *
1870NewTreeClient(TreeObject *treeObjPtr)
1871{
1872    TreeClient *clientPtr;
1873
1874    clientPtr = Rp_Calloc(1, sizeof(TreeClient));
1875    if (clientPtr != NULL) {
1876        Rp_TreeTagTable *tablePtr;
1877
1878        clientPtr->magic = TREE_MAGIC;
1879        clientPtr->linkPtr = Rp_ChainAppend(treeObjPtr->clients, clientPtr);
1880        clientPtr->events = Rp_ChainCreate();
1881        clientPtr->traces = Rp_ChainCreate();
1882        clientPtr->treeObject = treeObjPtr;
1883        clientPtr->root = treeObjPtr->root;
1884        tablePtr = Rp_Malloc(sizeof(Rp_TreeTagTable));
1885        Rp_InitHashTable(&tablePtr->tagTable, RP_STRING_KEYS);
1886        tablePtr->refCount = 1;
1887        clientPtr->tagTablePtr = tablePtr;
1888    }
1889    return clientPtr;
1890}
1891
1892int
1893Rp_TreeCreate(
1894//    Tcl_Interp *interp,         /* Interpreter to report errors back to. */
1895    CONST char *name,           /* Name of tree in namespace.  Tree
1896                                 * must not already exist. */
1897    TreeClient **clientPtrPtr)  /* (out) Client token of newly created
1898                                 * tree.  Releasing the token will
1899                                 * free the tree.  If NULL, no token
1900                                 * is generated. */
1901{
1902//    Tcl_DString dString;
1903//    Tcl_Namespace *nsPtr;
1904//    TreeInterpData *dataPtr;
1905    TreeObject *treeObjPtr;
1906    // CONST char *treeName;
1907//    char string[200];
1908
1909// we do not allow unnamed trees
1910//    // dataPtr = GetTreeInterpData(interp);
1911//    if (name != NULL) {
1912//        // FIXME: because dsk broke it
1913//        /* Check if this tree already exists the current namespace. */
1914//        /*
1915//        treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_CURRENT);
1916//        if (treeObjPtr != NULL) {
1917//            Tcl_AppendResult(interp, "a tree object \"", name,
1918//                             "\" already exists", (char *)NULL);
1919//            return TCL_ERROR;
1920//        }
1921//        */
1922//    } else {
1923//        // FIXME: because dsk broke it
1924//        /* Generate a unique tree name in the current namespace. */
1925//        /*
1926//        do  {
1927//            sprintf(string, "tree%d", dataPtr->nextId++);
1928//        } while (GetTreeObject(interp, name, NS_SEARCH_CURRENT) != NULL);
1929//        */
1930//        sprintf(string, "tree%d", dataPtr->nextId++);
1931//        name = string;
1932//    }
1933    /*
1934     * Tear apart and put back together the namespace-qualified name
1935     * of the tree. This is to ensure that naming is consistent.
1936     */
1937//    treeName = name;
1938//    if (Rp_ParseQualifiedName(name, &nsPtr, &treeName) != RP_OK) {
1939//        Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
1940//                (char *)NULL);
1941//        return TCL_ERROR;
1942//    }
1943//    if (nsPtr == NULL) {
1944//        /*
1945//         * Note: Unlike Tcl_CreateCommand, an unqualified name
1946//         * doesn't imply the global namespace, but the current one.
1947//         */
1948//        nsPtr = Tcl_GetCurrentNamespace(interp);
1949//    }
1950//    name = Rp_GetQualifiedName(nsPtr, treeName, &dString);
1951    // treeObjPtr = NewTreeObject(dataPtr, name);
1952    treeObjPtr = NewTreeObject(name);
1953    if (treeObjPtr == NULL) {
1954        fprintf(stderr, "can't allocate tree \"%s\"", name);
1955//        Tcl_DStringFree(&dString);
1956        return RP_ERROR;
1957    }
1958//    Tcl_DStringFree(&dString);
1959    if (clientPtrPtr != NULL) {
1960        TreeClient *clientPtr;
1961
1962        clientPtr = NewTreeClient(treeObjPtr);
1963        if (clientPtr == NULL) {
1964            fprintf(stderr, "can't allocate tree token");
1965            return RP_ERROR;
1966        }
1967        *clientPtrPtr = clientPtr;
1968    }
1969    return RP_OK;
1970}
1971
1972//int
1973//Rp_TreeGetToken(
1974//    CONST char *name,       /* Name of tree in namespace. */
1975//    TreeClient **clientPtrPtr)
1976//{
1977//    TreeClient *clientPtr;
1978//    TreeObject *treeObjPtr;
1979//
1980//    // FIXME: still need to find the tree where ever we store it.
1981//    // possibly store them in a global hashtable?
1982//    // or remove this function all together
1983//    /* find the tree */
1984//    // treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
1985//    if (treeObjPtr == NULL) {
1986//        // fprintf(stderr, "can't find a tree object \"%s\"", name);
1987//        return RP_ERROR;
1988//    }
1989//    clientPtr = NewTreeClient(treeObjPtr);
1990//    if (clientPtr == NULL) {
1991//        // fprintf(stderr, "can't allocate token for tree \"%s\"", name);
1992//        return RP_ERROR;
1993//    }
1994//    *clientPtrPtr = clientPtr;
1995//    return RP_OK;
1996//}
1997
1998int
1999Rp_TreeGetTokenFromToken(
2000    TreeClient *fromClientPtr, // old client from which to base the new client
2001    TreeClient **clientPtrPtr) // new client
2002{
2003    TreeClient *clientPtr;
2004    TreeObject *treeObjPtr;
2005
2006    if (fromClientPtr == NULL) {
2007        fprintf(stderr, "can't create new token from null token\n");
2008        return RP_ERROR;
2009    }
2010
2011    treeObjPtr = fromClientPtr->treeObject;
2012    if (treeObjPtr == NULL) {
2013        fprintf(stderr, "can't find a tree object based on provided client\n");
2014        return RP_ERROR;
2015    }
2016    clientPtr = NewTreeClient(treeObjPtr);
2017    if (clientPtr == NULL) {
2018        fprintf(stderr, "can't allocate token for tree \"%s\"",
2019            treeObjPtr->name);
2020        return RP_ERROR;
2021    }
2022    *clientPtrPtr = clientPtr;
2023    return RP_OK;
2024}
2025
2026void
2027Rp_TreeReleaseToken(TreeClient *clientPtr)
2028{
2029    TreeObject *treeObjPtr;
2030    Rp_ChainLink *linkPtr;
2031    EventHandler *notifyPtr;
2032    TraceHandler *tracePtr;
2033
2034    if (clientPtr->magic != TREE_MAGIC) {
2035        fprintf(stderr, "invalid tree object token 0x%lx\n",
2036                (unsigned long)clientPtr);
2037        return;
2038    }
2039    /* Remove any traces that may be set. */
2040    for (linkPtr = Rp_ChainFirstLink(clientPtr->traces); linkPtr != NULL;
2041         linkPtr = Rp_ChainNextLink(linkPtr)) {
2042        tracePtr = Rp_ChainGetValue(linkPtr);
2043        if (tracePtr->keyPattern != NULL) {
2044            Rp_Free(tracePtr->keyPattern);
2045        }
2046        Rp_Free(tracePtr);
2047    }
2048    Rp_ChainDestroy(clientPtr->traces);
2049    /* And any event handlers. */
2050    for(linkPtr = Rp_ChainFirstLink(clientPtr->events);
2051        linkPtr != NULL; linkPtr = Rp_ChainNextLink(linkPtr)) {
2052        notifyPtr = Rp_ChainGetValue(linkPtr);
2053        // FIXME: need to cancel any waiting calls
2054        /*
2055        if (notifyPtr->notifyPending) {
2056            Tcl_CancelIdleCall(NotifyIdleProc, notifyPtr);
2057        }
2058        */
2059        Rp_Free(notifyPtr);
2060    }
2061    if (clientPtr->tagTablePtr != NULL) {
2062        ReleaseTagTable(clientPtr->tagTablePtr);
2063    }
2064    Rp_ChainDestroy(clientPtr->events);
2065    treeObjPtr = clientPtr->treeObject;
2066    if (treeObjPtr != NULL) {
2067        /* Remove the client from the server's list */
2068        Rp_ChainDeleteLink(treeObjPtr->clients, clientPtr->linkPtr);
2069        if (Rp_ChainGetLength(treeObjPtr->clients) == 0) {
2070            DestroyTreeObject(treeObjPtr);
2071        }
2072    }
2073    clientPtr->magic = 0;
2074    Rp_Free(clientPtr);
2075}
2076
2077//int
2078//Rp_TreeExists(interp, name)
2079//    Tcl_Interp *interp;     /* Interpreter to report errors back to. */
2080//    CONST char *name;       /* Name of tree in designated namespace. */
2081//{
2082//    TreeObject *treeObjPtr;
2083//
2084//    treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
2085//    if (treeObjPtr == NULL) {
2086//        Tcl_ResetResult(interp);
2087//        return 0;
2088//    }
2089//    return 1;
2090//}
2091
2092/*ARGSUSED*/
2093static int
2094SizeApplyProc(
2095    Node *nodePtr,      /* Not used. */
2096    ClientData clientData,
2097    int order)          /* Not used. */
2098{
2099    int *sumPtr = clientData;
2100    *sumPtr = *sumPtr + 1;
2101    return RP_OK;
2102}
2103
2104int
2105Rp_TreeSize(Node *nodePtr)
2106{
2107    int sum;
2108
2109    sum = 0;
2110    Rp_TreeApply(nodePtr, SizeApplyProc, &sum);
2111    return sum;
2112}
2113
2114
2115void
2116Rp_TreeCreateEventHandler(
2117    TreeClient *clientPtr,
2118    unsigned int mask,
2119    Rp_TreeNotifyEventProc *proc,
2120    ClientData clientData)
2121{
2122    Rp_ChainLink *linkPtr;
2123    EventHandler *notifyPtr;
2124
2125    notifyPtr = NULL;       /* Suppress compiler warning. */
2126
2127    /* Check if the event is already handled. */
2128    for(linkPtr = Rp_ChainFirstLink(clientPtr->events);
2129        linkPtr != NULL; linkPtr = Rp_ChainNextLink(linkPtr)) {
2130        notifyPtr = Rp_ChainGetValue(linkPtr);
2131        if ((notifyPtr->proc == proc) &&
2132            (notifyPtr->mask == mask) &&
2133            (notifyPtr->clientData == clientData)) {
2134            break;
2135        }
2136    }
2137    if (linkPtr == NULL) {
2138        notifyPtr = Rp_Malloc(sizeof (EventHandler));
2139        assert(notifyPtr);
2140        linkPtr = Rp_ChainAppend(clientPtr->events, notifyPtr);
2141    }
2142    if (proc == NULL) {
2143        Rp_ChainDeleteLink(clientPtr->events, linkPtr);
2144        Rp_Free(notifyPtr);
2145    } else {
2146        notifyPtr->proc = proc;
2147        notifyPtr->clientData = clientData;
2148        notifyPtr->mask = mask;
2149        notifyPtr->notifyPending = FALSE;
2150        // notifyPtr->interp = clientPtr->treeObject->interp;
2151    }
2152}
2153
2154void
2155Rp_TreeDeleteEventHandler(
2156    TreeClient *clientPtr,
2157    unsigned int mask,
2158    Rp_TreeNotifyEventProc *proc,
2159    ClientData clientData)
2160{
2161    Rp_ChainLink *linkPtr;
2162    EventHandler *notifyPtr;
2163
2164    for(linkPtr = Rp_ChainFirstLink(clientPtr->events);
2165        linkPtr != NULL; linkPtr = Rp_ChainNextLink(linkPtr)) {
2166        notifyPtr = Rp_ChainGetValue(linkPtr);
2167        if ((notifyPtr->proc == proc) && (notifyPtr->mask == mask) &&
2168            (notifyPtr->clientData == clientData)) {
2169            // FIXME: Cancel waiting calls
2170            /*
2171            if (notifyPtr->notifyPending) {
2172                Tcl_CancelIdleCall(NotifyIdleProc, notifyPtr);
2173            }
2174            */
2175            Rp_ChainDeleteLink(clientPtr->events, linkPtr);
2176            Rp_Free(notifyPtr);
2177            return;
2178        }
2179    }
2180}
2181
2182// FIXME: rewrite this function to use a RpSimpleCharBuffer
2183/*
2184 *----------------------------------------------------------------------
2185 *
2186 * Rp_TreeNodePath --
2187 *
2188 *----------------------------------------------------------------------
2189 */
2190//char *
2191//Rp_TreeNodePath(Node *nodePtr, Tcl_DString *resultPtr)
2192//{
2193//    char **nameArr;     /* Used to stack the component names. */
2194//    char *staticSpace[64];
2195//    int nLevels;
2196//    register int i;
2197//
2198//    nLevels = nodePtr->depth;
2199//    if (nLevels > 64) {
2200//        nameArr = Rp_Malloc(nLevels * sizeof(char *));
2201//        assert(nameArr);
2202//    } else {
2203//        nameArr = staticSpace;
2204//    }
2205//    for (i = nLevels - 1; i >= 0; i--) {
2206//        /* Save the name of each ancestor in the name array.
2207//         * Note that we ignore the root. */
2208//        nameArr[i] = nodePtr->label;
2209//        nodePtr = nodePtr->parent;
2210//    }
2211//    // FIXME: add substitute for TCL DSTRING
2212//    /* Append each the names in the array. */
2213//    /*
2214//    Tcl_DStringInit(resultPtr);
2215//    for (i = 0; i < nLevels; i++) {
2216//        Tcl_DStringAppendElement(resultPtr, nameArr[i]);
2217//    }
2218//    if (nameArr != staticSpace) {
2219//        Rp_Free(nameArr);
2220//    }
2221//    return Tcl_DStringValue(resultPtr);
2222//    */
2223//
2224//    return NULL;
2225//}
2226
2227//int
2228//Rp_TreeArrayValueExists(
2229//    TreeClient *clientPtr,
2230//    Node *nodePtr,
2231//    CONST char *arrayName,
2232//    CONST char *elemName)
2233//{
2234//    Rp_TreeKey key;
2235//    Rp_HashEntry *hPtr;
2236//    Rp_HashTable *tablePtr;
2237//    register Value *valuePtr;
2238//
2239//    key = Rp_TreeGetKey(arrayName);
2240//    valuePtr = GetTreeValue(nodePtr, key);
2241//    if (valuePtr == NULL) {
2242//        return FALSE;
2243//    }
2244//    // FIXME: fix object count
2245//    /*
2246//    if (Tcl_IsShared(valuePtr->objPtr)) {
2247//        Tcl_DecrRefCount(valuePtr->objPtr);
2248//        valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2249//        Tcl_IncrRefCount(valuePtr->objPtr);
2250//    }
2251//    */
2252//    if (Rp_GetArrayFromObj(valuePtr->objPtr, &tablePtr) != RP_OK) {
2253//        return FALSE;
2254//    }
2255//    hPtr = Rp_FindHashEntry(tablePtr, elemName);
2256//    return (hPtr != NULL);
2257//}
2258//
2259//int
2260//Rp_TreeGetArrayValue(
2261//    TreeClient *clientPtr,
2262//    Node *nodePtr,
2263//    CONST char *arrayName,
2264//    CONST char *elemName,
2265//    void **valueObjPtrPtr)
2266//{
2267//    Rp_TreeKey key;
2268//    Rp_HashEntry *hPtr;
2269//    Rp_HashTable *tablePtr;
2270//    register Value *valuePtr;
2271//
2272//    key = Rp_TreeGetKey(arrayName);
2273//    valuePtr = GetTreeValue(nodePtr, key);
2274//    if (valuePtr == NULL) {
2275//        return RP_ERROR;
2276//    }
2277//    // FIXME: fix object count
2278//    /*
2279//    if (Tcl_IsShared(valuePtr->objPtr)) {
2280//        Tcl_DecrRefCount(valuePtr->objPtr);
2281//        valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2282//        Tcl_IncrRefCount(valuePtr->objPtr);
2283//    }
2284//    */
2285//    if (Rp_GetArrayFromObj(valuePtr->objPtr, &tablePtr) != RP_OK) {
2286//        return RP_ERROR;
2287//    }
2288//    hPtr = Rp_FindHashEntry(tablePtr, elemName);
2289//    if (hPtr == NULL) {
2290//        fprintf(stderr, "can't find \"%s(%s)\"", arrayName, elemName);
2291//        return RP_ERROR;
2292//    }
2293//    *valueObjPtrPtr = (void *)Rp_GetHashValue(hPtr);
2294//
2295//    /* Reading any element of the array can cause a trace to fire. */
2296//    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2297//        // CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr, key,
2298//        //            TREE_TRACE_READ);
2299//    }
2300//    return RP_OK;
2301//}
2302//
2303//int
2304//Rp_TreeSetArrayValue(
2305//    TreeClient *clientPtr,
2306//    Node *nodePtr,      /* Node to be updated. */
2307//    CONST char *arrayName,
2308//    CONST char *elemName,
2309//    Tcl_Obj *valueObjPtr)   /* New value of element. */
2310//{
2311//    Rp_TreeKey key;
2312//    Rp_HashEntry *hPtr;
2313//    Rp_HashTable *tablePtr;
2314//    register Value *valuePtr;
2315//    unsigned int flags;
2316//    int isNew;
2317//
2318//    assert(valueObjPtr != NULL);
2319//
2320//    /*
2321//     * Search for the array in the list of data fields.  If one
2322//     * doesn't exist, create it.
2323//     */
2324//    key = Rp_TreeGetKey(arrayName);
2325//    valuePtr = TreeCreateValue(nodePtr, key, &isNew);
2326//    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
2327//        fprintf(stderr, "can't set private field \"%s\"", key);
2328//        return RP_ERROR;
2329//    }
2330//    flags = TREE_TRACE_WRITE;
2331//    if (isNew) {
2332//        // FIXME: where is this function located?
2333//        //valuePtr->objPtr = Rp_NewArrayObj(0, (Tcl_Obj **)NULL);
2334//        // FIXME: increment ref counts
2335//        //Tcl_IncrRefCount(valuePtr->objPtr);
2336//        flags |= TREE_TRACE_CREATE;
2337//    } else if (Tcl_IsShared(valuePtr->objPtr)) {
2338//        // FIXME: ref counts and duplicateobj
2339//        //Tcl_DecrRefCount(valuePtr->objPtr);
2340//        //valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2341//        //Tcl_IncrRefCount(valuePtr->objPtr);
2342//    }
2343//    if (Rp_GetArrayFromObj(valuePtr->objPtr, &tablePtr) != RP_OK) {
2344//        return RP_ERROR;
2345//    }
2346//    // Tcl_InvalidateStringRep(valuePtr->objPtr);
2347//    hPtr = Rp_CreateHashEntry(tablePtr, elemName, &isNew);
2348//    assert(hPtr);
2349//
2350//    // FIXME: increment ref counts
2351////    Tcl_IncrRefCount(valueObjPtr);
2352////    if (!isNew) {
2353////        Tcl_Obj *oldValueObjPtr;
2354////
2355////        /* An element by the same name already exists. Decrement the
2356////         * reference count of the old value. */
2357////
2358////        oldValueObjPtr = (Tcl_Obj *)Rp_GetHashValue(hPtr);
2359////        if (oldValueObjPtr != NULL) {
2360////            Tcl_DecrRefCount(oldValueObjPtr);
2361////        }
2362////    }
2363//    Rp_SetHashValue(hPtr, valueObjPtr);
2364//
2365//    /*
2366//     * We don't handle traces on a per array element basis.  Setting
2367//     * any element can fire traces for the value.
2368//     */
2369//    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2370////        CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
2371////                valuePtr->key, flags);
2372//    }
2373//    return RP_OK;
2374//}
2375//
2376//int
2377//Rp_TreeUnsetArrayValue(
2378//    TreeClient *clientPtr,
2379//    Node *nodePtr,      /* Node to be updated. */
2380//    CONST char *arrayName,
2381//    CONST char *elemName)
2382//{
2383//    Rp_TreeKey key;     /* Name of field in node. */
2384//    Rp_HashEntry *hPtr;
2385//    Rp_HashTable *tablePtr;
2386//    Tcl_Obj *valueObjPtr;
2387//    Value *valuePtr;
2388//
2389//    key = Rp_TreeGetKey(arrayName);
2390//    valuePtr = TreeFindValue(nodePtr, key);
2391//    if (valuePtr == NULL) {
2392//        return RP_OK;
2393//    }
2394//    if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
2395//        fprintf(stderr, "can't unset private field \"%s\"", key);
2396//        return RP_ERROR;
2397//    }
2398//    // FIXME: increment ref counts
2399//    /*
2400//    if (Tcl_IsShared(valuePtr->objPtr)) {
2401//        Tcl_DecrRefCount(valuePtr->objPtr);
2402//        valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2403//        Tcl_IncrRefCount(valuePtr->objPtr);
2404//    }
2405//    */
2406//    if (Rp_GetArrayFromObj(valuePtr->objPtr, &tablePtr) != RP_OK) {
2407//        return RP_ERROR;
2408//    }
2409//    hPtr = Rp_FindHashEntry(tablePtr, elemName);
2410//    if (hPtr == NULL) {
2411//        return RP_OK;      /* Element doesn't exist, Ok. */
2412//    }
2413//    // FIXME: increment ref counts
2414//    //valueObjPtr = (Tcl_Obj *)Rp_GetHashValue(hPtr);
2415//    //Tcl_DecrRefCount(valueObjPtr);
2416//    Rp_DeleteHashEntry(tablePtr, hPtr);
2417//
2418//    /*
2419//     * Un-setting any element in the array can cause the trace on the value
2420//     * to fire.
2421//     */
2422//    if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2423////        CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
2424////                valuePtr->key, TREE_TRACE_WRITE);
2425//    }
2426//    return RP_OK;
2427//}
2428//
2429//int
2430//Rp_TreeArrayNames(
2431//    TreeClient *clientPtr,
2432//    Node *nodePtr,
2433//    CONST char *arrayName,
2434//    Tcl_Obj *listObjPtr)
2435//{
2436//    Rp_HashEntry *hPtr;
2437//    Rp_HashSearch cursor;
2438//    Rp_HashTable *tablePtr;
2439//    Tcl_Obj *objPtr;
2440//    Value *valuePtr;
2441//    char *key;
2442//
2443//    key = Rp_TreeGetKey(arrayName);
2444//    valuePtr = GetTreeValue(nodePtr, key);
2445//    if (valuePtr == NULL) {
2446//        return TCL_ERROR;
2447//    }
2448//    // FIXME: increment ref counts
2449//    /*
2450//    if (Tcl_IsShared(valuePtr->objPtr)) {
2451//        Tcl_DecrRefCount(valuePtr->objPtr);
2452//        valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2453//        Tcl_IncrRefCount(valuePtr->objPtr);
2454//    }
2455//    */
2456//    if (Rp_GetArrayFromObj(valuePtr->objPtr, &tablePtr) != RP_OK) {
2457//        return RP_ERROR;
2458//    }
2459//    tablePtr = (Rp_HashTable *)valuePtr->objPtr;
2460//    for (hPtr = Rp_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
2461//         hPtr = Rp_NextHashEntry(&cursor)) {
2462//    // FIXME: place names in array
2463//    //    objPtr = Tcl_NewStringObj(Rp_GetHashKey(tablePtr, hPtr), -1);
2464//    //    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2465//    }
2466//    return RP_OK;
2467//}
2468//
2469/*
2470 *----------------------------------------------------------------------
2471 *
2472 * Rp_TreeShareTagTable --
2473 *
2474 *----------------------------------------------------------------------
2475 */
2476int
2477Rp_TreeShareTagTable(
2478    TreeClient *sourcePtr,
2479    TreeClient *targetPtr)
2480{
2481    sourcePtr->tagTablePtr->refCount++;
2482    if (targetPtr->tagTablePtr != NULL) {
2483        ReleaseTagTable(targetPtr->tagTablePtr);
2484    }
2485    targetPtr->tagTablePtr = sourcePtr->tagTablePtr;
2486    return RP_OK;
2487}
2488
2489int
2490Rp_TreeTagTableIsShared(TreeClient *clientPtr)
2491{
2492    return (clientPtr->tagTablePtr->refCount > 1);
2493}
2494
2495void
2496Rp_TreeClearTags(TreeClient *clientPtr, Rp_TreeNode node)
2497{
2498    Rp_HashEntry *hPtr, *h2Ptr;
2499    Rp_HashSearch cursor;
2500
2501    for (hPtr = Rp_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, &cursor);
2502        hPtr != NULL; hPtr = Rp_NextHashEntry(&cursor)) {
2503        Rp_TreeTagEntry *tPtr;
2504
2505        tPtr = Rp_GetHashValue(hPtr);
2506        h2Ptr = Rp_FindHashEntry(&tPtr->nodeTable, (char *)node);
2507        if (h2Ptr != NULL) {
2508            Rp_DeleteHashEntry(&tPtr->nodeTable, h2Ptr);
2509        }
2510    }
2511}
2512
2513int
2514Rp_TreeHasTag(
2515    TreeClient *clientPtr,
2516    Rp_TreeNode node,
2517    CONST char *tagName)
2518{
2519    Rp_HashEntry *hPtr;
2520    Rp_TreeTagEntry *tPtr;
2521
2522    if (strcmp(tagName, "all") == 0) {
2523        return TRUE;
2524    }
2525    if ((strcmp(tagName, "root") == 0) &&
2526        (node == Rp_TreeRootNode(clientPtr))) {
2527        return TRUE;
2528    }
2529    hPtr = Rp_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
2530    if (hPtr == NULL) {
2531        return FALSE;
2532    }
2533    tPtr = Rp_GetHashValue(hPtr);
2534    hPtr = Rp_FindHashEntry(&tPtr->nodeTable, (char *)node);
2535    if (hPtr == NULL) {
2536        return FALSE;
2537    }
2538    return TRUE;
2539}
2540
2541void
2542Rp_TreeAddTag(
2543    TreeClient *clientPtr,
2544    Rp_TreeNode node,
2545    CONST char *tagName)
2546{
2547    int isNew;
2548    Rp_HashEntry *hPtr;
2549    Rp_HashTable *tablePtr;
2550    Rp_TreeTagEntry *tPtr;
2551
2552    if ((strcmp(tagName, "all") == 0) || (strcmp(tagName, "root") == 0)) {
2553        return;
2554    }
2555    tablePtr = &clientPtr->tagTablePtr->tagTable;
2556    hPtr = Rp_CreateHashEntry(tablePtr, tagName, &isNew);
2557    assert(hPtr);
2558    if (isNew) {
2559
2560        tPtr = Rp_Malloc(sizeof(Rp_TreeTagEntry));
2561        Rp_InitHashTable(&tPtr->nodeTable, RP_ONE_WORD_KEYS);
2562        Rp_SetHashValue(hPtr, tPtr);
2563        tPtr->hashPtr = hPtr;
2564        tPtr->tagName = Rp_GetHashKey(tablePtr, hPtr);
2565    } else {
2566        tPtr = Rp_GetHashValue(hPtr);
2567    }
2568    hPtr = Rp_CreateHashEntry(&tPtr->nodeTable, (char *)node, &isNew);
2569    assert(hPtr);
2570    if (isNew) {
2571        Rp_SetHashValue(hPtr, node);
2572    }
2573}
2574
2575void
2576Rp_TreeForgetTag(TreeClient *clientPtr, CONST char *tagName)
2577{
2578    if ((strcmp(tagName, "all") != 0) && (strcmp(tagName, "root") != 0)) {
2579        Rp_HashEntry *hPtr;
2580
2581        hPtr = Rp_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
2582        if (hPtr != NULL) {
2583            Rp_TreeTagEntry *tPtr;
2584
2585            Rp_DeleteHashEntry(&clientPtr->tagTablePtr->tagTable, hPtr);
2586            tPtr = Rp_GetHashValue(hPtr);
2587            Rp_DeleteHashTable(&tPtr->nodeTable);
2588            Rp_Free(tPtr);
2589        }
2590    }
2591}
2592
2593/*
2594 *----------------------------------------------------------------------
2595 *
2596 * Rp_TreeTagHashTable --
2597 *
2598 *----------------------------------------------------------------------
2599 */
2600Rp_HashTable *
2601Rp_TreeTagHashTable(TreeClient *clientPtr, CONST char *tagName)
2602{
2603    Rp_HashEntry *hPtr;
2604
2605    hPtr = Rp_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
2606    if (hPtr != NULL) {
2607        Rp_TreeTagEntry *tPtr;
2608
2609        tPtr = Rp_GetHashValue(hPtr);
2610        return &tPtr->nodeTable;
2611    }
2612    return NULL;
2613}
2614
2615Rp_HashEntry *
2616Rp_TreeFirstTag(TreeClient *clientPtr, Rp_HashSearch *cursorPtr)
2617{
2618    return Rp_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, cursorPtr);
2619}
2620
2621#if (SIZEOF_VOID_P == 8)
2622/*
2623 *----------------------------------------------------------------------
2624 *
2625 * HashOneWord --
2626 *
2627 *  Compute a one-word hash value of a 64-bit word, which then can
2628 *  be used to generate a hash index.
2629 *
2630 *  From Knuth, it's a multiplicative hash.  Multiplies an unsigned
2631 *  64-bit value with the golden ratio (sqrt(5) - 1) / 2.  The
2632 *  downshift value is 64 - n, when n is the log2 of the size of
2633 *  the hash table.
2634 *
2635 * Results:
2636 *  The return value is a one-word summary of the information in
2637 *  64 bit word.
2638 *
2639 * Side effects:
2640 *  None.
2641 *
2642 *----------------------------------------------------------------------
2643 */
2644static Rp_Hash
2645HashOneWord(
2646    uint64_t mask,
2647    unsigned int downshift,
2648    CONST void *key)
2649{
2650    uint64_t a0, a1;
2651    uint64_t y0, y1;
2652    uint64_t y2, y3;
2653    uint64_t p1, p2;
2654    uint64_t result;
2655
2656    /* Compute key * GOLDEN_RATIO in 128-bit arithmetic */
2657    a0 = (uint64_t)key & 0x00000000FFFFFFFF;
2658    a1 = (uint64_t)key >> 32;
2659
2660    y0 = a0 * 0x000000007f4a7c13;
2661    y1 = a0 * 0x000000009e3779b9;
2662    y2 = a1 * 0x000000007f4a7c13;
2663    y3 = a1 * 0x000000009e3779b9;
2664    y1 += y0 >> 32;     /* Can't carry */
2665    y1 += y2;           /* Might carry */
2666    if (y1 < y2) {
2667        y3 += (1LL << 32);  /* Propagate */
2668    }
2669
2670    /* 128-bit product: p1 = loword, p2 = hiword */
2671    p1 = ((y1 & 0x00000000FFFFFFFF) << 32) + (y0 & 0x00000000FFFFFFFF);
2672    p2 = y3 + (y1 >> 32);
2673
2674    /* Left shift the value downward by the size of the table */
2675    if (downshift > 0) {
2676        if (downshift < 64) {
2677            result = ((p2 << (64 - downshift)) | (p1 >> (downshift & 63)));
2678        } else {
2679            result = p2 >> (downshift & 63);
2680        }
2681    } else {
2682        result = p1;
2683    }
2684    /* Finally mask off the high bits */
2685    return (Rp_Hash)(result & mask);
2686}
2687
2688#endif /* SIZEOF_VOID_P == 8 */
2689
2690/*
2691 *----------------------------------------------------------------------
2692 *
2693 * RebuildTable --
2694 *
2695 *  This procedure is invoked when the ratio of entries to hash
2696 *  buckets becomes too large.  It creates a new table with a
2697 *  larger bucket array and moves all of the entries into the
2698 *  new table.
2699 *
2700 * Results:
2701 *  None.
2702 *
2703 * Side effects:
2704 *  Memory gets reallocated and entries get re-hashed to new
2705 *  buckets.
2706 *
2707 *----------------------------------------------------------------------
2708 */
2709static void
2710RebuildTable(Node *nodePtr)     /* Table to enlarge. */
2711{
2712    Value **newBucketPtr, **oldBuckets;
2713    register Value **bucketPtr, **endPtr;
2714    register Value *valuePtr, *nextPtr;
2715    unsigned int downshift;
2716    unsigned long mask;
2717    Value **buckets;
2718    size_t nBuckets;
2719
2720    oldBuckets = (Value **)nodePtr->values;
2721    nBuckets = (1 << nodePtr->logSize);
2722    endPtr = oldBuckets + nBuckets;
2723
2724    /*
2725     * Allocate and initialize the new bucket array, and set up
2726     * hashing constants for new array size.
2727     */
2728    nodePtr->logSize += 2;
2729    nBuckets = (1 << nodePtr->logSize);
2730    buckets = Rp_Calloc(nBuckets, sizeof(Value *));
2731
2732    /*
2733     * Move all of the existing entries into the new bucket array,
2734     * based on their hash values.
2735     */
2736    mask = nBuckets - 1;
2737    downshift = DOWNSHIFT_START - nodePtr->logSize;
2738    for (bucketPtr = oldBuckets; bucketPtr < endPtr; bucketPtr++) {
2739        for (valuePtr = *bucketPtr; valuePtr != NULL; valuePtr = nextPtr) {
2740            nextPtr = valuePtr->next;
2741            newBucketPtr = buckets + RANDOM_INDEX(valuePtr->key);
2742            valuePtr->next = *newBucketPtr;
2743            *newBucketPtr = valuePtr;
2744        }
2745    }
2746    nodePtr->values = (Value *)buckets;
2747    Rp_Free(oldBuckets);
2748}
2749
2750static void
2751ConvertValues(Node *nodePtr)
2752{
2753    unsigned int nBuckets;
2754    Value **buckets;
2755    unsigned int mask;
2756    int downshift;
2757    Value *valuePtr, *nextPtr, **bucketPtr;
2758
2759    /*
2760     * Convert list of values into a hash table.
2761     */
2762    nodePtr->logSize = START_LOGSIZE;
2763    nBuckets = 1 << nodePtr->logSize;
2764    buckets = Rp_Calloc(nBuckets, sizeof(Value *));
2765    mask = nBuckets - 1;
2766    downshift = DOWNSHIFT_START - nodePtr->logSize;
2767    for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
2768        nextPtr = valuePtr->next;
2769        bucketPtr = buckets + RANDOM_INDEX(valuePtr->key);
2770        valuePtr->next = *bucketPtr;
2771        *bucketPtr = valuePtr;
2772    }
2773    nodePtr->values = (Value *)buckets;
2774}
2775
2776/*
2777 *----------------------------------------------------------------------
2778 *
2779 * TreeDeleteValue --
2780 *
2781 *  Remove a single entry from a hash table.
2782 *
2783 * Results:
2784 *  None.
2785 *
2786 * Side effects:
2787 *  The entry given by entryPtr is deleted from its table and
2788 *  should never again be used by the caller.  It is up to the
2789 *  caller to free the clientData field of the entry, if that
2790 *  is relevant.
2791 *
2792 *----------------------------------------------------------------------
2793 */
2794static int
2795TreeDeleteValue(Node *nodePtr, Rp_TreeValue value)
2796{
2797    Value *valuePtr = value;
2798    register Value *prevPtr;
2799
2800    if (nodePtr->logSize > 0) {
2801        Value **bucketPtr;
2802        unsigned int downshift;
2803        unsigned long mask;
2804
2805        mask = (1 << nodePtr->logSize) - 1;
2806        downshift = DOWNSHIFT_START - nodePtr->logSize;
2807
2808        bucketPtr = (Value **)nodePtr->values + RANDOM_INDEX(valuePtr->key);
2809        if (*bucketPtr == valuePtr) {
2810            *bucketPtr = valuePtr->next;
2811        } else {
2812            for (prevPtr = *bucketPtr; /*empty*/; prevPtr = prevPtr->next) {
2813                if (prevPtr == NULL) {
2814                    return RP_ERROR; /* Can't find value in hash bucket. */
2815                }
2816                if (prevPtr->next == valuePtr) {
2817                    prevPtr->next = valuePtr->next;
2818                    break;
2819                }
2820            }
2821        }
2822    } else {
2823        prevPtr = NULL;
2824        for (valuePtr = nodePtr->values; valuePtr != NULL;
2825             valuePtr = valuePtr->next) {
2826            if (valuePtr == value) {
2827                break;
2828            }
2829            prevPtr = valuePtr;
2830        }
2831        if (valuePtr == NULL) {
2832            return RP_ERROR;   /* Can't find value in list. */
2833        }
2834        if (prevPtr == NULL) {
2835            nodePtr->values = valuePtr->next;
2836        } else {
2837            prevPtr->next = valuePtr->next;
2838        }
2839    }
2840    nodePtr->nValues--;
2841    FreeValue(nodePtr, valuePtr);
2842    return RP_OK;
2843}
2844
2845/*
2846 *----------------------------------------------------------------------
2847 *
2848 * TreeDestroyValues --
2849 *
2850 *  Free up everything associated with a hash table except for
2851 *  the record for the table itself.
2852 *
2853 * Results:
2854 *  None.
2855 *
2856 * Side effects:
2857 *  The hash table is no longer useable.
2858 *
2859 *----------------------------------------------------------------------
2860 */
2861static void
2862TreeDestroyValues(Node *nodePtr)
2863{
2864    register Value *valuePtr;
2865    Value *nextPtr;
2866
2867    /*
2868     * Free up all the entries in the table.
2869     */
2870    if (nodePtr->values != NULL) {
2871        return;
2872    }
2873    if (nodePtr->logSize > 0) {
2874        Value **buckets;
2875        int nBuckets;
2876        int i;
2877
2878        buckets = (Value **)nodePtr->values;
2879        nBuckets = (1 << nodePtr->logSize);
2880        for (i = 0; i < nBuckets; i++) {
2881            for (valuePtr = buckets[i]; valuePtr != NULL; valuePtr = nextPtr) {
2882                nextPtr = valuePtr->next;
2883                FreeValue(nodePtr, valuePtr);
2884            }
2885        }
2886        Rp_Free(buckets);
2887    } else {
2888        for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
2889            nextPtr = valuePtr->next;
2890            FreeValue(nodePtr, valuePtr);
2891        }
2892    }
2893    nodePtr->values = NULL;
2894    nodePtr->nValues = 0;
2895    nodePtr->logSize = 0;
2896}
2897
2898/*
2899 *----------------------------------------------------------------------
2900 *
2901 * TreeFirstValue --
2902 *
2903 *  Locate the first entry in a hash table and set up a record
2904 *  that can be used to step through all the remaining entries
2905 *  of the table.
2906 *
2907 * Results:
2908 *  The return value is a pointer to the first value in tablePtr,
2909 *  or NULL if tablePtr has no entries in it.  The memory at
2910 *  *searchPtr is initialized so that subsequent calls to
2911 *  Rp_TreeNextValue will return all of the values in the table,
2912 *  one at a time.
2913 *
2914 * Side effects:
2915 *  None.
2916 *
2917 *----------------------------------------------------------------------
2918 */
2919static Value *
2920TreeFirstValue(
2921    Node *nodePtr,
2922    Rp_TreeKeySearch *searchPtr)    /* Place to store information about
2923                                     * progress through the table. */
2924{
2925    searchPtr->node = nodePtr;
2926    searchPtr->nextIndex = 0;
2927    if (nodePtr->logSize > 0) {
2928        searchPtr->nextValue = NULL;
2929    } else {
2930        searchPtr->nextValue = nodePtr->values;
2931    }
2932    return TreeNextValue(searchPtr);
2933}
2934
2935/*
2936 *----------------------------------------------------------------------
2937 *
2938 * TreeNextValue --
2939 *
2940 *  Once a hash table enumeration has been initiated by calling
2941 *  Rp_TreeFirstValue, this procedure may be called to return
2942 *  successive elements of the table.
2943 *
2944 * Results:
2945 *  The return value is the next entry in the hash table being
2946 *  enumerated, or NULL if the end of the table is reached.
2947 *
2948 * Side effects:
2949 *  None.
2950 *
2951 *----------------------------------------------------------------------
2952 */
2953static Value *
2954TreeNextValue(
2955    Rp_TreeKeySearch *searchPtr)  /* Place to store information about
2956                                   * progress through the table.  Must
2957                                   * have been initialized by calling
2958                                   * Rp_TreeFirstValue. */
2959{
2960    Value *valuePtr;
2961
2962    if (searchPtr->node->logSize > 0) {
2963        size_t nBuckets;
2964        Value **buckets;
2965
2966        nBuckets = (1 << searchPtr->node->logSize);
2967        buckets = (Value **)searchPtr->node->values;
2968        while (searchPtr->nextValue == NULL) {
2969            if (searchPtr->nextIndex >= nBuckets) {
2970                return NULL;
2971            }
2972            searchPtr->nextValue = buckets[searchPtr->nextIndex];
2973            searchPtr->nextIndex++;
2974        }
2975    }
2976    valuePtr = searchPtr->nextValue;
2977    if (valuePtr != NULL) {
2978        searchPtr->nextValue = valuePtr->next;
2979    }
2980    return valuePtr;
2981}
2982
2983/*
2984 *----------------------------------------------------------------------
2985 *
2986 * TreeFindValue --
2987 *
2988 *  Given a hash table with one-word keys, and a one-word key, find
2989 *  the entry with a matching key.
2990 *
2991 * Results:
2992 *  The return value is a token for the matching entry in the
2993 *  hash table, or NULL if there was no matching entry.
2994 *
2995 * Side effects:
2996 *  None.
2997 *
2998 *----------------------------------------------------------------------
2999 */
3000static Value *
3001TreeFindValue(
3002    Node *nodePtr,
3003    Rp_TreeKey key)     /* Key to use to find matching entry. */
3004{
3005    register Value *valuePtr;
3006    Value *bucket;
3007
3008    if (nodePtr->logSize > 0) {
3009        unsigned int downshift;
3010        unsigned long mask;
3011
3012        mask = (1 << nodePtr->logSize) - 1;
3013        downshift = DOWNSHIFT_START - nodePtr->logSize;
3014        bucket = ((Value **)(nodePtr->values))[RANDOM_INDEX((void *)key)];
3015    } else {
3016        bucket = nodePtr->values; /* Single chain list. */
3017    }
3018    /*
3019     * Search all of the entries in the appropriate bucket.
3020     */
3021    for (valuePtr = bucket; valuePtr != NULL; valuePtr = valuePtr->next) {
3022        if (valuePtr->key == key) {
3023            return valuePtr;
3024        }
3025    }
3026    return NULL;
3027}
3028
3029/*
3030 *----------------------------------------------------------------------
3031 *
3032 * Rp_TreeCreateValue --
3033 *
3034 *  Find the value with a matching key.  If there is no matching
3035 *  value, then create a new one.
3036 *
3037 * Results:
3038 *  The return value is a pointer to the matching value.  If this
3039 *  is a newly-created value, then *newPtr will be set to a non-zero
3040 *  value;  otherwise *newPtr will be set to 0.
3041 *
3042 * Side effects:
3043 *  A new value may be added to the hash table.
3044 *
3045 *----------------------------------------------------------------------
3046 */
3047static Value *
3048TreeCreateValue(
3049    Node *nodePtr,
3050    Rp_TreeKey key,     /* Key to use to find or create matching
3051                         * entry. */
3052    int *newPtr)        /* Store info here telling whether a new
3053                         * entry was created. */
3054{
3055    register Value *valuePtr;
3056
3057    /*
3058     * Check if there as so many values that storage should be
3059     * converted from a hash table instead of a list.
3060     */
3061    if ((nodePtr->logSize == 0) && (nodePtr->nValues > MAX_LIST_VALUES)) {
3062        ConvertValues(nodePtr);
3063    }
3064    if (nodePtr->logSize > 0) {
3065        Value **bucketPtr;
3066        size_t nBuckets;
3067        unsigned int downshift;
3068        unsigned long mask;
3069
3070        nBuckets = (1 << nodePtr->logSize);
3071        mask = nBuckets - 1;
3072        downshift = DOWNSHIFT_START - nodePtr->logSize;
3073        bucketPtr = (Value **)nodePtr->values + RANDOM_INDEX((void *)key);
3074
3075        *newPtr = FALSE;
3076        for (valuePtr = *bucketPtr; valuePtr != NULL;
3077             valuePtr = valuePtr->next) {
3078            if (valuePtr->key == key) {
3079                return valuePtr;
3080            }
3081        }
3082
3083        /* Value not found. Add a new value to the bucket. */
3084        *newPtr = TRUE;
3085        valuePtr = Rp_PoolAllocItem(nodePtr->treeObject->valuePool,
3086                sizeof(Value));
3087        valuePtr->key = key;
3088        valuePtr->owner = NULL;
3089        valuePtr->next = *bucketPtr;
3090        valuePtr->objPtr = NULL;
3091        *bucketPtr = valuePtr;
3092        nodePtr->nValues++;
3093
3094        /*
3095         * If the table has exceeded a decent size, rebuild it with many
3096         * more buckets.
3097         */
3098        if ((unsigned int)nodePtr->nValues >= (nBuckets * 3)) {
3099            RebuildTable(nodePtr);
3100        }
3101    } else {
3102        Value *prevPtr;
3103
3104        prevPtr = NULL;
3105        *newPtr = FALSE;
3106        for (valuePtr = nodePtr->values; valuePtr != NULL;
3107             valuePtr = valuePtr->next) {
3108            if (valuePtr->key == key) {
3109                return valuePtr;
3110            }
3111            prevPtr = valuePtr;
3112        }
3113        /* Value not found. Add a new value to the list. */
3114        *newPtr = TRUE;
3115        valuePtr = Rp_PoolAllocItem(nodePtr->treeObject->valuePool,
3116                sizeof(Value));
3117        valuePtr->key = key;
3118        valuePtr->owner = NULL;
3119        valuePtr->next = NULL;
3120        valuePtr->objPtr = NULL;
3121        if (prevPtr == NULL) {
3122            nodePtr->values = valuePtr;
3123        } else {
3124            prevPtr->next = valuePtr;
3125        }
3126        nodePtr->nValues++;
3127    }
3128    return valuePtr;
3129}
3130
3131#endif /* NO_TREE */
Note: See TracBrowser for help on using the repository browser.