public static IList <AllocatedSpace> Alloc(this FileDBContext db, long length, AllocPageTypes type) { // 由于一页能用的空间很有限,所以可能需要从多个页上获取空间。 IList <AllocatedSpace> result = new List <AllocatedSpace>(); FileStream fs = db.fileStream; long allocated = 0; while (allocated < length) { Int16 partLength = (length - allocated >= Consts.maxAvailableSpaceInPage) ? Consts.maxAvailableSpaceInPage : (Int16)(length - allocated); // 找出一个可用空间充足的指定类型的页。 PageHeaderBlock page = PickPage(db, partLength, type); if (!db.transaction.affectedPages.ContainsKey(page.ThisPos))// 加入缓存备用。 { db.transaction.affectedPages.Add(page.ThisPos, page); } AllocatedSpace item = new AllocatedSpace(page.ThisPos + Consts.pageSize - page.AvailableBytes - partLength, (Int16)length); result.Add(item); allocated += partLength; } return(result); }
/// <summary> /// 申请一个空白页或新页。 /// </summary> /// <param name="db"></param> /// <returns></returns> private static PageHeaderBlock AllocEmptyPageOrNewPage(this FileDBContext db) { PageHeaderBlock page; FileStream fs = db.fileStream; DBHeaderBlock dbHeader = db.headerBlock; long emptyPagePos = dbHeader.FirstEmptyPagePos; if (emptyPagePos != 0)// 存在空白页,则使用此空白页。 { PageHeaderBlock emptyPage = fs.ReadBlock <PageHeaderBlock>(emptyPagePos); // 从链表中去掉此空白页。 dbHeader.FirstEmptyPagePos = emptyPage.NextPagePos; emptyPage.NextPagePos = 0; page = emptyPage; } else// 没有空白页,则拓展数据库文件,增加一个页的长度。 { PageHeaderBlock newPage = new PageHeaderBlock(); newPage.ThisPos = fs.Length; newPage.OccupiedBytes = (Int16)(Consts.pageSize - Consts.maxAvailableSpaceInPage); newPage.AvailableBytes = Consts.maxAvailableSpaceInPage; fs.SetLength(fs.Length + Consts.pageSize); page = newPage; } return(page); }
private static void SortPage(AllocPageTypes type, PageHeaderBlock page, FileStream fs, Transaction ts, Blocks.DBHeaderBlock dbHeader) { long headPos = dbHeader.GetPosOfFirstPage(type); if (headPos == 0)// 一个页也没有。 { dbHeader.SetPosOfFirstPage(type, page.ThisPos); } else { PageHeaderBlock head = GetPageHeaderBlock(fs, ts, headPos); if (page.AvailableBytes >= head.AvailableBytes)// 与第一个页进行比较。 { page.NextPagePos = head.ThisPos; dbHeader.SetPosOfFirstPage(type, page.ThisPos); } else// 与后续的页进行比较。 { PageHeaderBlock current = head; while (current.NextPagePos != 0) { PageHeaderBlock next = GetPageHeaderBlock(fs, ts, current.NextPagePos); if (page.AvailableBytes >= next.AvailableBytes) { if (next.ThisPos != current.NextPagePos) { throw new Exception(string.Format("this should not happen.")); } page.NextPagePos = current.NextPagePos; // next.ThisPos; current.NextPagePos = page.ThisPos; break; } else { current = next; } } if (current.NextPagePos == 0) { current.NextPagePos = page.ThisPos; } if (!ts.affectedPages.ContainsKey(current.ThisPos)) { ts.affectedPages.Add(current.ThisPos, current); } } } }
private static PageHeaderBlock PickPage(this FileDBContext db, Int16 allocatingLength, AllocPageTypes type) { PageHeaderBlock page; FileStream fs = db.fileStream; Transaction ts = db.transaction; DBHeaderBlock dbHeader = db.headerBlock; // 找到第一个给定类型的页的位置。 long pagePos = dbHeader.GetPosOfFirstPage(type); if (pagePos == 0)// 尚无给定类型的页。 { page = db.AllocEmptyPageOrNewPage(); page.AvailableBytes -= allocatingLength; page.OccupiedBytes += allocatingLength; dbHeader.SetPosOfFirstPage(type, page.ThisPos); } else { // 最前面的table页的可用空间是最大的(这需要在后续操作中进行排序) PageHeaderBlock firstTablePage = GetTablePage(fs, ts, pagePos); if (firstTablePage.AvailableBytes >= allocatingLength)// 此页的空间足够用。 { // 把此页从Page链表中移除。 dbHeader.SetPosOfFirstPage(type, firstTablePage.NextPagePos); firstTablePage.NextPagePos = 0; page = firstTablePage; } else// 此页的空间不够用,还是要申请一个新页。 { page = db.AllocEmptyPageOrNewPage(); } page.AvailableBytes -= allocatingLength; page.OccupiedBytes += allocatingLength; // 对申请的类型的页的链表进行排序。呼应上面的排序需求。 SortPage(type, page, fs, ts, dbHeader); } return(page); }
//static readonly Brush pageHeaderBrush = new LinearGradientBrush(new Rectangle(0, 0, Consts.pageHeaderBlockLength, bmpHeight), Color.Silver, Color.GreenYellow, 90); private static void DrawPageHeaders(FileStream fs, long pageCount, Bitmap[] bmps) { for (int i = 0; i < pageCount; i++) { long pos = i * Consts.pageSize; PageHeaderBlock page = fs.ReadBlock <PageHeaderBlock>(pos); long index = pos / Consts.pageSize; Bitmap bmp = bmps[index]; Graphics g = Graphics.FromImage(bmp); int startPos = (int)(page.ThisPos - Consts.pageSize * index); int length = page.ToBytes().Length; Brush brush = new LinearGradientBrush(new Point(startPos, 0), new Point(startPos + length, 0), Color.YellowGreen, Color.GreenYellow); g.FillRectangle(brush, startPos, 1, length, bmpHeight - 1); g.DrawRectangle(boundPen, startPos, 1, length, bmpHeight - 1); g.Dispose(); } }
/// <summary> /// 创建初始状态的数据库文件。 /// </summary> /// <param name="fullname">数据库文件据对路径。</param> /// <param name="config">数据库配置信息。</param> private void CreateDB(string fullname, DBConfig config) { FileInfo fileInfo = new FileInfo(fullname); Directory.CreateDirectory(fileInfo.DirectoryName); using (FileStream fs = new FileStream(fullname, FileMode.CreateNew, FileAccess.Write, FileShare.None, Consts.pageSize)) { PageHeaderBlock page = new PageHeaderBlock() { OccupiedBytes = Consts.pageSize, AvailableBytes = 0, }; fs.WriteBlock(page); DBHeaderBlock dbHeader = new DBHeaderBlock() { MaxLevelOfSkipList = config.MaxLevelOfSkipList, ProbabilityOfSkipList = config.ProbabilityOfSkipList, MaxSunkCountInMemory = config.MaxSunkCountInMemory, LockTimeout = config.LockTimeout, ThisPos = fs.Position }; fs.WriteBlock(dbHeader); TableBlock tableHead = new TableBlock() { ThisPos = fs.Position, }; fs.WriteBlock(tableHead); byte[] leftSpace = new byte[Consts.pageSize - fs.Length]; fs.Write(leftSpace, 0, leftSpace.Length); BlockCache.TryRemoveFloatingBlock(page); BlockCache.TryRemoveFloatingBlock(dbHeader); BlockCache.TryRemoveFloatingBlock(tableHead); BlockCache.TryRemoveSunkBlock(page); BlockCache.TryRemoveSunkBlock(dbHeader); BlockCache.TryRemoveSunkBlock(tableHead); } }
/// <summary> /// 打印数据库文件里所有表的所有主键的skip list和所有类型的页链表。 /// </summary> /// <param name="db"></param> /// <returns></returns> public static string Print(this FileDBContext db) { StringBuilder builder = new StringBuilder(); builder.AppendLine(db.Fullname); FileStream fs = db.GetFileStream(); //PageHeaderBlock firstPage = fs.ReadBlock<PageHeaderBlock>(0); DBHeaderBlock dbHeader = db.GetDBHeaderBlock();// fs.ReadBlock<DBHeaderBlock>(fs.Position); PrintSkipLists(db, builder, fs); builder.AppendLine(); builder.AppendLine("Page Info.:"); for (long i = 0; i < fs.Length; i += Consts.pageSize) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(i); string str = Print(pageInfo); builder.AppendLine(str); } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("table pages:"); long tablePos = dbHeader.FirstTablePagePos; while (tablePos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(tablePos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); tablePos = pageInfo.NextPagePos; } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("index pages:"); long indexPos = dbHeader.FirstIndexPagePos; while (indexPos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(indexPos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); indexPos = pageInfo.NextPagePos; } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("skip list node pages:"); long skiplistnodePos = dbHeader.FirstSkipListNodePagePos; while (skiplistnodePos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(skiplistnodePos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); skiplistnodePos = pageInfo.NextPagePos; } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("data block pages:"); long dataBlockPos = dbHeader.FirstDataPagePos; while (dataBlockPos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(dataBlockPos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); dataBlockPos = pageInfo.NextPagePos; } builder.AppendLine(); builder.AppendLine(); builder.AppendLine("empty pages:"); long emptyPos = dbHeader.FirstEmptyPagePos; while (emptyPos != 0) { PageHeaderBlock pageInfo = fs.ReadBlock <PageHeaderBlock>(emptyPos); builder.Append(string.Format(" {0}[{1}] ->", pageInfo.ThisPos, pageInfo.ThisPos / Consts.pageSize)); emptyPos = pageInfo.NextPagePos; } builder.AppendLine(); return(builder.ToString()); }
static string Print(PageHeaderBlock pageHeaderBlock) { return(string.Format("Pos: {0}, available: {1}, Occupied: {2}", pageHeaderBlock.ThisPos, pageHeaderBlock.AvailableBytes, pageHeaderBlock.OccupiedBytes)); }
/// <summary> /// 系统启动时初始化各项常量。 /// </summary> static Consts() { { PropertyInfo[] properties = typeof(Table).GetProperties(); foreach (var property in properties) { TableIndexAttribute attr = property.GetCustomAttribute <TableIndexAttribute>(); if (attr != null && property.PropertyType == typeof(ObjectId)) { Consts.TableIdString = property.Name; break; } } } { PageHeaderBlock page = new PageHeaderBlock(); using (MemoryStream ms = new MemoryStream()) { formatter.Serialize(ms, page); if (ms.Length > Consts.pageSize / 10) { throw new Exception("Page header block takes too much space!"); } Consts.pageHeaderBlockLength = (Int16)ms.Length; Consts.maxAvailableSpaceInPage = (Int16)(Consts.pageSize - ms.Length); Consts.minOccupiedBytes = (Int16)(Consts.pageSize - Consts.maxAvailableSpaceInPage); } BlockCache.TryRemoveFloatingBlock(page); } { PageHeaderBlock page = new PageHeaderBlock(); DBHeaderBlock dbHeader = new DBHeaderBlock(); TableBlock tableHead = new TableBlock(); Int16 usedSpaceInFirstPage; using (MemoryStream ms = new MemoryStream()) { formatter.Serialize(ms, page); dbHeader.ThisPos = ms.Length; formatter.Serialize(ms, dbHeader); tableHead.ThisPos = ms.Length; Consts.dbHeaderBlockLength = (Int16)(ms.Length - Consts.pageHeaderBlockLength); formatter.Serialize(ms, tableHead); Consts.tableBlockLength = (Int16)(ms.Length - tableHead.ThisPos); usedSpaceInFirstPage = (Int16)ms.Length; } if (usedSpaceInFirstPage > Consts.pageSize) { throw new Exception("First page is full!"); } BlockCache.TryRemoveFloatingBlock(page); BlockCache.TryRemoveFloatingBlock(dbHeader); BlockCache.TryRemoveFloatingBlock(tableHead); } { IndexBlock block = new IndexBlock(); int length = block.ToBytes().Length; if (length > Consts.pageSize / 10) { throw new Exception("index block takes too much space!"); } Consts.indexBlockLength = (Int16)length; BlockCache.TryRemoveFloatingBlock(block); } { SkipListNodeBlock block = new SkipListNodeBlock(); int length = block.ToBytes().Length; if (length > Consts.pageSize / 10) { throw new Exception("index block takes too much space!"); } Consts.skipListNodeBlockLength = (Int16)length; BlockCache.TryRemoveFloatingBlock(block); } { PageHeaderBlock page = new PageHeaderBlock(); DataBlock dataBlock = new DataBlock(); dataBlock.Data = new byte[0]; Int16 minValue; using (MemoryStream ms = new MemoryStream()) { formatter.Serialize(ms, page); long pos = ms.Length; formatter.Serialize(ms, dataBlock); Consts.dataBlockLength = (Int16)(ms.Length - pos); if (ms.Length > Consts.pageSize / 10) { throw new Exception("data block's metadata takes too much space!"); } minValue = (Int16)ms.Length; } Consts.maxDataBytes = (Int16)(Consts.pageSize - minValue); BlockCache.TryRemoveFloatingBlock(page); BlockCache.TryRemoveFloatingBlock(dataBlock); } }