private void UpdateListHeaderArea() { headerArea.Position = 4; headerArea.WriteInt4(BlockCount); headerArea.Position = 16; for (int i = 0; i < BlockCount; ++i) { headerArea.WriteInt8(blockElements[i]); } headerArea.Flush(); }
public long Create() { // Init the fixed record list area. // The fixed list entries are formatted as follows; // ( status (int), reference_count (int), // blob_size (long), blob_pointer (long) ) long fixedListOffset = fixedList.Create(); // Delete chain is empty when we start firstDeleteChainRecord = -1; fixedList.WriteDeleteHead(-1); // Allocate a small header that contains the MAGIC, and the pointer to the // fixed list structure. IArea blobStoreHeader = store.CreateArea(32); long blobStoreId = blobStoreHeader.Id; blobStoreHeader.WriteInt4(Magic); // Magic blobStoreHeader.WriteInt4(1); // The data version blobStoreHeader.WriteInt8(fixedListOffset); blobStoreHeader.Flush(); // Return the pointer to the blob store header return(blobStoreId); }
private void EstablishReference(long id) { try { lock (fixedList) { // Update the record in the fixed list. IArea block = fixedList.GetRecord(id); var recordPos = block.Position; int status = block.ReadInt32(); if (status != 1) { throw new Exception("Assertion failed: record is not static."); } int refCount = block.ReadInt32(); // Set the fixed blob record as complete. block.Position = recordPos + 4; block.Write(refCount + 1); block.Flush(); } } catch (IOException e) { throw new Exception("IO Error: " + e.Message); } }
public long Create() { // Allocate space for the list header (8 + 8 + (64 * 8)) IArea writer = store.CreateArea(528); headerAreaId = writer.Id; writer.WriteInt4(Magic); writer.Flush(); headerArea = store.GetArea(headerAreaId); BlockCount = 0; UpdateListHeaderArea(); return(headerAreaId); }
public ILargeObject CreateObject(long maxSize, bool compressed) { if (maxSize < 0) { throw new IOException("Negative object size not allowed."); } try { store.Lock(); // Allocate the area (plus header area) for storing the blob pages long pageCount = ((maxSize - 1) / (PageSize * 1024)) + 1; IArea objArea = store.CreateArea((pageCount * 8) + 32); long objAreaId = objArea.Id; var type = 2; // Binary Type if (compressed) { type |= CompressedFlag; } // Set up the area header objArea.Write(0); // Reserved for future objArea.Write(type); objArea.Write(maxSize); objArea.Write(0L); objArea.Write(pageCount); // Initialize the empty blob area for (long i = 0; i < pageCount; ++i) { objArea.Write(-1L); } // And finish objArea.Flush(); // Update the fixed_list and return the record number for this blob long refId = AddToRecordList(objAreaId); return(new LargeObject(this, refId, maxSize, 0, compressed, false)); } finally { store.Unlock(); } }
private void CompleteObject(LargeObject obj) { // Get the blob reference id (reference to the fixed record list). long refId = obj.Id; lock (fixedList) { // Update the record in the fixed list. IArea block = fixedList.GetRecord(refId); // Record the position var recordPos = block.Position; // Read the information in the fixed record int status = block.ReadInt32(); // Assert that the status is open if (status != 0) { throw new IOException("Assertion failed: record is not open."); } int refCount = block.ReadInt32(); long size = block.ReadInt64(); long currentSize = block.ReadInt64(); long pageCount = block.ReadInt64(); try { store.Lock(); block.Position = recordPos; block.Write(1); // Status block.Write(0); // Reference Count block.Write(obj.CurrentSize); // Final Size block.Write(obj.CurrentSize); block.Write(pageCount); // Page Count block.Flush(); } finally { store.Unlock(); } } // Now the object has been finalized so change the state of the object obj.MarkComplete(); }
public override void Flush() { area.Flush(); }
private bool ReleaseReference(long id) { try { lock (fixedList) { // Update the record in the fixed list. IArea block = fixedList.GetRecord(id); var recordPos = block.Position; int status = block.ReadInt4(); if (status != 1) { throw new Exception("Assertion failed: Record is not static (status = " + status + ")"); } int refCount = block.ReadInt4(); if (refCount == 0) { throw new Exception("Releasing when IBlob reference counter is at 0."); } var objSize = block.ReadInt8(); var objFinalSize = block.ReadInt8(); var objPos = block.ReadInt8(); // If reference count == 0 then we need to free all the resources // associated with this object in the store. if ((refCount - 1) == 0) { // Free the resources associated with this object. IArea area = store.GetArea(objPos); area.ReadInt4(); var type = (byte)area.ReadInt4(); var totalSize = area.ReadInt8(); var pageCount = area.ReadInt8(); // Free all of the pages in this blob. for (long i = 0; i < pageCount; ++i) { long pageOffset = area.ReadInt8(); if (pageOffset > 0) { store.DeleteArea(pageOffset); } } // Free the blob area object itself. store.DeleteArea(objPos); // Write out the blank record. block.Position = recordPos; block.WriteInt4(DeletedFlag); block.WriteInt4(0); block.WriteInt8(-1); block.WriteInt8(firstDeleteChainRecord); // CHeck out these changes block.Flush(); firstDeleteChainRecord = id; // Update the first_delete_chain_record field in the header fixedList.WriteDeleteHead(firstDeleteChainRecord); return(true); } // Simply decrement the reference counter for this record. block.Position = recordPos + 4; // Write the reference count - 1 block.WriteInt4(refCount - 1); // Check out this change block.Flush(); return(false); } } catch (IOException e) { throw new Exception("IO Error: " + e.Message); } }
private long AddToRecordList(long recordOffset) { lock (fixedList) { // If there is no free deleted records in the delete chain, if (firstDeleteChainRecord == -1) { // Increase the size of the list structure. fixedList.IncreaseSize(); // The start record of the new size int newBlockNumber = fixedList.BlockCount - 1; long startIndex = fixedList.BlockFirstPosition(newBlockNumber); long sizeOfBlock = fixedList.BlockNodeCount(newBlockNumber); // The IArea object for the new position IArea a = fixedList.GetRecord(startIndex); a.WriteInt4(0); a.WriteInt4(0); a.WriteInt8(-1); // Initially unknown size a.WriteInt8(0); // Initially unknown current size a.WriteInt8(recordOffset); // Set the rest of the block as deleted records for (long n = 1; n < sizeOfBlock - 1; ++n) { a.WriteInt4(DeletedFlag); a.WriteInt4(0); a.WriteInt8(-1); a.WriteInt8(startIndex + n + 1); } // The last block is end of delete chain. a.WriteInt4(DeletedFlag); a.WriteInt4(0); a.WriteInt8(-1); a.WriteInt8(-1); // Check out the changes. a.Flush(); // And set the new delete chain firstDeleteChainRecord = startIndex + 1; fixedList.WriteDeleteHead(firstDeleteChainRecord); // Return pointer to the record we just added. return(startIndex); } // Pull free block from the delete chain and recycle it. long recycledRecord = firstDeleteChainRecord; IArea block = fixedList.GetRecord(recycledRecord); var recordPos = block.Position; // Status of the recycled block int status = block.ReadInt4(); if ((status & DeletedFlag) == 0) { throw new InvalidOperationException("Assertion failed: record is not deleted!"); } // Reference count (currently unused in delete chains). block.ReadInt4(); // The size (should be -1); block.ReadInt8(); // The current size should be 0 block.ReadInt8(); // The pointer to the next in the chain. long nextChain = block.ReadInt8(); firstDeleteChainRecord = nextChain; // Update the first_delete_chain_record field in the header fixedList.WriteDeleteHead(firstDeleteChainRecord); // Update the block block.Position = recordPos; block.WriteInt4(0); block.WriteInt4(0); block.WriteInt8(-1); // Initially unknown size block.WriteInt8(0); block.WriteInt8(recordOffset); // Check out the changes block.Flush(); return(recycledRecord); } }
private void WriteObjectPart(long id, long objOffset, byte[] buffer, int off, int length) { // ASSERT: Read and Write position must be 64K aligned. if (objOffset % (PageSize * 1024) != 0) { throw new Exception("Assert failed: offset is not 64k aligned."); } // ASSERT: Length is less than or equal to 64K if (length > (PageSize * 1024)) { throw new Exception("Assert failed: length is greater than 64K."); } int refCount; long objPos; long maxSize; long currentSize; lock (fixedList) { if (id < 0 || id >= fixedList.NodeCount) { throw new IOException("Object id is out of range."); } IArea block = fixedList.GetRecord(id); var status = block.ReadInt4(); if ((status & DeletedFlag) != 0) { throw new InvalidOperationException("Assertion failed: record is deleted!"); } block.ReadInt4(); // Ref count maxSize = block.ReadInt8(); // Total Size / Max Size currentSize = block.ReadInt8(); // Current Size objPos = block.ReadInt8(); // Last Page Position } // Open an IArea into the blob IArea area = store.GetArea(objPos); area.ReadInt4(); var type = area.ReadInt4(); var size = area.ReadInt8(); // Assert that the area being Read is within the bounds of the blob if (objOffset < 0 || objOffset + length > size) { throw new IOException("Object invalid write. offset = " + objOffset + ", length = " + length + ", size = " + size); } // Convert to the page number long pageNumber = (objOffset / (PageSize * 1024)); area.Position = (int)((pageNumber * 8) + 32); long pagePos = area.ReadInt8(); if (pagePos != -1) { // This means we are trying to rewrite a page we've already written // before. throw new Exception("Assert failed: page position is not -1"); } // Is the compression bit set? byte[] toWrite; int writeLength; if ((type & CompressedFlag) != 0) { // Yes, compression #if !PCL using (var input = new MemoryStream(buffer, off, length)) { using (var output = new MemoryStream(PageSize * 1024)) { using (var deflateStream = new DeflateStream(output, CompressionMode.Compress, false)) { input.CopyTo(deflateStream); deflateStream.Flush(); deflateStream.Close(); toWrite = output.ToArray(); writeLength = toWrite.Length; } } } #else throw new NotSupportedException("Compression not supported in PCL."); #endif } else { // No compression toWrite = buffer; writeLength = length; } try { store.Lock(); // Allocate and Write the page. IArea pageArea = store.CreateArea(writeLength + 8); pagePos = pageArea.Id; pageArea.WriteInt4(1); pageArea.WriteInt4(writeLength); pageArea.Write(toWrite, 0, writeLength); // Finish this page pageArea.Flush(); // Update the page in the header. area.Position = (int)((pageNumber * 8) + 24); area.WriteInt8(currentSize + writeLength); area.WriteInt8(pagePos); // Check out this change. area.Flush(); } finally { store.Unlock(); } }
public void PrepareIndexLists(int count, int type, int blockSize) { lock (this) { try { Store.Lock(); // Allocate a new area for the list int newSize = 16 + ((indexBlocks.Length + count) * 16); using (var newIndexArea = Store.CreateArea(newSize)) { long newIndexPointer = newIndexArea.Id; var newIndexBlocks = new IndexBlock[(indexBlocks.Length + count)]; // Copy the existing area indexHeaderArea.Position = 0; int version = indexHeaderArea.ReadInt4(); int reserved = indexHeaderArea.ReadInt4(); long icount = indexHeaderArea.ReadInt8(); newIndexArea.WriteInt4(version); newIndexArea.WriteInt4(reserved); newIndexArea.WriteInt8(icount + count); for (int i = 0; i < indexBlocks.Length; ++i) { int itype = indexHeaderArea.ReadInt4(); int iblockSize = indexHeaderArea.ReadInt4(); long indexBlockP = indexHeaderArea.ReadInt8(); newIndexArea.WriteInt4(itype); newIndexArea.WriteInt4(iblockSize); newIndexArea.WriteInt8(indexBlockP); newIndexBlocks[i] = indexBlocks[i]; } // Add the new entries for (int i = 0; i < count; ++i) { long newBlankBlockP = CreateNewBlock(); newIndexArea.WriteInt4(type); newIndexArea.WriteInt4(blockSize); newIndexArea.WriteInt8(newBlankBlockP); var newBlock = new IndexBlock(this, indexBlocks.Length + i, blockSize, newBlankBlockP); newBlock.AddReference(); newIndexBlocks[indexBlocks.Length + i] = newBlock; } // Finished initializing the index. newIndexArea.Flush(); // The old index header pointer long oldIndexHeaderP = indexHeaderPointer; // Update the state of this object, indexHeaderPointer = newIndexPointer; indexHeaderArea = Store.GetArea(newIndexPointer); indexBlocks = newIndexBlocks; // Update the start pointer startArea.Position = 8; startArea.WriteInt8(newIndexPointer); startArea.Flush(); // Free the old header Store.DeleteArea(oldIndexHeaderP); } } finally { Store.Unlock(); } } }