public static List <DbItem> ReadFixedSizeItems(IPage page) { short[] itemLengths = ReadFixedSizeItemLengths(page); var result = new List <DbItem>(itemLengths.Length); short maxSize = DbItem.GetMaxSize(PageHeaderBase.GetSizeRange(page)); int offset = page.Content.Length - maxSize; var content = page.Content; int cnt = itemLengths.Length; for (int i = 0; i < cnt; i++) { var il = itemLengths[i]; if (il != -1) // item is actualy present on page, read it { var itemBytes = new byte[Math.Max(il, (short)0)]; Buffer.BlockCopy(content, offset, itemBytes, 0, itemBytes.Length); result.Add(new DbItem(itemBytes)); } offset -= maxSize; } return(result); }
public static bool HasFreeSpaceForFixedSizeItem(IPage page) { PageHeaderBase header = GetPageHeader(page); if (header.SizeRange == SizeRange.NotApplicable || header.SizeRange == SizeRange.MultiPage) { return(false); } short fixedSizeItemMarkersLength = ReadFixedSizeItemMarkersLength(page); short slotSize = DbItem.GetMaxSize(header.SizeRange); int remainingSpace = page.Length - // full page length header.Length - // subtract header length fixedSizeItemMarkersLength * OnPagePointerSize - OnPagePointerSize - // subtract item length markers array and its length ReadFixedSizeItemsCount(page) * slotSize // subtract non-deleted items - OnPagePointerSize; // subtruct new item length marker needed to add a new item if (remainingSpace < slotSize) { return(false); } return(true); }
public static void RewriteFixedSizeItem(IPage page, short itemIndex, DbItem item) { if (itemIndex < 0) { throw new ArgumentOutOfRangeException(nameof(itemIndex)); } PageHeaderBase header = GetPageHeader(page); if (header.SizeRange == SizeRange.MultiPage || header.SizeRange == SizeRange.NotApplicable) { throw new PageFormatException("Page is not dedicated to fixed size items."); } if (ReadFixedSizeItemMarkersLength(page) - 1 < itemIndex) { throw new PageFormatException("Unable to rewrite fixed size item. Index is too large."); } // write the item length marker byte[] lengthBytes = BitConverter.GetBytes((short)item.RawData.Length); lengthBytes.CopyTo(page.Content, header.Length + // length of header OnPagePointerSize + // length of length marker array itemIndex * OnPagePointerSize // offset in length marker array ); Buffer.BlockCopy(item.RawData, 0, page.Content, page.Length - DbItem.GetMaxSize(header.SizeRange) * (itemIndex + 1), item.RawData.Length); }
private byte[] GenerateRandomSequence(SizeRange sizeRange) { return (GenerateRandomSequence(sizeRange == SizeRange.MultiPage ? _r.Next(327681) : DbItem.GetMaxSize(sizeRange))); }
public void FixedSizeItemsLength() { var manager = new FileSystemPageManager(4096); using (var storage = new Storage(manager)) { storage.CreateNew(StoragePath); var fsm = new FreeSpaceMap(manager); var memoryManager = new MemoryManager(fsm, manager); int count = 1000; var items = new Dictionary <DbItemReference, byte[]>(); var r = new Random(); // allocate items for (int i = 0; i < count; i++) { var sizeRange = GetRandomFixedSizeItemsSizeRange(); var content = GenerateRandomSequence(DbItem.GetMaxSize(sizeRange) - r.Next(5)); var reference = memoryManager.Allocate(content); items[reference] = content; } // check lengths foreach (var reference in items.Keys) { Assert.AreEqual(items[reference].Length, memoryManager.GetLength(reference)); } } }
protected void CheckSizeRange(int pageSize) { if (DbItem.GetMaxSize(SizeRange) > pageSize - Length - 4) { throw new PageFormatException("Unable to format page. The class size " + Enum.GetName(typeof(SizeRange), SizeRange) + " is too large for page " + pageSize + "bytes length."); } }
public void FixedSizeItemsAllocation() { var manager = new FileSystemPageManager(4096); using (var storage = new Storage(manager)) { storage.CreateNew(StoragePath); var fsm = new FreeSpaceMap(manager); var memoryManager = new MemoryManager(fsm, manager); int count = 100000; var items = new Dictionary <DbItemReference, byte[]>(); // allocate items for (int i = 0; i < count; i++) { var sizeRange = GetRandomFixedSizeItemsSizeRange(); var content = GenerateRandomSequence(DbItem.GetMaxSize(sizeRange)); var reference = memoryManager.Allocate(content); items[reference] = content; } // check contents foreach (var reference in items.Keys) { Assert.IsTrue(AreEqualByteArrays(items[reference], memoryManager.Get(reference).RawData)); } // free half of the items var freedItems = new Dictionary <DbItemReference, byte[]>(); foreach (var reference in items.Keys) { if (_r.NextDouble() > 0.5) { memoryManager.Free(reference); freedItems[reference] = items[reference]; } } // check that the items do not exist foreach (var reference in freedItems.Keys) { Assert.IsNull(memoryManager.Get(reference)); } // and the remaining items still available foreach (var reference in items.Keys.Where(item => !freedItems.ContainsKey(item))) { Assert.IsTrue(AreEqualByteArrays(items[reference], memoryManager.Get(reference).RawData)); } } }
private static int GetNewSlotCount(IPage page, PageHeaderBase header) { short fixedSizeItemMarkersLength = BitConverter.ToInt16(page.Content, FixedSizeItemsPageHeader.FixedSizeItemsHeaderLength); short slotSize = DbItem.GetMaxSize(header.SizeRange); int remainingSpace = page.Length - // full page length header.Length - // subtract header length fixedSizeItemMarkersLength * OnPagePointerSize - OnPagePointerSize - // subtract item length markers array and its length fixedSizeItemMarkersLength * slotSize; // subtract items return(remainingSpace / (slotSize + OnPagePointerSize)); }
public void FixedSizeItemsPage() { int pageSize = 32768; var dummyPageManager = new FileSystemPageManager(pageSize); var p = new Page(dummyPageManager, 0, new byte[pageSize]); var header = new FixedSizeItemsPageHeader(); var r = new Random(); foreach (var sizeRange in EnumHelper.FixedSizeItemsSizeRanges()) { header.SizeRange = sizeRange; PageFormatter.InitPage(p, header); var item = new DbItem(new byte[DbItem.GetMaxSize(header.SizeRange)]); r.NextBytes(item.RawData); // fill the page with the items short count = 0; bool spaceRemains = true; while (PageFormatter.HasFreeSpaceForFixedSizeItem(p)) { Assert.IsTrue(spaceRemains); PageFormatter.AddFixedSizeItem(p, item, out spaceRemains); count++; Assert.AreEqual(count, PageFormatter.ReadFixedSizeItemsCount(p)); } Assert.IsFalse(spaceRemains); // check if fetched objects are equal to originals for (short j = 0; j < PageFormatter.ReadFixedSizeItemsCount(p); j++) { DbItem readItem = PageFormatter.ReadFixedSizeItem(p, j); Assert.IsTrue(AreEqualByteArrays(item.RawData, readItem.RawData)); } // delete all added items short itemindex = 0; while (PageFormatter.ReadFixedSizeItemsCount(p) > 0) { PageFormatter.DeleteFixedSizeItem(p, itemindex); count--; itemindex++; Assert.AreEqual(count, PageFormatter.ReadFixedSizeItemsCount(p)); } } }
public static void FormatFixedSizeItemsPage(IPage page, PageHeaderBase header, DbItem[] items) { var sizeRange = header.SizeRange; if (items.Any()) { if (items.Any(item => item.SizeRange != sizeRange)) { throw new ArgumentException("Size ranges should be equal", nameof(items)); } } header.WriteToPage(page); int remainingSpace = page.Length - // full page length header.Length - // subtract header length items.Length * OnPagePointerSize - OnPagePointerSize; // subtract item length markers array and its length if (remainingSpace < 0) { throw new ArgumentException("Page have no space to add specified items", nameof(items)); } var maxSize = DbItem.GetMaxSize(sizeRange); var content = page.Content; int contentLength = page.Length; int cnt = items.Length; int headerLength = header.Length; for (int index = 0; index < cnt; index++) { var rawData = items[index].RawData; // write the length marker Buffer.BlockCopy(BitConverter.GetBytes((short)rawData.Length), 0, content, headerLength + OnPagePointerSize * (index + 1), sizeof(short)); // write the body of item Buffer.BlockCopy(rawData, 0, content, contentLength - maxSize * (index + 1), rawData.Length); } // write the length of length marker array byte[] lengthLengthMarkers = BitConverter.GetBytes((short)cnt); lengthLengthMarkers.CopyTo(content, headerLength); }
public static DbItem ReadFixedSizeItem(IPage page, short itemIndex) { short itemLength = ReadFixedSizeItemLength(page, itemIndex); if (itemLength == -1) { throw new PageFormatException("Item has been deleted."); } var itemBytes = new byte[itemLength]; short slotSize = DbItem.GetMaxSize(PageHeaderBase.GetSizeRange(page)); int offset = page.Content.Length - slotSize * (itemIndex + 1); for (int j = 0; j < itemBytes.Length; j++) { itemBytes[j] = page.Content[j + offset]; } return(new DbItem(itemBytes)); }
public static DbItemReference AddFixedSizeItem(IPage page, DbItem item, out bool hasRemainingSpace) { var header = (FixedSizeItemsPageHeader)GetPageHeader(page); if (header.SizeRange == SizeRange.MultiPage || header.SizeRange == SizeRange.NotApplicable) { throw new PageFormatException("Page is not dedicated to fixed size items."); } if (item.GetAllocationType(page.Length) == AllocationType.MultiPage) { throw new PageFormatException("Unable to add item on page. Item is too large."); } if (header.SizeRange != item.SizeRange) { throw new PageFormatException("Unable to add item on page. Mismatch size ranges."); } hasRemainingSpace = false; byte[] lengthBytes; if (header.EmptySlotCount > 0) { // find an empty slot for a new item var slotIndex = GetFirstFixedSizeItemEmptySlotIndex(page, header.Length); if (slotIndex == -1) { throw new DataTankerException($"Page is corrupt: empty slot counter is {header.EmptySlotCount}, but there is no empty slot found"); } // write the item length marker lengthBytes = BitConverter.GetBytes((short)item.RawData.Length); lengthBytes.CopyTo(page.Content, header.Length + // length of header OnPagePointerSize + // length of length marker array slotIndex * OnPagePointerSize // offset in length marker array ); Buffer.BlockCopy(item.RawData, 0, page.Content, page.Length - DbItem.GetMaxSize(header.SizeRange) * (slotIndex + 1), item.RawData.Length); if (header.EmptySlotCount > 1) { hasRemainingSpace = true; } // decrease empty slot count BitConverter.GetBytes((short)(header.EmptySlotCount - 1)).CopyTo(page.Content, OnPageOffsets.FixedSizeItem.EmptySlotCount); return(new DbItemReference(page.Index, slotIndex)); } // there are no empty slots on this page // check the page have the enough space to allocate a new slot var newSlotCount = GetNewSlotCount(page, header); if (newSlotCount == 0) { throw new PageFormatException("The page have no space to add an item."); } var itemLengthsLength = BitConverter.ToInt16(page.Content, header.Length); // write the length marker lengthBytes = BitConverter.GetBytes((short)item.RawData.Length); lengthBytes.CopyTo(page.Content, header.Length + // length of header OnPagePointerSize + // length of length marker array itemLengthsLength * OnPagePointerSize // skip to the end of length marker array ); // write the increased length of length marker array byte[] lengthLengthMarkers = BitConverter.GetBytes((short)(itemLengthsLength + 1)); lengthLengthMarkers.CopyTo(page.Content, header.Length); // write the item body Buffer.BlockCopy(item.RawData, 0, page.Content, page.Length - DbItem.GetMaxSize(header.SizeRange) * (itemLengthsLength + 1), item.RawData.Length); if (newSlotCount > 1) { hasRemainingSpace = true; } return(new DbItemReference(page.Index, itemLengthsLength)); }