123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- //go:build windows
- // +build windows
- package memory_map
- import (
- "os"
- "reflect"
- "syscall"
- "unsafe"
- "golang.org/x/sys/windows"
- )
- type DWORDLONG = uint64
- type DWORD = uint32
- type WORD = uint16
- var (
- modkernel32 = syscall.NewLazyDLL("kernel32.dll")
- procGetSystemInfo = modkernel32.NewProc("GetSystemInfo")
- procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx")
- procGetProcessWorkingSetSize = modkernel32.NewProc("GetProcessWorkingSetSize")
- procSetProcessWorkingSetSize = modkernel32.NewProc("SetProcessWorkingSetSize")
- )
- var currentProcess, _ = windows.GetCurrentProcess()
- var currentMinWorkingSet uint64 = 0
- var currentMaxWorkingSet uint64 = 0
- var _ = getProcessWorkingSetSize(uintptr(currentProcess), ¤tMinWorkingSet, ¤tMaxWorkingSet)
- var systemInfo, _ = getSystemInfo()
- var chunkSize = uint64(systemInfo.dwAllocationGranularity) * 128
- var memoryStatusEx, _ = globalMemoryStatusEx()
- var maxMemoryLimitBytes = uint64(float64(memoryStatusEx.ullTotalPhys) * 0.8)
- func (mMap *MemoryMap) CreateMemoryMap(file *os.File, maxLength uint64) {
- chunks := (maxLength / chunkSize)
- if chunks*chunkSize < maxLength {
- chunks = chunks + 1
- }
- alignedMaxLength := chunks * chunkSize
- maxLength_high := uint32(alignedMaxLength >> 32)
- maxLength_low := uint32(alignedMaxLength & 0xFFFFFFFF)
- file_memory_map_handle, err := windows.CreateFileMapping(windows.Handle(file.Fd()), nil, windows.PAGE_READWRITE, maxLength_high, maxLength_low, nil)
- if err == nil {
- mMap.File = file
- mMap.file_memory_map_handle = uintptr(file_memory_map_handle)
- mMap.write_map_views = make([]MemoryBuffer, 0, alignedMaxLength/chunkSize)
- mMap.max_length = alignedMaxLength
- mMap.End_of_file = -1
- }
- }
- func (mMap *MemoryMap) DeleteFileAndMemoryMap() {
- //First we close the file handles first to delete the file,
- //Then we unmap the memory to ensure the unmapping process doesn't write the data to disk
- windows.CloseHandle(windows.Handle(mMap.file_memory_map_handle))
- windows.CloseHandle(windows.Handle(mMap.File.Fd()))
- for _, view := range mMap.write_map_views {
- view.releaseMemory()
- }
- mMap.write_map_views = nil
- mMap.max_length = 0
- }
- func min(x, y uint64) uint64 {
- if x < y {
- return x
- }
- return y
- }
- func (mMap *MemoryMap) WriteMemory(offset uint64, length uint64, data []byte) {
- for {
- if ((offset+length)/chunkSize)+1 > uint64(len(mMap.write_map_views)) {
- allocateChunk(mMap)
- } else {
- break
- }
- }
- remaining_length := length
- sliceIndex := offset / chunkSize
- sliceOffset := offset - (sliceIndex * chunkSize)
- dataOffset := uint64(0)
- for {
- writeEnd := min((remaining_length + sliceOffset), chunkSize)
- copy(mMap.write_map_views[sliceIndex].Buffer[sliceOffset:writeEnd], data[dataOffset:])
- remaining_length -= (writeEnd - sliceOffset)
- dataOffset += (writeEnd - sliceOffset)
- if remaining_length > 0 {
- sliceIndex += 1
- sliceOffset = 0
- } else {
- break
- }
- }
- if mMap.End_of_file < int64(offset+length-1) {
- mMap.End_of_file = int64(offset + length - 1)
- }
- }
- func (mMap *MemoryMap) ReadMemory(offset uint64, length uint64) (dataSlice []byte, err error) {
- dataSlice = make([]byte, length)
- mBuffer, err := allocate(windows.Handle(mMap.file_memory_map_handle), offset, length, false)
- copy(dataSlice, mBuffer.Buffer)
- mBuffer.releaseMemory()
- return dataSlice, err
- }
- func (mBuffer *MemoryBuffer) releaseMemory() {
- windows.VirtualUnlock(mBuffer.aligned_ptr, uintptr(mBuffer.aligned_length))
- windows.UnmapViewOfFile(mBuffer.aligned_ptr)
- currentMinWorkingSet -= mBuffer.aligned_length
- currentMaxWorkingSet -= mBuffer.aligned_length
- if currentMinWorkingSet < maxMemoryLimitBytes {
- var _ = setProcessWorkingSetSize(uintptr(currentProcess), currentMinWorkingSet, currentMaxWorkingSet)
- }
- mBuffer.ptr = 0
- mBuffer.aligned_ptr = 0
- mBuffer.length = 0
- mBuffer.aligned_length = 0
- mBuffer.Buffer = nil
- }
- func allocateChunk(mMap *MemoryMap) {
- start := uint64(len(mMap.write_map_views)) * chunkSize
- mBuffer, err := allocate(windows.Handle(mMap.file_memory_map_handle), start, chunkSize, true)
- if err == nil {
- mMap.write_map_views = append(mMap.write_map_views, mBuffer)
- }
- }
- func allocate(hMapFile windows.Handle, offset uint64, length uint64, write bool) (MemoryBuffer, error) {
- mBuffer := MemoryBuffer{}
- //align memory allocations to the minium virtal memory allocation size
- dwSysGran := systemInfo.dwAllocationGranularity
- start := (offset / uint64(dwSysGran)) * uint64(dwSysGran)
- diff := offset - start
- aligned_length := diff + length
- offset_high := uint32(start >> 32)
- offset_low := uint32(start & 0xFFFFFFFF)
- access := windows.FILE_MAP_READ
- if write {
- access = windows.FILE_MAP_WRITE
- }
- currentMinWorkingSet += aligned_length
- currentMaxWorkingSet += aligned_length
- if currentMinWorkingSet < maxMemoryLimitBytes {
- // increase the process working set size to hint to windows memory manager to
- // prioritise keeping this memory mapped in physical memory over other standby memory
- var _ = setProcessWorkingSetSize(uintptr(currentProcess), currentMinWorkingSet, currentMaxWorkingSet)
- }
- addr_ptr, errno := windows.MapViewOfFile(hMapFile,
- uint32(access), // read/write permission
- offset_high,
- offset_low,
- uintptr(aligned_length))
- if addr_ptr == 0 {
- return mBuffer, errno
- }
- if currentMinWorkingSet < maxMemoryLimitBytes {
- windows.VirtualLock(mBuffer.aligned_ptr, uintptr(mBuffer.aligned_length))
- }
- mBuffer.aligned_ptr = addr_ptr
- mBuffer.aligned_length = aligned_length
- mBuffer.ptr = addr_ptr + uintptr(diff)
- mBuffer.length = length
- slice_header := (*reflect.SliceHeader)(unsafe.Pointer(&mBuffer.Buffer))
- slice_header.Data = addr_ptr + uintptr(diff)
- slice_header.Len = int(length)
- slice_header.Cap = int(length)
- return mBuffer, nil
- }
- //typedef struct _MEMORYSTATUSEX {
- // DWORD dwLength;
- // DWORD dwMemoryLoad;
- // DWORDLONG ullTotalPhys;
- // DWORDLONG ullAvailPhys;
- // DWORDLONG ullTotalPageFile;
- // DWORDLONG ullAvailPageFile;
- // DWORDLONG ullTotalVirtual;
- // DWORDLONG ullAvailVirtual;
- // DWORDLONG ullAvailExtendedVirtual;
- // } MEMORYSTATUSEX, *LPMEMORYSTATUSEX;
- //https://docs.microsoft.com/en-gb/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex
- type _MEMORYSTATUSEX struct {
- dwLength DWORD
- dwMemoryLoad DWORD
- ullTotalPhys DWORDLONG
- ullAvailPhys DWORDLONG
- ullTotalPageFile DWORDLONG
- ullAvailPageFile DWORDLONG
- ullTotalVirtual DWORDLONG
- ullAvailVirtual DWORDLONG
- ullAvailExtendedVirtual DWORDLONG
- }
- // BOOL GlobalMemoryStatusEx(
- // LPMEMORYSTATUSEX lpBuffer
- // );
- // https://docs.microsoft.com/en-gb/windows/win32/api/sysinfoapi/nf-sysinfoapi-globalmemorystatusex
- func globalMemoryStatusEx() (_MEMORYSTATUSEX, error) {
- var mem_status _MEMORYSTATUSEX
- mem_status.dwLength = uint32(unsafe.Sizeof(mem_status))
- _, _, err := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&mem_status)))
- if err != syscall.Errno(0) {
- return mem_status, err
- }
- return mem_status, nil
- }
- // typedef struct _SYSTEM_INFO {
- // union {
- // DWORD dwOemId;
- // struct {
- // WORD wProcessorArchitecture;
- // WORD wReserved;
- // };
- // };
- // DWORD dwPageSize;
- // LPVOID lpMinimumApplicationAddress;
- // LPVOID lpMaximumApplicationAddress;
- // DWORD_PTR dwActiveProcessorMask;
- // DWORD dwNumberOfProcessors;
- // DWORD dwProcessorType;
- // DWORD dwAllocationGranularity;
- // WORD wProcessorLevel;
- // WORD wProcessorRevision;
- // } SYSTEM_INFO;
- // https://docs.microsoft.com/en-gb/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
- type _SYSTEM_INFO struct {
- dwOemId DWORD
- dwPageSize DWORD
- lpMinimumApplicationAddress uintptr
- lpMaximumApplicationAddress uintptr
- dwActiveProcessorMask uintptr
- dwNumberOfProcessors DWORD
- dwProcessorType DWORD
- dwAllocationGranularity DWORD
- wProcessorLevel WORD
- wProcessorRevision WORD
- }
- // void WINAPI GetSystemInfo(
- // _Out_ LPSYSTEM_INFO lpSystemInfo
- // );
- // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo
- func getSystemInfo() (_SYSTEM_INFO, error) {
- var si _SYSTEM_INFO
- _, _, err := procGetSystemInfo.Call(uintptr(unsafe.Pointer(&si)))
- if err != syscall.Errno(0) {
- return si, err
- }
- return si, nil
- }
- // BOOL GetProcessWorkingSetSize(
- // HANDLE hProcess,
- // PSIZE_T lpMinimumWorkingSetSize,
- // PSIZE_T lpMaximumWorkingSetSize
- // );
- // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getprocessworkingsetsize
- func getProcessWorkingSetSize(process uintptr, dwMinWorkingSet *uint64, dwMaxWorkingSet *uint64) error {
- r1, _, err := syscall.Syscall(procGetProcessWorkingSetSize.Addr(), 3, process, uintptr(unsafe.Pointer(dwMinWorkingSet)), uintptr(unsafe.Pointer(dwMaxWorkingSet)))
- if r1 == 0 {
- if err != syscall.Errno(0) {
- return err
- }
- }
- return nil
- }
- // BOOL SetProcessWorkingSetSize(
- // HANDLE hProcess,
- // SIZE_T dwMinimumWorkingSetSize,
- // SIZE_T dwMaximumWorkingSetSize
- // );
- // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setprocessworkingsetsize
- func setProcessWorkingSetSize(process uintptr, dwMinWorkingSet uint64, dwMaxWorkingSet uint64) error {
- r1, _, err := syscall.Syscall(procSetProcessWorkingSetSize.Addr(), 3, process, uintptr(dwMinWorkingSet), uintptr(dwMaxWorkingSet))
- if r1 == 0 {
- if err != syscall.Errno(0) {
- return err
- }
- }
- return nil
- }
|