private Bucket SetBucket(byte[] key, int offset, Bucket b)
        {
            bool found = false;
            int  pos   = FindPointerOrLower(b, new bytearr(key), out found);

            if (found)
            {
                KeyPointer p = b.Pointers[pos];
                int        v = p.RecordNum;

                // duplicate found
                if (v != offset)
                {
                    p.RecordNum = offset;
                    DirtyBucket(b);
                }
            }
            else
            {
                if (b.Pointers.Count < _bucketItems)
                {
                    KeyPointer k = new KeyPointer(new bytearr(key), offset);
                    pos++;
                    if (pos < b.Pointers.Count)
                    {
                        b.Pointers.Insert(pos, k);
                    }
                    else
                    {
                        b.Pointers.Add(k);
                    }
                    DirtyBucket(b);
                }
                else
                {
                    int p = b.NextPageNumber;
                    if (p != -1)
                    {
                        b = LoadBucket(p);
                        SetBucket(key, offset, b);
                    }
                    else
                    {
                        Bucket newb = new Bucket(_indexfile.GetNewPageNumber());
                        b.NextPageNumber = newb.DiskPageNumber;
                        DirtyBucket(b);
                        SetBucket(key, offset, newb);
                    }
                }
            }
            return(b);
        }
        public Bucket LoadBucketFromPage(int page)
        {
            SeekPage(page);
            byte[] b = new byte[_PageLength];
            _file.Read(b, 0, _PageLength);

            if (b[0] == _BlockHeader[0] && b[1] == _BlockHeader[1] && b[2] == _BlockHeader[2] && b[3] == _BlockHeader[3])
            {
                // create node here
                int bucketnum           = Helper.ToInt32(b, 7);
                List <KeyPointer> list  = new List <KeyPointer>();
                List <int>        dups  = new List <int>();
                short             count = Helper.ToInt16(b, 5);
                if (count > _PageNodeCount)
                {
                    throw new Exception("Count > node size");
                }
                int nextpage = Helper.ToInt32(b, 11);
                int index    = _BlockHeader.Length;
                if ((b[4] & 4) == 4)
                {
                    int dupc = Helper.ToInt32(b, index);
                    index += 4;
                    for (int i = 0; i < dupc; i++)
                    {
                        int l = Helper.ToInt32(b, index);
                        dups.Add(l);
                        index += 4;
                    }
                }
                else
                {
                    for (int i = 0; i < count; i++)
                    {
                        int    idx = index + _rowSize * i;
                        byte   ks  = b[idx];
                        byte[] key = new byte[ks];
                        Buffer.BlockCopy(b, idx + 1, key, 0, ks);
                        int offset  = Helper.ToInt32(b, idx + 1 + _maxKeySize);
                        int duppage = Helper.ToInt32(b, idx + 1 + _maxKeySize + 4);

                        KeyPointer kp = new KeyPointer(new bytearr(key), offset, duppage);
                        list.Add(kp);
                    }
                }
                Bucket n = new Bucket(b[4], bucketnum, list, dups, page, nextpage);
                return(n);
            }
            throw new Exception("Page read error");
        }
        private bool SearchBucket(Bucket b, byte[] key, ref int offset)
        {
            bool found = false;
            int  pos   = FindPointerOrLower(b, new bytearr(key), out found);

            if (found)
            {
                KeyPointer k = b.Pointers[pos];
                offset = k.RecordNum;
                return(true);
            }
            else
            {
                if (b.NextPageNumber != -1)
                {
                    b = LoadBucket(b.NextPageNumber);
                    return(SearchBucket(b, key, ref offset));
                }
            }
            return(false);
        }
        private int FindPointerOrLower(Bucket b, bytearr key, out bool found)
        {
            found = false;
            if (b.Pointers.Count == 0)
            {
                return(0);
            }
            // binary search
            int lastlower = -1;
            int first     = 0;
            int last      = b.Pointers.Count - 1;
            int mid       = 0;

            while (first <= last)
            {
                mid = (first + last) >> 1;
                KeyPointer k       = b.Pointers[mid];
                int        compare = Helper.Compare(k.Key, key);
                if (compare < 0)
                {
                    lastlower = mid;
                    first     = mid + 1;
                }
                if (compare == 0)
                {
                    found = true;
                    return(mid);
                }
                if (compare > 0)
                {
                    last = mid - 1;
                }
            }

            return(lastlower);
        }
		public Bucket LoadBucketFromPage(int page)
		{
			SeekPage(page);
			byte[] b = new byte[_PageLength];
			_file.Read(b, 0, _PageLength);

			if (b[0] == _BlockHeader[0] && b[1] == _BlockHeader[1] && b[2] == _BlockHeader[2] && b[3] == _BlockHeader[3])
			{
				// create node here
				int bucketnum = Helper.ToInt32(b, 7);
				List<KeyPointer> list = new List<KeyPointer>();
				List<int> dups = new List<int>();
				short count = Helper.ToInt16(b, 5);
				if (count > _PageNodeCount)
					throw new Exception("Count > node size");
				int nextpage = Helper.ToInt32(b, 11);
				int index = _BlockHeader.Length;
				if ((b[4] & 4) == 4)
				{
					int dupc = Helper.ToInt32(b, index);
					index += 4;
					for (int i = 0; i < dupc; i++)
					{
						int l = Helper.ToInt32(b, index);
						dups.Add(l);
						index += 4;
					}
				}
				else
				{
					for (int i = 0; i < count; i++)
					{
						int idx = index + _rowSize*i;
						byte ks = b[idx];
						byte[] key = new byte[ks];
						Buffer.BlockCopy(b, idx + 1, key, 0, ks);
						int offset = Helper.ToInt32(b, idx + 1 + _maxKeySize);
						int duppage = Helper.ToInt32(b, idx + 1 + _maxKeySize + 4);

						KeyPointer kp = new KeyPointer(new bytearr(key), offset, duppage);
						list.Add(kp);
					}
				}
				Bucket n = new Bucket(b[4], bucketnum, list, dups, page, nextpage);
				return n;
			}
			throw new Exception("Page read error");
		}
        public void SaveBucket(Bucket bucket)
        {
            int pnum = bucket.DiskPageNumber;

            if (pnum > _LastPageNumber)
            {
                throw new Exception("should not be here: page out of bounds");
            }

            SeekPage(pnum);
            byte[] page = new byte[_PageLength];
            Buffer.BlockCopy(_BlockHeader, 0, page, 0, _BlockHeader.Length);
            // bucket type
            if (bucket.isBucket)
            {
                page[4] = 8;
            }
            if (bucket.isOverflow)
            {
                page[4] += 16;
            }

            // bucket count
            byte[] b = Helper.GetBytes(bucket.Count, false);
            Buffer.BlockCopy(b, 0, page, 5, b.Length);
            // bucket number
            b = Helper.GetBytes(bucket.BucketNumber, false);
            Buffer.BlockCopy(b, 0, page, 7, b.Length);
            // next page number
            b = Helper.GetBytes(bucket.NextPageNumber, false);
            Buffer.BlockCopy(b, 0, page, 11, b.Length);

            int index = _BlockHeader.Length;

            if (bucket.isOverflow == true)
            {
                b = Helper.GetBytes(bucket.Duplicates.Count, false);
                Buffer.BlockCopy(b, 0, page, index, b.Length);
                index += b.Length;
                foreach (long p in bucket.Duplicates)
                {
                    b = Helper.GetBytes(p, false);
                    Buffer.BlockCopy(b, 0, page, index, b.Length);
                    index += b.Length;
                }
            }
            else
            {
                // bucket children
                for (int i = 0; i < bucket.Count; i++)
                {
                    KeyPointer kp   = bucket.Pointers[i];
                    int        idx  = index + _rowSize * i;
                    byte       size = (byte)kp.Key.val.Length;
                    if (size > _maxKeySize)
                    {
                        size = _maxKeySize;
                    }
                    // key size = 1 byte
                    page[idx] = size;
                    // key bytes
                    Buffer.BlockCopy(bucket.Pointers[i].Key.val, 0, page, idx + 1, page[idx]);
                    // offset = 4 bytes
                    b = Helper.GetBytes(kp.RecordNum, false);
                    Buffer.BlockCopy(b, 0, page, idx + 1 + _maxKeySize, b.Length);
                    // duplicatepage = 4 bytes
                    b = Helper.GetBytes(kp.DuplicatesPage, false);
                    Buffer.BlockCopy(b, 0, page, idx + 1 + _maxKeySize + 4, b.Length);
                }
            }
            bucket.isOverflow = false;
            _file.Write(page, 0, page.Length);
            _file.Flush();
        }
		private Bucket SetBucket(byte[] key, int offset, Bucket b)
		{
			bool found = false;
			int pos = FindPointerOrLower(b, new bytearr(key), out found);

			if (found)
			{
				KeyPointer p = b.Pointers[pos];
				int v = p.RecordNum;

				// duplicate found
				if (v != offset)
				{
					p.RecordNum = offset;
					DirtyBucket(b);
				}
			}
			else
			{
				if (b.Pointers.Count < _bucketItems)
				{
					KeyPointer k = new KeyPointer(new bytearr(key), offset);
					pos++;
					if (pos < b.Pointers.Count)
						b.Pointers.Insert(pos, k);
					else
						b.Pointers.Add(k);
					DirtyBucket(b);
				}
				else
				{
					int p = b.NextPageNumber;
					if (p != -1)
					{
						b = LoadBucket(p);
						SetBucket(key, offset, b);
					}
					else
					{
						Bucket newb = new Bucket(_indexfile.GetNewPageNumber());
						b.NextPageNumber = newb.DiskPageNumber;
						DirtyBucket(b);
						SetBucket(key, offset, newb);
					}
				}
			}
			return b;
		}