Update to correct a bug in GlobalSize with GMEM_MOVEABLE memory. Now a valid heap...
[reactos.git] / reactos / lib / kernel32 / mem / global.c
1 /* $Id: global.c,v 1.15 2003/10/14 01:44:39 mtempel Exp $
2 *
3 * Win32 Global/Local heap functions (GlobalXXX, LocalXXX).
4 * These functions included in Win32 for compatibility with 16 bit Windows
5 * Especially the moveable blocks and handles are oldish.
6 * But the ability to directly allocate memory with GPTR and LPTR is widely
7 * used.
8 *
9 * Updated to support movable memory with algorithms taken from wine.
10 */
11
12 #include <k32.h>
13
14 #define NDEBUG
15 #include <kernel32/kernel32.h>
16
17 #ifdef _GNUC_
18 #define STRUCT_PACK __attribute__((packed))
19 #else
20 #define STRUCT_PACK
21 #endif
22
23 #define MAGIC_GLOBAL_USED 0x5342BEEF
24 #define GLOBAL_LOCK_MAX 0xFF
25
26 /*Wine found that some applications complain if memory isn't 8 byte aligned.
27 * We make use of that experience here.
28 */
29 #define HANDLE_SIZE 8 /*sizeof(HANDLE) *2 */
30
31
32 typedef struct __GLOBAL_LOCAL_HANDLE
33 {
34 DWORD Magic;
35 LPVOID Pointer; STRUCT_PACK
36 BYTE Flags;
37 BYTE LockCount;
38 } GLOBAL_HANDLE, LOCAL_HANDLE, *PGLOBAL_HANDLE, *PLOCAL_HANDLE;
39
40 #define HANDLE_TO_INTERN(h) ((PGLOBAL_HANDLE)(((char *)(h))-4))
41 #define INTERN_TO_HANDLE(i) ((HGLOBAL) &((i)->Pointer))
42 #define POINTER_TO_HANDLE(p) (*(PHANDLE)(p - HANDLE_SIZE))
43 #define ISHANDLE(h) ((((ULONG)(h)) & 0x4)!=0)
44 #define ISPOINTER(h) ((((ULONG)(h)) & 0x4)==0)
45
46
47 static void DbgPrintStruct(PGLOBAL_HANDLE h)
48 {
49 DbgPrint("Magic: 0x%X\n", h->Magic);
50 DbgPrint("Pointer: 0x%X\n", h->Pointer);
51 DbgPrint("Flags: 0x%X\n", h->Flags);
52 DbgPrint("LockCount: 0x%X\n", h->LockCount);
53 }
54
55
56
57 /* FUNCTIONS ***************************************************************/
58
59 /*
60 * @implemented
61 */
62 HGLOBAL STDCALL
63 GlobalAlloc(UINT uFlags,
64 DWORD dwBytes)
65 {
66
67 PGLOBAL_HANDLE phandle = 0;
68 PVOID palloc = 0;
69 UINT heap_flags = 0;
70 /*Fixme: When we are sure all allocations are 8-byte aligned,
71 **we can remove this hack.
72 */
73 PGLOBAL_HANDLE hack_fix = 0;
74
75 if (uFlags & GMEM_ZEROINIT)
76 {
77 heap_flags = HEAP_ZERO_MEMORY;
78 }
79
80 DPRINT("GlobalAlloc( 0x%X, 0x%lX )\n", uFlags, dwBytes);
81
82 //Changed hProcessHeap to GetProcessHeap()
83 if ((uFlags & GMEM_MOVEABLE)==0) /* POINTER */
84 {
85 return ((HGLOBAL)RtlAllocateHeap(GetProcessHeap(), heap_flags, dwBytes));
86 }
87 else /* HANDLE */
88 {
89 HeapLock(hProcessHeap);
90
91 phandle = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(GLOBAL_HANDLE));
92 /* This little hack is to make sure that we get a pointer with 8-byte
93 ** alignment.
94 ** Fixme: When we are sure all allocations are 8-byte aligned,
95 ** we can remove this hack.
96 */
97 if (ISPOINTER(INTERN_TO_HANDLE(phandle)))
98 {
99 hack_fix = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(GLOBAL_HANDLE));
100 RtlFreeHeap(GetProcessHeap(), 0, phandle);
101 phandle = hack_fix;
102 }
103 if (phandle)
104 {
105 phandle->Magic = MAGIC_GLOBAL_USED;
106 phandle->Flags = uFlags >> 8;
107 phandle->LockCount = 0;
108 phandle->Pointer = 0;
109
110 if (dwBytes)
111 {
112 palloc = RtlAllocateHeap(GetProcessHeap(), heap_flags, dwBytes + HANDLE_SIZE);
113 if (palloc)
114 {
115 *(PHANDLE)palloc = INTERN_TO_HANDLE(phandle);
116 phandle->Pointer = palloc + HANDLE_SIZE;
117 }
118 else /*failed to allocate the memory block*/
119 {
120 RtlFreeHeap(GetProcessHeap(), 0, phandle);
121 phandle = 0;
122 }
123 }
124 else
125 {
126 DbgPrint("Allocated a 0 size movable block.\n");
127 DbgPrintStruct(phandle);
128 DbgPrint("Address of the struct: 0x%X\n", phandle);
129 DbgPrint("Address of pointer: 0x%X\n", &(phandle->Pointer));
130 }
131 }
132 HeapUnlock(hProcessHeap);
133
134 if (phandle)
135 return INTERN_TO_HANDLE(phandle);
136 else
137 return (HGLOBAL)0;
138 }
139 }
140
141
142 /*
143 * @implemented
144 */
145 UINT STDCALL
146 GlobalCompact(DWORD dwMinFree)
147 {
148 return RtlCompactHeap(hProcessHeap, 0);
149 }
150
151
152 /*
153 * @implemented
154 */
155 VOID STDCALL
156 GlobalFix(HGLOBAL hMem)
157 {
158 if (INVALID_HANDLE_VALUE != hMem)
159 GlobalLock(hMem);
160 }
161
162 /*
163 * @implemented
164 */
165 UINT STDCALL
166 GlobalFlags(HGLOBAL hMem)
167 {
168 DWORD retval;
169 PGLOBAL_HANDLE phandle;
170
171 DbgPrint("GlobalFlags( 0x%lX )\n", (ULONG)hMem);
172
173 if(!ISHANDLE(hMem))
174 {
175 DbgPrint("GlobalFlags: Fixed memory.\n");
176 retval = 0;
177 }
178 else
179 {
180 HeapLock(GetProcessHeap());
181
182 phandle = HANDLE_TO_INTERN(hMem);
183
184 /*DbgPrintStruct(phandle);*/
185
186 if (MAGIC_GLOBAL_USED == phandle->Magic)
187 {
188 /*DbgPrint("GlobalFlags: Magic number ok\n");
189 **DbgPrint("GlobalFlags: pointer is 0x%X\n", phandle->Pointer);
190 */
191 retval = phandle->LockCount + (phandle->Flags << 8);
192 if (0 == phandle->Pointer)
193 {
194 retval = retval | GMEM_DISCARDED;
195 }
196 }
197 else
198 {
199 DbgPrint("GlobalSize: invalid handle\n");
200 retval = 0;
201 }
202 HeapUnlock(GetProcessHeap());
203 }
204 return retval;
205 }
206
207
208 /*
209 * @implemented
210 */
211 HGLOBAL STDCALL
212 GlobalFree(HGLOBAL hMem)
213 {
214 PGLOBAL_HANDLE phandle;
215
216 DPRINT("GlobalFree( 0x%lX )\n", (ULONG)hMem);
217
218 if (ISPOINTER(hMem)) /* POINTER */
219 {
220 RtlFreeHeap(GetProcessHeap(), 0, (PVOID)hMem);
221 hMem = 0;
222 }
223 else /* HANDLE */
224 {
225 HeapLock(GetProcessHeap());
226
227 phandle = HANDLE_TO_INTERN(hMem);
228
229 if(MAGIC_GLOBAL_USED == phandle->Magic)
230 {
231
232 if(phandle->LockCount!=0)
233 {
234 DbgPrint("Warning! GlobalFree(0x%X) Freeing a handle to a locked object.\n", hMem);
235 SetLastError(ERROR_INVALID_HANDLE);
236 }
237
238 if(phandle->Pointer)
239 RtlFreeHeap(GetProcessHeap(), 0, phandle->Pointer - HANDLE_SIZE);
240
241 RtlFreeHeap(GetProcessHeap(), 0, phandle);
242 }
243 HeapUnlock(GetProcessHeap());
244
245 hMem = 0;
246 }
247 return hMem;
248 }
249
250
251 /*
252 * @implemented
253 */
254 HGLOBAL STDCALL
255 GlobalHandle(LPCVOID pMem)
256 {
257 HGLOBAL handle = 0;
258 PGLOBAL_HANDLE test = 0;
259 LPCVOID pointer_test = 0;
260
261 DbgPrint("GlobalHandle( 0x%lX )\n", (ULONG)pMem);
262 if (0 == pMem) /*Invalid argument */
263 {
264 SetLastError(ERROR_INVALID_PARAMETER);
265 DbgPrint("Error: 0 handle.\n");
266 return 0;
267 }
268
269 HeapLock(GetProcessHeap());
270 /* Now test to see if this pointer is associated with a handle.
271 * This is done by calling RtlValidateHeap() and seeing if it fails.
272 */
273 if (RtlValidateHeap(GetProcessHeap(), 0, (char *)pMem)) /*FIXED*/
274 {
275 handle = (HGLOBAL)pMem;
276 return handle;
277 }
278 else /*MOVABLE*/
279 {
280 handle = POINTER_TO_HANDLE(pMem);
281 }
282
283
284 /* Test to see if this memory is valid*/
285 test = HANDLE_TO_INTERN(handle);
286 if (!IsBadReadPtr(test, sizeof(GLOBAL_HANDLE)))
287 {
288 if (MAGIC_GLOBAL_USED == test->Magic)
289 {
290 pointer_test = test->Pointer;
291 if (!RtlValidateHeap(GetProcessHeap(), 0, ((char *)pointer_test) - HANDLE_SIZE) ||
292 !RtlValidateHeap(GetProcessHeap(), 0, test))
293 {
294 SetLastError(ERROR_INVALID_HANDLE);
295 handle = 0;
296 }
297 }
298 }
299 else
300 {
301 DbgPrint("GlobalHandle: Bad read pointer.\n");
302 SetLastError(ERROR_INVALID_HANDLE);
303 handle = 0;
304 }
305
306 HeapUnlock(GetProcessHeap());
307
308 return handle;
309 }
310
311
312 /*
313 * @implemented
314 */
315 LPVOID STDCALL
316 GlobalLock(HGLOBAL hMem)
317 {
318 PGLOBAL_HANDLE phandle;
319 LPVOID palloc;
320
321 DPRINT("GlobalLock( 0x%lX )\n", (ULONG)hMem);
322
323 if (ISPOINTER(hMem))
324 return (LPVOID) hMem;
325
326 HeapLock(GetProcessHeap());
327
328 phandle = HANDLE_TO_INTERN(hMem);
329
330 if(MAGIC_GLOBAL_USED == phandle->Magic)
331 {
332 if(GLOBAL_LOCK_MAX > phandle->LockCount)
333 {
334 phandle->LockCount++;
335 }
336 palloc = phandle->Pointer;
337 }
338 else
339 {
340 DPRINT("GlobalLock: invalid handle\n");
341 palloc = (LPVOID) hMem;
342 }
343
344 HeapUnlock(GetProcessHeap());
345
346 return palloc;
347 }
348
349
350 /*
351 * @unimplemented
352 */
353 VOID STDCALL
354 GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer)
355 {
356 NTSTATUS Status;
357 SYSTEM_PERFORMANCE_INFORMATION Spi;
358 QUOTA_LIMITS Ql;
359 VM_COUNTERS Vmc;
360 PIMAGE_NT_HEADERS ImageNtHeader;
361
362 RtlZeroMemory (lpBuffer, sizeof (MEMORYSTATUS));
363 lpBuffer->dwLength = sizeof (MEMORYSTATUS);
364 Status = NtQuerySystemInformation (
365 SystemPerformanceInformation,
366 & Spi,
367 sizeof Spi,
368 NULL
369 );
370 /* FIXME: perform computations and fill lpBuffer fields */
371 Status = NtQueryInformationProcess (
372 GetCurrentProcess(),
373 ProcessQuotaLimits,
374 & Ql,
375 sizeof Ql,
376 NULL
377 );
378 /* FIXME: perform computations and fill lpBuffer fields */
379 Status = NtQueryInformationProcess (
380 GetCurrentProcess(),
381 ProcessVmCounters,
382 & Vmc,
383 sizeof Vmc,
384 NULL
385 );
386 /* FIXME: perform computations and fill lpBuffer fields */
387 ImageNtHeader = RtlImageNtHeader ((PVOID)NtCurrentPeb()->ImageBaseAddress);
388 /* FIXME: perform computations and fill lpBuffer fields */
389 }
390
391
392 HGLOBAL STDCALL
393 GlobalReAlloc(HGLOBAL hMem,
394 DWORD dwBytes,
395 UINT uFlags)
396 {
397
398 LPVOID palloc = 0;
399 HGLOBAL hnew = 0;
400 PGLOBAL_HANDLE phandle = 0;
401 ULONG heap_flags = 0;
402
403 DPRINT("GlobalReAlloc( 0x%lX, 0x%lX, 0x%X )\n", (ULONG)hMem, dwBytes, uFlags);
404
405 hnew = 0;
406
407 if (uFlags & GMEM_ZEROINIT)
408 {
409 heap_flags = HEAP_ZERO_MEMORY;
410 }
411
412 HeapLock(GetProcessHeap());
413
414 if(uFlags & GMEM_MODIFY) /* modify flags */
415 {
416 if( ISPOINTER(hMem) && (uFlags & GMEM_MOVEABLE))
417 {
418 /* make a fixed block moveable
419 * actually only NT is able to do this. And it's soo simple
420 */
421 if (0 == hMem)
422 {
423 SetLastError( ERROR_NOACCESS );
424 hnew = 0;
425 }
426 else
427 {
428 dwBytes = RtlSizeHeap(GetProcessHeap(), 0, (LPVOID) hMem);
429 hnew = GlobalAlloc( uFlags, dwBytes);
430 palloc = GlobalLock(hnew);
431 memcpy(palloc, (LPVOID) hMem, dwBytes);
432 GlobalUnlock(hnew);
433 RtlFreeHeap(GetProcessHeap(),0,hMem);
434 }
435 }
436 else if(ISPOINTER(hMem) && (uFlags & GMEM_DISCARDABLE))
437 {
438 /* change the flags to make our block "discardable" */
439 phandle = HANDLE_TO_INTERN(hMem);
440 phandle->Flags = phandle->Flags | (GMEM_DISCARDABLE >> 8);
441 hnew = hMem;
442 }
443 else
444 {
445 SetLastError(ERROR_INVALID_PARAMETER);
446 hnew = 0;
447 }
448 }
449 else
450 {
451 if(ISPOINTER(hMem))
452 {
453 /* reallocate fixed memory */
454 hnew = (HANDLE)RtlReAllocateHeap(GetProcessHeap(), heap_flags, (LPVOID) hMem, dwBytes);
455 }
456 else
457 {
458 /* reallocate a moveable block */
459 phandle= HANDLE_TO_INTERN(hMem);
460 #if 0
461 if(phandle->LockCount != 0)
462 {
463 SetLastError(ERROR_INVALID_HANDLE);
464 }
465 else
466 #endif
467 if (0 != dwBytes)
468 {
469 hnew = hMem;
470 if(phandle->Pointer)
471 {
472 palloc = RtlReAllocateHeap(GetProcessHeap(), heap_flags,
473 phandle->Pointer - HANDLE_SIZE,
474 dwBytes + HANDLE_SIZE);
475 if (0 == palloc)
476 {
477 hnew = 0;
478 }
479 else
480 {
481 *(PHANDLE)palloc = hMem;
482 phandle->Pointer = palloc + HANDLE_SIZE;
483 }
484 }
485 else
486 {
487 palloc = RtlAllocateHeap(GetProcessHeap(), heap_flags, dwBytes + HANDLE_SIZE);
488 if (0 == palloc)
489 {
490 hnew = 0;
491 }
492 else
493 {
494 *(PHANDLE)palloc = hMem;
495 phandle->Pointer = palloc + HANDLE_SIZE;
496 }
497 }
498 }
499 else
500 {
501 hnew = hMem;
502 if(phandle->Pointer)
503 {
504 RtlFreeHeap(GetProcessHeap(), 0, phandle->Pointer - HANDLE_SIZE);
505 phandle->Pointer = 0;
506 }
507 }
508 }
509 }
510 HeapUnlock(GetProcessHeap());
511
512 return hnew;
513 }
514
515
516 DWORD STDCALL
517 GlobalSize(HGLOBAL hMem)
518 {
519 SIZE_T retval = 0;
520 PGLOBAL_HANDLE phandle = 0;
521
522 DbgPrint("GlobalSize( 0x%lX )\n", (ULONG)hMem);
523
524 if(ISPOINTER(hMem)) /*FIXED*/
525 {
526 retval = RtlSizeHeap(GetProcessHeap(), 0, hMem);
527 }
528 else /*MOVEABLE*/
529 {
530 HeapLock(GetProcessHeap());
531
532 phandle = HANDLE_TO_INTERN(hMem);
533
534 if (MAGIC_GLOBAL_USED == phandle->Magic)
535 {
536 if (0 != phandle->Pointer)/*NOT DISCARDED*/
537 {
538 retval = RtlSizeHeap(GetProcessHeap(), 0, phandle->Pointer - HANDLE_SIZE);
539
540 if (retval == (SIZE_T)-1) /*RtlSizeHeap failed*/
541 {
542 /*
543 **TODO: RtlSizeHeap does not set last error.
544 ** We should choose an error value to set as
545 ** the last error. Which One?
546 */
547 DbgPrint("GlobalSize: RtlSizeHeap failed.\n");
548 retval = 0;
549 }
550 else /*Everything is ok*/
551 {
552 retval = retval - HANDLE_SIZE;
553 }
554 }
555 }
556 else
557 {
558 DPRINT("GlobalSize: invalid handle\n");
559 }
560 HeapUnlock(GetProcessHeap());
561 }
562 return retval;
563 }
564
565
566 /*
567 * @implemented
568 */
569 VOID STDCALL
570 GlobalUnfix(HGLOBAL hMem)
571 {
572 if (hMem != INVALID_HANDLE_VALUE)
573 GlobalUnlock(hMem);
574 }
575
576
577 /*
578 * @implemented
579 */
580 BOOL STDCALL
581 GlobalUnlock(HGLOBAL hMem)
582 {
583
584 PGLOBAL_HANDLE phandle;
585 BOOL locked;
586
587 DPRINT("GlobalUnlock( 0x%lX )\n", (ULONG)hMem);
588
589
590 if(ISPOINTER(hMem))
591 return TRUE;
592
593 HeapLock(GetProcessHeap());
594
595 phandle = HANDLE_TO_INTERN(hMem);
596 if(MAGIC_GLOBAL_USED == phandle->Magic)
597 {
598 if((GLOBAL_LOCK_MAX > phandle->LockCount) &&
599 (0 < phandle->LockCount) )
600
601 phandle->LockCount--;
602
603 locked = (0 == phandle->LockCount) ? TRUE : FALSE;
604 }
605 else
606 {
607 DPRINT("GlobalUnlock: invalid handle\n");
608 locked = FALSE;
609 }
610 HeapUnlock(GetProcessHeap());
611 return locked;
612 }
613
614
615 /*
616 * @implemented
617 */
618 BOOL STDCALL
619 GlobalUnWire(HGLOBAL hMem)
620 {
621 return GlobalUnlock(hMem);
622 }
623
624
625 /*
626 * @implemented
627 */
628 LPVOID STDCALL
629 GlobalWire(HGLOBAL hMem)
630 {
631 return GlobalLock(hMem);
632 }
633
634 /*
635 * @implemented
636 */
637 //HGLOBAL STDCALL
638 //GlobalDiscard(HGLOBAL hMem)
639 //{
640 // return GlobalReAlloc(hMem, 0, GMEM_MOVEABLE);
641 //}
642 /* EOF */