/* * Server-side atom management * * Copyright (C) 1999, 2000 Alexandre Julliard * Copyright (C) 2000 Turchanov Sergei */ #include #include #include #include #include "unicode.h" #include "request.h" #include "object.h" #include "process.h" #define HASH_SIZE 37 #define MIN_HASH_SIZE 4 #define MAX_HASH_SIZE 0x200 #define MAX_ATOM_LEN 255 #define MIN_STR_ATOM 0xc000 #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 */ int hash; /* string hash */ atom_t atom; /* atom handle */ 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 */ int entries_count; /* humber of hash entries */ struct atom_entry **entries; /* hash table entries */ }; 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 */ NULL, /* satified */ NULL, /* get_poll_events */ NULL, /* poll_event */ no_get_fd, /* get_fd */ no_flush, /* flush */ no_get_file_info, /* get_file_info */ atom_table_destroy /* destroy */ }; static struct atom_table *global_table; /* copy an atom name from the request to a temporary area */ static const WCHAR *copy_request_name(void) { static WCHAR buffer[MAX_ATOM_LEN+1]; const WCHAR *str = get_req_data(); size_t len = get_req_data_size(); if (len > MAX_ATOM_LEN*sizeof(WCHAR)) { set_error( STATUS_INVALID_PARAMETER ); return NULL; } memcpy( buffer, str, len ); buffer[len / sizeof(WCHAR)] = 0; return buffer; } /* create an atom table */ static struct atom_table *create_table(int entries_count) { struct atom_table *table; if ((table = alloc_object( &atom_table_ops, -1 ))) { 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 ); table->count = 64; table->last = -1; if ((table->handles = mem_alloc( sizeof(*table->handles) * table->count ))) return table; fail: release_object( table ); table = NULL; } return table; } /* retrieve an entry pointer from its atom */ static struct atom_entry *get_atom_entry( struct atom_table *table, atom_t atom ) { struct atom_entry *entry = NULL; if (table && (atom >= MIN_STR_ATOM) && (atom <= MIN_STR_ATOM + table->last)) entry = table->handles[atom - MIN_STR_ATOM]; if (!entry) set_error( STATUS_INVALID_HANDLE ); return entry; } /* add an atom entry in the table and return its handle */ static atom_t add_atom_entry( struct atom_table *table, struct atom_entry *entry ) { 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 ); return 0; } table->count = new_size; table->handles = new_table; } table->last = i; found: table->handles[i] = entry; entry->atom = i + MIN_STR_ATOM; return entry->atom; } /* compute the hash code for a string */ static int atom_hash( struct atom_table *table, const WCHAR *str ) { int i; WCHAR hash = 0; for (i = 0; str[i]; i++) hash ^= toupperW(str[i]) + i; return hash % table->entries_count; } /* 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 ); fprintf( stderr, "Atom table size=%d entries=%d\n", table->last + 1, table->entries_count ); if (!verbose) return; for (i = 0; i <= table->last; i++) { struct atom_entry *entry = table->handles[i]; if (!entry) continue; fprintf( stderr, " %04x: ref=%d hash=%d \"", entry->atom, entry->count, entry->hash ); dump_strW( entry->str, strlenW(entry->str), stderr, "\"\""); 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 ); if (table->handles) { for (i = 0; i <= table->last; i++) free( table->handles[i] ); free( table->handles ); } if (table->entries) free( table->entries ); } /* find an atom entry in its hash list */ static struct atom_entry *find_atom_entry( struct atom_table *table, const WCHAR *str, int hash ) { struct atom_entry *entry = table->entries[hash]; while (entry) { if (!strcmpiW( entry->str, str )) break; entry = entry->next; } return entry; } /* close the atom table; used on server exit */ void close_atom_table(void) { if (global_table) release_object( global_table ); } /* add an atom to the table */ static atom_t add_atom( struct atom_table *table, const WCHAR *str ) { struct atom_entry *entry; int hash = atom_hash( table, str ); atom_t atom = 0; if (!*str) { set_error( STATUS_OBJECT_NAME_INVALID ); return 0; } if ((entry = find_atom_entry( table, str, hash ))) /* exists already */ { entry->count++; return entry->atom; } if ((entry = mem_alloc( sizeof(*entry) + strlenW(str) * sizeof(WCHAR) ))) { if ((atom = add_atom_entry( table, entry ))) { entry->prev = NULL; if ((entry->next = table->entries[hash])) entry->next->prev = entry; table->entries[hash] = entry; entry->count = 1; entry->hash = hash; strcpyW( entry->str, str ); } else free( entry ); } else set_error( STATUS_NO_MEMORY ); return atom; } /* delete an atom from the table */ static void delete_atom( struct atom_table *table, atom_t atom ) { struct atom_entry *entry = get_atom_entry( table, atom ); if (entry && !--entry->count) { 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[atom - MIN_STR_ATOM] = NULL; free( entry ); } } /* find an atom in the table */ static atom_t find_atom( struct atom_table *table, const WCHAR *str ) { struct atom_entry *entry; if (table && ((entry = find_atom_entry( table, str, atom_hash(table, str) )))) return entry->atom; if (!*str) set_error( STATUS_OBJECT_NAME_INVALID ); else set_error( STATUS_OBJECT_NAME_NOT_FOUND ); return 0; } /* increment the ref count of a global atom; used for window properties */ int grab_global_atom( atom_t atom ) { struct atom_entry *entry = get_atom_entry( global_table, atom ); if (entry) entry->count++; return (entry != NULL); } /* decrement the ref count of a global atom; used for window properties */ void release_global_atom( atom_t atom ) { delete_atom( global_table, atom ); } /* add a global atom */ DECL_HANDLER(add_atom) { struct atom_table **table_ptr = req->local ? ¤t->process->atom_table : &global_table; if (!*table_ptr) *table_ptr = create_table(0); if (*table_ptr) { const WCHAR *name = copy_request_name(); if (name) reply->atom = add_atom( *table_ptr, name ); } } /* delete a global atom */ DECL_HANDLER(delete_atom) { delete_atom( req->local ? current->process->atom_table : global_table, req->atom ); } /* find a global atom */ DECL_HANDLER(find_atom) { const WCHAR *name = copy_request_name(); if (name) reply->atom = find_atom( req->local ? current->process->atom_table : global_table, name ); } /* get global atom name */ DECL_HANDLER(get_atom_name) { struct atom_entry *entry; size_t len = 0; reply->count = -1; if ((entry = get_atom_entry( req->local ? current->process->atom_table : global_table, req->atom ))) { reply->count = entry->count; len = strlenW( entry->str ) * sizeof(WCHAR); if (len <= get_reply_max_size()) set_reply_data( entry->str, len ); else set_error( STATUS_BUFFER_OVERFLOW ); } } /* init the process atom table */ DECL_HANDLER(init_atom_table) { if (!current->process->atom_table) current->process->atom_table = create_table( req->entries ); }