예제 #1
0
        /// <summary>
        /// Сохранить данные на диск.
        /// </summary>
        /// <param name="record">Массив байт.</param>
        public void Save(DataRecord record)
        {
            lock (_createDataPageLock)
            {
                var recordId = ++_currentDataRecordId;
                record.SetDataRecordId(recordId);

                var data       = record.GetBytes();
                var dataLength = data.Length;

                // пишем последовательно, поэтому получаем всегда для записи последнюю страницу.
                var currentDataPage = GetLastPage();

                // если на текущей странице хватает свободного места для данных, указанной длины
                if (currentDataPage.HasEnoughSpaceFor(dataLength))
                {
                    // то пишем в текущую страницу.
                    currentDataPage.TrySaveData(recordId, data, out var dataOffset);
                    // добавляем в индекс
                    _dataRecordIndexStorage.AddToIndex(new DataRecordIndexPointer(
                                                           recordId,
                                                           currentDataPage.PageId,
                                                           dataOffset,
                                                           dataLength
                                                           )
                                                       );

                    return;
                }

                // если же места не хватило на одной странице, то уже пишем в несколько:
                var dataRecordIndexPointers = new List <DataRecordIndexPointer>();
                var writtenBytes            = 0;
                var dataSpan = data.AsSpan();

                // узнаем, сколько можно записать в текущую страницу:
                var freeSpaceLength = currentDataPage.GetFreeSpaceFor(dataSpan.Length);

                // если оказалось что мы что-то можем записать.
                if (freeSpaceLength > 0)
                {
                    // то срезаем кусок данных
                    var firstWrite = dataSpan.Slice(0, freeSpaceLength).ToArray();

                    // и заполним оставшееся место в текущей странице
                    if (currentDataPage.TrySaveData(recordId, firstWrite, out var offset))
                    {
                        dataRecordIndexPointers.Add(new DataRecordIndexPointer(
                                                        recordId,
                                                        currentDataPage.PageId,
                                                        offset,
                                                        firstWrite.Length
                                                        )
                                                    );
                    }

                    writtenBytes += freeSpaceLength;
                }

                // и до тех пор, пока все не будет записано...
                while (writtenBytes < dataLength)
                {
                    // создадём новую страницу.
                    currentDataPage = CreateNew();

                    // получим кол-во байт, которое можно записать в текущую страницу.
                    var bytesToWrite = currentDataPage.GetFreeSpaceFor(dataLength - writtenBytes);

                    Debug.Assert(bytesToWrite != 0);

                    // создаем разрез для записи.
                    var slice = dataSpan.Slice(writtenBytes, bytesToWrite).ToArray();

                    // сохраняем.
                    currentDataPage.TrySaveData(recordId, slice, out var offset);
                    dataRecordIndexPointers.Add(new DataRecordIndexPointer(
                                                    recordId,
                                                    currentDataPage.PageId,
                                                    offset,
                                                    slice.Length
                                                    )
                                                );
                    writtenBytes += slice.Length;
                }

                // Первый индекс сделаем основным, остальные как дополнения.
                var index = dataRecordIndexPointers.First();
                _dataRecordIndexStorage.AddToIndex(new DataRecordIndexPointer(
                                                       index.DataRecordId,
                                                       index.DataPageNumber,
                                                       index.Offset,
                                                       index.Length,
                                                       dataRecordIndexPointers.Skip(1).ToArray()
                                                       )
                                                   );
            }
        }