Example #1
0
        internal byte *DirectRead(Slice key)
        {
            Lazy <Cursor> lazy;
            var           p    = FindPageFor(key, out lazy);
            var           node = p.Search(key);

            if (node == null)
            {
                return(null);
            }

            var item1 = new Slice(node);

            if (item1.Compare(key) != 0)
            {
                return(null);
            }

            if (node->Flags == (NodeFlags.PageRef))
            {
                var overFlowPage = _tx.GetReadOnlyPage(node->PageNumber);
                return(overFlowPage.Base + Constants.PageHeaderSize);
            }

            return((byte *)node + node->KeySize + Constants.NodeHeaderSize);
        }
Example #2
0
        public IIterator MultiRead(Slice key)
        {
            Lazy <Cursor> lazy;
            NodeHeader *  node;
            var           page = FindPageFor(key, out node, out lazy);

            if (page == null || page.LastMatch != 0)
            {
                return(new EmptyIterator());
            }

            Debug.Assert(node != null);

            var fetchedNodeKey = new Slice(node);

            if (fetchedNodeKey.Compare(key) != 0)
            {
                throw new InvalidDataException("Was unable to retrieve the correct node. Data corruption possible");
            }

            if (node->Flags == NodeFlags.MultiValuePageRef)
            {
                var tree = OpenOrCreateMultiValueTree(_tx, key, node);

                return(tree.Iterate());
            }

            var nestedPage = new Page(NodeHeader.DirectAccess(_tx, node), "multi tree", (ushort)NodeHeader.GetDataSize(_tx, node));

            return(new PageIterator(nestedPage));
        }
Example #3
0
        public IIterator MultiRead(Transaction tx, Slice key)
        {
            using (var cursor = tx.NewCursor(this))
            {
                var page = FindPageFor(tx, key, cursor);

                if (page == null || page.LastMatch != 0)
                {
                    return(new EmptyIterator());
                }

                var item = page.Search(key, _cmp);

                var fetchedNodeKey = new Slice(item);
                if (fetchedNodeKey.Compare(key, _cmp) != 0)
                {
                    throw new InvalidDataException("Was unable to retrieve the correct node. Data corruption possible");
                }

                if (item->Flags == NodeFlags.MultiValuePageRef)
                {
                    var tree = OpenOrCreateMultiValueTree(tx, key, item);

                    return(tree.Iterate(tx));
                }

                return(new SingleEntryIterator(_cmp, item, tx));
            }
        }
        public FoundPage Find(Slice key)
        {
            for (int i = 0; i < _cache.Length; i++)
            {
                var page = _cache[i];
                if (page == null)
                    continue;

                var first = page.FirstKey;
                var last = page.LastKey;
                
                switch (key.Options)
                {
                    case SliceOptions.BeforeAllKeys:
                        if (first.Options == SliceOptions.BeforeAllKeys)
                            return page;
                        break;
                    case SliceOptions.AfterAllKeys:
                        if (last.Options == SliceOptions.AfterAllKeys)
                            return page;
                        break;
                    case SliceOptions.Key:
                        if ((first.Options != SliceOptions.BeforeAllKeys && key.Compare(first) < 0))
                            continue;
                        if (last.Options != SliceOptions.AfterAllKeys && key.Compare(last) > 0)
                            continue;
                        return page;
                    default:
                        throw new ArgumentException(key.Options.ToString());
                }
            }

            return null;
        }
Example #5
0
        internal byte *DirectRead(Transaction tx, Slice key)
        {
            using (var cursor = tx.NewCursor(this))
            {
                var p    = FindPageFor(tx, key, cursor);
                var node = p.Search(key, _cmp);

                if (node == null)
                {
                    return(null);
                }

                var item1 = new Slice(node);

                if (item1.Compare(key, _cmp) != 0)
                {
                    return(null);
                }

                if (node->Flags == (NodeFlags.PageRef))
                {
                    var overFlowPage = tx.GetReadOnlyPage(node->PageNumber);
                    return(overFlowPage.Base + Constants.PageHeaderSize);
                }

                return((byte *)node + node->KeySize + Constants.NodeHeaderSize);
            }
        }
Example #6
0
        public void DebugValidate(Transaction tx, long root)
        {
            if (NumberOfEntries == 0)
            {
                return;
            }

            var prev  = new Slice(GetNode(0));
            var pages = new HashSet <long>();

            for (int i = 1; i < NumberOfEntries; i++)
            {
                var node    = GetNode(i);
                var current = new Slice(node);

                if (prev.Compare(current) >= 0)
                {
                    DebugStuff.RenderAndShow(tx, root, 1);
                    throw new InvalidOperationException("The page " + PageNumber + " is not sorted");
                }

                if (node->Flags == (NodeFlags.PageRef))
                {
                    if (pages.Add(node->PageNumber) == false)
                    {
                        DebugStuff.RenderAndShow(tx, root, 1);
                        throw new InvalidOperationException("The page " + PageNumber + " references same page multiple times");
                    }
                }

                prev = current;
            }
        }
Example #7
0
        public FoundPage Find(Slice key)
        {
            for (int i = 0; i < _cache.Length; i++)
            {
                var page = _cache[i];
                if (page == null)
                {
                    continue;
                }

                var first = page.FirstKey;
                var last  = page.LastKey;

                switch (key.Options)
                {
                case SliceOptions.BeforeAllKeys:
                    if (first.Options == SliceOptions.BeforeAllKeys)
                    {
                        return(page);
                    }
                    break;

                case SliceOptions.AfterAllKeys:
                    if (last.Options == SliceOptions.AfterAllKeys)
                    {
                        return(page);
                    }
                    break;

                case SliceOptions.Key:
                    if ((first.Options != SliceOptions.BeforeAllKeys && key.Compare(first) < 0))
                    {
                        continue;
                    }
                    if (last.Options != SliceOptions.AfterAllKeys && key.Compare(last) > 0)
                    {
                        continue;
                    }
                    return(page);

                default:
                    throw new ArgumentException(key.Options.ToString());
                }
            }

            return(null);
        }
Example #8
0
        private byte *SplitPageInHalf(Page rightPage)
        {
            int  currentIndex = _page.LastSearchPosition;
            bool newPosition  = true;
            int  splitIndex   = _page.NumberOfEntries / 2;

            if (currentIndex < splitIndex)
            {
                newPosition = false;
            }

            if (_page.IsLeaf)
            {
                splitIndex = AdjustSplitPosition(_newKey, _len, _page, currentIndex, splitIndex, ref newPosition);
            }

            NodeHeader *currentNode = _page.GetNode(splitIndex);
            var         currentKey  = new Slice(currentNode);

            // here we the current key is the separator key and can go either way, so
            // use newPosition to decide if it stays on the left node or moves to the right
            Slice seperatorKey;

            if (currentIndex == splitIndex && newPosition)
            {
                seperatorKey = currentKey.Compare(_newKey) < 0 ? currentKey : _newKey;
            }
            else
            {
                seperatorKey = currentKey;
            }

            AddSeparatorToParentPage(rightPage, seperatorKey);

            // move the actual entries from page to right page
            ushort nKeys = _page.NumberOfEntries;

            for (int i = splitIndex; i < nKeys; i++)
            {
                NodeHeader *node = _page.GetNode(i);
                if (_page.IsBranch && rightPage.NumberOfEntries == 0)
                {
                    rightPage.CopyNodeDataToEndOfPage(node, Slice.Empty);
                }
                else
                {
                    rightPage.CopyNodeDataToEndOfPage(node);
                }
            }
            _page.Truncate(_tx, splitIndex);

            // actually insert the new key
            return((currentIndex > splitIndex || newPosition && currentIndex == splitIndex)
                ? InsertNewKey(rightPage)
                : InsertNewKey(_page));
        }
Example #9
0
        public void MultiAdd(Transaction tx, Slice key, Slice value, ushort?version = null)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            if (value.Size > tx.DataPager.MaxNodeSize)
            {
                throw new ArgumentException(
                          "Cannot add a value to child tree that is over " + tx.DataPager.MaxNodeSize + " bytes in size", "value");
            }
            if (value.Size == 0)
            {
                throw new ArgumentException("Cannot add empty value to child tree");
            }

            State.IsModified = true;
            Lazy <Cursor> lazy;
            var           page = FindPageFor(tx, key, out lazy);

            if (page == null || page.LastMatch != 0)
            {
                var ptr = DirectAdd(tx, key, value.Size, version: version);
                value.CopyTo(ptr);
                return;
            }

            page = tx.ModifyPage(page.PageNumber, page);

            var item = page.GetNode(page.LastSearchPosition);

            CheckConcurrency(key, version, item->Version, TreeActionType.Add);
            var existingValue = new Slice(DirectRead(tx, key), (ushort)item->DataSize);

            if (existingValue.Compare(value, _cmp) == 0)
            {
                return;                 //nothing to do, the exact value is already there
            }
            if (item->Flags == NodeFlags.MultiValuePageRef)
            {
                var tree = OpenOrCreateMultiValueTree(tx, key, item);
                tree.DirectAdd(tx, value, 0);
            }
            else             // need to turn to tree
            {
                var tree    = Create(tx, _cmp, TreeFlags.MultiValue);
                var current = NodeHeader.GetData(tx, item);
                tree.DirectAdd(tx, current, 0);
                tree.DirectAdd(tx, value, 0);
                tx.AddMultiValueTree(this, key, tree);

                // we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again
                DirectAdd(tx, key, sizeof(TreeRootHeader), NodeFlags.MultiValuePageRef);
            }
        }
Example #10
0
 public unsafe static bool ValidateCurrentKey(this IIterator self, NodeHeader *node, SliceComparer cmp)
 {
     if (self.RequiredPrefix != null)
     {
         var currentKey = new Slice(node);
         if (currentKey.StartsWith(self.RequiredPrefix, cmp) == false)
         {
             return(false);
         }
     }
     if (self.MaxKey != null)
     {
         var currentKey = new Slice(node);
         if (currentKey.Compare(self.MaxKey, cmp) >= 0)
         {
             return(false);
         }
     }
     return(true);
 }
Example #11
0
        protected unsafe Tuple <Slice, Slice> ReadKey(Transaction tx, Slice key)
        {
            Lazy <Cursor> lazy;
            var           p    = tx.State.Root.FindPageFor(key, out lazy);
            var           node = p.Search(key);

            if (node == null)
            {
                return(null);
            }

            var item1 = new Slice(node);

            if (item1.Compare(key) != 0)
            {
                return(null);
            }
            return(Tuple.Create(item1,
                                new Slice((byte *)node + node->KeySize + Constants.NodeHeaderSize,
                                          (ushort)node->DataSize)));
        }
Example #12
0
        public Stream Read(Transaction tx, Slice key)
        {
            using (var cursor = tx.NewCursor(this))
            {
                var p    = FindPageFor(tx, key, cursor);
                var node = p.Search(key, _cmp);

                if (node == null)
                {
                    return(null);
                }

                var item = new Slice(node);

                if (item.Compare(key, _cmp) != 0)
                {
                    return(null);
                }
                return(NodeHeader.Stream(tx, node));
            }
        }
Example #13
0
            public unsafe int CompareTo(BatchOperation other)
            {
                var r = SliceEqualityComparer.Instance.Compare(Key, other.Key);

                if (r != 0)
                {
                    return(r);
                }
                if (valSlice != null)
                {
                    if (other.valSlice == null)
                    {
                        return(-1);
                    }
                    return(valSlice.Compare(other.valSlice));
                }
                else if (other.valSlice != null)
                {
                    return(1);
                }
                return(0);
            }
Example #14
0
        protected unsafe Tuple <Slice, Slice> ReadKey(Transaction tx, Slice key)
        {
            using (var c = tx.NewCursor(Env.Root))
            {
                var p    = Env.Root.FindPageFor(tx, key, c);
                var node = p.Search(key, Env.SliceComparer);

                if (node == null)
                {
                    return(null);
                }

                var item1 = new Slice(node);

                if (item1.Compare(key, Env.SliceComparer) != 0)
                {
                    return(null);
                }
                return(Tuple.Create(item1,
                                    new Slice((byte *)node + node->KeySize + Constants.NodeHeaderSize,
                                              (ushort)node->DataSize)));
            }
        }
Example #15
0
		public IIterator MultiRead(Transaction tx, Slice key)
		{
			Lazy<Cursor> lazy;
			var page = FindPageFor(tx, key, out lazy);

			if (page == null || page.LastMatch != 0)
			{
				return new EmptyIterator();
			}

			var item = page.Search(key, _cmp);

			var fetchedNodeKey = new Slice(item);
			if (fetchedNodeKey.Compare(key, _cmp) != 0)
			{
				throw new InvalidDataException("Was unable to retrieve the correct node. Data corruption possible");
			}

			if (item->Flags == NodeFlags.MultiValuePageRef)
			{
				var tree = OpenOrCreateMultiValueTree(tx, key, item);

				return tree.Iterate(tx);
			}

			return new SingleEntryIterator(_cmp, item, tx);
		}
Example #16
0
		public unsafe static bool ValidateCurrentKey(this IIterator self, NodeHeader* node)
		{
			if (self.RequiredPrefix != null)
			{
				var currentKey = new Slice(node);
				if (currentKey.StartsWith(self.RequiredPrefix) == false)
					return false;
			}
			if (self.MaxKey != null)
			{
				var currentKey = new Slice(node);
				if (currentKey.Compare(self.MaxKey) >= 0)
					return false;
			}
			return true;
		}
Example #17
0
        public void DebugValidate(Transaction tx, long root)
        {
            if (NumberOfEntries == 0)
                return;

            var prev = new Slice(GetNode(0));
            var pages = new HashSet<long>();
            for (int i = 1; i < NumberOfEntries; i++)
            {
                var node = GetNode(i);
                var current = new Slice(node);

                if (prev.Compare(current) >= 0)
                {
                    DebugStuff.RenderAndShow(tx, root, 1);
                    throw new InvalidOperationException("The page " + PageNumber + " is not sorted");
                }

                if (node->Flags==(NodeFlags.PageRef))
                {
                    if (pages.Add(node->PageNumber) == false)
                    {
                        DebugStuff.RenderAndShow(tx, root, 1);
                        throw new InvalidOperationException("The page " + PageNumber + " references same page multiple times");
                    }
                }

                prev = current;
            }
        }
Example #18
0
		public NodeHeader* Search(Slice key)
		{
			if (NumberOfEntries == 0)
			{
				LastSearchPosition = 0;
				LastMatch = 1;
				return null;
			}

			if (key.Options == SliceOptions.BeforeAllKeys)
			{
				LastSearchPosition = 0;
				LastMatch = 1;
				return GetNode(0);
			}

			if (key.Options == SliceOptions.AfterAllKeys)
			{
				LastMatch = -1;
				LastSearchPosition = NumberOfEntries - 1;
				return GetNode(LastSearchPosition);
			}

			var pageKey = new Slice(SliceOptions.Key);
			if (NumberOfEntries == 1)
			{
				pageKey.Set(GetNode(0));
				LastMatch = key.Compare(pageKey);
				LastSearchPosition = LastMatch > 0 ? 1 : 0;
				return LastSearchPosition == 0 ? GetNode(0) : null;
			}

			int low = IsLeaf ? 0 : 1;
			int high = NumberOfEntries - 1;
			int position = 0;

			while (low <= high)
			{
				position = (low + high) >> 1;

				var node = GetNode(position);
				pageKey.Set(node);

				LastMatch = key.Compare(pageKey);
				if (LastMatch == 0)
					break;

				if (LastMatch > 0)
					low = position + 1;
				else
					high = position - 1;
			}

			if (LastMatch > 0) // found entry less than key
			{
				position++; // move to the smallest entry larger than the key
			}

			Debug.Assert(position < ushort.MaxValue);
			LastSearchPosition = position;

			if (position >= NumberOfEntries)
				return null;
			return GetNode(position);
		}
Example #19
0
		public IIterator MultiRead(Slice key)
		{
			Lazy<Cursor> lazy;
			var page = FindPageFor(key, out lazy);

			if (page == null || page.LastMatch != 0)
			{
				return new EmptyIterator();
			}

			var item = page.Search(key);

			var fetchedNodeKey = new Slice(item);
			if (fetchedNodeKey.Compare(key) != 0)
			{
				throw new InvalidDataException("Was unable to retrieve the correct node. Data corruption possible");
			}

			if (item->Flags == NodeFlags.MultiValuePageRef)
			{
				var tree = OpenOrCreateMultiValueTree(_tx, key, item);

				return tree.Iterate();
			}

			var nestedPage = new Page(NodeHeader.DirectAccess(_tx, item), "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item));
				
			return new PageIterator(nestedPage);
		}
Example #20
0
		public void MultiAdd(Slice key, Slice value, ushort? version = null)
		{
			if (value == null) throw new ArgumentNullException("value");
			int maxNodeSize = _tx.DataPager.MaxNodeSize;
			if (value.Size > maxNodeSize)
				throw new ArgumentException(
					"Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", "value");
			if (value.Size == 0)
				throw new ArgumentException("Cannot add empty value to child tree");

			State.IsModified = true;

			Lazy<Cursor> lazy;
			var page = FindPageFor(key, out lazy);
			if ((page == null || page.LastMatch != 0))
			{
				MultiAddOnNewValue(_tx, key, value, version, maxNodeSize);
				return;
			}

			page = _tx.ModifyPage(page.PageNumber, page);

			var item = page.GetNode(page.LastSearchPosition);

			// already was turned into a multi tree, not much to do here
			if (item->Flags == NodeFlags.MultiValuePageRef)
			{
				var existingTree = OpenOrCreateMultiValueTree(_tx, key, item);
				existingTree.DirectAdd(value, 0, version: version);
				return;
			}

			byte* nestedPagePtr;
			if (item->Flags == NodeFlags.PageRef)
			{
				var overFlowPage = _tx.ModifyPage(item->PageNumber, null);
				nestedPagePtr = overFlowPage.Base + Constants.PageHeaderSize;
			}
			else
			{
				nestedPagePtr = NodeHeader.DirectAccess(_tx, item);
			}

			var nestedPage = new Page(nestedPagePtr, "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item));

			var existingItem = nestedPage.Search(value);
			if (nestedPage.LastMatch != 0)
				existingItem = null;// not an actual match, just greater than

			ushort previousNodeRevision = existingItem != null ?  existingItem->Version : (ushort)0;
			CheckConcurrency(key, value, version, previousNodeRevision, TreeActionType.Add);
			
			if (existingItem != null)
			{
				// maybe same value added twice?
				var tmpKey = new Slice(item);
				if (tmpKey.Compare(value) == 0)
					return; // already there, turning into a no-op
				nestedPage.RemoveNode(nestedPage.LastSearchPosition);
			}

			if (nestedPage.HasSpaceFor(_tx, value, 0))
			{
				// we are now working on top of the modified root page, we can just modify the memory directly
				nestedPage.AddDataNode(nestedPage.LastSearchPosition, value, 0, previousNodeRevision);
				return;
			}

			int pageSize = nestedPage.CalcSizeUsed() + Constants.PageHeaderSize;
			var newRequiredSize = pageSize + nestedPage.GetRequiredSpace(value, 0);
			if (newRequiredSize <= maxNodeSize)
			{
				// we can just expand the current value... no need to create a nested tree yet
				var actualPageSize = (ushort)Math.Min(Utils.NearestPowerOfTwo(newRequiredSize), maxNodeSize);
				ExpandMultiTreeNestedPageSize(_tx, key, value, nestedPagePtr, actualPageSize, item->DataSize);

				return;
			}
			// we now have to convert this into a tree instance, instead of just a nested page
			var tree = Create(_tx, TreeFlags.MultiValue);
			for (int i = 0; i < nestedPage.NumberOfEntries; i++)
			{
				var existingValue = nestedPage.GetNodeKey(i);
				tree.DirectAdd(existingValue, 0);
			}
			tree.DirectAdd(value, 0, version: version);
			_tx.AddMultiValueTree(this, key, tree);
			// we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again
			DirectAdd(key, sizeof (TreeRootHeader), NodeFlags.MultiValuePageRef);
		}
        private bool TryFindSection(Transaction tx, int minSeq, Slice currentKey, Slice start, Slice end, out NodeHeader *current)
        {
            int minFreeSpace = _minimumFreePagesInSectionSet ?
                               _minimumFreePagesInSection :
                               Math.Min(256, _lastTransactionPageUsage);

            current = null;
            int currentMax = 0;

            using (var it = _env.FreeSpaceRoot.Iterate(tx))
            {
                it.RequiredPrefix = _sectionsPrefix;
                it.MaxKey         = end;
                if (it.Seek(start) == false)
                {
                    return(false);
                }

                int triesAfterFindingSuitable = 256;
                do
                {
                    if (_recordsToSkip.Exists(x => x.Compare(it.CurrentKey, _env.SliceComparer) == 0))
                    {
                        continue; // if it is marked in memory for either update / delete, we don't want it
                    }
                    if (current != null)
                    {
                        triesAfterFindingSuitable--;
                    }

                    if (currentKey != null)
                    {
                        if (_currentKey.Compare(it.CurrentKey, _env.SliceComparer) == 0)
                        {
                            continue; // skip current one
                        }
                    }

                    using (var stream = it.CreateStreamForCurrent())
                        using (var reader = new BinaryReader(stream))
                        {
                            stream.Position = sizeof(long);
                            var largestSeq = reader.ReadInt32();
                            if (largestSeq < minSeq)
                            {
                                continue;
                            }

                            var pageCount = reader.ReadInt32();

                            if (pageCount < minFreeSpace || pageCount < currentMax)
                            {
                                continue;
                            }


                            current    = it.Current;
                            currentMax = pageCount;
                        }
                } while (it.MoveNext() && triesAfterFindingSuitable >= 0);
            }
            return(current != null);
        }
Example #22
0
        public NodeHeader* Search(Slice key, SliceComparer cmp)
        {
            if (NumberOfEntries == 0)
            {
                LastSearchPosition = 0;
                LastMatch = 1;
                return null;
            }

            if (key.Options == SliceOptions.BeforeAllKeys)
            {
                LastSearchPosition = 0;
                LastMatch = 1;
                return GetNode(0);
            }

            if (key.Options == SliceOptions.AfterAllKeys)
            {
                LastMatch = -1;
                LastSearchPosition = NumberOfEntries - 1;
                return GetNode(LastSearchPosition);
            }

            int low = IsLeaf ? 0 : 1;
            int high = NumberOfEntries - 1;
            int position = 0;

            var pageKey = new Slice(SliceOptions.Key);
            bool matched = false;
            NodeHeader* node = null;
            while (low <= high)
            {
                position = (low + high) >> 1;

                node = GetNode(position);
                pageKey.Set(node);

                LastMatch = key.Compare(pageKey, cmp);
                matched = true;
                if (LastMatch == 0)
                    break;

                if (LastMatch > 0)
                    low = position + 1;
                else
                    high = position - 1;
            }

            if (matched == false)
            {
                node = GetNode(position);
                LastMatch = key.Compare(pageKey, cmp);
            }

            if (LastMatch > 0) // found entry less than key
                position++; // move to the smallest entry larger than the key

            Debug.Assert(position < ushort.MaxValue);
            LastSearchPosition = position;

            if (position >= NumberOfEntries)
                return null;
            return node;
        }
Example #23
0
		protected unsafe Tuple<Slice, Slice> ReadKey(Transaction tx, Slice key)
		{
			Lazy<Cursor> lazy;
			var p = tx.State.Root.FindPageFor(tx, key, out lazy);
			var node = p.Search(key, Env.SliceComparer);

			if (node == null)
				return null;

			var item1 = new Slice(node);

			if (item1.Compare(key, Env.SliceComparer) != 0)
				return null;
			return Tuple.Create(item1,
				new Slice((byte*) node + node->KeySize + Constants.NodeHeaderSize,
					(ushort) node->DataSize));
		}
Example #24
0
		internal byte* DirectRead(Transaction tx, Slice key)
		{
			Lazy<Cursor> lazy;
			var p = FindPageFor(tx, key, out lazy);
			var node = p.Search(key, _cmp);

			if (node == null)
				return null;

			var item1 = new Slice(node);

			if (item1.Compare(key, _cmp) != 0)
				return null;

			if (node->Flags == (NodeFlags.PageRef))
			{
				var overFlowPage = tx.GetReadOnlyPage(node->PageNumber);
				return overFlowPage.Base + Constants.PageHeaderSize;
			}

			return (byte*) node + node->KeySize + Constants.NodeHeaderSize;
		}
Example #25
0
        private byte* SplitPageInHalf(Page rightPage)
        {
            int currentIndex = _page.LastSearchPosition;
            bool newPosition = true;
            int splitIndex = _page.NumberOfEntries/2;
            if (currentIndex < splitIndex)
                newPosition = false;

            if (_page.IsLeaf)
            {
                splitIndex = AdjustSplitPosition(_newKey, _len, _page, currentIndex, splitIndex, ref newPosition);
            }

            NodeHeader* currentNode = _page.GetNode(splitIndex);
            var currentKey = new Slice(currentNode);

            // here we the current key is the separator key and can go either way, so 
            // use newPosition to decide if it stays on the left node or moves to the right
            Slice seperatorKey;
            if (currentIndex == splitIndex && newPosition)
            {
                seperatorKey = currentKey.Compare(_newKey, NativeMethods.memcmp) < 0 ? currentKey : _newKey;
            }
            else
            {
                seperatorKey = currentKey;
            }

            AddSeparatorToParentPage(rightPage, seperatorKey);

            // move the actual entries from page to right page
            ushort nKeys = _page.NumberOfEntries;
            for (int i = splitIndex; i < nKeys; i++)
            {
                NodeHeader* node = _page.GetNode(i);
                if (_page.IsBranch && rightPage.NumberOfEntries == 0)
                {
                    rightPage.CopyNodeDataToEndOfPage(node, Slice.Empty);
                }
                else
                {
                    rightPage.CopyNodeDataToEndOfPage(node);
                }
            }
            _page.Truncate(_tx, splitIndex);

            // actually insert the new key
            return (currentIndex > splitIndex || newPosition && currentIndex == splitIndex)
                ? InsertNewKey(rightPage)
                : InsertNewKey(_page);
        }
Example #26
0
 public override int Compare(Slice a, Slice b)
 {
     return a.Compare(b);
 }
Example #27
0
        public NodeHeader *Search(Slice key)
        {
            if (NumberOfEntries == 0)
            {
                LastSearchPosition = 0;
                LastMatch          = 1;
                return(null);
            }

            if (key.Options == SliceOptions.BeforeAllKeys)
            {
                LastSearchPosition = 0;
                LastMatch          = 1;
                return(GetNode(0));
            }

            if (key.Options == SliceOptions.AfterAllKeys)
            {
                LastMatch          = -1;
                LastSearchPosition = NumberOfEntries - 1;
                return(GetNode(LastSearchPosition));
            }

            var pageKey = new Slice(SliceOptions.Key);

            if (NumberOfEntries == 1)
            {
                pageKey.Set(GetNode(0));
                LastMatch          = key.Compare(pageKey);
                LastSearchPosition = LastMatch > 0 ? 1 : 0;
                return(LastSearchPosition == 0 ? GetNode(0) : null);
            }

            int low      = IsLeaf ? 0 : 1;
            int high     = NumberOfEntries - 1;
            int position = 0;

            while (low <= high)
            {
                position = (low + high) >> 1;

                var node = GetNode(position);
                pageKey.Set(node);

                LastMatch = key.Compare(pageKey);
                if (LastMatch == 0)
                {
                    break;
                }

                if (LastMatch > 0)
                {
                    low = position + 1;
                }
                else
                {
                    high = position - 1;
                }
            }

            if (LastMatch > 0)             // found entry less than key
            {
                position++;                // move to the smallest entry larger than the key
            }

            Debug.Assert(position < ushort.MaxValue);
            LastSearchPosition = position;

            if (position >= NumberOfEntries)
            {
                return(null);
            }
            return(GetNode(position));
        }
Example #28
0
		public void MultiAdd(Transaction tx, Slice key, Slice value, ushort? version = null)
		{
			if (value == null) throw new ArgumentNullException("value");
			if (value.Size > tx.DataPager.MaxNodeSize)
				throw new ArgumentException(
					"Cannot add a value to child tree that is over " + tx.DataPager.MaxNodeSize + " bytes in size", "value");
			if (value.Size == 0)
				throw new ArgumentException("Cannot add empty value to child tree");

			State.IsModified = true;
			Lazy<Cursor> lazy;
			var page = FindPageFor(tx, key, out lazy);

			if (page == null || page.LastMatch != 0)
			{
				var ptr = DirectAdd(tx, key, value.Size, version: version);
				value.CopyTo(ptr);
				return;
			}

			page = tx.ModifyPage(page.PageNumber, page);

			var item = page.GetNode(page.LastSearchPosition);

			CheckConcurrency(key, version, item->Version, TreeActionType.Add);
			var existingValue = new Slice(DirectRead(tx, key), (ushort) item->DataSize);
			if (existingValue.Compare(value, _cmp) == 0)
				return; //nothing to do, the exact value is already there				

			if (item->Flags == NodeFlags.MultiValuePageRef)
			{
				var tree = OpenOrCreateMultiValueTree(tx, key, item);
				tree.DirectAdd(tx, value, 0);
			}
			else // need to turn to tree
			{
				var tree = Create(tx, _cmp, TreeFlags.MultiValue);
				var current = NodeHeader.GetData(tx, item);
				tree.DirectAdd(tx, current, 0);
				tree.DirectAdd(tx, value, 0);
				tx.AddMultiValueTree(this, key, tree);

				// we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again
				DirectAdd(tx, key, sizeof (TreeRootHeader), NodeFlags.MultiValuePageRef);
			}
		}
Example #29
0
        public void MultiAdd(Slice key, Slice value, ushort?version = null)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            int maxNodeSize = _tx.DataPager.MaxNodeSize;

            if (value.Size > maxNodeSize)
            {
                throw new ArgumentException(
                          "Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", "value");
            }
            if (value.Size == 0)
            {
                throw new ArgumentException("Cannot add empty value to child tree");
            }

            State.IsModified = true;

            Lazy <Cursor> lazy;
            NodeHeader *  node;
            var           page = FindPageFor(key, out node, out lazy);

            if ((page == null || page.LastMatch != 0))
            {
                MultiAddOnNewValue(_tx, key, value, version, maxNodeSize);
                return;
            }

            page = _tx.ModifyPage(page.PageNumber, page);
            var item = page.GetNode(page.LastSearchPosition);

            // already was turned into a multi tree, not much to do here
            if (item->Flags == NodeFlags.MultiValuePageRef)
            {
                var existingTree = OpenOrCreateMultiValueTree(_tx, key, item);
                existingTree.DirectAdd(value, 0, version: version);
                return;
            }

            byte *nestedPagePtr;

            if (item->Flags == NodeFlags.PageRef)
            {
                var overFlowPage = _tx.ModifyPage(item->PageNumber, null);
                nestedPagePtr = overFlowPage.Base + Constants.PageHeaderSize;
            }
            else
            {
                nestedPagePtr = NodeHeader.DirectAccess(_tx, item);
            }

            var nestedPage = new Page(nestedPagePtr, "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item));

            var existingItem = nestedPage.Search(value);

            if (nestedPage.LastMatch != 0)
            {
                existingItem = null;                // not an actual match, just greater than
            }
            ushort previousNodeRevision = existingItem != null ?  existingItem->Version : (ushort)0;

            CheckConcurrency(key, value, version, previousNodeRevision, TreeActionType.Add);

            if (existingItem != null)
            {
                // maybe same value added twice?
                var tmpKey = new Slice(item);
                if (tmpKey.Compare(value) == 0)
                {
                    return;                     // already there, turning into a no-op
                }
                nestedPage.RemoveNode(nestedPage.LastSearchPosition);
            }

            if (nestedPage.HasSpaceFor(_tx, value, 0))
            {
                // we are now working on top of the modified root page, we can just modify the memory directly
                nestedPage.AddDataNode(nestedPage.LastSearchPosition, value, 0, previousNodeRevision);
                return;
            }

            int pageSize        = nestedPage.CalcSizeUsed() + Constants.PageHeaderSize;
            var newRequiredSize = pageSize + nestedPage.GetRequiredSpace(value, 0);

            if (newRequiredSize <= maxNodeSize)
            {
                // we can just expand the current value... no need to create a nested tree yet
                var actualPageSize = (ushort)Math.Min(Utils.NearestPowerOfTwo(newRequiredSize), maxNodeSize);
                ExpandMultiTreeNestedPageSize(_tx, key, value, nestedPagePtr, actualPageSize, item->DataSize);

                return;
            }
            // we now have to convert this into a tree instance, instead of just a nested page
            var tree = Create(_tx, TreeFlags.MultiValue);

            for (int i = 0; i < nestedPage.NumberOfEntries; i++)
            {
                var existingValue = nestedPage.GetNodeKey(i);
                tree.DirectAdd(existingValue, 0);
            }
            tree.DirectAdd(value, 0, version: version);
            _tx.AddMultiValueTree(this, key, tree);
            // we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again
            DirectAdd(key, sizeof(TreeRootHeader), NodeFlags.MultiValuePageRef);
        }