/// <summary> /// Read page bytes from disk /// </summary> public virtual byte[] ReadPage(uint pageID) { // if stream are not initialized but need header, create new header if (_stream == null && pageID == 0) { var header = new HeaderPage { LastPageID = 1 }; return(header.WritePage()); } else if (_stream == null) { this.InternalInitialize(); } var buffer = new byte[BasePage.PAGE_SIZE]; var position = BasePage.GetSizeOfPages(pageID); // position cursor if (_stream.Position != position) { _stream.Seek(position, SeekOrigin.Begin); } // read bytes from data file _stream.Read(buffer, 0, BasePage.PAGE_SIZE); return(buffer); }
private static Dictionary <uint, string> RecoveryCollectionPages(UltraLiteEngine engine, HeaderPage header, StringBuilder log) { var result = new Dictionary <uint, string>(); // get collection page foreach (var col in header.CollectionPages) { CollectionPage colPage = null; try { // read collection page var buffer = engine._disk.ReadPage(col.Value); var page = BasePage.ReadPage(buffer); if (page.PageType != PageType.Collection) { continue; } colPage = page as CollectionPage; } catch (Exception ex) { log.AppendLine($"Page {col.Value} (Collection) Error: {ex.Message}"); continue; } // get all pageID from all valid indexes var pagesID = new HashSet <uint>(colPage.Indexes.Where(x => x.IsEmpty == false && x.HeadNode.PageID != uint.MaxValue).Select(x => x.HeadNode.PageID)); // load all dataPages from this initial index pageIDs var dataPages = RecoveryDetectCollectionByIndexPages(engine, pagesID, log); // populate resultset with this collection name/data page foreach (var page in dataPages) { result[page] = col.Key; } } return(result); }
/// <summary> /// Initialize new datafile with header page + lock reserved area zone /// </summary> public static void CreateDatabase(Stream stream, string password = null, long initialSize = 0) { // calculate how many empty pages will be added on disk var emptyPages = initialSize == 0 ? 0 : (initialSize - (2 * BasePage.PAGE_SIZE)) / BasePage.PAGE_SIZE; // if too small size (less than 2 pages), assume no initial size if (emptyPages < 0) { emptyPages = 0; } // create a new header page in bytes (keep second page empty) var header = new HeaderPage { LastPageID = initialSize == 0 ? 1 : (uint)emptyPages + 1, FreeEmptyPageID = initialSize == 0 ? uint.MaxValue : 2 }; if (password != null) { header.Password = AesEncryption.HashSHA1(password); header.Salt = AesEncryption.Salt(); } // point to begin file stream.Seek(0, SeekOrigin.Begin); // get header page in bytes var buffer = header.WritePage(); stream.Write(buffer, 0, BasePage.PAGE_SIZE); // write second page as an empty AREA (it's not a page) just to use as lock control stream.Write(new byte[BasePage.PAGE_SIZE], 0, BasePage.PAGE_SIZE); // create crypto class if has password var crypto = password != null ? new AesEncryption(password, header.Salt) : null; // if initial size is defined, lets create empty pages in a linked list if (emptyPages > 0) { stream.SetLength(initialSize); var pageID = 1u; while (++pageID < (emptyPages + 2)) { var empty = new EmptyPage(pageID) { PrevPageID = pageID == 2 ? 0 : pageID - 1, NextPageID = pageID == emptyPages + 1 ? uint.MaxValue : pageID + 1 }; var bytes = empty.WritePage(); if (password != null) { bytes = crypto.Encrypt(bytes); } stream.Write(bytes, 0, BasePage.PAGE_SIZE); } } if (crypto != null) { crypto.Dispose(); } }