예제 #1
0
        /// <summary>
        /// Reads all of the bytes referenced by this location into the buffer supplied
        /// </summary>
        public void ReadInto(
            ITransaction transaction,
            CacheHints hints,
            byte[] record)
        {
#if DEBUG
            if ((ulong)record.LongLength != Length)
            {
                throw new FileLayerException("The buffer passed is the wrong length");
            }
#endif

            if (ContunuationPages == null)
            {
                using (var page = PageStore.Get(transaction, PageNumber, hints))
                    Array.Copy(page.Data, Offset, record, 0, record.Length);
            }
            else
            {
                var recordOffset = 0UL;
                var bytesToCopy  = PageStore.PageSize - Offset;

                using (var page = PageStore.Get(transaction, PageNumber, hints))
                    Array.Copy(page.Data, Offset, record, (long)recordOffset, bytesToCopy);

                foreach (var pageNumber in ContunuationPages)
                {
                    recordOffset += bytesToCopy;

                    bytesToCopy = PageStore.PageSize;

                    if (recordOffset + bytesToCopy > Length)
                    {
                        bytesToCopy = (uint)(Length - recordOffset);
                    }

                    using (var page = PageStore.Get(transaction, pageNumber, hints))
                        Array.Copy(page.Data, 0, record, (long)recordOffset, bytesToCopy);
                }
            }
        }
예제 #2
0
        PageLocation ISequentialRecordAccessor.Append(ulong firstPageNumber, ITransaction transaction, ulong recordSize)
        {
            if (recordSize == 0)
            {
                return(null);
            }

            // Find the last index page

            var indexPageNumber = firstPageNumber;

            while (true)
            {
                _pageStore.Lock(transaction, indexPageNumber);

                using (var indexPage = _pageStore.Get(transaction, indexPageNumber, CacheHints.MetaData | CacheHints.WithLock))
                {
                    var nextIndexPageNumber = BitConverter.ToUInt64(indexPage.Data, 0);
                    if (nextIndexPageNumber == 0UL)
                    {
                        break;
                    }
                    indexPageNumber = nextIndexPageNumber;
                }
            }

            var indexEntryOffset           = 8U;
            var lastRecordSize             = 0UL;
            var lastRecordEndingPageNumber = 0UL;
            var lastRecordBytesOnLastPage  = 0U;

            using (var lastIndexPage = _pageStore.Get(transaction, indexPageNumber, CacheHints.MetaData | CacheHints.WithLock))
            {
                while (lastIndexPage.Data[indexEntryOffset] != 0)
                {
                    var indexEntryType = lastIndexPage.Data[indexEntryOffset + 1];

                    if (indexEntryType == 1 || indexEntryType == 2)
                    {
                        // This is the start of a record
                        lastRecordSize = BitConverter.ToUInt64(lastIndexPage.Data, (int)indexEntryOffset + 2);
                        ulong lastRecordStartingPageNumber = BitConverter.ToUInt64(lastIndexPage.Data, (int)indexEntryOffset + 10);
                        uint  lastRecordOffset             = BitConverter.ToUInt32(lastIndexPage.Data, (int)indexEntryOffset + 18);
                        lastRecordBytesOnLastPage = (uint)((lastRecordSize + lastRecordOffset) % _pageSize);
                        ulong lastRecordPageCount = (lastRecordSize + lastRecordOffset + _pageSize - 1) / _pageSize;
                        lastRecordEndingPageNumber = lastRecordStartingPageNumber;
                    }
                    else if (indexEntryType == 3 && lastRecordSize != 0)
                    {
                        // This is a record continuation
                        var pageCount = lastIndexPage.Data[indexEntryOffset + 2];
                        lastRecordEndingPageNumber = BitConverter.ToUInt64(lastIndexPage.Data, (int)indexEntryOffset + 3 + (pageCount - 1) * 8);
                    }

                    // Find the last record and last index entry on this page

                    uint priorIndexEntryOffset = indexEntryOffset;
                    indexEntryOffset += lastIndexPage.Data[indexEntryOffset];
                }
            }

            var thisRecordStartingPageNumber = (lastRecordEndingPageNumber > 0 && lastRecordBytesOnLastPage < _pageSize)
                ? lastRecordEndingPageNumber
                : _pageStore.Allocate();

            var thisRecordOffset = thisRecordStartingPageNumber == lastRecordEndingPageNumber
                ? lastRecordBytesOnLastPage
                : 0U;

            var updates  = new List <PageUpdate>();
            var sequence = 1U;

            var additionalPageNumbers = AddRecordIndex(
                updates,
                ref sequence,
                indexPageNumber,
                indexEntryOffset,
                recordSize,
                thisRecordStartingPageNumber,
                thisRecordOffset);

            _pageStore.Update(transaction, updates);

            return(new PageLocation
            {
                PageStore = _pageStore,
                PageNumber = thisRecordStartingPageNumber,
                Offset = thisRecordOffset,
                Length = recordSize,
                ContunuationPages = additionalPageNumbers
            });
        }
예제 #3
0
        PageLocation ISequentialRecordAccessor.Append(
            ulong firstPageNumber,
            ITransaction transaction,
            ulong recordSize)
        {
            if (recordSize == 0)
            {
                return(null);
            }

            var pageSize = _pageStore.PageSize;

            if (recordSize > pageSize)
            {
                throw new FileLayerException("Invalid attempt to write " + recordSize +
                                             " bytes into a page store with " + pageSize + " byte pages");
            }

            var updates  = new List <PageUpdate>();
            var sequence = 1U;

            PageLocation indexLocation = new PageLocation
            {
                PageStore  = _pageStore,
                PageNumber = firstPageNumber,
                Offset     = _indexPageHeadSize,
                Length     = _indexEntrySize
            };

            PageLocation recordLocation = new PageLocation
            {
                PageStore = _pageStore,
                Length    = recordSize
            };

            while (true)
            {
                _pageStore.Lock(transaction, indexLocation.PageNumber);

                using (var indexPage = _pageStore.Get(transaction, indexLocation.PageNumber, CacheHints.MetaData | CacheHints.WithLock))
                {
                    var nextIndexPageNumber = BitConverter.ToUInt64(indexPage.Data, 0);
                    if (nextIndexPageNumber == 0UL)
                    {
                        var nextIndexOffset = _indexPageHeadSize;
                        recordLocation.PageNumber = 0UL;

                        while (true)
                        {
                            var nextRecordPageNumber = BitConverter.ToUInt64(indexPage.Data, (int)nextIndexOffset);
                            var nextRecordOffset     = BitConverter.ToUInt32(indexPage.Data, (int)(nextIndexOffset + _pageNumberSize));

                            if (nextRecordPageNumber == 0UL)
                            {
                                indexLocation.Offset = nextIndexOffset;
                                break;
                            }

                            recordLocation.PageNumber = nextRecordPageNumber;
                            recordLocation.Offset     = nextRecordOffset;

                            nextIndexOffset += _indexEntrySize;

                            if (nextIndexOffset + _indexEntrySize > pageSize)
                            {
                                var newIndexPageNumber = _pageStore.Allocate();

                                updates.Add(new PageUpdate
                                {
                                    SequenceNumber = sequence++,
                                    PageNumber     = indexLocation.PageNumber,
                                    Offset         = 0U,
                                    Data           = BitConverter.GetBytes(newIndexPageNumber)
                                });

                                indexLocation.PageNumber = newIndexPageNumber;
                                indexLocation.Offset     = _indexPageHeadSize;

                                break;
                            }
                        }

                        break;
                    }
                    indexLocation.PageNumber = nextIndexPageNumber;
                }
            }

            if (recordLocation.PageNumber == 0UL || recordLocation.Offset + recordSize > pageSize)
            {
                recordLocation.PageNumber = _pageStore.Allocate();
                recordLocation.Offset     = 0U;
            }

            var newIndexEntry = new byte[_indexEntrySize];

            BitConverter.GetBytes(recordLocation.PageNumber).CopyTo(newIndexEntry, 0);
            BitConverter.GetBytes(recordLocation.Offset + (uint)recordSize).CopyTo(newIndexEntry, _pageNumberSize);

            updates.Add(new PageUpdate
            {
                SequenceNumber = sequence++,
                PageNumber     = indexLocation.PageNumber,
                Offset         = indexLocation.Offset,
                Data           = newIndexEntry
            });

            _pageStore.Update(transaction, updates);

            return(recordLocation);
        }
예제 #4
0
        public void should_allow_multiple_transactions_to_append_records()
        {
            const ushort objectType  = 128;
            const int    threadCount = 5;

            var threads         = new List <Thread>();
            var exceptions      = new List <Exception>();
            var firstPageNumber = _pageStore.GetFirstIndexPage(objectType);

            for (var i = 1; i <= threadCount; i++)
            {
                var transactionNumber = i;
                var thread            = new Thread(() =>
                {
                    try
                    {
                        var writeTransaction = _database.BeginTransaction(null);
                        _pageStore.BeginTransaction(writeTransaction);

                        var buffer   = Encoding.UTF8.GetBytes("Transaction " + transactionNumber);
                        var location = _accessor.Append(firstPageNumber, writeTransaction, (uint)buffer.LongLength);
                        _accessor.Write(writeTransaction, location, buffer);

                        Thread.Sleep(50);

                        _database.CommitTransaction(writeTransaction);
                        _pageStore.CommitTransaction(writeTransaction);
                        _pageStore.FinalizeTransaction(writeTransaction);
                    }
                    catch (Exception ex)
                    {
                        lock (exceptions)
                            exceptions.Add(ex);
                    }
                })
                {
                    Name         = "Transaction " + i,
                    IsBackground = true
                };
                threads.Add(thread);
            }

            foreach (var thread in threads)
            {
                thread.Start();
            }
            foreach (var thread in threads)
            {
                thread.Join();
            }

            Assert.AreEqual(0, exceptions.Count);

            var transaction = _database.BeginTransaction(null);

            _pageStore.BeginTransaction(transaction);

            var results = new HashSet <string>();

            Action <PageLocation> check = (location) =>
            {
                using (var page = _pageStore.Get(transaction, location.PageNumber, CacheHints.None))
                {
                    var actual = Encoding.UTF8.GetString(page.Data, (int)location.Offset, (int)location.Length);
                    Assert.IsTrue(results.Add(actual));
                }
            };

            var record = _accessor.LocateFirst(firstPageNumber, transaction, out object indexLocation);

            Assert.IsNotNull(record);

            for (var i = 1; i <= threadCount; i++)
            {
                check(record);
                record = _accessor.LocateNext(firstPageNumber, transaction, indexLocation);
            }

            for (var i = 1; i <= threadCount; i++)
            {
                Assert.IsTrue(results.Contains("Transaction " + i));
            }

            Assert.IsNull(record);
        }
예제 #5
0
        public void should_update_pages()
        {
            var transaction = _database.BeginTransaction(null);

            _pageStore.BeginTransaction(transaction);

            _pageStore.Update(
                transaction, new[]
            {
                new PageUpdate
                {
                    SequenceNumber = 1,
                    PageNumber     = 1,
                    Offset         = 10,
                    Data           = new byte[] { 1, 2, 3 }
                }
            });

            using (var page = _pageStore.Get(transaction, 1, CacheHints.None))
            {
                Assert.AreEqual(1, page.Data[10]);
                Assert.AreEqual(2, page.Data[11]);
                Assert.AreEqual(3, page.Data[12]);
            }

            _database.CommitTransaction(transaction);
            _pageStore.CommitTransaction(transaction);
            _pageStore.FinalizeTransaction(transaction);
        }