Example #1
0
            //public static void Write(Stream stream, SpecialPostingBlockEntry entry)
            //{
            //    TermLink.Write(stream, entry.FirstPosting ?? new TermLink());
            //    stream.WriteInt64Network(entry.Offset);
            //}

            public static int Write(byte[] buffer, SpecialPostingBlockEntry entry, int offset)
            {
                var size = TermLink.Write(buffer, entry.FirstPosting, offset);

                offset += size;
                buffer.WriteInt64Network(entry.Offset, offset);
                size += 8;
                return(size);
            }
Example #2
0
        /// <summary>
        /// ibatrak очистка полей с количеством ссылок в заголовке записи
        /// </summary>
        public static void ClearItems(Stream stream, long offset)
        {
            //в заголовке проставляем 0 записей, сами ссылки не трогаем
            //ирбис так делает, проверено
            var ifpRecord = ReadLeader(stream, offset);
            //в записи со спец блоком зануляется только TotalLinkCount и оба поля во вложенных записях
            var special = ifpRecord.Special;

            if (!special)
            {
                ifpRecord.BlockLinkCount = 0;
            }
            ifpRecord.TotalLinkCount = 0;
            WriteLeader(stream, ifpRecord, offset);
            if (special)
            {
                //начитаем первую запись из спец блока, в ней ссылка на первую запись обыкновенного формата
                //остальные записи спец блока здесь не нужны

                var specBlock       = new SpecialPostingBlockEntry[ifpRecord.Capacity];
                var specBlockBuffer = new byte[ifpRecord.Capacity * SpecialPostingBlockEntry.RecordSize];
                var fileOffset      = stream.Position;
                if (stream.Read(specBlockBuffer, 0, specBlockBuffer.Length) != specBlockBuffer.Length)
                {
                    throw new IOException();
                }

                for (int i = 0; i < ifpRecord.Capacity; i++)
                {
                    var entry = SpecialPostingBlockEntry.Read(specBlockBuffer, i * SpecialPostingBlockEntry.RecordSize);
                    entry.FileOffset = fileOffset + i + SpecialPostingBlockEntry.RecordSize;
                    specBlock[i]     = entry;
                }

                var nonEmptyEntries = specBlock.Where(e => e.Offset > 0).Count();
                if (nonEmptyEntries != ifpRecord.BlockLinkCount)
                {
                    throw new InvalidOperationException("Ошибка чтения ifp offset=" + offset);
                }

                var nestedRecords = new IfpRecord[ifpRecord.BlockLinkCount];

                for (int i = 0; i < ifpRecord.BlockLinkCount; i++)
                {
                    if (specBlock[i].Offset <= 0 ||
                        (i > 0 && nestedRecords[i - 1].FullOffset != specBlock[i].Offset))
                    {
                        throw new InvalidOperationException("Ошибка чтения ifp offset=" + offset);
                    }
                    var nestedRecord = ReadLeader(stream, specBlock[i].Offset);
                    nestedRecord.BlockLinkCount = 0;
                    nestedRecord.TotalLinkCount = 0;
                    nestedRecords[i]            = nestedRecord;
                    WriteLeader(stream, ifpRecord, specBlock[i].Offset);
                }
            }
        }
Example #3
0
            //public static SpecialPostingBlockEntry Read(Stream stream)
            //{
            //    var result = new SpecialPostingBlockEntry
            //    {
            //        FileOffset = stream.Position,
            //        FirstPosting = TermLink.Read(stream),
            //        Offset = stream.ReadInt64Network()
            //    };
            //    return result;
            //}

            public static SpecialPostingBlockEntry Read(byte[] buffer, int offset)
            {
                //ibatrak чтение из буфера
                var result = new SpecialPostingBlockEntry
                {
                    FirstPosting = TermLink.Read(buffer, offset),
                    Offset       = buffer.ReadInt64Network(offset + TermLink.RecordSize)
                };

                return(result);
            }
Example #4
0
        /// <summary>
        /// Read the record.
        /// </summary>
        public static IfpRecord Read
        (
            [NotNull] Stream stream,
            long offset
        )
        {
            stream.Position = offset;

            IfpRecord result = new IfpRecord
            {
                LowOffset      = stream.ReadInt32Network(),
                HighOffset     = stream.ReadInt32Network(),
                TotalLinkCount = stream.ReadInt32Network(),
                BlockLinkCount = stream.ReadInt32Network(),
                Capacity       = stream.ReadInt32Network()
            };

            // ibatrak чтение вложенных записей в спец блоке
            // Специальный формат записи .ifp
            // В этом случае первой записью является специальный блок,
            // который представляет собой заголовок (обыкновенного формата),
            // в котором смещения имеют специальные значения = -1001
            if (result.LowOffset == -1001 && result.HighOffset == -1001)
            {
                // irbis64.dll делает так

                // читает первые 24 байта блока спец ссылок
                // (это 2 записи)
                // записи с реальными адресами идут через одну
                // берет вторую запись, адрес из нее в файле
                // и читает записеи IFP
                // по количеству result.BlockLinkCount

                // первую запись спец блока просто пропускаем,
                // читаем сразу вторую
                stream.Position += 12;

                SpecialPostingBlockEntry entry = new SpecialPostingBlockEntry
                {
                    Posting = stream.ReadInt32Network(),
                    Low     = stream.ReadInt32Network(),
                    High    = stream.ReadInt32Network()
                };

                stream.Position = entry.Offset;

                IfpRecord[] nestedRecords
                    = new IfpRecord[result.BlockLinkCount];
                for (int i = 0; i < result.BlockLinkCount; i++)
                {
                    var nestedRecord = Read(stream, stream.Position);
                    nestedRecords[i] = nestedRecord;

                    // Last record in the list must have
                    // negative offset values
                    if (nestedRecord.LowOffset == -1 &&
                        nestedRecord.HighOffset == -1)
                    {
                        if (i != result.BlockLinkCount - 1)
                        {
                            throw new InvalidOperationException
                                  (
                                      "IFP reading error"
                                  );
                        }
                        break;
                    }
                    if (nestedRecord.FullOffset < 0)
                    {
                        throw new InvalidOperationException
                              (
                                  "IFP reading error"
                              );
                    }

                    stream.Position = nestedRecord.FullOffset;
                }

                TermLink[] links = nestedRecords
                                   .SelectMany(r => r.Links)
                                   .ToArray();
                if (links.Length != result.TotalLinkCount)
                {
                    throw new InvalidOperationException
                          (
                              "IFP reading error"
                          );
                }
                result.Links.AddRange(links);

                return(result);
            }
            //ibatrak до сюда

            for (int i = 0; i < result.BlockLinkCount; i++)
            {
                TermLink link = TermLink.Read(stream);
                result.Links.Add(link);
            }

            return(result);
        }
Example #5
0
        /// <summary>
        /// ibatrak добавление новой или обновление существующей записи
        /// </summary>
        public static int Write(Stream stream, NodeItem item, TermLink[] links, long offset, bool padding)
        {
            int       size      = 0;
            IfpRecord ifpRecord = null;
            //повторное использование существующих записей
            long oldOffset = item.FullOffset;

            //если элемент новый - отметить в нем ссылку на запись ifp
            if (item.FullOffset == 0)
            {
                item.FullOffset = offset;
            }
            int capacity = 0;
            //требуется писать записи обыкновенного формата, в случае расширения старой записи
            bool expanding = false;

            if (oldOffset > 0)
            {
                bool topRecord  = true;
                int  linksCount = links.Length;
                //перезаписываем в цикле все записи по цепочке
                while (true)
                {
                    //начитаем старую версию записи с нужным объемом или последнюю в цепочке
                    ifpRecord = Read(stream, oldOffset);

                    //в записи со спец блоком реальная вместимость записи в ссылках считается по вложенным записям
                    capacity = ifpRecord.Special ? ifpRecord._NestedRecordLeaders.Sum(r => r.Capacity) : ifpRecord.Capacity;

                    bool last = ifpRecord.Last;
                    //если в старой записи хватает места, перезапишем ее
                    if (links.Length <= capacity)
                    {
                        ifpRecord._changed = false;
                        ReplaceLinks(ifpRecord, links, links.Length);

                        //счетчик ссылок обновим
                        ifpRecord.TotalLinkCount = links.Length;
                        if (!ifpRecord.Special)
                        {
                            ifpRecord.BlockLinkCount = links.Length;
                        }
                        //запись в файл только при наличии изменений
                        if (ifpRecord._changed)
                        {
                            Write(stream, ifpRecord, oldOffset, false /*при обновлении записей всегда padding = false*/);
                        }
                        links = null;
                    }
                    else //перезапись старой записи при расширении
                    {
                        expanding = true;

                        //ирбис до упора не заполняет, при расширении делит пополам
                        //уменьшение размеров последнего блока возможно, если
                        //количество новых элементов плюс половина последнего блока не превышают размер блока
                        int countToAdd = 0;
                        //запись со спец блоком в цепочке быть не может
                        //деление записей происходит только в последней по цепочке записи
                        int newLinksCount = links.Length - capacity;
                        if (!ifpRecord.Special)
                        {
                            //половина последнего блока + количество новых ссылок вмещается в блок, тогда делим
                            if (last && capacity > 1 &&
                                ((int)Math.Ceiling((double)capacity / 2d) + newLinksCount) <= PostingsInBlock)
                            {
                                countToAdd = (int)Math.Ceiling((double)capacity / 2d);
                            }
                            else
                            {
                                countToAdd = capacity;
                            }
                        }
                        else
                        {
                            //запись со спец блоком
                            //половина последнего блока + количество новых ссылок вмещается в блок, тогда делим
                            if (((int)Math.Ceiling((double)ifpRecord._NestedRecordLeaders[ifpRecord._NestedRecordLeaders.Length - 1].Capacity / 2d) + newLinksCount) <= PostingsInBlock)
                            {
                                //в записи со спец блоком последняя делится пополам
                                countToAdd = capacity - (int)Math.Ceiling((double)ifpRecord._NestedRecordLeaders[ifpRecord._NestedRecordLeaders.Length - 1].Capacity / 2d);
                            }
                            else
                            {
                                countToAdd = capacity;
                            }
                        }

                        //if (!last)
                        //    countToAdd = capacity;
                        //else
                        //{
                        //    if (!ifpRecord.Special)
                        //        countToAdd = capacity > 1 ? (int)Math.Ceiling((double)capacity / 2d) : capacity;
                        //    else //в записи со спец блоком прследняя делится пополам
                        //        countToAdd = capacity - (int)Math.Ceiling((double)ifpRecord._NestedRecordLeaders[ifpRecord._NestedRecordLeaders.Length - 1].Capacity / 2d);
                        //}
                        //если в старой записи не хватает места, перезапишем в нее сколько влезет
                        //остальное запишем в новую запись
                        ifpRecord._changed = false;
                        ReplaceLinks(ifpRecord, links, countToAdd);
                        //ifpRecord.Links.Clear();
                        //ifpRecord.Links.AddRange(links.Take(countToAdd).ToArray());
                        //счетчик ссылок обновим
                        //верхняя в цепочке запись получает в заголовке полное количество ссылок
                        if (topRecord)
                        {
                            ifpRecord.TotalLinkCount = linksCount;
                        }
                        else
                        {
                            ifpRecord.TotalLinkCount = countToAdd;
                        }

                        //в обычной записи здесь ставится количество записей в блоке
                        if (!ifpRecord.Special)
                        {
                            ifpRecord.BlockLinkCount = countToAdd;
                        }
                        //так быстрее, чем ToArray
                        Array.Copy(links, countToAdd, links, 0, links.Length - countToAdd);
                        Array.Resize(ref links, links.Length - countToAdd);
                        //links = links.Skip(countToAdd).ToArray();

                        //установка ссылки на следующую запись если мы достигли конца цепочки для обычных записей
                        //для записи со спец блоком будет расширение спец блока и там установка ссылки на следующую запись
                        int newBlocks = (int)Math.Ceiling((double)links.Length / (double)PostingsInBlock);
                        //расширение записи со спец блоком идет не на одну запись, а по количеству блоков
                        //if (last || (ifpRecord.Special && ifpRecord._NestedRecordLeaders.Length < ifpRecord.Capacity))
                        if (last || (ifpRecord.Special && ifpRecord._NestedRecordLeaders.Length + newBlocks <= ifpRecord.Capacity))
                        {
                            SetNext(ifpRecord, offset, links, newBlocks);
                            //SetNext(stream, ifpRecord, offset, links[0], newBlocks);
                        }
                        //если расширяется запись со спец блоком, то лидер записи и новый спец блок помещаются в другое место
                        //в новом спец блоке добавляется 4 блока
                        //else if (ifpRecord.Special && ifpRecord._NestedRecordLeaders.Length >= ifpRecord.Capacity)
                        else if (ifpRecord.Special && ifpRecord._NestedRecordLeaders.Length + newBlocks > ifpRecord.Capacity)
                        {
                            var newLeader = new IfpRecord();
                            newLeader.Special = true;

                            var specialBlock = ifpRecord._SpecialBlock;
                            int count        = specialBlock.Length;
                            int newCapacity  = 4 * (int)Math.Ceiling(((double)ifpRecord._NestedRecordLeaders.Length + (double)newBlocks) / 4d);
                            Array.Resize(ref specialBlock, newCapacity);
                            for (int i = count; i < specialBlock.Length; i++)
                            {
                                specialBlock[i] = new SpecialPostingBlockEntry();
                            }

                            newLeader.Capacity       = newCapacity;
                            newLeader.TotalLinkCount = linksCount;
                            //count - это количество элементов в спец блоке всего, надо количество непустых элементов
                            //newLeader.BlockLinkCount = count /*при вызове SetNext будет инкремент*/;
                            newLeader.BlockLinkCount = ifpRecord.BlockLinkCount /*при вызове SetNext будет инкремент*/;

                            int newLeaderSize = LeaderSize + newLeader.Capacity * SpecialPostingBlockEntry.RecordSize;
                            newLeader._SpecialBlock = specialBlock;

                            newLeader._NestedRecordLeaders = ifpRecord._NestedRecordLeaders;
                            //newLeader._NestedRecordLeaders используется для SetNext
                            SetNext(newLeader, offset + newLeaderSize, links, newBlocks);
                            //SetNext(stream, newLeader, offset + newLeaderSize, links[0], newBlocks);
                            newLeader._NestedRecordLeaders = null;

                            //в элементе надо заменить адрес старой записи
                            item.FullOffset = offset;

                            Write(stream, newLeader, offset, true);

                            offset += newLeaderSize;
                            size   += newLeaderSize;
                        }

                        if (ifpRecord._changed)
                        {
                            Write(stream, ifpRecord, oldOffset, false /*при обновлении записей всегда padding = false*/);
                        }
                    }

                    if (last || ifpRecord.FullOffset <= 0 || links == null || links.Length == 0)
                    {
                        //если будем расширять запись со спец блоком, то максимальный размер у нее не более PostingsInBlock
                        if (ifpRecord.Special)
                        {
                            capacity = PostingsInBlock;
                        }
                        break;
                    }
                    oldOffset = ifpRecord.FullOffset;
                    topRecord = false;
                }
            }
            //добавление новой записи
            if (links != null && links.Length > 0)
            {
                //при расширении записи всегда пишем записи обычного формата
                //здесь назначаем емкость записи
                //if (ifpRecord.Links.Count < MinPostingsInBlock)
                if (expanding || links.Length < MinPostingsInBlock)
                {
                    //делаем несколько блоков только если идет расширение старой записи
                    int blockCount = expanding ? (int)Math.Ceiling((double)links.Length / (double)PostingsInBlock) : 1;
                    for (int i = 0; i < blockCount; i++)
                    {
                        var lastBlock = i == blockCount - 1;
                        ifpRecord = new IfpRecord();
                        ifpRecord.Links.AddRange(blockCount == 1 ? links : links.Skip(i * PostingsInBlock).Take(PostingsInBlock));
                        //при расширении записи следующая запись получает столько ссылок, сколько емкость предыдущей записи + 1
                        int count       = ifpRecord.Links.Count;
                        int recCapacity = 0;
                        if (lastBlock && capacity > 0)
                        {
                            recCapacity = Math.Max(capacity, ifpRecord.Links.Count) + 1;
                            for (int j = count; j < recCapacity; j++)
                            {
                                ifpRecord.Links.Add(new TermLink());
                            }
                        }
                        else
                        {
                            recCapacity = count;
                        }
                        ifpRecord.TotalLinkCount = count;
                        ifpRecord.BlockLinkCount = count;
                        ifpRecord.Capacity       = recCapacity;
                        ifpRecord.Last           = lastBlock;
                        //у непоследней записи нужно установить ссылку на следующую
                        if (!lastBlock)
                        {
                            ifpRecord.FullOffset = offset + /*размер ifp записи обычного формата*/ LeaderSize + count * TermLink.RecordSize;
                        }
                        int written = Write(stream, ifpRecord, offset, padding);
                        offset += written;
                        size   += written;
                    }
                }
                else
                {
                    //новая запись со спецблоками только в случае создания новой,
                    //при расширении всегда идут записи обыкновенного формата
                    ifpRecord = new IfpRecord();
                    ifpRecord.Links.AddRange(links);
                    //для новой записи здесь нужно обозначить спец блок
                    //при создании новой записи со спец блоком ирбис не выравнивает количество записей под размер блока
                    //если в последствии такую запись расширяют,
                    //то добавляется еще одна запись обычного формата и дальше по обычной схеме
                    ifpRecord.Special        = true;
                    ifpRecord.TotalLinkCount = links.Length;
                    //в записи со спец блоком здесь количество вложенных записей
                    ifpRecord.BlockLinkCount = (int)Math.Ceiling((double)links.Length / (double)PostingsInBlock);
                    //в записи со спецблоками в поле емкость ставится количество вложенных записей кратное 4
                    //емкость выставляется кратно 4, реальное количество блоков в BlockLinkCount
                    ifpRecord.Capacity = 4 * (int)Math.Ceiling((double)links.Length / (double)PostingsInBlock / 4d);

                    size += Write(stream, ifpRecord, offset, padding);
                }
            }
            ////если элемент новый - отметить в нем ссылку на запись ifp
            //if (item.FullOffset == 0)
            //    item.FullOffset = offset;
            return(size);
        }
Example #6
0
        ///// <summary>
        ///// ibatrak установка ссылки на следующую запись
        ///// </summary>
        //private static void SetNext(Stream stream, IfpRecord ifpRecord, long next, TermLink link, int newBlocks)
        //{
        //    if (ifpRecord.FullOffset != next && next > 0)
        //    {
        //        var r = ifpRecord;
        //        //поиск последней записи по цепочке
        //        //метод вызывается только для последней записи, потребности в этом нет
        //        //оставлено для совместимости
        //        while (r.FullOffset > 0)
        //        {
        //            r = ReadLeader(stream, r.FullOffset);
        //        }
        //        if (r.Special)
        //        {
        //            //дальше работаем со вложенной записью
        //            var nestedRecord = r._NestedRecordLeaders[r._NestedRecordLeaders.Length - 1];

        //            //установка ссылки на новую запись в спец блок, если есть место
        //            if (r._NestedRecordLeaders.Length < r._SpecialBlock.Length)
        //            {
        //                var specialBlockEntry = r._SpecialBlock[r._NestedRecordLeaders.Length];
        //                if (specialBlockEntry.Offset == 0)
        //                {
        //                    specialBlockEntry.Offset = next;
        //                    specialBlockEntry.FirstPosting = link;
        //                    //обновим счетчик блоков
        //                    r.BlockLinkCount += newBlocks;

        //                    if (ifpRecord._NestedRecordLeaders == null || !ifpRecord._NestedRecordLeaders.Contains(nestedRecord))
        //                    {
        //                        //если запись не является вложенной, то записать спец блок
        //                        stream.Position = specialBlockEntry.FileOffset;
        //                        SpecialPostingBlockEntry.Write(stream, specialBlockEntry);
        //                    }
        //                }
        //            }
        //            r = nestedRecord;

        //        }
        //        r.FullOffset = next;
        //        //обновить ссылку на следующую запись в последней по цепочке записи, если она отличается от текущей
        //        //и не содержится во вложенных
        //        if (r != ifpRecord && (ifpRecord._NestedRecordLeaders == null || !ifpRecord._NestedRecordLeaders.Contains(r)))
        //            WriteLeader(stream, r, r._FileOffset);
        //    }

        //}

        /// <summary>
        /// Запись данных в простом формате или со спец блоками
        /// </summary>
        private static int Write(Stream stream, IfpRecord ifpRecord, long offset, bool padding)
        {
            int size = 0;

            byte[] buffer = null;
            //обработка обычной записи
            if (!ifpRecord.Special)
            {
                //в записи обыкновенного формата указываем либо указатель на следующую из параметров
                //либо обозначаем последнюю запись
                size   = LeaderSize + ifpRecord.Links.Count * TermLink.RecordSize;
                buffer = new byte[size];
                Write(buffer, ifpRecord, 0);
            }
            else
            {
                //обработка записи со спец блоком
                var links = ifpRecord.Links.ToArray();
                //если нет - делаем запись со спец-блоком
                //здесь записи делятся по 254, то есть будет минимум 2 блока
                var blockCount = ifpRecord._NestedRecordLeaders != null ? ifpRecord._NestedRecordLeaders.Length :
                                 (int)Math.Ceiling((double)links.Length / (double)PostingsInBlock);

                int specialBlockSize = ifpRecord.Capacity * SpecialPostingBlockEntry.RecordSize;

                size = /*размер первой записи*/ LeaderSize /*в первой записи ссылок нет*/ +
                       /*размер спец блока*/ specialBlockSize +
                       (ifpRecord._NestedRecordLeaders == null ? /*размер вложенных записей только если они будут созданы*/
                        /*размер записей по числу блоков*/ LeaderSize * blockCount + links.Length * TermLink.RecordSize : 0);

                buffer = new byte[size];

                ifpRecord.Links.Clear();
                //устанавливается через Special
                //ifpRecord.LowOffset = -1001;
                //ifpRecord.HighOffset = -1001;

                //уже сделали ifpRecord.Links.Clear(); будет запись только заголовка
                //int bufferOffset = Write(buffer, ifpRecord, 0);
                int bufferOffset = WriteLeader(buffer, ifpRecord, 0);


                //запись спец блока
                //пишем записи спец блока по количеству Capacity
                int linksWritten = 0;
                for (int i = 0; i < ifpRecord.Capacity; i++)
                {
                    int count = 0;

                    var entry = ifpRecord._SpecialBlock != null ? ifpRecord._SpecialBlock[i] : new SpecialPostingBlockEntry();
                    //генерация адресов для новых записей спец блока
                    if (ifpRecord._NestedRecordLeaders == null && i < blockCount)
                    {
                        //в ирбисе здесь номер слова из первой ссылки, зачем непонятно
                        entry.FirstPosting = links[linksWritten];
                        //ссылка на место за спец блоком + размер записей обыкновенного формата столько,
                        //сколько указывают предыдущие записи спец блока
                        entry.Offset = offset +
                                       /*размер первой записи*/ LeaderSize +
                                       /*рамер спец блока*/ specialBlockSize +
                                       /*размер записей обыкновенного формата*/ LeaderSize * i + linksWritten * TermLink.RecordSize;

                        //ifpRecord._NestedRecordLeaders уже null
                        //if (ifpRecord._NestedRecordLeaders != null)
                        //    count = Math.Min(links.Length - linksWritten, ifpRecord._NestedRecordLeaders[i].TotalLinkCount);
                        //else
                        //    count = Math.Min(links.Length - linksWritten, PostingsInBlock);

                        count         = Math.Min(links.Length - linksWritten, PostingsInBlock);
                        linksWritten += count;
                    }
                    bufferOffset += SpecialPostingBlockEntry.Write(buffer, entry, bufferOffset);
                }

                linksWritten = 0;
                for (int i = 0; i < blockCount; i++)
                {
                    //если обновляется существующая запись, количество ссылок брать из существующих блоков
                    int count = 0;

                    IfpRecord nestedRecord = null;

                    //здесь пишем записи обычного формата
                    if (ifpRecord._NestedRecordLeaders != null)
                    {
                        //для старой вложенной записи перезаписываем блок по количеству записей в заголовке
                        //вместимость в ссылках и ссылки на следующие блоки не переписываем
                        count        = Math.Min(links.Length - linksWritten, ifpRecord._NestedRecordLeaders[i].Capacity);
                        nestedRecord = ifpRecord._NestedRecordLeaders[i];
                        nestedRecord.TotalLinkCount = count;
                        nestedRecord.BlockLinkCount = count;
                    }
                    else
                    {
                        //для новой записи записываем PostingsInBlock
                        count        = Math.Min(links.Length - linksWritten, PostingsInBlock);
                        nestedRecord = new IfpRecord
                        {
                            TotalLinkCount = count,
                            BlockLinkCount = count,
                            Capacity       = count
                        };

                        if (i < blockCount - 1)
                        {
                            //ссылка на место сразу за этой записью
                            nestedRecord.FullOffset = offset +
                                                      /*размер первой записи*/ LeaderSize +
                                                      /*рамер спец блока*/ specialBlockSize +
                                                      /*размер предыдущих записей + размер этой записи*/ LeaderSize * (i + 1) + (linksWritten + count) * TermLink.RecordSize;
                        }
                        else
                        {
                            //в последней записи обыкновенного формата обозначаем последнюю запись
                            nestedRecord.Last = true;
                        }
                    }

                    var linksBlock = new TermLink[count];
                    Array.Copy(links, linksWritten, linksBlock, 0, linksBlock.Length);
                    nestedRecord.Links.AddRange(linksBlock);
                    linksWritten += count;
                    //старые вложенные записи пишутся на старое место
                    if (ifpRecord._NestedRecordLeaders != null)
                    {
                        Write(stream, nestedRecord, nestedRecord._FileOffset, padding);
                    }
                    else
                    {
                        bufferOffset += Write(buffer, nestedRecord, bufferOffset);
                    }
                }
            }

            if (buffer.Length != size)
            {
                throw new InvalidOperationException("Ошибка записи ifp");
            }

            //выравнивание размера файла
            if (padding && (offset + buffer.Length) > stream.Length)
            {
                long newSize = LeaderSize + (long)Math.Ceiling((double)(offset + buffer.Length - LeaderSize) / (double)BlockSize) * BlockSize;
                stream.SetLength(newSize);
            }

            if (stream.Seek(offset, SeekOrigin.Begin) != offset)
            {
                throw new IOException();
            }

            stream.Write(buffer, 0, buffer.Length);
            stream.Flush();

            return(size);
        }
Example #7
0
        public static IfpRecord Read
        (
            Stream stream,
            long offset
        )
        {
            //new ObjectDumper()
            //    .DumpStream(stream, offset, 100);

            //ibatrak чтение лидера вынесено отдельно
            //stream.Position = offset;

            //IfpRecord result = new IfpRecord
            //    {
            //        LowOffset = stream.ReadInt32Network(),
            //        HighOffset = stream.ReadInt32Network(),
            //        TotalLinkCount = stream.ReadInt32Network(),
            //        BlockLinkCount = stream.ReadInt32Network(),
            //        Capacity = stream.ReadInt32Network(),
            //        _FileOffset = offset
            //    };

            IfpRecord result = ReadLeader(stream, offset);

            //ibatrak чтение вложенных записей в спец блоке
            //Специальный формат записи .ifp
            //В этом случае первой записью является специальный блок,
            //который представляет собой заголовок (обыкновенного формата), в котором смещения имеют специальные значения = -1001, и набор вхождений следующего формата:
            //Число бит	Параметр	Описание
            //32	POSTING	1-я ссылка из записи обыкновенного формата
            //32	LOW	младшее слово смещения на следующую запись (если нет 0)
            //32	HIGH	старшее слово смещения на следующую запись (если нет 0)
            //Число вхождений кратно 4. Записи, на которые ссылается специальный блок связаны между собой как описано выше.
            //Общее количество ссылок для данного термина сохраняется только в специальном блоке.
            if (result.Special)
            {
                //irbis64.dll делает так

                //читает первые 24 байта блока спец ссылок
                //берет первую запись, адрес из нее в файле и читает записей IFP
                //по количеству result.BlockLinkCount

                //записи спец блока идут по количеству Capacity
                //каждая указывает на запись обычного формата
                //также в каждой записи обычного формата FullOffset указывает на следующую запись (кроме последней)
                //это должно соответствовать

                var specBlock       = new SpecialPostingBlockEntry[result.Capacity];
                var specBlockBuffer = new byte[result.Capacity * SpecialPostingBlockEntry.RecordSize];
                var fileOffset      = stream.Position;
                if (stream.Read(specBlockBuffer, 0, specBlockBuffer.Length) != specBlockBuffer.Length)
                {
                    throw new IOException();
                }

                for (int i = 0; i < result.Capacity; i++)
                {
                    var entry = SpecialPostingBlockEntry.Read(specBlockBuffer, i * SpecialPostingBlockEntry.RecordSize);
                    entry.FileOffset = fileOffset + i + SpecialPostingBlockEntry.RecordSize;
                    specBlock[i]     = entry;
                }

                //запомнить для редактирования
                result._SpecialBlock = specBlock;

                var nonEmptyEntries = specBlock.Where(e => e.Offset > 0).Count();
                if (nonEmptyEntries != result.BlockLinkCount)
                {
                    throw new InvalidOperationException("Ошибка чтения ifp offset=" + offset);
                }

                var nestedRecords = new IfpRecord[result.BlockLinkCount];
                for (int i = 0; i < result.BlockLinkCount; i++)
                {
                    if (specBlock[i].Offset <= 0 ||
                        (i > 0 && nestedRecords[i - 1].FullOffset != specBlock[i].Offset))
                    {
                        throw new InvalidOperationException("Ошибка чтения ifp offset=" + offset);
                    }
                    var nestedRecord = Read(stream, specBlock[i].Offset);
                    nestedRecords[i] = nestedRecord;
                }
                var links = nestedRecords.SelectMany(r => r.Links).ToArray();
                if (links.Length != result.TotalLinkCount)
                {
                    throw new InvalidOperationException("Ошибка чтения ifp offset=" + offset);
                }
                result.Links.AddRange(links);
                result._NestedRecordLeaders = nestedRecords.ToArray();
                foreach (var r in nestedRecords)
                {
                    r._links.Clear();
                }
                return(result);
            }
            //ibatrak до сюда

            //ibatrak читаем ссылки за раз, так быстрее
            var linkBuffer = new byte[result.BlockLinkCount * TermLink.RecordSize];

            if (stream.Read(linkBuffer, 0, linkBuffer.Length) != linkBuffer.Length)
            {
                throw new IOException();
            }

            for (int i = 0; i < result.BlockLinkCount; i++)
            {
                TermLink link = TermLink.Read(linkBuffer, i * TermLink.RecordSize);
                result.Links.Add(link);
            }

            //for (int i = 0; i < result.BlockLinkCount; i++)
            //{
            //    TermLink link = TermLink.Read(stream);
            //    result.Links.Add(link);
            //}

            return(result);
        }