/// <summary> /// Checks if a buffer can be created within a given set of pages described by pageInfo /// satisfying the given size, minimum and maximum memory location. /// </summary> /// <param name="pageInfo">Contains the information about a singular memory page.</param> /// <param name="bufferSize">The size that a <see cref="MemoryBuffer"/> would occupy. Pre-aligned to page-size.</param> /// <param name="minimumPtr">The maximum pointer a <see cref="MemoryBuffer"/> can occupy.</param> /// <param name="maximumPtr">The minimum pointer a <see cref="MemoryBuffer"/> can occupy.</param> /// <returns>Zero if the operation fails; otherwise positive value.</returns> private IntPtr GetBufferPointerInPageRange(ref MEMORY_BASIC_INFORMATION pageInfo, int bufferSize, IntPtr minimumPtr, IntPtr maximumPtr) { // Fast return if page is not free. if (pageInfo.State != (uint)MEM_ALLOCATION_TYPE.MEM_FREE) { return(IntPtr.Zero); } // This is valid in both 32bit and 64bit Windows. // We can call GetSystemInfo to get this but that's a waste; these are constant for x86 and x64. int allocationGranularity = 65536; // Do not align page start/end to allocation granularity yet. // Align it when we map the possible buffer ranges in the pages. long pageStart = (long)pageInfo.BaseAddress; long pageEnd = (long)pageInfo.BaseAddress + (long)pageInfo.RegionSize; // Get range for page and min-max region. var minMaxRange = new AddressRange((long)minimumPtr, (long)maximumPtr); var pageRange = new AddressRange(pageStart, pageEnd); if (!pageRange.Overlaps(ref minMaxRange)) { return(IntPtr.Zero); } /* Three possible cases here: * 1. Page fits entirely inside min-max range and is smaller. * 2. Min-max range is inside page (i.e. page is bigger than the range) * 3. Page and min-max intersect, e.g. first half of pages in end of min-max * or second half of pages in start of min-max. * * Below we will build a set of possible buffer allocation ranges * and check if they satisfy our conditions. */ /* Try placing range at start and end of page boundaries. * Since we are allocating in page boundaries, we must compare against Min-Max. */ // Note: We are rounding page boundary addresses up/down, possibly beyond the original ends/starts of page. // We need to validate that we are still in the bounds of the actual page itself. var allocPtrPageMaxAligned = Mathematics.RoundDown(pageRange.EndPointer - bufferSize, allocationGranularity); var allocRangePageMaxStart = new AddressRange(allocPtrPageMaxAligned, allocPtrPageMaxAligned + bufferSize); if (pageRange.Contains(ref allocRangePageMaxStart) && minMaxRange.Contains(ref allocRangePageMaxStart)) { return((IntPtr)allocRangePageMaxStart.StartPointer); } var allocPtrPageMinAligned = Mathematics.RoundUp(pageRange.StartPointer, allocationGranularity); var allocRangePageMinStart = new AddressRange(allocPtrPageMinAligned, allocPtrPageMinAligned + bufferSize); if (pageRange.Contains(ref allocRangePageMinStart) && minMaxRange.Contains(ref allocRangePageMinStart)) { return((IntPtr)allocRangePageMinStart.StartPointer); } /* Try placing range at start and end of given minimum-maximum. * Since we are allocating in Min-Max, we must compare against Page Boundaries. */ // Note: Remember that rounding is dangerous and could potentially cause max and min to cross as usual, // must check proposed page range against both given min-max and page memory range. var allocPtrMaxAligned = Mathematics.RoundDown((long)maximumPtr - bufferSize, allocationGranularity); var allocRangeMaxStart = new AddressRange(allocPtrMaxAligned, allocPtrMaxAligned + bufferSize); if (pageRange.Contains(ref allocRangeMaxStart) && minMaxRange.Contains(ref allocRangeMaxStart)) { return((IntPtr)allocRangeMaxStart.StartPointer); } var allocPtrMinAligned = Mathematics.RoundUp((long)minimumPtr, allocationGranularity); var allocRangeMinStart = new AddressRange(allocPtrMinAligned, allocPtrMinAligned + bufferSize); if (pageRange.Contains(ref allocRangeMinStart) && minMaxRange.Contains(ref allocRangeMinStart)) { return((IntPtr)allocRangeMinStart.StartPointer); } return(IntPtr.Zero); }