source: trunk/src/objects/RpTree.c @ 1615

Last change on this file since 1615 was 1560, checked in by dkearney, 15 years ago

updates to the object system, fixed up tree and xml parser objects, added some basic tests for them and adopted number object to accept xml text and configure itself from the parsed xml. added fermi3.cc example program which contains suggested interface from apps meeting.

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.