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();
                }
            }
        }