示例#1
0
        private NodeRecord _ReadNode
        (
            bool isLeaf,
            Stream stream,
            long offset
        )
        {
            stream.Position = offset;

            NodeRecord result = new NodeRecord(isLeaf)
            {
                _stream = stream,
                Leader  =
                {
                    Number     = stream.ReadInt32Network(),
                    Previous   = stream.ReadInt32Network(),
                    Next       = stream.ReadInt32Network(),
                    TermCount  = stream.ReadInt16Network(),
                    FreeOffset = stream.ReadInt16Network()
                }
            };

            for (int i = 0; i < result.Leader.TermCount; i++)
            {
                NodeItem item = new NodeItem
                {
                    Length     = stream.ReadInt16Network(),
                    KeyOffset  = stream.ReadInt16Network(),
                    LowOffset  = stream.ReadInt32Network(),
                    HighOffset = stream.ReadInt32Network()
                };
                result.Items.Add(item);
            }

            foreach (NodeItem item in result.Items)
            {
                stream.Position = offset + item.KeyOffset;
                byte[] buffer = stream.ReadBytes(item.Length);
                string text   = _encoding.GetString(buffer);
                item.Text = text;
            }

            return(result);
        }
示例#2
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);
        }
示例#3
0
        public TermLink[] SearchStart(string key)
        {
            List <TermLink> result = new List <TermLink>();

            if (string.IsNullOrEmpty(key))
            {
                return(new TermLink[0]);
            }

            key = key.ToUpperInvariant();

            NodeRecord firstNode   = ReadNode(1);
            NodeRecord rootNode    = ReadNode(firstNode.Leader.Number);
            NodeRecord currentNode = rootNode;

            NodeItem goodItem = null;

            while (true)
            {
                bool found  = false;
                bool beyond = false;

                foreach (NodeItem item in currentNode.Items)
                {
                    int compareResult = string.CompareOrdinal(item.Text, key);
                    if (compareResult > 0)
                    {
                        beyond = true;
                        break;
                    }

                    goodItem = item;
                    found    = true;
                }
                if (goodItem == null)
                {
                    break;
                }
                if (found)
                {
                    if (beyond || (currentNode.Leader.Next == -1))
                    {
                        if (goodItem.RefersToLeaf)
                        {
                            goto FOUND;
                        }
                        currentNode = ReadNode(goodItem.LowOffset);
                    }
                    else
                    {
                        currentNode = ReadNext(currentNode);
                    }
                }
                else
                {
                    if (goodItem.RefersToLeaf)
                    {
                        goto FOUND;
                    }
                    currentNode = ReadNode(goodItem.LowOffset);
                }
                //Console.WriteLine(currentNode);
            }

FOUND:
            if (goodItem != null)
            {
                currentNode = ReadLeaf(goodItem.LowOffset);
                while (true)
                {
                    foreach (NodeItem item in currentNode.Items)
                    {
                        int compareResult = string.CompareOrdinal(item.Text, key);
                        if (compareResult >= 0)
                        {
                            bool starts = item.Text.StartsWith(key);
                            if ((compareResult > 0) && !starts)
                            {
                                goto DONE;
                            }
                            if (starts)
                            {
                                IfpRecord ifp = ReadIfpRecord(item.FullOffset);
                                result.AddRange(ifp.Links);
                            }
                        }
                    }
                    if (currentNode.Leader.Next > 0)
                    {
                        currentNode = ReadNext(currentNode);
                    }
                }
            }

DONE:
            return(result
                   .Distinct()
                   .ToArray());
        }
示例#4
0
        public TermLink[] SearchExact(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                return(new TermLink[0]);
            }

            key = key.ToUpperInvariant();

            NodeRecord firstNode   = ReadNode(1);
            NodeRecord rootNode    = ReadNode(firstNode.Leader.Number);
            NodeRecord currentNode = rootNode;

            NodeItem goodItem = null;

            while (true)
            {
                bool found  = false;
                bool beyond = false;

                foreach (NodeItem item in currentNode.Items)
                {
                    int compareResult = string.CompareOrdinal(item.Text, key);
                    if (compareResult > 0)
                    {
                        beyond = true;
                        break;
                    }

                    goodItem = item;
                    found    = true;

                    if ((compareResult == 0) &&
                        currentNode.IsLeaf)
                    {
                        goto FOUND;
                    }
                }
                if (goodItem == null)
                {
                    break;
                }
                if (found)
                {
                    if (beyond || (currentNode.Leader.Next == -1))
                    {
                        currentNode = goodItem.RefersToLeaf
                            ? ReadLeaf(goodItem.LowOffset)
                            : ReadNode(goodItem.LowOffset);
                    }
                    else
                    {
                        currentNode = ReadNext(currentNode);
                    }
                }
                else
                {
                    currentNode = goodItem.RefersToLeaf
                        ? ReadLeaf(goodItem.LowOffset)
                        : ReadNode(goodItem.LowOffset);
                }
                //Console.WriteLine(currentNode);
            }

FOUND:
            if (goodItem != null)
            {
                IfpRecord ifp = ReadIfpRecord(goodItem.FullOffset);
                return(ifp.Links.ToArray());
            }

            return(new TermLink[0]);
        }