Base implementation

This commit is contained in:
Andrew Golovashevich 2025-12-18 23:59:18 +03:00
commit 6ca1d4e306
9 changed files with 318 additions and 0 deletions

2
.cargo/config.toml Normal file
View File

@ -0,0 +1,2 @@
[registries]
landgrafhomyak = { index = "sparse+https://cargo.landgrafhomyak.ru/" }

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/.idea/
Cargo.lock
target/

3
.gitmodules vendored Normal file
View File

@ -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

19
Cargo.toml Normal file
View File

@ -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" }

1
dynamic-memory-api-0 Submodule

@ -0,0 +1 @@
Subproject commit 4d5f71df4bf191255cfcf1f5ae82d8b8c2183aa9

58
src/api.rs Normal file
View File

@ -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<Self::Reservation<'s>> {
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");
}
}
}

13
src/lib.rs Normal file
View File

@ -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");
}

132
src/reservation.rs Normal file
View File

@ -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<c_void>,
pagesCount: usize,
}
impl<'s> WindowsReservation<'s> {
pub(super) fn tryReserve(
owner: &'s WindowsVirtualMemoryApi,
extend: ReservationToExtend<&WindowsReservation<'s>>,
pagesCount: usize,
) -> Option<Self> {
let start: Option<NonNull<c_void>>;
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<c_void> {
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<Self> for WindowsReservation<'_> {
fn eq(&self, other: &Self) -> bool {
return self.start.eq(&other.start);
}
}
impl PartialOrd<Self> for WindowsReservation<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
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);
}
}

87
src/winapi_wrappers.rs Normal file
View File

@ -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<NonNull<c_void>>,
size: usize,
) -> Option<NonNull<c_void>> {
let raw = VirtualAlloc(
start.map(|p| p.as_ptr() as *const c_void),
size,
MEM_RESERVE,
PAGE_NOACCESS,
);
if raw == null_mut::<c_void>() {
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<c_void>, size: usize) {
let raw = VirtualAlloc(
Some(start.as_ptr() as *const c_void),
size,
MEM_COMMIT,
PAGE_READWRITE,
);
if raw == null_mut::<c_void>() {
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<c_void>, 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<c_void>, 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;
}