public static int WriteMultipageItemBlock(IPage page, DbItem item, long offset) { if (offset < 0 || offset >= item.RawData.LongLength) { throw new ArgumentOutOfRangeException(nameof(offset)); } MultipageItemPageHeader header = GetMultipageItemPageHeader(page); if (item.GetAllocationType(page.Length) == AllocationType.SinglePage) { throw new PageFormatException("Unable to add item block on page. Item allocation type is single page."); } int last = Math.Min(page.Length - header.Length, (int)(item.RawData.LongLength - offset)) - 1; int contentOffset = header.Length; if (offset == 0) { // we should write the length of the object var lengthBytes = BitConverter.GetBytes(item.RawData.LongLength); lengthBytes.CopyTo(page.Content, header.Length); last -= 8; contentOffset += 8; } for (long i = 0; i <= last; i++) { page.Content[i + contentOffset] = item.RawData[i + offset]; } return(last + 1); }
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)); }