4 * Copyright 1993, 1994, 1995 Alexandre Julliard
8 * Warning: The code assumes that LocalAlloc() returns a block aligned
9 * on a 4-bytes boundary (because of the shifting done in
10 * HANDLETOATOM). If this is not the case, the allocation code will
22 #include "wine/winbase16.h"
23 #include "wine/winuser16.h"
27 #include "stackframe.h"
29 #include "debugtools.h"
32 DEFAULT_DEBUG_CHANNEL(atom);
34 #define DEFAULT_ATOMTABLE_SIZE 37
35 #define MIN_STR_ATOM 0xc000
36 #define MAX_ATOM_LEN 255
38 #define ATOMTOHANDLE(atom) ((HANDLE16)(atom) << 2)
39 #define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> 2)))
55 static WORD ATOM_UserDS = 0; /* USER data segment */
57 /***********************************************************************
60 * Global table initialisation.
62 BOOL ATOM_Init( WORD globalTableSel )
64 ATOM_UserDS = globalTableSel;
69 /***********************************************************************
72 * Return a pointer to the atom table of a given segment, creating
76 * Pointer to table: Success
79 static ATOMTABLE *ATOM_GetTable( BOOL create /* [in] Create */ )
81 INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
84 ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
85 if (table->size) return table;
87 if (!create) return NULL;
88 if (!InitAtomTable16( 0 )) return NULL;
89 /* Reload ptr in case it moved in linear memory */
90 ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
91 return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
95 /***********************************************************************
98 * The hash value for the input string
100 static WORD ATOM_Hash(
101 WORD entries, /* [in] Total number of entries */
102 LPCSTR str, /* [in] Pointer to string to hash */
103 WORD len /* [in] Length of string */
107 TRACE("%x, %s, %x\n", entries, str, len);
109 for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
110 return hash % entries;
114 /***********************************************************************
117 static BOOL ATOM_IsIntAtomA(LPCSTR atomstr,WORD *atomid)
120 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
123 if (*atomstr++ != '#') return FALSE;
124 while (*atomstr >= '0' && *atomstr <= '9')
126 atom = atom * 10 + *atomstr - '0';
129 if (*atomstr) return FALSE;
131 if (!atom || (atom >= MIN_STR_ATOM))
133 SetLastError( ERROR_INVALID_PARAMETER );
141 /***********************************************************************
144 static BOOL ATOM_IsIntAtomW(LPCWSTR atomstr,WORD *atomid)
147 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
150 if (*atomstr++ != '#') return FALSE;
151 while (*atomstr >= '0' && *atomstr <= '9')
153 atom = atom * 10 + *atomstr - '0';
156 if (*atomstr) return FALSE;
158 if (!atom || (atom >= MIN_STR_ATOM))
160 SetLastError( ERROR_INVALID_PARAMETER );
168 /***********************************************************************
171 * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
173 static inline ATOMENTRY *ATOM_MakePtr( HANDLE16 handle /* [in] Handle */ )
175 return (ATOMENTRY *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, handle );
179 /***********************************************************************
180 * InitAtomTable16 (KERNEL.68)
182 WORD WINAPI InitAtomTable16( WORD entries )
188 /* We consider the first table to be initialized as the global table.
189 * This works, as USER (both built-in and native) is the first one to
195 ATOM_UserDS = CURRENT_DS;
196 /* return dummy local handle */
197 return LocalAlloc16( LMEM_FIXED, 1 );
200 /* Allocate the table */
202 if (!entries) entries = DEFAULT_ATOMTABLE_SIZE; /* sanity check */
203 handle = LocalAlloc16( LMEM_FIXED, sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
204 if (!handle) return 0;
205 table = (ATOMTABLE *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, handle );
206 table->size = entries;
207 for (i = 0; i < entries; i++) table->entries[i] = 0;
209 /* Store a pointer to the table in the instance data */
211 ((INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 ))->atomtable = handle;
216 /***********************************************************************
217 * GetAtomHandle (KERNEL.73)
219 HANDLE16 WINAPI GetAtomHandle16( ATOM atom )
221 if (atom < MIN_STR_ATOM) return 0;
222 return ATOMTOHANDLE( atom );
226 /***********************************************************************
227 * AddAtom16 (KERNEL.70)
229 * Windows DWORD aligns the atom entry size.
230 * The remaining unused string space created by the alignment
231 * gets padded with '\0's in a certain way to ensure
232 * that at least one trailing '\0' remains.
238 ATOM WINAPI AddAtom16( LPCSTR str )
240 char buffer[MAX_ATOM_LEN+1];
243 ATOMENTRY * entryPtr;
248 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
250 TRACE("%s\n",debugstr_a(buffer));
252 /* Make a copy of the string to be sure it doesn't move in linear memory. */
253 lstrcpynA( buffer, str, sizeof(buffer) );
255 len = strlen( buffer );
256 if (!(table = ATOM_GetTable( TRUE ))) return 0;
257 if (CURRENT_DS == ATOM_UserDS) return GlobalAddAtomA( str );
259 hash = ATOM_Hash( table->size, buffer, len );
260 entry = table->entries[hash];
263 entryPtr = ATOM_MakePtr( entry );
264 if ((entryPtr->length == len) &&
265 (!lstrncmpiA( entryPtr->str, buffer, len )))
267 entryPtr->refCount++;
268 TRACE("-- existing 0x%x\n", entry);
269 return HANDLETOATOM( entry );
271 entry = entryPtr->next;
274 ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
275 entry = LocalAlloc16( LMEM_FIXED, ae_len );
276 if (!entry) return 0;
277 /* Reload the table ptr in case it moved in linear memory */
278 table = ATOM_GetTable( FALSE );
279 entryPtr = ATOM_MakePtr( entry );
280 entryPtr->next = table->entries[hash];
281 entryPtr->refCount = 1;
282 entryPtr->length = len;
283 /* Some applications _need_ the '\0' padding provided by this strncpy */
284 strncpy( entryPtr->str, buffer, ae_len - sizeof(ATOMENTRY) + 1 );
285 entryPtr->str[ae_len - sizeof(ATOMENTRY)] = '\0';
286 table->entries[hash] = entry;
287 TRACE("-- new 0x%x\n", entry);
288 return HANDLETOATOM( entry );
292 /***********************************************************************
293 * DeleteAtom16 (KERNEL.71)
295 ATOM WINAPI DeleteAtom16( ATOM atom )
297 ATOMENTRY * entryPtr;
299 HANDLE16 entry, *prevEntry;
302 if (atom < MIN_STR_ATOM) return 0; /* Integer atom */
303 if (CURRENT_DS == ATOM_UserDS) return GlobalDeleteAtom( atom );
305 TRACE("0x%x\n",atom);
307 if (!(table = ATOM_GetTable( FALSE ))) return 0;
308 entry = ATOMTOHANDLE( atom );
309 entryPtr = ATOM_MakePtr( entry );
311 /* Find previous atom */
312 hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
313 prevEntry = &table->entries[hash];
314 while (*prevEntry && *prevEntry != entry)
316 ATOMENTRY * prevEntryPtr = ATOM_MakePtr( *prevEntry );
317 prevEntry = &prevEntryPtr->next;
319 if (!*prevEntry) return atom;
322 if (--entryPtr->refCount == 0)
324 *prevEntry = entryPtr->next;
325 LocalFree16( entry );
331 /***********************************************************************
332 * FindAtom16 (KERNEL.69)
334 ATOM WINAPI FindAtom16( LPCSTR str )
341 if (CURRENT_DS == ATOM_UserDS) return GlobalFindAtomA( str );
343 TRACE("%s\n",debugres_a(str));
345 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
346 if ((len = strlen( str )) > 255) len = 255;
347 if (!(table = ATOM_GetTable( FALSE ))) return 0;
348 hash = ATOM_Hash( table->size, str, len );
349 entry = table->entries[hash];
352 ATOMENTRY * entryPtr = ATOM_MakePtr( entry );
353 if ((entryPtr->length == len) &&
354 (!lstrncmpiA( entryPtr->str, str, len )))
356 TRACE("-- found %x\n", entry);
357 return HANDLETOATOM( entry );
359 entry = entryPtr->next;
361 TRACE("-- not found\n");
366 /***********************************************************************
367 * GetAtomName16 (KERNEL.72)
369 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
372 ATOMENTRY * entryPtr;
378 if (CURRENT_DS == ATOM_UserDS) return GlobalGetAtomNameA( atom, buffer, count );
382 if (!count) return 0;
383 if (atom < MIN_STR_ATOM)
385 sprintf( text, "#%d", atom );
391 if (!(table = ATOM_GetTable( FALSE ))) return 0;
392 entry = ATOMTOHANDLE( atom );
393 entryPtr = ATOM_MakePtr( entry );
394 len = entryPtr->length;
395 strPtr = entryPtr->str;
397 if (len >= count) len = count-1;
398 memcpy( buffer, strPtr, len );
404 /***********************************************************************
405 * AddAtomA (KERNEL32.0)
406 * Adds a string to the atom table and returns the atom identifying the
413 ATOM WINAPI AddAtomA(
414 LPCSTR str /* [in] Pointer to string to add */
416 return GlobalAddAtomA( str ); /* FIXME */
420 /***********************************************************************
421 * AddAtomW (KERNEL32.1)
424 ATOM WINAPI AddAtomW( LPCWSTR str )
426 return GlobalAddAtomW( str ); /* FIXME */
430 /***********************************************************************
431 * DeleteAtom (KERNEL32.69)
432 * Decrements the reference count of a string atom. If count becomes
433 * zero, the string associated with the atom is removed from the table.
439 ATOM WINAPI DeleteAtom(
440 ATOM atom /* [in] Atom to delete */
442 return GlobalDeleteAtom( atom ); /* FIXME */
446 /***********************************************************************
447 * FindAtomA (KERNEL32.117)
448 * Searches the local atom table for the string and returns the atom
449 * associated with that string.
455 ATOM WINAPI FindAtomA(
456 LPCSTR str /* [in] Pointer to string to find */
458 return GlobalFindAtomA( str ); /* FIXME */
462 /***********************************************************************
463 * FindAtomW (KERNEL32.118)
466 ATOM WINAPI FindAtomW( LPCWSTR str )
468 return GlobalFindAtomW( str ); /* FIXME */
472 /***********************************************************************
473 * GetAtomNameA (KERNEL32.149)
474 * Retrieves a copy of the string associated with the atom.
477 * Length of string: Success
480 UINT WINAPI GetAtomNameA(
481 ATOM atom, /* [in] Atom */
482 LPSTR buffer, /* [out] Pointer to string for atom string */
483 INT count /* [in] Size of buffer */
485 return GlobalGetAtomNameA( atom, buffer, count ); /* FIXME */
489 /***********************************************************************
490 * GetAtomNameW (KERNEL32.150)
493 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
495 return GlobalGetAtomNameW( atom, buffer, count ); /* FIXME */
499 /***********************************************************************
500 * GlobalAddAtomA (USER.268) (KERNEL32.313)
502 * Adds a character string to the global atom table and returns a unique
503 * value identifying the string.
509 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
512 if (!ATOM_IsIntAtomA( str, &atom ))
514 struct add_atom_request *req = get_req_buffer();
515 server_strcpyAtoW( req->name, str );
516 if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
518 TRACE( "%s -> %x\n", debugres_a(str), atom );
523 /***********************************************************************
524 * GlobalAddAtomW (KERNEL32.314)
526 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
529 if (!ATOM_IsIntAtomW( str, &atom ))
531 struct add_atom_request *req = get_req_buffer();
532 server_strcpyW( req->name, str );
533 if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
535 TRACE( "%s -> %x\n", debugres_w(str), atom );
540 /***********************************************************************
541 * GlobalDeleteAtom (USER.269) (KERNEL32.317)
542 * Decrements the reference count of a string atom. If the count is
543 * zero, the string associated with the atom is removed from the table.
549 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
551 TRACE( "%x\n", atom );
552 if (atom < MIN_STR_ATOM) atom = 0;
555 struct delete_atom_request *req = get_req_buffer();
556 req->atom = atom - MIN_STR_ATOM;
557 if (!server_call( REQ_DELETE_ATOM )) atom = 0;
563 /***********************************************************************
564 * GlobalFindAtomA (USER.270) (KERNEL32.318)
566 * Searches the atom table for the string and returns the atom
567 * associated with it.
573 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
576 if (!ATOM_IsIntAtomA( str, &atom ))
578 struct find_atom_request *req = get_req_buffer();
579 server_strcpyAtoW( req->name, str );
580 if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
582 TRACE( "%s -> %x\n", debugres_a(str), atom );
587 /***********************************************************************
588 * GlobalFindAtomW (KERNEL32.319)
590 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
593 if (!ATOM_IsIntAtomW( str, &atom ))
595 struct find_atom_request *req = get_req_buffer();
596 server_strcpyW( req->name, str );
597 if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
599 TRACE( "%s -> %x\n", debugres_w(str), atom );
604 /***********************************************************************
605 * GlobalGetAtomNameA (USER.271) (KERNEL32.323)
607 * Retrieves a copy of the string associated with an atom.
610 * Length of string in characters: Success
613 UINT WINAPI GlobalGetAtomNameA(
614 ATOM atom, /* [in] Atom identifier */
615 LPSTR buffer, /* [out] Pointer to buffer for atom string */
616 INT count ) /* [in] Size of buffer */
619 if (atom < MIN_STR_ATOM)
624 SetLastError( ERROR_INVALID_PARAMETER );
627 len = sprintf( name, "#%d", atom );
628 lstrcpynA( buffer, name, count );
632 struct get_atom_name_request *req = get_req_buffer();
633 req->atom = atom - MIN_STR_ATOM;
634 if (server_call( REQ_GET_ATOM_NAME )) return 0;
635 lstrcpynWtoA( buffer, req->name, count );
636 len = lstrlenW( req->name );
640 SetLastError( ERROR_MORE_DATA );
643 TRACE( "%x -> %s\n", atom, debugstr_a(buffer) );
648 /***********************************************************************
649 * GlobalGetAtomNameW (KERNEL32.324)
651 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
654 if (atom < MIN_STR_ATOM)
659 SetLastError( ERROR_INVALID_PARAMETER );
662 len = sprintf( name, "#%d", atom );
663 lstrcpynAtoW( buffer, name, count );
667 struct get_atom_name_request *req = get_req_buffer();
668 req->atom = atom - MIN_STR_ATOM;
669 if (server_call( REQ_GET_ATOM_NAME )) return 0;
670 lstrcpynW( buffer, req->name, count );
671 len = lstrlenW( req->name );
675 SetLastError( ERROR_MORE_DATA );
678 TRACE( "%x -> %s\n", atom, debugstr_w(buffer) );