Moved global atoms to the server (based on the work of Sergei
Turchanov <turchanov@otvprim.ru>).
diff --git a/server/atom.c b/server/atom.c
new file mode 100644
index 0000000..c1f4059
--- /dev/null
+++ b/server/atom.c
@@ -0,0 +1,281 @@
+/*
+ * Server-side atom management
+ *
+ * Copyright (C) 1999, 2000 Alexandre Julliard
+ * Copyright (C) 2000 Turchanov Sergei
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "unicode.h"
+#include "request.h"
+#include "object.h"
+
+#define HASH_SIZE 37
+#define MAX_ATOM_LEN 255
+#define MAX_ATOMS 0x4000
+
+struct atom_entry
+{
+ struct atom_entry *next; /* hash table list */
+ struct atom_entry *prev; /* hash table list */
+ int atom; /* atom handle */
+ int count; /* reference count */
+ int hash; /* string hash */
+ 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 */
+ struct atom_entry *entries[HASH_SIZE]; /* 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_read_fd, /* get_read_fd */
+ no_write_fd, /* get_write_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 to a temporary area */
+static const WCHAR *copy_name( const WCHAR *str )
+{
+ static WCHAR buffer[MAX_ATOM_LEN+1];
+ WCHAR *p = buffer;
+
+ while (p < buffer + sizeof(buffer) - 1) if (!(*p++ = *str++)) break;
+ *p = 0;
+ return buffer;
+}
+
+/* create an atom table */
+static struct atom_table *create_table(void)
+{
+ struct atom_table *table;
+
+ if ((table = alloc_object( &atom_table_ops, -1 )))
+ {
+ table->count = 64;
+ table->last = -1;
+ memset( table->entries, 0, sizeof(table->entries) );
+ if ((table->handles = mem_alloc( sizeof(*table->handles) * table->count )))
+ return table;
+ 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, int atom )
+{
+ struct atom_entry *entry = NULL;
+ if (table && (atom >= 0) && (atom <= table->last)) entry = table->handles[atom];
+ if (!entry) set_error( STATUS_INVALID_HANDLE );
+ return entry;
+}
+
+/* add an atom entry in the table and return its handle */
+static int 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 -1;
+ }
+ table->count = new_size;
+ table->handles = new_table;
+ }
+ table->last = i;
+ found:
+ table->handles[i] = entry;
+ entry->atom = i;
+ return i;
+}
+
+/* compute the hash code for a string */
+static int atom_hash( const WCHAR *str )
+{
+ int i;
+ WCHAR hash = 0;
+ for (i = 0; str[i]; i++) hash ^= towupper(str[i]) + i;
+ return hash % HASH_SIZE;
+}
+
+/* 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\n", table->last + 1 );
+ if (!verbose) return;
+ for (i = 0; i <= table->last; i++)
+ {
+ struct atom_entry *entry = table->handles[i];
+ if (!entry) continue;
+ fprintf( stderr, " %5d: ref=%d hash=%d \"", i, 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 );
+ for (i = 0; i <= table->last; i++) free( table->handles[i] );
+ free( table->handles );
+}
+
+/* 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 int add_atom( struct atom_table *table, const WCHAR *str )
+{
+ struct atom_entry *entry;
+ int hash = atom_hash( str );
+ int atom = -1;
+
+ if (!*str)
+ {
+ set_error( STATUS_OBJECT_NAME_INVALID );
+ return -1;
+ }
+ 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 )) != -1)
+ {
+ 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, int 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] = NULL;
+ free( entry );
+ }
+}
+
+/* find an atom in the table */
+static int find_atom( struct atom_table *table, const WCHAR *str )
+{
+ struct atom_entry *entry;
+
+ if (table && ((entry = find_atom_entry( table, str, atom_hash(str) )))) return entry->atom;
+ if (!*str) set_error( STATUS_OBJECT_NAME_INVALID );
+ else set_error( STATUS_OBJECT_NAME_NOT_FOUND );
+ return -1;
+}
+
+/* get an atom name and refcount*/
+static int get_atom_name( struct atom_table *table, int atom, WCHAR *str )
+{
+ int count = -1;
+ struct atom_entry *entry = get_atom_entry( table, atom );
+ if (entry)
+ {
+ strcpyW( str, entry->str );
+ count = entry->count;
+ }
+ return count;
+}
+
+/* add a global atom */
+DECL_HANDLER(add_atom)
+{
+ if (!global_table) global_table = create_table();
+ if (global_table) req->atom = add_atom( global_table, copy_name( req->name ) );
+}
+
+/* delete a global atom */
+DECL_HANDLER(delete_atom)
+{
+ delete_atom( global_table, req->atom );
+}
+
+/* find a global atom */
+DECL_HANDLER(find_atom)
+{
+ req->atom = find_atom( global_table, copy_name( req->name ) );
+}
+
+/* get global atom name */
+DECL_HANDLER(get_atom_name)
+{
+ req->count = get_atom_name( global_table, req->atom, req->name );
+}