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