atom.c 14.1 KB
Newer Older
1 2 3 4 5
/*
 * Server-side atom management
 *
 * Copyright (C) 1999, 2000 Alexandre Julliard
 * Copyright (C) 2000 Turchanov Sergei
6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 21
 */

22 23 24
#include "config.h"
#include "wine/port.h"

25 26 27
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
28
#include <string.h>
29

30 31 32
#include "ntstatus.h"
#define WIN32_NO_STATUS

33 34 35
#include "unicode.h"
#include "request.h"
#include "object.h"
36
#include "process.h"
37
#include "handle.h"
38 39
#include "user.h"
#include "winuser.h"
40
#include "winternl.h"
41 42

#define HASH_SIZE     37
43 44 45
#define MIN_HASH_SIZE 4
#define MAX_HASH_SIZE 0x200

46
#define MAX_ATOM_LEN  255
47
#define MIN_STR_ATOM  0xc000
48 49 50 51 52 53 54
#define MAX_ATOMS     0x4000

struct atom_entry
{
    struct atom_entry *next;   /* hash table list */
    struct atom_entry *prev;   /* hash table list */
    int                count;  /* reference count */
55
    short              pinned; /* whether the atom is pinned or not */
56
    atom_t             atom;   /* atom handle */
57 58
    unsigned short     hash;   /* string hash */
    unsigned short     len;    /* string len */
59 60 61 62 63 64 65 66 67
    WCHAR              str[1]; /* atom string */
};

struct atom_table
{
    struct object       obj;                 /* object header */
    int                 count;               /* count of atom handles */
    int                 last;                /* last handle in-use */
    struct atom_entry **handles;             /* atom handles */
68 69
    int                 entries_count;       /* humber of hash entries */
    struct atom_entry **entries;             /* hash table entries */
70 71 72 73 74 75 76 77 78 79 80 81
};

static void atom_table_dump( struct object *obj, int verbose );
static void atom_table_destroy( struct object *obj );

static const struct object_ops atom_table_ops =
{
    sizeof(struct atom_table),    /* size */
    atom_table_dump,              /* dump */
    no_add_queue,                 /* add_queue */
    NULL,                         /* remove_queue */
    NULL,                         /* signaled */
82 83
    NULL,                         /* satisfied */
    no_signal,                    /* signal */
84
    no_get_fd,                    /* get_fd */
85
    no_map_access,                /* map_access */
86
    no_lookup_name,               /* lookup_name */
87
    no_close_handle,              /* close_handle */
88 89 90
    atom_table_destroy            /* destroy */
};

91
static struct atom_table *global_table;
92 93

/* create an atom table */
94
static struct atom_table *create_table(int entries_count)
95 96 97
{
    struct atom_table *table;

98
    if ((table = alloc_object( &atom_table_ops )))
99
    {
100 101 102 103 104 105 106 107 108
        if ((entries_count < MIN_HASH_SIZE) ||
            (entries_count > MAX_HASH_SIZE)) entries_count = HASH_SIZE;
        table->entries_count = entries_count;
        if (!(table->entries = malloc( sizeof(*table->entries) * table->entries_count )))
        {
            set_error( STATUS_NO_MEMORY );
            goto fail;
        }
        memset( table->entries, 0, sizeof(*table->entries) * table->entries_count );
109 110 111 112
        table->count = 64;
        table->last  = -1;
        if ((table->handles = mem_alloc( sizeof(*table->handles) * table->count )))
            return table;
113
fail:
114 115 116 117 118 119 120
        release_object( table );
        table = NULL;
    }
    return table;
}

/* retrieve an entry pointer from its atom */
121
static struct atom_entry *get_atom_entry( struct atom_table *table, atom_t atom )
122 123
{
    struct atom_entry *entry = NULL;
124 125
    if (table && (atom >= MIN_STR_ATOM) && (atom <= MIN_STR_ATOM + table->last))
        entry = table->handles[atom - MIN_STR_ATOM];
126 127 128 129 130
    if (!entry) set_error( STATUS_INVALID_HANDLE );
    return entry;
}

/* add an atom entry in the table and return its handle */
131
static atom_t add_atom_entry( struct atom_table *table, struct atom_entry *entry )
132 133 134 135 136 137 138 139 140 141 142 143 144 145
{
    int i;
    for (i = 0; i <= table->last; i++)
        if (!table->handles[i]) goto found;
    if (i == table->count)
    {
        struct atom_entry **new_table = NULL;
        int new_size = table->count + table->count / 2;
        if (new_size > MAX_ATOMS) new_size = MAX_ATOMS;
        if (new_size > table->count)
            new_table = realloc( table->handles, sizeof(*table->handles) * new_size );
        if (!new_table)
        {
            set_error( STATUS_NO_MEMORY );
146
            return 0;
147 148 149 150 151 152 153
        }
        table->count = new_size;
        table->handles = new_table;
    }
    table->last = i;
 found:
    table->handles[i] = entry;
154 155
    entry->atom = i + MIN_STR_ATOM;
    return entry->atom;
156 157 158
}

/* compute the hash code for a string */
159
static unsigned short atom_hash( struct atom_table *table, const WCHAR *str, size_t len )
160
{
161 162 163
    unsigned int i;
    unsigned short hash = 0;
    for (i = 0; i < len; i++) hash ^= toupperW(str[i]) + i;
164
    return hash % table->entries_count;
165 166 167 168 169 170 171 172 173
}

/* dump an atom table */
static void atom_table_dump( struct object *obj, int verbose )
{
    int i;
    struct atom_table *table = (struct atom_table *)obj;
    assert( obj->ops == &atom_table_ops );

174 175
    fprintf( stderr, "Atom table size=%d entries=%d\n",
             table->last + 1, table->entries_count );
176 177 178 179 180
    if (!verbose) return;
    for (i = 0; i <= table->last; i++)
    {
        struct atom_entry *entry = table->handles[i];
        if (!entry) continue;
181
        fprintf( stderr, "  %04x: ref=%d pinned=%c hash=%d \"",
182
                 entry->atom, entry->count, entry->pinned ? 'Y' : 'N', entry->hash );
183
        dump_strW( entry->str, entry->len, stderr, "\"\"");
184 185 186 187 188 189 190 191 192 193
        fprintf( stderr, "\"\n" );
    }
}

/* destroy the atom table */
static void atom_table_destroy( struct object *obj )
{
    int i;
    struct atom_table *table = (struct atom_table *)obj;
    assert( obj->ops == &atom_table_ops );
194 195 196 197 198 199
    if (table->handles)
    {
        for (i = 0; i <= table->last; i++) free( table->handles[i] );
        free( table->handles );
    }
    if (table->entries) free( table->entries );
200 201 202
}

/* find an atom entry in its hash list */
203 204
static struct atom_entry *find_atom_entry( struct atom_table *table, const WCHAR *str,
                                           size_t len, unsigned short hash )
205 206 207 208
{
    struct atom_entry *entry = table->entries[hash];
    while (entry)
    {
209
        if (entry->len == len && !memicmpW( entry->str, str, len )) break;
210 211 212 213 214 215
        entry = entry->next;
    }
    return entry;
}

/* add an atom to the table */
216
static atom_t add_atom( struct atom_table *table, const WCHAR *str, size_t len )
217 218
{
    struct atom_entry *entry;
219
    unsigned short hash = atom_hash( table, str, len );
220
    atom_t atom = 0;
221

222
    if (!len)
223 224
    {
        set_error( STATUS_OBJECT_NAME_INVALID );
225
        return 0;
226
    }
227 228 229 230 231 232
    if (len > MAX_ATOM_LEN)
    {
        set_error( STATUS_INVALID_PARAMETER );
        return 0;
    }
    if ((entry = find_atom_entry( table, str, len, hash )))  /* exists already */
233 234 235 236 237
    {
        entry->count++;
        return entry->atom;
    }

238
    if ((entry = mem_alloc( sizeof(*entry) + (len - 1) * sizeof(WCHAR) )))
239
    {
240
        if ((atom = add_atom_entry( table, entry )))
241 242 243 244
        {
            entry->prev  = NULL;
            if ((entry->next = table->entries[hash])) entry->next->prev = entry;
            table->entries[hash] = entry;
245
            entry->count  = 1;
246
            entry->pinned = 0;
247 248 249
            entry->hash   = hash;
            entry->len    = len;
            memcpy( entry->str, str, len * sizeof(WCHAR) );
250 251 252 253 254 255 256 257
        }
        else free( entry );
    }
    else set_error( STATUS_NO_MEMORY );
    return atom;
}

/* delete an atom from the table */
258
static void delete_atom( struct atom_table *table, atom_t atom, int if_pinned )
259 260
{
    struct atom_entry *entry = get_atom_entry( table, atom );
261 262 263
    if (!entry) return;
    if (entry->pinned && !if_pinned) set_error( STATUS_WAS_LOCKED );
    else if (!--entry->count)
264 265 266 267
    {
        if (entry->next) entry->next->prev = entry->prev;
        if (entry->prev) entry->prev->next = entry->next;
        else table->entries[entry->hash] = entry->next;
268
        table->handles[atom - MIN_STR_ATOM] = NULL;
269 270 271 272 273
        free( entry );
    }
}

/* find an atom in the table */
274
static atom_t find_atom( struct atom_table *table, const WCHAR *str, size_t len )
275 276 277
{
    struct atom_entry *entry;

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
    if (!len)
    {
        set_error( STATUS_OBJECT_NAME_INVALID );
        return 0;
    }
    if (len > MAX_ATOM_LEN)
    {
        set_error( STATUS_INVALID_PARAMETER );
        return 0;
    }
    if (table && (entry = find_atom_entry( table, str, len, atom_hash(table, str, len) )))
        return entry->atom;
    set_error( STATUS_OBJECT_NAME_NOT_FOUND );
    return 0;
}

294
static struct atom_table *get_global_table( struct winstation *winstation, int create )
295
{
296 297
    struct atom_table *table = winstation ? winstation->atom_table : global_table;
    if (!table)
298
    {
299 300 301 302 303 304 305 306 307 308
        if (create)
        {
            table = create_table( HASH_SIZE );
            if (winstation) winstation->atom_table = table;
            else
            {
                global_table = table;
                make_object_static( &global_table->obj );
            }
        }
309 310
        else set_error( STATUS_OBJECT_NAME_NOT_FOUND );
    }
311
    return table;
312 313 314
}

static struct atom_table *get_table( obj_handle_t h, int create )
315
{
316
    struct atom_table *table = NULL;
317

318 319 320 321 322 323
    if (h)
    {
        table = (struct atom_table *)get_handle_obj( current->process, h, 0, &atom_table_ops );
    }
    else
    {
324 325
        table = get_global_table( NULL, 1 );
        if (table) grab_object( table );
326 327
    }
    return table;
328 329 330
}

/* add an atom in the global table; used for window properties */
331
atom_t add_global_atom( struct winstation *winstation, const WCHAR *str, size_t len )
332
{
333
    struct atom_table *global_table = get_global_table( winstation, 1 );
334
    if (!global_table) return 0;
335 336 337 338
    return add_atom( global_table, str, len );
}

/* find an atom in the global table; used for window properties */
339
atom_t find_global_atom( struct winstation *winstation, const WCHAR *str, size_t len )
340
{
341
    struct atom_table *global_table = get_global_table( winstation, 0 );
342 343 344 345
    struct atom_entry *entry;

    if (!len || len > MAX_ATOM_LEN || !global_table) return 0;
    if ((entry = find_atom_entry( global_table, str, len, atom_hash(global_table, str, len) )))
346 347
        return entry->atom;
    return 0;
348 349
}

350
/* increment the ref count of a global atom; used for window properties */
351
int grab_global_atom( struct winstation *winstation, atom_t atom )
352
{
353 354
    if (atom >= MIN_STR_ATOM)
    {
355
        struct atom_table *global_table = get_global_table( winstation, 0 );
356 357 358 359 360 361 362
        if (global_table)
        {
            struct atom_entry *entry = get_atom_entry( global_table, atom );
            if (entry) entry->count++;
            return (entry != NULL);
        }
        else return 0;
363 364
    }
    else return 1;
365 366 367
}

/* decrement the ref count of a global atom; used for window properties */
368
void release_global_atom( struct winstation *winstation, atom_t atom )
369
{
370 371
    if (atom >= MIN_STR_ATOM)
    {
372
        struct atom_table *global_table = get_global_table( winstation, 0 );
373 374
        if (global_table) delete_atom( global_table, atom, 1 );
    }
375 376
}

377 378 379
/* add a global atom */
DECL_HANDLER(add_atom)
{
380
    struct atom_table *table = get_table( req->table, 1 );
381
    if (table)
382
    {
383
        reply->atom = add_atom( table, get_req_data(), get_req_data_size() / sizeof(WCHAR) );
384
        release_object( table );
385
    }
386 387 388 389 390
}

/* delete a global atom */
DECL_HANDLER(delete_atom)
{
391
    struct atom_table *table = get_table( req->table, 0 );
392 393
    if (table)
    {
394
        delete_atom( table, req->atom, 0 );
395 396
        release_object( table );
    }
397 398 399 400 401
}

/* find a global atom */
DECL_HANDLER(find_atom)
{
402
    struct atom_table *table = get_table( req->table, 0 );
403 404
    if (table)
    {
405
        reply->atom = find_atom( table, get_req_data(), get_req_data_size() / sizeof(WCHAR) );
406 407
        release_object( table );
    }
408 409 410
}

/* get global atom name */
411
DECL_HANDLER(get_atom_information)
412
{
413
    struct atom_table *table = get_table( req->table, 0 );
414 415 416
    if (table)
    {
        struct atom_entry *entry;
417

418 419
        if ((entry = get_atom_entry( table, req->atom )))
        {
420
            size_t len = entry->len * sizeof(WCHAR);
421 422
            if (get_reply_max_size())
                set_reply_data( entry->str, min( len, get_reply_max_size()));
423 424
            reply->count = entry->count;
            reply->pinned = entry->pinned;
425
            reply->total = len;
426 427 428 429 430 431 432 433 434
        }
        else reply->count = -1;
        release_object( table );
    }
}

/* set global atom name */
DECL_HANDLER(set_atom_information)
{
435
    struct atom_table *table = get_table( req->table, 0 );
436
    if (table)
437
    {
438 439 440 441 442 443 444
        struct atom_entry *entry;

        if ((entry = get_atom_entry( table, req->atom )))
        {
            if (req->pinned) entry->pinned = 1;
        }
        release_object( table );
445
    }
446 447
}

448
/* init a (local) atom table */
449 450
DECL_HANDLER(init_atom_table)
{
451
    struct atom_table* table = create_table( req->entries );
452

453 454
    if (table)
    {
455
        reply->table = alloc_handle( current->process, table, 0, 0 );
456 457
        release_object( table );
    }
458 459 460 461 462
}

/* set global atom name */
DECL_HANDLER(empty_atom_table)
{
463
    struct atom_table *table = get_table( req->table, 1 );
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
    if (table)
    {
        int i;
        struct atom_entry *entry;

        for (i = 0; i <= table->last; i++)
        {
            entry = table->handles[i];
            if (entry && (!entry->pinned || req->if_pinned))
            {
                if (entry->next) entry->next->prev = entry->prev;
                if (entry->prev) entry->prev->next = entry->next;
                else table->entries[entry->hash] = entry->next;
                table->handles[i] = NULL;
                free( entry );
            }
        }
        release_object( table );
    }
483
}