]> icculus.org git repositories - icculus/xz.git/blob - src/common/physmem.h
Fix incorrect use of "restrict".
[icculus/xz.git] / src / common / physmem.h
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       physmem.h
4 /// \brief      Get the amount of physical memory
5 //
6 //  Author:     Lasse Collin
7 //
8 //  This file has been put into the public domain.
9 //  You can do whatever you want with this file.
10 //
11 ///////////////////////////////////////////////////////////////////////////////
12
13 #ifndef PHYSMEM_H
14 #define PHYSMEM_H
15
16 // Test for Windows first, because we want to use Windows-specific code
17 // on Cygwin, which also has memory information available via sysconf(), but
18 // on Cygwin 1.5 and older it gives wrong results (from our point of view).
19 #if defined(_WIN32) || defined(__CYGWIN__)
20 #       ifndef _WIN32_WINNT
21 #               define _WIN32_WINNT 0x0500
22 #       endif
23 #       include <windows.h>
24
25 #elif defined(__OS2__)
26 #       define INCL_DOSMISC
27 #       include <os2.h>
28
29 #elif defined(__DJGPP__)
30 #       include <dpmi.h>
31
32 #elif defined(HAVE_PHYSMEM_SYSCONF)
33 #       include <unistd.h>
34
35 #elif defined(HAVE_PHYSMEM_SYSCTL)
36 #       ifdef HAVE_SYS_PARAM_H
37 #               include <sys/param.h>
38 #       endif
39 #       ifdef HAVE_SYS_SYSCTL_H
40 #               include <sys/sysctl.h>
41 #       endif
42
43 #elif defined(HAVE_PHYSMEM_SYSINFO)
44 #       include <sys/sysinfo.h>
45 #endif
46
47
48 /// \brief      Get the amount of physical memory in bytes
49 ///
50 /// \return     Amount of physical memory in bytes. On error, zero is
51 ///             returned.
52 static inline uint64_t
53 physmem(void)
54 {
55         uint64_t ret = 0;
56
57 #if defined(_WIN32) || defined(__CYGWIN__)
58         if ((GetVersion() & 0xFF) >= 5) {
59                 // Windows 2000 and later have GlobalMemoryStatusEx() which
60                 // supports reporting values greater than 4 GiB. To keep the
61                 // code working also on older Windows versions, use
62                 // GlobalMemoryStatusEx() conditionally.
63                 HMODULE kernel32 = GetModuleHandle("kernel32.dll");
64                 if (kernel32 != NULL) {
65                         BOOL (WINAPI *gmse)(LPMEMORYSTATUSEX) = GetProcAddress(
66                                         kernel32, "GlobalMemoryStatusEx");
67                         if (gmse != NULL) {
68                                 MEMORYSTATUSEX meminfo;
69                                 meminfo.dwLength = sizeof(meminfo);
70                                 if (gmse(&meminfo))
71                                         ret = meminfo.ullTotalPhys;
72                         }
73                 }
74         }
75
76         if (ret == 0) {
77                 // GlobalMemoryStatus() is supported by Windows 95 and later,
78                 // so it is fine to link against it unconditionally. Note that
79                 // GlobalMemoryStatus() has no return value.
80                 MEMORYSTATUS meminfo;
81                 meminfo.dwLength = sizeof(meminfo);
82                 GlobalMemoryStatus(&meminfo);
83                 ret = meminfo.dwTotalPhys;
84         }
85
86 #elif defined(__OS2__)
87         unsigned long mem;
88         if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
89                         &mem, sizeof(mem)) == 0)
90                 ret = mem;
91
92 #elif defined(__DJGPP__)
93         __dpmi_free_mem_info meminfo;
94         if (__dpmi_get_free_memory_information(&meminfo) == 0
95                         && meminfo.total_number_of_physical_pages
96                                 != (unsigned long)(-1))
97                 ret = (uint64_t)(meminfo.total_number_of_physical_pages)
98                                 * 4096;
99
100 #elif defined(HAVE_PHYSMEM_SYSCONF)
101         const long pagesize = sysconf(_SC_PAGESIZE);
102         const long pages = sysconf(_SC_PHYS_PAGES);
103         if (pagesize != -1 || pages != -1)
104                 // According to docs, pagesize * pages can overflow.
105                 // Simple case is 32-bit box with 4 GiB or more RAM,
106                 // which may report exactly 4 GiB of RAM, and "long"
107                 // being 32-bit will overflow. Casting to uint64_t
108                 // hopefully avoids overflows in the near future.
109                 ret = (uint64_t)(pagesize) * (uint64_t)(pages);
110
111 #elif defined(HAVE_PHYSMEM_SYSCTL)
112         int name[2] = {
113                 CTL_HW,
114 #ifdef HW_PHYSMEM64
115                 HW_PHYSMEM64
116 #else
117                 HW_PHYSMEM
118 #endif
119         };
120         union {
121                 uint32_t u32;
122                 uint64_t u64;
123         } mem;
124         size_t mem_ptr_size = sizeof(mem.u64);
125         if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) {
126                 // IIRC, 64-bit "return value" is possible on some 64-bit
127                 // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
128                 // so support both.
129                 if (mem_ptr_size == sizeof(mem.u64))
130                         ret = mem.u64;
131                 else if (mem_ptr_size == sizeof(mem.u32))
132                         ret = mem.u32;
133         }
134
135 #elif defined(HAVE_PHYSMEM_SYSINFO)
136         struct sysinfo si;
137         if (sysinfo(&si) == 0)
138                 ret = (uint64_t)(si.totalram) * si.mem_unit;
139 #endif
140
141         return ret;
142 }
143
144 #endif