6bd82368adfd538fb68426b3dfe4f4a1db13111c
[pandora-libraries.git] / lib / pnd_container.c
1
2 #include <stdlib.h> // for malloc, free, etc
3 #include <string.h> // for strdup()
4
5 #include "pnd_container.h"
6
7 // This is implemented in a very vaguely 'class-like' fashion; sure, I could use union's and
8 // void*'s to payload areas and so forth, but this method is pretty convenient instead. We allocate
9 // a buffer of the size of the node PLUS the user requested payload, and then return the payload
10 // part of it. We can go back to the node part with some basic pointer subtraction whenever we
11 // want, and are reasonably assured the user won't monkey with anything in the mechanism.
12 //  In short, we can change or replace the container code without impacting the user, and without
13 // boring anyone with void*s or unions or sub-structs and the like.
14
15 // a Box node is just a key-name and a pointer to the next; the payload is whatever data
16 // follows this. The container itself doesn't care.
17 typedef struct _pnd_box_node_t {
18   char *key;
19   struct _pnd_box_node_t *next;
20 } pnd_box_node_t;
21
22 typedef struct {
23   char *name;             // for when you're using gdb and wondering wtf you're looking at
24   pnd_box_node_t *head;   // the first node
25 } pnd_box_t;
26
27 #define PAYLOAD2NODE(x) ((pnd_box_node_t*)( ((unsigned char *)(x)) - sizeof(pnd_box_node_t) ))
28 #define NODE2PAYLOAD(x) ( ((unsigned char *)(x)) + sizeof(pnd_box_node_t) )
29
30 pnd_box_handle pnd_box_new ( char *name ) {
31
32   pnd_box_t *p = malloc ( sizeof(pnd_box_t) );
33
34   if ( ! p ) {
35     return ( NULL ); // youch!
36   }
37
38   if ( name ) {
39     p -> name = strdup ( name );
40   } else {
41     p -> name = NULL;
42   }
43
44   p -> head = NULL;
45
46   return ( p );
47 }
48
49 void pnd_box_delete ( pnd_box_handle box ) {
50   pnd_box_t *p = (pnd_box_t*) box;
51   pnd_box_node_t *n, *next;
52
53   /* free up the list
54    */
55
56   n = p -> head;
57
58   while ( n ) {
59
60     if ( n -> key ) {
61       free ( n -> key );
62     }
63
64     next = n -> next;
65
66     free ( n );
67
68     n = next;
69
70   } // while
71
72   /* free up the box itself
73    */
74
75   if ( p -> name ) {
76     free ( p -> name );
77   }
78
79   p -> head = (void*)123; // if you're looking at a stale pointer in gdb, this might tip you off
80
81   free ( p );
82
83   return;
84 }
85
86 void *pnd_box_allocinsert ( pnd_box_handle box, char *key, unsigned int size ) {
87   pnd_box_t *p = (pnd_box_t*) box;
88
89   pnd_box_node_t *n = malloc ( sizeof(pnd_box_node_t) + size );
90
91   if ( ! n ) {
92     return ( NULL ); // must be getting bloody tight!
93   }
94
95   if ( key ) {
96     n -> key = strdup ( key );
97   } else {
98     n -> key = NULL;
99   }
100
101   n -> next = p -> head;
102
103   p -> head = n;
104
105   return ( NODE2PAYLOAD(n) );
106 }
107
108 void *pnd_box_find_by_key ( pnd_box_handle box, char *key ) {
109   pnd_box_t *p = (pnd_box_t*) box;
110   pnd_box_node_t *n;
111
112   n = p -> head;
113
114   while ( n ) {
115
116     if ( strcmp ( n -> key, key ) == 0 ) {
117       return ( NODE2PAYLOAD(n) );
118     }
119
120     n = n -> next;
121   } // while
122
123   return ( NULL );
124 }
125
126 char *pnd_box_get_name ( pnd_box_handle box ) {
127   pnd_box_t *p = (pnd_box_t*) box;
128   return ( p -> name );
129 }
130
131 void *pnd_box_get_head ( pnd_box_handle box ) {
132   pnd_box_t *p = (pnd_box_t*) box;
133   if ( ! p -> head ) {
134     return ( NULL );
135   }
136   return ( NODE2PAYLOAD(p -> head) );
137 }
138
139 void *pnd_box_get_next ( void *node ) {
140   pnd_box_node_t *p = PAYLOAD2NODE(node);
141   p = p -> next;
142   if ( ! p ) {
143     return ( NULL );
144   }
145   return ( NODE2PAYLOAD(p) );
146 }
147
148 char *pnd_box_get_key ( void *node ) {
149   pnd_box_node_t *p = PAYLOAD2NODE(node);
150   return ( p -> key );
151 }