commit 6ca1d4e3061d72ac8de2dafe0f72d22f813c885e Author: Andrew Golovashevich Date: Thu Dec 18 23:59:18 2025 +0300 Base implementation diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..d4fa5d9 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[registries] +landgrafhomyak = { index = "sparse+https://cargo.landgrafhomyak.ru/" } \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b5488f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea/ +Cargo.lock +target/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..15b01e7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dynamic-memory-api-0"] + path = dynamic-memory-api-0 + url = https://git.landgrafhomyak.ru/MemoryManagement/dynamic-memory-api-0.git diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..93386fa --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "virtual-memory-windows-0" +version = "0.1.0" +edition = "2024" + +[lints] +rust = { nonstandard_style = "allow", unsafe_op_in_unsafe_fn = "allow" } + +[dependencies] +dynamic-memory-api-0 = { version = "0.0.0", registry = "landgrafhomyak" } + +[dependencies.windows] +version = ">=0.41.0, <=0.62.2" +registry = "crates-io" +features = ["Win32_System_SystemInformation", "Win32_System_Memory"] + +[patch.landgrafhomyak] +dynamic-memory-api-0 = { path = "dynamic-memory-api-0" } + diff --git a/dynamic-memory-api-0 b/dynamic-memory-api-0 new file mode 160000 index 0000000..4d5f71d --- /dev/null +++ b/dynamic-memory-api-0 @@ -0,0 +1 @@ +Subproject commit 4d5f71df4bf191255cfcf1f5ae82d8b8c2183aa9 diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 0000000..6e49d22 --- /dev/null +++ b/src/api.rs @@ -0,0 +1,58 @@ +use crate::reservation::WindowsReservation; +use crate::winapi_wrappers; +use dynamic_memory_api_0::virtual_memory::{ReservationToExtend, VirtualMemoryApi}; +use std::sync::atomic::{AtomicUsize, Ordering}; + +pub struct WindowsVirtualMemoryApi { + pageSize: usize, + totalReservationsOwned: AtomicUsize, +} + +impl WindowsVirtualMemoryApi { + pub fn startScope() -> Self { + return Self { + pageSize: winapi_wrappers::getPageSize(), + totalReservationsOwned: AtomicUsize::new(0), + }; + } + + pub(super) fn changeReservationsCount(&self, diff: isize) { + let mut current = self.totalReservationsOwned.load(Ordering::Relaxed); + loop { + let updated = current.strict_add_signed(diff); + match self.totalReservationsOwned.compare_exchange_weak( + current, + updated, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => return, + Err(new) => current = new, + } + } + } +} + +unsafe impl VirtualMemoryApi for WindowsVirtualMemoryApi { + type Reservation<'s> = WindowsReservation<'s>; + + fn getPageSize(&self) -> usize { + return self.pageSize; + } + + fn reserveMemory<'s>( + &'s self, + extend: ReservationToExtend<&Self::Reservation<'s>>, + pagesCount: usize, + ) -> Option> { + return WindowsReservation::tryReserve(self, extend, pagesCount); + } +} + +impl Drop for WindowsVirtualMemoryApi { + fn drop(&mut self) { + if self.totalReservationsOwned.load(Ordering::Relaxed) > 0 { + panic!("Not all reservations got during this scope was released"); + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0c460dc --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,13 @@ +extern crate core; + +mod api; +mod reservation; +mod winapi_wrappers; + +pub use api::WindowsVirtualMemoryApi; + +#[cfg(not(windows))] +mod _target_check { + use core::compile_error; + compile_error!("This crate can be compiled only for windows targets"); +} \ No newline at end of file diff --git a/src/reservation.rs b/src/reservation.rs new file mode 100644 index 0000000..56af34c --- /dev/null +++ b/src/reservation.rs @@ -0,0 +1,132 @@ +use crate::{winapi_wrappers, WindowsVirtualMemoryApi}; +use dynamic_memory_api_0::virtual_memory::{Reservation, ReservationToExtend, VirtualMemoryApi}; +use std::cmp::Ordering; +use std::ffi::c_void; +use std::mem::ManuallyDrop; +use std::ptr::NonNull; +use std::slice::from_raw_parts_mut; +use std::thread::panicking; + +pub struct WindowsReservation<'s> { + owner: &'s WindowsVirtualMemoryApi, + start: NonNull, + pagesCount: usize, +} + +impl<'s> WindowsReservation<'s> { + pub(super) fn tryReserve( + owner: &'s WindowsVirtualMemoryApi, + extend: ReservationToExtend<&WindowsReservation<'s>>, + pagesCount: usize, + ) -> Option { + let start: Option>; + match extend { + ReservationToExtend::ReserveAnywhere => start = None, + ReservationToExtend::TryExtend(prev) => unsafe { + start = Some(prev._offset(prev.pagesCount)) + }, + }; + + let ptr_o; + unsafe { + ptr_o = winapi_wrappers::reserve(start, owner.getPageSize() * pagesCount); + } + + let ptr; + match ptr_o { + None => return None, + Some(ptr_nn) => ptr = unsafe { NonNull::new_unchecked(ptr_nn.as_ptr()) }, + } + + owner.changeReservationsCount(1); + + let reservation = WindowsReservation { + owner: owner, + start: ptr, + pagesCount: pagesCount, + }; + + return Some(reservation); + } + + unsafe fn _offset(&self, index: usize) -> NonNull { + return self.start.add(self.owner.getPageSize() * index); + } + + unsafe fn _size(&self, start: usize, size: usize) -> usize { + return self + ._offset(start + size) + .offset_from_unsigned(self._offset(start)); + } + + fn _assertBounds(&self, start: usize, size: usize) { + if start >= self.pagesCount { + panic!("First page's index out of bounds") + } + if size == 0 { + panic!("Zero pages commited") + } + if start + size >= self.pagesCount { + panic!("Last page's index out of bounds") + } + } + + unsafe fn _release(&mut self) { + winapi_wrappers::release(self.start, self._size(0, self.pagesCount)); + self.owner.changeReservationsCount(-1); + } +} + +impl Ord for WindowsReservation<'_> { + fn cmp(&self, other: &Self) -> Ordering { + return self.start.cmp(&other.start); + } +} + +impl Eq for WindowsReservation<'_> {} + +impl PartialEq for WindowsReservation<'_> { + fn eq(&self, other: &Self) -> bool { + return self.start.eq(&other.start); + } +} + +impl PartialOrd for WindowsReservation<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + return self.start.partial_cmp(&other.start); + } +} + +impl Drop for WindowsReservation<'_> { + fn drop(&mut self) { + unsafe { + self._release(); + } + if !panicking() { + panic!("Reserved virtual memory wasn't released explicitly"); + } + } +} + +unsafe impl Reservation for WindowsReservation<'_> { + unsafe fn commitPages(&mut self, indexOfFirst: usize, count: usize) -> *mut [u8] { + self._assertBounds(indexOfFirst, count); + let start = self._offset(indexOfFirst); + let size = self._size(indexOfFirst, count); + winapi_wrappers::commit(start, size); + return from_raw_parts_mut(start.as_ptr().cast(), size); + } + + unsafe fn decommitPages(&mut self, indexOfFirst: usize, count: usize) { + self._assertBounds(indexOfFirst, count); + winapi_wrappers::decommit( + self._offset(indexOfFirst), + self._size(indexOfFirst, count), + ) + } + + unsafe fn release(mut self) { + self._release(); + let _ = ManuallyDrop::new(self); + } +} diff --git a/src/winapi_wrappers.rs b/src/winapi_wrappers.rs new file mode 100644 index 0000000..cb183f4 --- /dev/null +++ b/src/winapi_wrappers.rs @@ -0,0 +1,87 @@ +use std::cmp::max; +use std::ffi::c_void; +use std::ptr::{NonNull, null_mut}; +use windows::Win32::Foundation::{ + ERROR_INVALID_ADDRESS, ERROR_NOT_ENOUGH_MEMORY, GetLastError, WIN32_ERROR, +}; +use windows::Win32::System::Memory::{ + MEM_COMMIT, MEM_DECOMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_NOACCESS, PAGE_READWRITE, + VirtualAlloc, VirtualFree, +}; +use windows::Win32::System::SystemInformation::{GetSystemInfo, SYSTEM_INFO}; + +pub(super) unsafe fn reserve( + start: Option>, + size: usize, +) -> Option> { + let raw = VirtualAlloc( + start.map(|p| p.as_ptr() as *const c_void), + size, + MEM_RESERVE, + PAGE_NOACCESS, + ); + if raw == null_mut::() { + match GetLastError() { + ERROR_NOT_ENOUGH_MEMORY => return None, + ERROR_INVALID_ADDRESS => { + panic!("Requested address already in use (ERROR_INVALID_ADDRESS)") + } + WIN32_ERROR(code) => panic!("Unexpected Win32 API Error: code={}", code), + } + }; + + return NonNull::new(raw); +} + +pub(super) unsafe fn commit(start: NonNull, size: usize) { + let raw = VirtualAlloc( + Some(start.as_ptr() as *const c_void), + size, + MEM_COMMIT, + PAGE_READWRITE, + ); + if raw == null_mut::() { + match GetLastError() { + ERROR_INVALID_ADDRESS => { + panic!("Some of pages being commited aren't reserved (ERROR_INVALID_ADDRESS)") + } + WIN32_ERROR(code) => panic!("Unexpected Win32 API Error: code={}", code), + } + }; +} + +pub(super) unsafe fn decommit(start: NonNull, size: usize) { + let result = VirtualFree(start.as_ptr(), size, MEM_DECOMMIT); + + if result.is_err() { + match GetLastError() { + ERROR_INVALID_ADDRESS => { + panic!("Some of pages being decommited aren't commited (ERROR_INVALID_ADDRESS)") + } + WIN32_ERROR(code) => panic!("Unexpected Win32 API Error: code={}", code), + } + }; +} + +pub(super) unsafe fn release(start: NonNull, size: usize) { + let result = VirtualFree(start.as_ptr(), size, MEM_RELEASE); + + if result.is_err() { + match GetLastError() { + ERROR_INVALID_ADDRESS => { + panic!( + "Some of pages being released aren't reserved or still commited (ERROR_INVALID_ADDRESS)" + ) + } + WIN32_ERROR(code) => panic!("Unexpected Win32 API Error: code={}", code), + } + }; +} + +pub(super) fn getPageSize() -> usize { + let mut si = SYSTEM_INFO::default(); + unsafe { + GetSystemInfo(&mut si as *mut _); + } + return max(si.dwPageSize, si.dwAllocationGranularity) as usize; +}