forked from mbdavid/LiteDB
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
1,432 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
using System; | ||
|
||
namespace LiteDB_V6 | ||
{ | ||
public enum PageType { Empty = 0, Header = 1, Collection = 2, Index = 3, Data = 4, Extend = 5 } | ||
|
||
internal abstract class BasePage | ||
{ | ||
#region Page Constants | ||
|
||
/// <summary> | ||
/// The size of each page in disk - 4096 is NTFS default | ||
/// </summary> | ||
public const int PAGE_SIZE = 4096; | ||
|
||
/// <summary> | ||
/// This size is used bytes in header pages 17 bytes (+8 reserved to future use) = 25 bytes | ||
/// </summary> | ||
public const int PAGE_HEADER_SIZE = 25; | ||
|
||
/// <summary> | ||
/// Bytes avaiable to store data removing page header size - 4071 bytes | ||
/// </summary> | ||
public const int PAGE_AVAILABLE_BYTES = PAGE_SIZE - PAGE_HEADER_SIZE; | ||
|
||
#endregion Page Constants | ||
|
||
/// <summary> | ||
/// Represent page number - start in 0 with HeaderPage [4 bytes] | ||
/// </summary> | ||
public uint PageID { get; set; } | ||
|
||
/// <summary> | ||
/// Indicate the page type [1 byte] - Must be implemented for each page type | ||
/// </summary> | ||
public abstract PageType PageType { get; } | ||
|
||
/// <summary> | ||
/// Represent the previous page. Used for page-sequences - MaxValue represent that has NO previous page [4 bytes] | ||
/// </summary> | ||
public uint PrevPageID { get; set; } | ||
|
||
/// <summary> | ||
/// Represent the next page. Used for page-sequences - MaxValue represent that has NO next page [4 bytes] | ||
/// </summary> | ||
public uint NextPageID { get; set; } | ||
|
||
/// <summary> | ||
/// Used for all pages to count itens inside this page(bytes, nodes, blocks, ...) [2 bytes] | ||
/// Its Int32 but writes in UInt16 | ||
/// </summary> | ||
public int ItemCount { get; set; } | ||
|
||
/// <summary> | ||
/// Used to find a free page using only header search [used in FreeList] [2 bytes] | ||
/// Its Int32 but writes in UInt16 | ||
/// Its updated when a page modify content length (add/remove items) | ||
/// </summary> | ||
public int FreeBytes { get; set; } | ||
|
||
public BasePage(uint pageID) | ||
{ | ||
this.PageID = pageID; | ||
this.PrevPageID = uint.MaxValue; | ||
this.NextPageID = uint.MaxValue; | ||
this.ItemCount = 0; | ||
this.FreeBytes = PAGE_AVAILABLE_BYTES; | ||
} | ||
|
||
/// <summary> | ||
/// Create a new instance of page based on T type | ||
/// </summary> | ||
public static T CreateInstance<T>(uint pageID) | ||
where T : BasePage | ||
{ | ||
var type = typeof(T); | ||
|
||
// casting using "as T" #90 / thanks @Skysper | ||
if (type == typeof(HeaderPage)) return new HeaderPage() as T; | ||
if (type == typeof(CollectionPage)) return new CollectionPage(pageID) as T; | ||
if (type == typeof(IndexPage)) return new IndexPage(pageID) as T; | ||
if (type == typeof(DataPage)) return new DataPage(pageID) as T; | ||
if (type == typeof(ExtendPage)) return new ExtendPage(pageID) as T; | ||
|
||
throw new Exception("Invalid base page type T"); | ||
} | ||
|
||
/// <summary> | ||
/// Create a new instance of page based on PageType | ||
/// </summary> | ||
public static BasePage CreateInstance(uint pageID, PageType pageType) | ||
{ | ||
switch (pageType) | ||
{ | ||
case PageType.Header: return new HeaderPage(); | ||
case PageType.Collection: return new CollectionPage(pageID); | ||
case PageType.Index: return new IndexPage(pageID); | ||
case PageType.Data: return new DataPage(pageID); | ||
case PageType.Extend: return new ExtendPage(pageID); | ||
default: throw new Exception("Invalid pageType"); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Read a page with correct instance page object. Checks for pageType | ||
/// </summary> | ||
public static BasePage ReadPage(byte[] buffer) | ||
{ | ||
var reader = new ByteReader(buffer); | ||
|
||
var pageID = reader.ReadUInt32(); | ||
var pageType = (PageType)reader.ReadByte(); | ||
|
||
if (pageID == 0 && (byte)pageType > 5) | ||
{ | ||
throw LiteException.InvalidDatabase(); | ||
} | ||
|
||
var page = CreateInstance(pageID, pageType); | ||
|
||
page.ReadHeader(reader); | ||
page.ReadContent(reader); | ||
|
||
page.DiskData = buffer; | ||
|
||
return page; | ||
} | ||
|
||
private void ReadHeader(ByteReader reader) | ||
{ | ||
// first 5 bytes (pageID + pageType) was readed before class create | ||
// this.PageID | ||
// this.PageType | ||
|
||
this.PrevPageID = reader.ReadUInt32(); | ||
this.NextPageID = reader.ReadUInt32(); | ||
this.ItemCount = reader.ReadUInt16(); | ||
this.FreeBytes = reader.ReadUInt16(); | ||
reader.Skip(8); // reserved 8 bytes | ||
} | ||
|
||
protected abstract void ReadContent(ByteReader reader); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text.RegularExpressions; | ||
|
||
namespace LiteDB_V6 | ||
{ | ||
/// <summary> | ||
/// Represents the collection page AND a collection item, because CollectionPage represent a Collection (1 page = 1 collection). All collections pages are linked with Prev/Next links | ||
/// </summary> | ||
internal class CollectionPage : BasePage | ||
{ | ||
/// <summary> | ||
/// Represent maximun bytes that all collections names can be used in header | ||
/// </summary> | ||
public const ushort MAX_COLLECTIONS_SIZE = 3000; | ||
|
||
public static Regex NamePattern = new Regex(@"^[\w-]{1,30}$"); | ||
|
||
/// <summary> | ||
/// Page type = Collection | ||
/// </summary> | ||
public override PageType PageType { get { return PageType.Collection; } } | ||
|
||
/// <summary> | ||
/// Name of collection | ||
/// </summary> | ||
public string CollectionName { get; set; } | ||
|
||
/// <summary> | ||
/// Get a reference for the free list data page - its private list per collection - each DataPage contains only data for 1 collection (no mixing) | ||
/// Must to be a Field to be used as parameter reference | ||
/// </summary> | ||
public uint FreeDataPageID; | ||
|
||
/// <summary> | ||
/// Get the number of documents inside this collection | ||
/// </summary> | ||
public long DocumentCount { get; set; } | ||
|
||
/// <summary> | ||
/// Get all indexes from this collection - includes non-used indexes | ||
/// </summary> | ||
public CollectionIndex[] Indexes { get; set; } | ||
|
||
public CollectionPage(uint pageID) | ||
: base(pageID) | ||
{ | ||
this.FreeDataPageID = uint.MaxValue; | ||
this.DocumentCount = 0; | ||
this.ItemCount = 1; // fixed for CollectionPage | ||
this.FreeBytes = 0; // no free bytes on collection-page - only one collection per page | ||
this.Indexes = new CollectionIndex[CollectionIndex.INDEX_PER_COLLECTION]; | ||
|
||
for (var i = 0; i < Indexes.Length; i++) | ||
{ | ||
this.Indexes[i] = new CollectionIndex() { Page = this, Slot = i }; | ||
} | ||
} | ||
|
||
protected override void ReadContent(ByteReader reader) | ||
{ | ||
this.CollectionName = reader.ReadString(); | ||
this.FreeDataPageID = reader.ReadUInt32(); | ||
var uintCount = reader.ReadUInt32(); // read as uint (4 bytes) | ||
|
||
foreach (var index in this.Indexes) | ||
{ | ||
index.Field = reader.ReadString(); | ||
index.HeadNode = reader.ReadPageAddress(); | ||
index.TailNode = reader.ReadPageAddress(); | ||
index.FreeIndexPageID = reader.ReadUInt32(); | ||
index.Options.Unique = reader.ReadBoolean(); | ||
index.Options.IgnoreCase = reader.ReadBoolean(); | ||
index.Options.TrimWhitespace = reader.ReadBoolean(); | ||
index.Options.EmptyStringToNull = reader.ReadBoolean(); | ||
index.Options.RemoveAccents = reader.ReadBoolean(); | ||
} | ||
|
||
// be compatible with v2_beta | ||
var longCount = reader.ReadInt64(); | ||
this.DocumentCount = Math.Max(uintCount, longCount); | ||
|
||
} | ||
|
||
/// <summary> | ||
/// Get primary key index (_id index) | ||
/// </summary> | ||
public CollectionIndex PK { get { return this.Indexes[0]; } } | ||
|
||
/// <summary> | ||
/// Returns all used indexes | ||
/// </summary> | ||
public IEnumerable<CollectionIndex> GetIndexes(bool includePK) | ||
{ | ||
return this.Indexes.Where(x => x.IsEmpty == false && x.Slot >= (includePK ? 0 : 1)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace LiteDB_V6 | ||
{ | ||
/// <summary> | ||
/// The DataPage thats stores object data. | ||
/// </summary> | ||
internal class DataPage : BasePage | ||
{ | ||
/// <summary> | ||
/// Page type = Extend | ||
/// </summary> | ||
public override PageType PageType { get { return PageType.Data; } } | ||
|
||
/// <summary> | ||
/// Returns all data blocks - Each block has one object | ||
/// </summary> | ||
public Dictionary<ushort, DataBlock> DataBlocks { get; set; } | ||
|
||
public DataPage(uint pageID) | ||
: base(pageID) | ||
{ | ||
this.DataBlocks = new Dictionary<ushort, DataBlock>(); | ||
} | ||
|
||
protected override void ReadContent(ByteReader reader) | ||
{ | ||
this.DataBlocks = new Dictionary<ushort, DataBlock>(ItemCount); | ||
|
||
for (var i = 0; i < ItemCount; i++) | ||
{ | ||
var block = new DataBlock(); | ||
|
||
block.Page = this; | ||
block.Position = new PageAddress(this.PageID, reader.ReadUInt16()); | ||
block.ExtendPageID = reader.ReadUInt32(); | ||
|
||
for (var j = 0; j < CollectionIndex.INDEX_PER_COLLECTION; j++) | ||
{ | ||
block.IndexRef[j] = reader.ReadPageAddress(); | ||
} | ||
|
||
var size = reader.ReadUInt16(); | ||
block.Data = reader.ReadBytes(size); | ||
|
||
this.DataBlocks.Add(block.Position.Index, block); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System; | ||
|
||
namespace LiteDB_V6 | ||
{ | ||
/// <summary> | ||
/// Represent a extra data page that contains the object when is not possible store in DataPage (bigger then PAGE_SIZE or on update has no more space on page) | ||
/// Can be used in sequence of pages to store big objects | ||
/// </summary> | ||
internal class ExtendPage : BasePage | ||
{ | ||
/// <summary> | ||
/// Page type = Extend | ||
/// </summary> | ||
public override PageType PageType { get { return PageType.Extend; } } | ||
|
||
/// <summary> | ||
/// Represent the part or full of the object - if this page has NextPageID the object is bigger than this page | ||
/// </summary> | ||
public Byte[] Data { get; set; } | ||
|
||
public ExtendPage(uint pageID) | ||
: base(pageID) | ||
{ | ||
this.Data = new byte[0]; | ||
} | ||
|
||
protected override void ReadContent(ByteReader reader) | ||
{ | ||
this.Data = reader.ReadBytes(this.ItemCount); | ||
} | ||
} | ||
} |
Oops, something went wrong.