d3d9: Do not expect consistent failures.
[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  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32
33 #include "unicode.h"
34 #include "request.h"
35 #include "object.h"
36 #include "process.h"
37 #include "handle.h"
38 #include "user.h"
39 #include "winuser.h"
40 #include "winternl.h"
41
42 #define HASH_SIZE     37
43 #define MIN_HASH_SIZE 4
44 #define MAX_HASH_SIZE 0x200
45
46 #define MAX_ATOM_LEN  255
47 #define MIN_STR_ATOM  0xc000
48 #define MAX_ATOMS     0x4000
49
50 struct atom_entry
51 {
52     struct atom_entry *next;   /* hash table list */
53     struct atom_entry *prev;   /* hash table list */
54     int                count;  /* reference count */
55     short              pinned; /* whether the atom is pinned or not */
56     atom_t             atom;   /* atom handle */
57     unsigned short     hash;   /* string hash */
58     unsigned short     len;    /* string len */
59     WCHAR              str[1]; /* atom string */
60 };
61
62 struct atom_table
63 {
64     struct object       obj;                 /* object header */
65     int                 count;               /* count of atom handles */
66     int                 last;                /* last handle in-use */
67     struct atom_entry **handles;             /* atom handles */
68     int                 entries_count;       /* humber of hash entries */
69     struct atom_entry **entries;             /* hash table entries */
70 };
71
72 static void atom_table_dump( struct object *obj, int verbose );
73 static void atom_table_destroy( struct object *obj );
74
75 static const struct object_ops atom_table_ops =
76 {
77     sizeof(struct atom_table),    /* size */
78     atom_table_dump,              /* dump */
79     no_get_type,                  /* get_type */
80     no_add_queue,                 /* add_queue */
81     NULL,                         /* remove_queue */
82     NULL,                         /* signaled */
83     NULL,                         /* satisfied */
84     no_signal,                    /* signal */
85     no_get_fd,                    /* get_fd */
86     no_map_access,                /* map_access */
87     default_get_sd,               /* get_sd */
88     default_set_sd,               /* set_sd */
89     no_lookup_name,               /* lookup_name */
90     no_open_file,                 /* open_file */
91     no_close_handle,              /* close_handle */
92     atom_table_destroy            /* destroy */
93 };
94
95 static struct atom_table *global_table;
96
97 /* create an atom table */
98 static struct atom_table *create_table(int entries_count)
99 {
100     struct atom_table *table;
101
102     if ((table = alloc_object( &atom_table_ops )))
103     {
104         if ((entries_count < MIN_HASH_SIZE) ||
105             (entries_count > MAX_HASH_SIZE)) entries_count = HASH_SIZE;
106         table->entries_count = entries_count;
107         if (!(table->entries = malloc( sizeof(*table->entries) * table->entries_count )))
108         {
109             set_error( STATUS_NO_MEMORY );
110             goto fail;
111         }
112         memset( table->entries, 0, sizeof(*table->entries) * table->entries_count );
113         table->count = 64;
114         table->last  = -1;
115         if ((table->handles = mem_alloc( sizeof(*table->handles) * table->count )))
116             return table;
117 fail:
118         release_object( table );
119         table = NULL;
120     }
121     return table;
122 }
123
124 /* retrieve an entry pointer from its atom */
125 static struct atom_entry *get_atom_entry( struct atom_table *table, atom_t atom )
126 {
127     struct atom_entry *entry = NULL;
128     if (table && (atom >= MIN_STR_ATOM) && (atom <= MIN_STR_ATOM + table->last))
129         entry = table->handles[atom - MIN_STR_ATOM];
130     if (!entry) set_error( STATUS_INVALID_HANDLE );
131     return entry;
132 }
133
134 /* add an atom entry in the table and return its handle */
135 static atom_t add_atom_entry( struct atom_table *table, struct atom_entry *entry )
136 {
137     int i;
138     for (i = 0; i <= table->last; i++)
139         if (!table->handles[i]) goto found;
140     if (i == table->count)
141     {
142         struct atom_entry **new_table = NULL;
143         int new_size = table->count + table->count / 2;
144         if (new_size > MAX_ATOMS) new_size = MAX_ATOMS;
145         if (new_size > table->count)
146             new_table = realloc( table->handles, sizeof(*table->handles) * new_size );
147         if (!new_table)
148         {
149             set_error( STATUS_NO_MEMORY );
150             return 0;
151         }
152         table->count = new_size;
153         table->handles = new_table;
154     }
155     table->last = i;
156  found:
157     table->handles[i] = entry;
158     entry->atom = i + MIN_STR_ATOM;
159     return entry->atom;
160 }
161
162 /* compute the hash code for a string */
163 static unsigned short atom_hash( struct atom_table *table, const WCHAR *str, data_size_t len )
164 {
165     unsigned int i;
166     unsigned short hash = 0;
167     for (i = 0; i < len; i++) hash ^= toupperW(str[i]) + i;
168     return hash % table->entries_count;
169 }
170
171 /* dump an atom table */
172 static void atom_table_dump( struct object *obj, int verbose )
173 {
174     int i;
175     struct atom_table *table = (struct atom_table *)obj;
176     assert( obj->ops == &atom_table_ops );
177
178     fprintf( stderr, "Atom table size=%d entries=%d\n",
179              table->last + 1, table->entries_count );
180     if (!verbose) return;
181     for (i = 0; i <= table->last; i++)
182     {
183         struct atom_entry *entry = table->handles[i];
184         if (!entry) continue;
185         fprintf( stderr, "  %04x: ref=%d pinned=%c hash=%d \"",
186                  entry->atom, entry->count, entry->pinned ? 'Y' : 'N', entry->hash );
187         dump_strW( entry->str, entry->len, stderr, "\"\"");
188         fprintf( stderr, "\"\n" );
189     }
190 }
191
192 /* destroy the atom table */
193 static void atom_table_destroy( struct object *obj )
194 {
195     int i;
196     struct atom_table *table = (struct atom_table *)obj;
197     assert( obj->ops == &atom_table_ops );
198     if (table->handles)
199     {
200         for (i = 0; i <= table->last; i++) free( table->handles[i] );
201         free( table->handles );
202     }
203     free( table->entries );
204 }
205
206 /* find an atom entry in its hash list */
207 static struct atom_entry *find_atom_entry( struct atom_table *table, const WCHAR *str,
208                                            data_size_t len, unsigned short hash )
209 {
210     struct atom_entry *entry = table->entries[hash];
211     while (entry)
212     {
213         if (entry->len == len && !memicmpW( entry->str, str, len )) break;
214         entry = entry->next;
215     }
216     return entry;
217 }
218
219 /* add an atom to the table */
220 static atom_t add_atom( struct atom_table *table, const WCHAR *str, data_size_t len )
221 {
222     struct atom_entry *entry;
223     unsigned short hash = atom_hash( table, str, len );
224     atom_t atom = 0;
225
226     if (!len)
227     {
228         set_error( STATUS_OBJECT_NAME_INVALID );
229         return 0;
230     }
231     if (len > MAX_ATOM_LEN)
232     {
233         set_error( STATUS_INVALID_PARAMETER );
234         return 0;
235     }
236     if ((entry = find_atom_entry( table, str, len, hash )))  /* exists already */
237     {
238         entry->count++;
239         return entry->atom;
240     }
241
242     if ((entry = mem_alloc( sizeof(*entry) + (len - 1) * sizeof(WCHAR) )))
243     {
244         if ((atom = add_atom_entry( table, entry )))
245         {
246             entry->prev  = NULL;
247             if ((entry->next = table->entries[hash])) entry->next->prev = entry;
248             table->entries[hash] = entry;
249             entry->count  = 1;
250             entry->pinned = 0;
251             entry->hash   = hash;
252             entry->len    = len;
253             memcpy( entry->str, str, len * sizeof(WCHAR) );
254         }
255         else free( entry );
256     }
257     else set_error( STATUS_NO_MEMORY );
258     return atom;
259 }
260
261 /* delete an atom from the table */
262 static void delete_atom( struct atom_table *table, atom_t atom, int if_pinned )
263 {
264     struct atom_entry *entry = get_atom_entry( table, atom );
265     if (!entry) return;
266     if (entry->pinned && !if_pinned) set_error( STATUS_WAS_LOCKED );
267     else if (!--entry->count)
268     {
269         if (entry->next) entry->next->prev = entry->prev;
270         if (entry->prev) entry->prev->next = entry->next;
271         else table->entries[entry->hash] = entry->next;
272         table->handles[atom - MIN_STR_ATOM] = NULL;
273         free( entry );
274     }
275 }
276
277 /* find an atom in the table */
278 static atom_t find_atom( struct atom_table *table, const WCHAR *str, data_size_t len )
279 {
280     struct atom_entry *entry;
281
282     if (!len)
283     {
284         set_error( STATUS_OBJECT_NAME_INVALID );
285         return 0;
286     }
287     if (len > MAX_ATOM_LEN)
288     {
289         set_error( STATUS_INVALID_PARAMETER );
290         return 0;
291     }
292     if (table && (entry = find_atom_entry( table, str, len, atom_hash(table, str, len) )))
293         return entry->atom;
294     set_error( STATUS_OBJECT_NAME_NOT_FOUND );
295     return 0;
296 }
297
298 static struct atom_table *get_global_table( struct winstation *winstation, int create )
299 {
300     struct atom_table *table = winstation ? winstation->atom_table : global_table;
301     if (!table)
302     {
303         if (create)
304         {
305             table = create_table( HASH_SIZE );
306             if (winstation) winstation->atom_table = table;
307             else
308             {
309                 global_table = table;
310                 make_object_static( &global_table->obj );
311             }
312         }
313         else set_error( STATUS_OBJECT_NAME_NOT_FOUND );
314     }
315     return table;
316 }
317
318 static struct atom_table *get_table( obj_handle_t h, int create )
319 {
320     struct atom_table *table = NULL;
321
322     if (h)
323     {
324         table = (struct atom_table *)get_handle_obj( current->process, h, 0, &atom_table_ops );
325     }
326     else
327     {
328         table = get_global_table( NULL, 1 );
329         if (table) grab_object( table );
330     }
331     return table;
332 }
333
334 /* add an atom in the global table; used for window properties */
335 atom_t add_global_atom( struct winstation *winstation, const WCHAR *str, data_size_t len )
336 {
337     struct atom_table *global_table = get_global_table( winstation, 1 );
338     if (!global_table) return 0;
339     return add_atom( global_table, str, len );
340 }
341
342 /* find an atom in the global table; used for window properties */
343 atom_t find_global_atom( struct winstation *winstation, const WCHAR *str, data_size_t len )
344 {
345     struct atom_table *global_table = get_global_table( winstation, 0 );
346     struct atom_entry *entry;
347
348     if (!len || len > MAX_ATOM_LEN || !global_table) return 0;
349     if ((entry = find_atom_entry( global_table, str, len, atom_hash(global_table, str, len) )))
350         return entry->atom;
351     return 0;
352 }
353
354 /* increment the ref count of a global atom; used for window properties */
355 int grab_global_atom( struct winstation *winstation, atom_t atom )
356 {
357     if (atom >= MIN_STR_ATOM)
358     {
359         struct atom_table *global_table = get_global_table( winstation, 0 );
360         if (global_table)
361         {
362             struct atom_entry *entry = get_atom_entry( global_table, atom );
363             if (entry) entry->count++;
364             return (entry != NULL);
365         }
366         else return 0;
367     }
368     else return 1;
369 }
370
371 /* decrement the ref count of a global atom; used for window properties */
372 void release_global_atom( struct winstation *winstation, atom_t atom )
373 {
374     if (atom >= MIN_STR_ATOM)
375     {
376         struct atom_table *global_table = get_global_table( winstation, 0 );
377         if (global_table) delete_atom( global_table, atom, 1 );
378     }
379 }
380
381 /* add a global atom */
382 DECL_HANDLER(add_atom)
383 {
384     struct atom_table *table = get_table( req->table, 1 );
385     if (table)
386     {
387         reply->atom = add_atom( table, get_req_data(), get_req_data_size() / sizeof(WCHAR) );
388         release_object( table );
389     }
390 }
391
392 /* delete a global atom */
393 DECL_HANDLER(delete_atom)
394 {
395     struct atom_table *table = get_table( req->table, 0 );
396     if (table)
397     {
398         delete_atom( table, req->atom, 0 );
399         release_object( table );
400     }
401 }
402
403 /* find a global atom */
404 DECL_HANDLER(find_atom)
405 {
406     struct atom_table *table = get_table( req->table, 0 );
407     if (table)
408     {
409         reply->atom = find_atom( table, get_req_data(), get_req_data_size() / sizeof(WCHAR) );
410         release_object( table );
411     }
412 }
413
414 /* get global atom name */
415 DECL_HANDLER(get_atom_information)
416 {
417     struct atom_table *table = get_table( req->table, 0 );
418     if (table)
419     {
420         struct atom_entry *entry;
421
422         if ((entry = get_atom_entry( table, req->atom )))
423         {
424             data_size_t len = entry->len * sizeof(WCHAR);
425             if (get_reply_max_size())
426                 set_reply_data( entry->str, min( len, get_reply_max_size()));
427             reply->count = entry->count;
428             reply->pinned = entry->pinned;
429             reply->total = len;
430         }
431         else reply->count = -1;
432         release_object( table );
433     }
434 }
435
436 /* set global atom name */
437 DECL_HANDLER(set_atom_information)
438 {
439     struct atom_table *table = get_table( req->table, 0 );
440     if (table)
441     {
442         struct atom_entry *entry;
443
444         if ((entry = get_atom_entry( table, req->atom )))
445         {
446             if (req->pinned) entry->pinned = 1;
447         }
448         release_object( table );
449     }
450 }
451
452 /* init a (local) atom table */
453 DECL_HANDLER(init_atom_table)
454 {
455     struct atom_table* table = create_table( req->entries );
456
457     if (table)
458     {
459         reply->table = alloc_handle( current->process, table, 0, 0 );
460         release_object( table );
461     }
462 }
463
464 /* set global atom name */
465 DECL_HANDLER(empty_atom_table)
466 {
467     struct atom_table *table = get_table( req->table, 1 );
468     if (table)
469     {
470         int i;
471         struct atom_entry *entry;
472
473         for (i = 0; i <= table->last; i++)
474         {
475             entry = table->handles[i];
476             if (entry && (!entry->pinned || req->if_pinned))
477             {
478                 if (entry->next) entry->next->prev = entry->prev;
479                 if (entry->prev) entry->prev->next = entry->next;
480                 else table->entries[entry->hash] = entry->next;
481                 table->handles[i] = NULL;
482                 free( entry );
483             }
484         }
485         release_object( table );
486     }
487 }