Implemented HeapWalk().
[wine] / server / atom.c
1 /*
2  * Server-side atom management
3  *
4  * Copyright (C) 1999, 2000 Alexandre Julliard
5  * Copyright (C) 2000 Turchanov Sergei
6  */
7
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11
12 #include "unicode.h"
13 #include "request.h"
14 #include "object.h"
15
16 #define HASH_SIZE     37
17 #define MAX_ATOM_LEN  255
18 #define MAX_ATOMS     0x4000
19
20 struct atom_entry
21 {
22     struct atom_entry *next;   /* hash table list */
23     struct atom_entry *prev;   /* hash table list */
24     int                atom;   /* atom handle */
25     int                count;  /* reference count */
26     int                hash;   /* string hash */
27     WCHAR              str[1]; /* atom string */
28 };
29
30 struct atom_table
31 {
32     struct object       obj;                 /* object header */
33     int                 count;               /* count of atom handles */
34     int                 last;                /* last handle in-use */
35     struct atom_entry **handles;             /* atom handles */
36     struct atom_entry  *entries[HASH_SIZE];  /* hash table entries */
37 };
38
39
40 static void atom_table_dump( struct object *obj, int verbose );
41 static void atom_table_destroy( struct object *obj );
42
43 static const struct object_ops atom_table_ops =
44 {
45     sizeof(struct atom_table),    /* size */
46     atom_table_dump,              /* dump */
47     no_add_queue,                 /* add_queue */
48     NULL,                         /* remove_queue */
49     NULL,                         /* signaled */
50     NULL,                         /* satified */
51     NULL,                         /* get_poll_events */
52     NULL,                         /* poll_event */
53     no_read_fd,                   /* get_read_fd */
54     no_write_fd,                  /* get_write_fd */
55     no_flush,                     /* flush */
56     no_get_file_info,             /* get_file_info */
57     atom_table_destroy            /* destroy */
58 };
59
60 static struct atom_table *global_table;
61
62
63 /* copy an atom name to a temporary area */
64 static const WCHAR *copy_name( const WCHAR *str )
65 {
66     static WCHAR buffer[MAX_ATOM_LEN+1];
67     WCHAR *p = buffer;
68
69     while (p < buffer + sizeof(buffer) - 1) if (!(*p++ = *str++)) break;
70     *p = 0;
71     return buffer;
72 }
73
74 /* create an atom table */
75 static struct atom_table *create_table(void)
76 {
77     struct atom_table *table;
78
79     if ((table = alloc_object( &atom_table_ops, -1 )))
80     {
81         table->count = 64;
82         table->last  = -1;
83         memset( table->entries, 0, sizeof(table->entries) );
84         if ((table->handles = mem_alloc( sizeof(*table->handles) * table->count )))
85             return table;
86         release_object( table );
87         table = NULL;
88     }
89     return table;
90 }
91
92 /* retrieve an entry pointer from its atom */
93 static struct atom_entry *get_atom_entry( struct atom_table *table, int atom )
94 {
95     struct atom_entry *entry = NULL;
96     if (table && (atom >= 0) && (atom <= table->last)) entry = table->handles[atom];
97     if (!entry) set_error( STATUS_INVALID_HANDLE );
98     return entry;
99 }
100
101 /* add an atom entry in the table and return its handle */
102 static int add_atom_entry( struct atom_table *table, struct atom_entry *entry )
103 {
104     int i;
105     for (i = 0; i <= table->last; i++)
106         if (!table->handles[i]) goto found;
107     if (i == table->count)
108     {
109         struct atom_entry **new_table = NULL;
110         int new_size = table->count + table->count / 2;
111         if (new_size > MAX_ATOMS) new_size = MAX_ATOMS;
112         if (new_size > table->count)
113             new_table = realloc( table->handles, sizeof(*table->handles) * new_size );
114         if (!new_table)
115         {
116             set_error( STATUS_NO_MEMORY );
117             return -1;
118         }
119         table->count = new_size;
120         table->handles = new_table;
121     }
122     table->last = i;
123  found:
124     table->handles[i] = entry;
125     entry->atom = i;
126     return i;
127 }
128
129 /* compute the hash code for a string */
130 static int atom_hash( const WCHAR *str )
131 {
132     int i;
133     WCHAR hash = 0;
134     for (i = 0; str[i]; i++) hash ^= towupper(str[i]) + i;
135     return hash % HASH_SIZE;
136 }
137
138 /* dump an atom table */
139 static void atom_table_dump( struct object *obj, int verbose )
140 {
141     int i;
142     struct atom_table *table = (struct atom_table *)obj;
143     assert( obj->ops == &atom_table_ops );
144
145     fprintf( stderr, "Atom table size=%d\n", table->last + 1 );
146     if (!verbose) return;
147     for (i = 0; i <= table->last; i++)
148     {
149         struct atom_entry *entry = table->handles[i];
150         if (!entry) continue;
151         fprintf( stderr, "  %5d: ref=%d hash=%d \"", i, entry->count, entry->hash );
152         dump_strW( entry->str, strlenW(entry->str), stderr, "\"\"");
153         fprintf( stderr, "\"\n" );
154     }
155 }
156
157 /* destroy the atom table */
158 static void atom_table_destroy( struct object *obj )
159 {
160     int i;
161     struct atom_table *table = (struct atom_table *)obj;
162     assert( obj->ops == &atom_table_ops );
163     for (i = 0; i <= table->last; i++) free( table->handles[i] );
164     free( table->handles );
165 }
166
167 /* find an atom entry in its hash list */
168 static struct atom_entry *find_atom_entry( struct atom_table *table, const WCHAR *str, int hash )
169 {
170     struct atom_entry *entry = table->entries[hash];
171     while (entry)
172     {
173         if (!strcmpiW( entry->str, str )) break;
174         entry = entry->next;
175     }
176     return entry;
177 }
178
179 /* close the atom table; used on server exit */
180 void close_atom_table(void)
181 {
182     if (global_table) release_object( global_table );
183 }
184
185 /* add an atom to the table */
186 static int add_atom( struct atom_table *table, const WCHAR *str )
187 {
188     struct atom_entry *entry;
189     int hash = atom_hash( str );
190     int atom = -1;
191
192     if (!*str)
193     {
194         set_error( STATUS_OBJECT_NAME_INVALID );
195         return -1;
196     }
197     if ((entry = find_atom_entry( table, str, hash )))  /* exists already */
198     {
199         entry->count++;
200         return entry->atom;
201     }
202
203     if ((entry = mem_alloc( sizeof(*entry) + strlenW(str) * sizeof(WCHAR) )))
204     {
205         if ((atom = add_atom_entry( table, entry )) != -1)
206         {
207             entry->prev  = NULL;
208             if ((entry->next = table->entries[hash])) entry->next->prev = entry;
209             table->entries[hash] = entry;
210             entry->count = 1;
211             entry->hash  = hash;
212             strcpyW( entry->str, str );
213         }
214         else free( entry );
215     }
216     else set_error( STATUS_NO_MEMORY );
217     return atom;
218 }
219
220 /* delete an atom from the table */
221 static void delete_atom( struct atom_table *table, int atom )
222 {
223     struct atom_entry *entry = get_atom_entry( table, atom );
224     if (entry && !--entry->count)
225     {
226         if (entry->next) entry->next->prev = entry->prev;
227         if (entry->prev) entry->prev->next = entry->next;
228         else table->entries[entry->hash] = entry->next;
229         table->handles[atom] = NULL;
230         free( entry );
231     }
232 }
233
234 /* find an atom in the table */
235 static int find_atom( struct atom_table *table, const WCHAR *str )
236 {
237     struct atom_entry *entry;
238
239     if (table && ((entry = find_atom_entry( table, str, atom_hash(str) )))) return entry->atom;
240     if (!*str) set_error( STATUS_OBJECT_NAME_INVALID );
241     else set_error( STATUS_OBJECT_NAME_NOT_FOUND );
242     return -1;
243 }
244
245 /* get an atom name and refcount*/
246 static int get_atom_name( struct atom_table *table, int atom, WCHAR *str )
247 {
248     int count = -1;
249     struct atom_entry *entry = get_atom_entry( table, atom );
250     if (entry)
251     {
252         strcpyW( str, entry->str );
253         count = entry->count;
254     }
255     return count;
256 }
257
258 /* add a global atom */
259 DECL_HANDLER(add_atom)
260 {
261     if (!global_table) global_table = create_table();
262     if (global_table) req->atom = add_atom( global_table, copy_name( req->name ) );
263 }
264
265 /* delete a global atom */
266 DECL_HANDLER(delete_atom)
267 {
268     delete_atom( global_table, req->atom );
269 }
270
271 /* find a global atom */
272 DECL_HANDLER(find_atom)
273 {
274     req->atom = find_atom( global_table, copy_name( req->name ) );
275 }
276
277 /* get global atom name */
278 DECL_HANDLER(get_atom_name)
279 {
280     req->name[0] = 0;
281     req->count = get_atom_name( global_table, req->atom, req->name );
282 }