/// <summary> /// Creates a new buffer for the specified range, if needed. /// If a buffer where this range can be fully contained already exists, /// then the creation of a new buffer is not necessary. /// </summary> /// <param name="address">Address of the buffer in guest memory</param> /// <param name="size">Size in bytes of the buffer</param> private void CreateBufferAligned(ulong address, ulong size) { int overlapsCount; lock (_buffers) { overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps); } if (overlapsCount != 0) { // The buffer already exists. We can just return the existing buffer // if the buffer we need is fully contained inside the overlapping buffer. // Otherwise, we must delete the overlapping buffers and create a bigger buffer // that fits all the data we need. We also need to copy the contents from the // old buffer(s) to the new buffer. ulong endAddress = address + size; if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress) { for (int index = 0; index < overlapsCount; index++) { Buffer buffer = _bufferOverlaps[index]; address = Math.Min(address, buffer.Address); endAddress = Math.Max(endAddress, buffer.EndAddress); lock (_buffers) { _buffers.Remove(buffer); } } Buffer newBuffer = new Buffer(_context, _physicalMemory, address, endAddress - address, _bufferOverlaps.Take(overlapsCount)); lock (_buffers) { _buffers.Add(newBuffer); } for (int index = 0; index < overlapsCount; index++) { Buffer buffer = _bufferOverlaps[index]; int dstOffset = (int)(buffer.Address - newBuffer.Address); buffer.CopyTo(newBuffer, dstOffset); newBuffer.InheritModifiedRanges(buffer); buffer.DisposeData(); } newBuffer.SynchronizeMemory(address, endAddress - address); // Existing buffers were modified, we need to rebind everything. NotifyBuffersModified?.Invoke(); } } else { // No overlap, just create a new buffer. Buffer buffer = new Buffer(_context, _physicalMemory, address, size); lock (_buffers) { _buffers.Add(buffer); } } ShrinkOverlapsBufferIfNeeded(); }
/// <summary> /// Creates a new buffer for the specified range, if needed. /// If a buffer where this range can be fully contained already exists, /// then the creation of a new buffer is not necessary. /// </summary> /// <param name="address">Address of the buffer in guest memory</param> /// <param name="size">Size in bytes of the buffer</param> private void CreateBufferAligned(ulong address, ulong size) { int overlapsCount; lock (_buffers) { overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps); } if (overlapsCount != 0) { // The buffer already exists. We can just return the existing buffer // if the buffer we need is fully contained inside the overlapping buffer. // Otherwise, we must delete the overlapping buffers and create a bigger buffer // that fits all the data we need. We also need to copy the contents from the // old buffer(s) to the new buffer. ulong endAddress = address + size; if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress) { // Check if the following conditions are met: // - We have a single overlap. // - The overlap starts at or before the requested range. That is, the overlap happens at the end. // - The size delta between the new, merged buffer and the old one is of at most 2 pages. // In this case, we attempt to extend the buffer further than the requested range, // this can potentially avoid future resizes if the application keeps using overlapping // sequential memory. // Allowing for 2 pages (rather than just one) is necessary to catch cases where the // range crosses a page, and after alignment, ends having a size of 2 pages. if (overlapsCount == 1 && address >= _bufferOverlaps[0].Address && endAddress - _bufferOverlaps[0].EndAddress <= BufferAlignmentSize * 2) { // Try to grow the buffer by 1.5x of its current size. // This improves performance in the cases where the buffer is resized often by small amounts. ulong existingSize = _bufferOverlaps[0].Size; ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask; size = Math.Max(size, growthSize); endAddress = address + size; overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps); } for (int index = 0; index < overlapsCount; index++) { Buffer buffer = _bufferOverlaps[index]; address = Math.Min(address, buffer.Address); endAddress = Math.Max(endAddress, buffer.EndAddress); lock (_buffers) { _buffers.Remove(buffer); } } ulong newSize = endAddress - address; Buffer newBuffer = new Buffer(_context, _physicalMemory, address, newSize, _bufferOverlaps.Take(overlapsCount)); lock (_buffers) { _buffers.Add(newBuffer); } for (int index = 0; index < overlapsCount; index++) { Buffer buffer = _bufferOverlaps[index]; int dstOffset = (int)(buffer.Address - newBuffer.Address); buffer.CopyTo(newBuffer, dstOffset); newBuffer.InheritModifiedRanges(buffer); buffer.DisposeData(); } newBuffer.SynchronizeMemory(address, newSize); // Existing buffers were modified, we need to rebind everything. NotifyBuffersModified?.Invoke(); } } else { // No overlap, just create a new buffer. Buffer buffer = new Buffer(_context, _physicalMemory, address, size); lock (_buffers) { _buffers.Add(buffer); } } ShrinkOverlapsBufferIfNeeded(); }