/// <summary> /// Write entries directory to pages. If pages not enough for new directory - additional pages will allocated. /// Also write header and pageAllocator state /// </summary> public void Write(Stream stm, PagedContainerHeader header, PageAllocator pageAllocator) { var targetPages = pages; var buff = entries.WriteEntries(header.DataHandler); var requiredPages = header.GetRequiredPages(buff.Length); if (requiredPages > targetPages.Length) // need to allocate additional pages? { targetPages = targetPages.Concat(pageAllocator.AllocatePages(requiredPages - targetPages.Length)).ToArray(); } else if (requiredPages < targetPages.Length) // can free unused pages? { var mustBeFreePages = targetPages.Skip(requiredPages).ToArray(); targetPages = targetPages.Take(requiredPages).ToArray(); pageAllocator.FreePages(mustBeFreePages); } stm.WriteIntoPages(header, buff, 0, targetPages); header.DirectoryFirstPage = targetPages[0]; header.Write(stm); pageAllocator.Write(stm); pages = targetPages; }
/// <summary> /// Write buffer to targetPages. /// Last of 32 bit of each page contain next page index. /// Last page in sequence contain next page index == 0 /// </summary> internal static void WriteIntoPages(this Stream stm, PagedContainerHeader header, Span <byte> data, int offset, int[] targetPages) { var currentPageIndex = 0; var page = new byte[header.PageSize].AsSpan(); foreach (var pageIndex in targetPages) { var writeLength = data.Length - offset; if (writeLength > header.PageUserDataSize) { data.Slice(offset, header.PageUserDataSize).CopyTo(page); offset += header.PageUserDataSize; } else { page.Clear(); data.Slice(offset, writeLength).CopyTo(page); offset += writeLength; } BitConverter.TryWriteBytes(page.Slice(header.PageUserDataSize), currentPageIndex + 1 < targetPages.Length ? targetPages[currentPageIndex + 1] : 0); var newPosition = header.PageSize * pageIndex; if (newPosition != stm.Position) { stm.Position = newPosition; } stm.Write(page); currentPageIndex++; } }
protected PagedContainerAbstract(Stream stm, PersistentContainerSettings?settings = null) { Stream = stm; settings ??= new PersistentContainerSettings(); try { if (stm.Length == 0) // new file { Header = new PagedContainerHeader(settings); Header.Write(stm); pageAllocator = new PageAllocator(Header); pageAllocator.Write(stm); } else { Header = new PagedContainerHeader(stm, settings.encryptorDecryptor); pageAllocator = new PageAllocator(Header, stm); } entries = Header.DirectoryFirstPage == 0 ? new PagedContainerEntryCollection() : new PagedContainerEntryCollection(Header, stm.ReadWithPageSequence(Header, Header.DirectoryFirstPage)); } catch { Dispose(); throw; } }
public PageAllocator(PagedContainerHeader header, Stream stm) { this.header = header; var buff = stm.ReadWithPageSequence(header, FIRST_PA_PAGE).Data; pageAllocations = new ExpandableBitArray(header.DataHandler.Unpack(buff).ToArray()); }
public PageAllocator(PagedContainerHeader header) { this.header = header; pageAllocations = new ExpandableBitArray(2); pageAllocations[0] = true; pageAllocations[1] = true; }
/// <summary> /// Read only sequence page numbers from 'startFromPage'. /// Last 32 bit contain next page index. /// Last page in sequence contain 'next page index' value == 0 /// </summary> internal static int[] ReadPageSequence(this Stream stm, PagedContainerHeader header, int startFromPage) { var pages = new List <int>(); var buff = new byte[4]; var currentPageIndex = startFromPage; while (currentPageIndex > 0) { pages.Add(currentPageIndex); stm.Position = currentPageIndex * header.PageSize + header.PageUserDataSize; stm.Read(buff, 0, 4); currentPageIndex = BitConverter.ToInt32(buff, 0); } return(pages.ToArray()); }
/// <summary> /// Read data and page numbers, starting from 'startFromPage' /// Used in PageAllocator & PagedContainerEntryCollection /// </summary> internal static PageSequence ReadWithPageSequence(this Stream stm, PagedContainerHeader header, int startFromPage) { using var stmCollector = new MemoryStream(); var pages = new List <int>(); var buff = new byte[header.PageSize]; while (startFromPage > 0) { pages.Add(startFromPage); stm.Position = startFromPage * header.PageSize; stm.Read(buff, 0, header.PageSize); stmCollector.Write(buff, 0, header.PageUserDataSize); startFromPage = BitConverter.ToInt32(buff, header.PageUserDataSize); } return(new PageSequence(stmCollector.ToArray(), pages.ToArray())); }
/// <summary> Read data from pages, starting with entry.FirstPage </summary> internal static byte[] ReadEntryPageSequence(this Stream stm, PagedContainerHeader header, PagedContainerEntry entry) { using var stmCollector = new MemoryStream(entry.Length); // todo replace with byte[] var remainLength = entry.Flags.HasFlag(EntryFlags.Compressed) ? entry.CompressedLength : entry.Length; var currentPageIndex = entry.FirstPage; var buff = new byte[header.PageSize]; while (currentPageIndex > 0) { stm.Position = currentPageIndex * header.PageSize; stm.Read(buff, 0, header.PageSize); var requestLength = remainLength > header.PageUserDataSize ? header.PageUserDataSize : remainLength; stmCollector.Write(buff, 0, requestLength); remainLength -= requestLength; currentPageIndex = BitConverter.ToInt32(buff, header.PageUserDataSize); } return(header.DataHandler.Unpack(stmCollector.ToArray()).ToArray()); }
internal PagedContainerEntryCollection(PagedContainerHeader header, PageSequence ps) { entries = ps.Data.ReadEntries(header.DataHandler); pages = ps.Pages; Modified = false; }