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
21 #include "wine/winbase16.h"
22 #include "wine/unicode.h"
25 #include "stackframe.h"
26 #include "debugtools.h"
29 DEFAULT_DEBUG_CHANNEL(atom);
31 #define DEFAULT_ATOMTABLE_SIZE 37
32 #define MIN_STR_ATOM 0xc000
33 #define MAX_ATOM_LEN 255
35 #define ATOMTOHANDLE(atom) ((HANDLE16)(atom) << 2)
36 #define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> 2)))
52 static WORD ATOM_UserDS = 0; /* USER data segment */
54 /***********************************************************************
57 * Global table initialisation.
59 BOOL ATOM_Init( WORD globalTableSel )
61 ATOM_UserDS = globalTableSel;
66 /***********************************************************************
69 * Return a pointer to the atom table of a given segment, creating
73 * Pointer to table: Success
76 static ATOMTABLE *ATOM_GetTable( BOOL create /* [in] Create */ )
78 INSTANCEDATA *ptr = MapSL( MAKESEGPTR( CURRENT_DS, 0 ) );
81 ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
82 if (table->size) return table;
84 if (!create) return NULL;
85 if (!InitAtomTable16( 0 )) return NULL;
86 /* Reload ptr in case it moved in linear memory */
87 ptr = MapSL( MAKESEGPTR( CURRENT_DS, 0 ) );
88 return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
92 /***********************************************************************
95 * The hash value for the input string
97 static WORD ATOM_Hash(
98 WORD entries, /* [in] Total number of entries */
99 LPCSTR str, /* [in] Pointer to string to hash */
100 WORD len /* [in] Length of string */
104 TRACE("%x, %s, %x\n", entries, str, len);
106 for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
107 return hash % entries;
111 /***********************************************************************
114 static BOOL ATOM_IsIntAtomA(LPCSTR atomstr,WORD *atomid)
117 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
120 if (*atomstr++ != '#') return FALSE;
121 while (*atomstr >= '0' && *atomstr <= '9')
123 atom = atom * 10 + *atomstr - '0';
126 if (*atomstr) return FALSE;
128 if (!atom || (atom >= MIN_STR_ATOM))
130 SetLastError( ERROR_INVALID_PARAMETER );
138 /***********************************************************************
141 static BOOL ATOM_IsIntAtomW(LPCWSTR atomstr,WORD *atomid)
144 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
147 if (*atomstr++ != '#') return FALSE;
148 while (*atomstr >= '0' && *atomstr <= '9')
150 atom = atom * 10 + *atomstr - '0';
153 if (*atomstr) return FALSE;
155 if (!atom || (atom >= MIN_STR_ATOM))
157 SetLastError( ERROR_INVALID_PARAMETER );
165 /***********************************************************************
168 * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
170 static inline ATOMENTRY *ATOM_MakePtr( HANDLE16 handle /* [in] Handle */ )
172 return MapSL( MAKESEGPTR( CURRENT_DS, handle ) );
176 /***********************************************************************
177 * InitAtomTable16 (KERNEL.68)
179 WORD WINAPI InitAtomTable16( WORD entries )
185 /* We consider the first table to be initialized as the global table.
186 * This works, as USER (both built-in and native) is the first one to
192 ATOM_UserDS = CURRENT_DS;
193 /* return dummy local handle */
194 return LocalAlloc16( LMEM_FIXED, 1 );
197 /* Allocate the table */
199 if (!entries) entries = DEFAULT_ATOMTABLE_SIZE; /* sanity check */
200 handle = LocalAlloc16( LMEM_FIXED, sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
201 if (!handle) return 0;
202 table = MapSL( MAKESEGPTR( CURRENT_DS, handle ) );
203 table->size = entries;
204 for (i = 0; i < entries; i++) table->entries[i] = 0;
206 /* Store a pointer to the table in the instance data */
208 ((INSTANCEDATA *)MapSL( MAKESEGPTR( CURRENT_DS, 0 )))->atomtable = handle;
212 /***********************************************************************
213 * GetAtomHandle (KERNEL.73)
215 HANDLE16 WINAPI GetAtomHandle16( ATOM atom )
217 if (atom < MIN_STR_ATOM) return 0;
218 return ATOMTOHANDLE( atom );
222 /***********************************************************************
223 * AddAtom16 (KERNEL.70)
225 * Windows DWORD aligns the atom entry size.
226 * The remaining unused string space created by the alignment
227 * gets padded with '\0's in a certain way to ensure
228 * that at least one trailing '\0' remains.
234 ATOM WINAPI AddAtom16( LPCSTR str )
236 char buffer[MAX_ATOM_LEN+1];
239 ATOMENTRY * entryPtr;
244 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
246 TRACE("%s\n",debugstr_a(buffer));
248 /* Make a copy of the string to be sure it doesn't move in linear memory. */
249 lstrcpynA( buffer, str, sizeof(buffer) );
251 len = strlen( buffer );
252 if (!(table = ATOM_GetTable( TRUE ))) return 0;
253 if (CURRENT_DS == ATOM_UserDS) return GlobalAddAtomA( str );
255 hash = ATOM_Hash( table->size, buffer, len );
256 entry = table->entries[hash];
259 entryPtr = ATOM_MakePtr( entry );
260 if ((entryPtr->length == len) &&
261 (!strncasecmp( entryPtr->str, buffer, len )))
263 entryPtr->refCount++;
264 TRACE("-- existing 0x%x\n", entry);
265 return HANDLETOATOM( entry );
267 entry = entryPtr->next;
270 ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
271 entry = LocalAlloc16( LMEM_FIXED, ae_len );
272 if (!entry) return 0;
273 /* Reload the table ptr in case it moved in linear memory */
274 table = ATOM_GetTable( FALSE );
275 entryPtr = ATOM_MakePtr( entry );
276 entryPtr->next = table->entries[hash];
277 entryPtr->refCount = 1;
278 entryPtr->length = len;
279 /* Some applications _need_ the '\0' padding provided by this strncpy */
280 strncpy( entryPtr->str, buffer, ae_len - sizeof(ATOMENTRY) + 1 );
281 entryPtr->str[ae_len - sizeof(ATOMENTRY)] = '\0';
282 table->entries[hash] = entry;
283 TRACE("-- new 0x%x\n", entry);
284 return HANDLETOATOM( entry );
288 /***********************************************************************
289 * DeleteAtom16 (KERNEL.71)
291 ATOM WINAPI DeleteAtom16( ATOM atom )
293 ATOMENTRY * entryPtr;
295 HANDLE16 entry, *prevEntry;
298 if (atom < MIN_STR_ATOM) return 0; /* Integer atom */
299 if (CURRENT_DS == ATOM_UserDS) return GlobalDeleteAtom( atom );
301 TRACE("0x%x\n",atom);
303 if (!(table = ATOM_GetTable( FALSE ))) return 0;
304 entry = ATOMTOHANDLE( atom );
305 entryPtr = ATOM_MakePtr( entry );
307 /* Find previous atom */
308 hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
309 prevEntry = &table->entries[hash];
310 while (*prevEntry && *prevEntry != entry)
312 ATOMENTRY * prevEntryPtr = ATOM_MakePtr( *prevEntry );
313 prevEntry = &prevEntryPtr->next;
315 if (!*prevEntry) return atom;
318 if (--entryPtr->refCount == 0)
320 *prevEntry = entryPtr->next;
321 LocalFree16( entry );
327 /***********************************************************************
328 * FindAtom16 (KERNEL.69)
330 ATOM WINAPI FindAtom16( LPCSTR str )
337 if (CURRENT_DS == ATOM_UserDS) return GlobalFindAtomA( str );
339 TRACE("%s\n",debugres_a(str));
341 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
342 if ((len = strlen( str )) > 255) len = 255;
343 if (!(table = ATOM_GetTable( FALSE ))) return 0;
344 hash = ATOM_Hash( table->size, str, len );
345 entry = table->entries[hash];
348 ATOMENTRY * entryPtr = ATOM_MakePtr( entry );
349 if ((entryPtr->length == len) &&
350 (!strncasecmp( entryPtr->str, str, len )))
352 TRACE("-- found %x\n", entry);
353 return HANDLETOATOM( entry );
355 entry = entryPtr->next;
357 TRACE("-- not found\n");
362 /***********************************************************************
363 * GetAtomName16 (KERNEL.72)
365 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
368 ATOMENTRY * entryPtr;
374 if (CURRENT_DS == ATOM_UserDS) return GlobalGetAtomNameA( atom, buffer, count );
378 if (!count) return 0;
379 if (atom < MIN_STR_ATOM)
381 sprintf( text, "#%d", atom );
387 if (!(table = ATOM_GetTable( FALSE ))) return 0;
388 entry = ATOMTOHANDLE( atom );
389 entryPtr = ATOM_MakePtr( entry );
390 len = entryPtr->length;
391 strPtr = entryPtr->str;
393 if (len >= count) len = count-1;
394 memcpy( buffer, strPtr, len );
399 /***********************************************************************
400 * InitAtomTable (KERNEL32.@)
402 BOOL WINAPI InitAtomTable( DWORD entries )
405 SERVER_START_REQ( init_atom_table )
407 req->entries = entries;
408 ret = !SERVER_CALL_ERR();
415 static ATOM ATOM_AddAtomA( LPCSTR str, BOOL local )
418 if (!ATOM_IsIntAtomA( str, &atom ))
420 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
421 if (len > MAX_ATOM_LEN)
423 SetLastError( ERROR_INVALID_PARAMETER );
426 SERVER_START_VAR_REQ( add_atom, len * sizeof(WCHAR) )
428 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
430 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
434 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
439 /***********************************************************************
440 * GlobalAddAtomA (USER.268)
441 * GlobalAddAtomA (KERNEL32.@)
443 * Adds a character string to the global atom table and returns a unique
444 * value identifying the string.
450 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
452 return ATOM_AddAtomA( str, FALSE );
456 /***********************************************************************
457 * AddAtomA (KERNEL32.@)
458 * Adds a string to the atom table and returns the atom identifying the
465 ATOM WINAPI AddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
467 return ATOM_AddAtomA( str, TRUE );
471 static ATOM ATOM_AddAtomW( LPCWSTR str, BOOL local )
474 if (!ATOM_IsIntAtomW( str, &atom ))
476 DWORD len = strlenW(str);
477 if (len > MAX_ATOM_LEN)
479 SetLastError( ERROR_INVALID_PARAMETER );
482 SERVER_START_VAR_REQ( add_atom, len * sizeof(WCHAR) )
484 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
486 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
490 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
495 /***********************************************************************
496 * GlobalAddAtomW (KERNEL32.@)
498 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
500 return ATOM_AddAtomW( str, FALSE );
504 /***********************************************************************
505 * AddAtomW (KERNEL32.@)
507 ATOM WINAPI AddAtomW( LPCWSTR str )
509 return ATOM_AddAtomW( str, TRUE );
513 static ATOM ATOM_DeleteAtom( ATOM atom, BOOL local)
515 TRACE( "(%s) %x\n", local ? "local" : "global", atom );
516 if (atom < MIN_STR_ATOM) atom = 0;
519 SERVER_START_REQ( delete_atom )
521 req->atom = atom - MIN_STR_ATOM;
523 if (!SERVER_CALL_ERR()) atom = 0;
531 /***********************************************************************
532 * GlobalDeleteAtom (USER.269)
533 * GlobalDeleteAtom (KERNEL32.@)
534 * Decrements the reference count of a string atom. If the count is
535 * zero, the string associated with the atom is removed from the table.
541 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
543 return ATOM_DeleteAtom( atom, FALSE);
547 /***********************************************************************
548 * DeleteAtom (KERNEL32.@)
549 * Decrements the reference count of a string atom. If count becomes
550 * zero, the string associated with the atom is removed from the table.
556 ATOM WINAPI DeleteAtom( ATOM atom /* [in] Atom to delete */ )
558 return ATOM_DeleteAtom( atom, TRUE );
562 static ATOM ATOM_FindAtomA( LPCSTR str, BOOL local )
565 if (!ATOM_IsIntAtomA( str, &atom ))
567 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
568 if (len > MAX_ATOM_LEN)
570 SetLastError( ERROR_INVALID_PARAMETER );
573 SERVER_START_VAR_REQ( find_atom, len * sizeof(WCHAR) )
575 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
577 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
581 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
586 /***********************************************************************
587 * GlobalFindAtomA (USER.270)
588 * GlobalFindAtomA (KERNEL32.@)
590 * Searches the atom table for the string and returns the atom
591 * associated with it.
597 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
599 return ATOM_FindAtomA( str, FALSE );
602 /***********************************************************************
603 * FindAtomA (KERNEL32.@)
604 * Searches the local atom table for the string and returns the atom
605 * associated with that string.
611 ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ )
613 return ATOM_FindAtomA( str, TRUE );
617 static ATOM ATOM_FindAtomW( LPCWSTR str, BOOL local )
620 if (!ATOM_IsIntAtomW( str, &atom ))
622 DWORD len = strlenW(str);
623 if (len > MAX_ATOM_LEN)
625 SetLastError( ERROR_INVALID_PARAMETER );
628 SERVER_START_VAR_REQ( find_atom, len * sizeof(WCHAR) )
630 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
632 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
636 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
641 /***********************************************************************
642 * GlobalFindAtomW (KERNEL32.@)
644 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
646 return ATOM_FindAtomW( str, FALSE );
650 /***********************************************************************
651 * FindAtomW (KERNEL32.@)
653 ATOM WINAPI FindAtomW( LPCWSTR str )
655 return ATOM_FindAtomW( str, TRUE );
659 static UINT ATOM_GetAtomNameA( ATOM atom, LPSTR buffer, INT count, BOOL local )
665 SetLastError( ERROR_MORE_DATA );
668 if (atom < MIN_STR_ATOM)
673 SetLastError( ERROR_INVALID_PARAMETER );
676 len = sprintf( name, "#%d", atom );
677 lstrcpynA( buffer, name, count );
682 SERVER_START_VAR_REQ( get_atom_name, MAX_ATOM_LEN * sizeof(WCHAR) )
684 req->atom = atom - MIN_STR_ATOM;
686 if (!SERVER_CALL_ERR())
688 len = WideCharToMultiByte( CP_ACP, 0, server_data_ptr(req),
689 server_data_size(req) / sizeof(WCHAR),
690 buffer, count - 1, NULL, NULL );
691 if (!len) len = count; /* overflow */
692 else buffer[len] = 0;
698 if (len && count <= len)
700 SetLastError( ERROR_MORE_DATA );
704 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_a(buffer) );
709 /***********************************************************************
710 * GlobalGetAtomNameA (USER.271)
711 * GlobalGetAtomNameA (KERNEL32.@)
713 * Retrieves a copy of the string associated with an atom.
716 * Length of string in characters: Success
719 UINT WINAPI GlobalGetAtomNameA(
720 ATOM atom, /* [in] Atom identifier */
721 LPSTR buffer, /* [out] Pointer to buffer for atom string */
722 INT count ) /* [in] Size of buffer */
724 return ATOM_GetAtomNameA( atom, buffer, count, FALSE );
728 /***********************************************************************
729 * GetAtomNameA (KERNEL32.@)
730 * Retrieves a copy of the string associated with the atom.
733 * Length of string: Success
736 UINT WINAPI GetAtomNameA(
737 ATOM atom, /* [in] Atom */
738 LPSTR buffer, /* [out] Pointer to string for atom string */
739 INT count) /* [in] Size of buffer */
741 return ATOM_GetAtomNameA( atom, buffer, count, TRUE );
745 static UINT ATOM_GetAtomNameW( ATOM atom, LPWSTR buffer, INT count, BOOL local )
751 SetLastError( ERROR_MORE_DATA );
754 if (atom < MIN_STR_ATOM)
759 SetLastError( ERROR_INVALID_PARAMETER );
762 sprintf( name, "#%d", atom );
763 len = MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, count );
764 if (!len) buffer[count-1] = 0; /* overflow */
769 SERVER_START_VAR_REQ( get_atom_name, MAX_ATOM_LEN * sizeof(WCHAR) )
771 req->atom = atom - MIN_STR_ATOM;
773 if (!SERVER_CALL_ERR())
775 len = server_data_size(req) / sizeof(WCHAR);
776 if (count > len) count = len + 1;
777 memcpy( buffer, server_data_ptr(req), (count-1) * sizeof(WCHAR) );
786 SetLastError( ERROR_MORE_DATA );
789 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_w(buffer) );
794 /***********************************************************************
795 * GlobalGetAtomNameW (KERNEL32.@)
797 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
799 return ATOM_GetAtomNameW( atom, buffer, count, FALSE);
803 /***********************************************************************
804 * GetAtomNameW (KERNEL32.@)
806 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
808 return ATOM_GetAtomNameW( atom, buffer, count, TRUE );