コード例 #1
0
ファイル: BtreePage.cs プロジェクト: kjk/tenderbase
        internal static int HandlePageUnderflow(StorageImpl db, Page pg, int r, int type, BtreeKey rem, int height)
        {
            int nItems = GetItemsCount(pg);
            if (type == ClassDescriptor.tpString)
            {
                Page a = db.PutPage(GetKeyStrOid(pg, r));
                int an = GetItemsCount(a);
                if (r < nItems)
                {
                    // exists greater page
                    Page b = db.GetPage(GetKeyStrOid(pg, r + 1));
                    int bn = GetItemsCount(b);
                    int merged_size = (an + bn) * strKeySize + GetSize(a) + GetSize(b);
                    if (height != 1)
                    {
                        merged_size += GetKeyStrSize(pg, r) * 2 + strKeySize * 2;
                    }

                    if (merged_size > keySpace)
                    {
                        // reallocation of nodes between pages a and b
                        int i, j, k;
                        db.pool.Unfix(b);
                        b = db.PutPage(GetKeyStrOid(pg, r + 1));
                        int size_a = GetSize(a);
                        int size_b = GetSize(b);
                        int addSize, subSize;
                        if (height != 1)
                        {
                            addSize = GetKeyStrSize(pg, r);
                            subSize = GetKeyStrSize(b, 0);
                        }
                        else
                        {
                            addSize = subSize = GetKeyStrSize(b, 0);
                        }
                        i = 0;
                        int prevDelta = (an * strKeySize + size_a) - (bn * strKeySize + size_b);
                        while (true)
                        {
                            i += 1;
                            int delta = ((an + i) * strKeySize + size_a + addSize * 2) - ((bn - i) * strKeySize + size_b - subSize * 2);
                            if (delta >= 0)
                            {
                                if (delta >= -prevDelta)
                                {
                                    i -= 1;
                                }
                                break;
                            }
                            size_a += addSize * 2;
                            size_b -= subSize * 2;
                            prevDelta = delta;
                            if (height != 1)
                            {
                                addSize = subSize;
                                subSize = GetKeyStrSize(b, i);
                            }
                            else
                            {
                                addSize = subSize = GetKeyStrSize(b, i);
                            }
                        }
                        int result = Btree.op_done;
                        if (i > 0)
                        {
                            k = i;
                            if (height != 1)
                            {
                                int len = GetKeyStrSize(pg, r);
                                SetSize(a, GetSize(a) + len * 2);
                                SetKeyStrOffs(a, an, keySpace - GetSize(a));
                                SetKeyStrSize(a, an, len);
                                MemCopy(a, GetKeyStrOffs(a, an), pg, GetKeyStrOffs(pg, r), len * 2, 1);
                                k -= 1;
                                an += 1;
                                SetKeyStrOid(a, an + k, GetKeyStrOid(b, k));
                                SetSize(b, GetSize(b) - GetKeyStrSize(b, k) * 2);
                            }
                            for (j = 0; j < k; j++)
                            {
                                int len = GetKeyStrSize(b, j);
                                SetSize(a, GetSize(a) + len * 2);
                                SetSize(b, GetSize(b) - len * 2);
                                SetKeyStrOffs(a, an, keySpace - GetSize(a));
                                SetKeyStrSize(a, an, len);
                                SetKeyStrOid(a, an, GetKeyStrOid(b, j));
                                MemCopy(a, GetKeyStrOffs(a, an), b, GetKeyStrOffs(b, j), len * 2, 1);
                                an += 1;
                            }
                            rem.GetStr(b, i - 1);
                            result = ReplaceStrKey(db, pg, r, rem, height);
                            SetItemsCount(a, an);
                            SetItemsCount(b, CompactifyStrings(b, i));
                        }
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        return result;
                    }
                    else
                    {
                        // merge page b to a
                        if (height != 1)
                        {
                            int r_len = GetKeyStrSize(pg, r);
                            SetKeyStrSize(a, an, r_len);
                            SetSize(a, GetSize(a) + r_len * 2);
                            SetKeyStrOffs(a, an, keySpace - GetSize(a));
                            MemCopy(a, GetKeyStrOffs(a, an), pg, GetKeyStrOffs(pg, r), r_len * 2, 1);
                            an += 1;
                            SetKeyStrOid(a, an + bn, GetKeyStrOid(b, bn));
                        }
                        for (int i = 0; i < bn; i++, an++)
                        {
                            SetKeyStrSize(a, an, GetKeyStrSize(b, i));
                            SetKeyStrOffs(a, an, GetKeyStrOffs(b, i) - GetSize(a));
                            SetKeyStrOid(a, an, GetKeyStrOid(b, i));
                        }
                        SetSize(a, GetSize(a) + GetSize(b));
                        SetItemsCount(a, an);
                        MemCopy(a, keySpace - GetSize(a), b, keySpace - GetSize(b), GetSize(b), 1);
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        db.FreePage(GetKeyStrOid(pg, r + 1));
                        SetKeyStrOid(pg, r + 1, GetKeyStrOid(pg, r));
                        return RemoveStrKey(pg, r);
                    }
                }
                else
                {
                    // page b is before a
                    Page b = db.GetPage(GetKeyStrOid(pg, r - 1));
                    int bn = GetItemsCount(b);
                    int merged_size = (an + bn) * strKeySize + GetSize(a) + GetSize(b);
                    if (height != 1)
                    {
                        merged_size += GetKeyStrSize(pg, r - 1) * 2 + strKeySize * 2;
                    }
                    if (merged_size > keySpace)
                    {
                        // reallocation of nodes between pages a and b
                        int i, j, k, len;
                        db.pool.Unfix(b);
                        b = db.PutPage(GetKeyStrOid(pg, r - 1));
                        int size_a = GetSize(a);
                        int size_b = GetSize(b);
                        int addSize, subSize;
                        if (height != 1)
                        {
                            addSize = GetKeyStrSize(pg, r - 1);
                            subSize = GetKeyStrSize(b, bn - 1);
                        }
                        else
                        {
                            addSize = subSize = GetKeyStrSize(b, bn - 1);
                        }
                        i = 0;
                        int prevDelta = (an * strKeySize + size_a) - (bn * strKeySize + size_b);
                        while (true)
                        {
                            i += 1;
                            int delta = ((an + i) * strKeySize + size_a + addSize * 2) - ((bn - i) * strKeySize + size_b - subSize * 2);
                            if (delta >= 0)
                            {
                                if (delta >= -prevDelta)
                                {
                                    i -= 1;
                                }
                                break;
                            }
                            prevDelta = delta;
                            size_a += addSize * 2;
                            size_b -= subSize * 2;
                            if (height != 1)
                            {
                                addSize = subSize;
                                subSize = GetKeyStrSize(b, bn - i - 1);
                            }
                            else
                            {
                                addSize = subSize = GetKeyStrSize(b, bn - i - 1);
                            }
                        }
                        int result = Btree.op_done;
                        if (i > 0)
                        {
                            k = i;
                            Assert.That(i < bn);
                            if (height != 1)
                            {
                                SetSize(b, GetSize(b) - GetKeyStrSize(b, bn - k) * 2);
                                MemCopy(a, i, a, 0, an + 1, strKeySize);
                                k -= 1;
                                SetKeyStrOid(a, k, GetKeyStrOid(b, bn));
                                len = GetKeyStrSize(pg, r - 1);
                                SetKeyStrSize(a, k, len);
                                SetSize(a, GetSize(a) + len * 2);
                                SetKeyStrOffs(a, k, keySpace - GetSize(a));
                                MemCopy(a, GetKeyStrOffs(a, k), pg, GetKeyStrOffs(pg, r - 1), len * 2, 1);
                            }
                            else
                            {
                                MemCopy(a, i, a, 0, an, strKeySize);
                            }
                            for (j = 0; j < k; j++)
                            {
                                len = GetKeyStrSize(b, bn - k + j);
                                SetSize(a, GetSize(a) + len * 2);
                                SetSize(b, GetSize(b) - len * 2);
                                SetKeyStrOffs(a, j, keySpace - GetSize(a));
                                SetKeyStrSize(a, j, len);
                                SetKeyStrOid(a, j, GetKeyStrOid(b, bn - k + j));
                                MemCopy(a, GetKeyStrOffs(a, j), b, GetKeyStrOffs(b, bn - k + j), len * 2, 1);
                            }
                            an += i;
                            SetItemsCount(a, an);
                            rem.GetStr(b, bn - k - 1);
                            result = ReplaceStrKey(db, pg, r - 1, rem, height);
                            SetItemsCount(b, CompactifyStrings(b, -i));
                        }
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        return result;
                    }
                    else
                    {
                        // merge page b to a
                        if (height != 1)
                        {
                            MemCopy(a, bn + 1, a, 0, an + 1, strKeySize);
                            int len = GetKeyStrSize(pg, r - 1);
                            SetKeyStrSize(a, bn, len);
                            SetSize(a, GetSize(a) + len * 2);
                            SetKeyStrOffs(a, bn, keySpace - GetSize(a));
                            SetKeyStrOid(a, bn, GetKeyStrOid(b, bn));
                            MemCopy(a, GetKeyStrOffs(a, bn), pg, GetKeyStrOffs(pg, r - 1), len * 2, 1);
                            an += 1;
                        }
                        else
                        {
                            MemCopy(a, bn, a, 0, an, strKeySize);
                        }
                        for (int i = 0; i < bn; i++)
                        {
                            SetKeyStrOid(a, i, GetKeyStrOid(b, i));
                            SetKeyStrSize(a, i, GetKeyStrSize(b, i));
                            SetKeyStrOffs(a, i, GetKeyStrOffs(b, i) - GetSize(a));
                        }
                        an += bn;
                        SetItemsCount(a, an);
                        SetSize(a, GetSize(a) + GetSize(b));
                        MemCopy(a, keySpace - GetSize(a), b, keySpace - GetSize(b), GetSize(b), 1);
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        db.FreePage(GetKeyStrOid(pg, r - 1));
                        return RemoveStrKey(pg, r - 1);
                    }
                }
            }
            else if (type == ClassDescriptor.tpArrayOfByte)
            {
                Page a = db.PutPage(GetKeyStrOid(pg, r));
                int an = GetItemsCount(a);
                if (r < nItems)
                {
                    // exists greater page
                    Page b = db.GetPage(GetKeyStrOid(pg, r + 1));
                    int bn = GetItemsCount(b);
                    int merged_size = (an + bn) * strKeySize + GetSize(a) + GetSize(b);
                    if (height != 1)
                    {
                        merged_size += GetKeyStrSize(pg, r) + strKeySize * 2;
                    }
                    if (merged_size > keySpace)
                    {
                        // reallocation of nodes between pages a and b
                        int i, j, k;
                        db.pool.Unfix(b);
                        b = db.PutPage(GetKeyStrOid(pg, r + 1));
                        int size_a = GetSize(a);
                        int size_b = GetSize(b);
                        int addSize, subSize;
                        if (height != 1)
                        {
                            addSize = GetKeyStrSize(pg, r);
                            subSize = GetKeyStrSize(b, 0);
                        }
                        else
                        {
                            addSize = subSize = GetKeyStrSize(b, 0);
                        }
                        i = 0;
                        int prevDelta = (an * strKeySize + size_a) - (bn * strKeySize + size_b);
                        while (true)
                        {
                            i += 1;
                            int delta = ((an + i) * strKeySize + size_a + addSize) - ((bn - i) * strKeySize + size_b - subSize);
                            if (delta >= 0)
                            {
                                if (delta >= -prevDelta)
                                {
                                    i -= 1;
                                }
                                break;
                            }
                            size_a += addSize;
                            size_b -= subSize;
                            prevDelta = delta;
                            if (height != 1)
                            {
                                addSize = subSize;
                                subSize = GetKeyStrSize(b, i);
                            }
                            else
                            {
                                addSize = subSize = GetKeyStrSize(b, i);
                            }
                        }
                        int result = Btree.op_done;
                        if (i > 0)
                        {
                            k = i;
                            if (height != 1)
                            {
                                int len = GetKeyStrSize(pg, r);
                                SetSize(a, GetSize(a) + len);
                                SetKeyStrOffs(a, an, keySpace - GetSize(a));
                                SetKeyStrSize(a, an, len);
                                MemCopy(a, GetKeyStrOffs(a, an), pg, GetKeyStrOffs(pg, r), len, 1);
                                k -= 1;
                                an += 1;
                                SetKeyStrOid(a, an + k, GetKeyStrOid(b, k));
                                SetSize(b, GetSize(b) - GetKeyStrSize(b, k));
                            }
                            for (j = 0; j < k; j++)
                            {
                                int len = GetKeyStrSize(b, j);
                                SetSize(a, GetSize(a) + len);
                                SetSize(b, GetSize(b) - len);
                                SetKeyStrOffs(a, an, keySpace - GetSize(a));
                                SetKeyStrSize(a, an, len);
                                SetKeyStrOid(a, an, GetKeyStrOid(b, j));
                                MemCopy(a, GetKeyStrOffs(a, an), b, GetKeyStrOffs(b, j), len, 1);
                                an += 1;
                            }
                            rem.GetByteArray(b, i - 1);
                            result = ReplaceByteArrayKey(db, pg, r, rem, height);
                            SetItemsCount(a, an);
                            SetItemsCount(b, CompactifyByteArrays(b, i));
                        }
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        return result;
                    }
                    else
                    {
                        // merge page b to a
                        if (height != 1)
                        {
                            int r_len = GetKeyStrSize(pg, r);
                            SetKeyStrSize(a, an, r_len);
                            SetSize(a, GetSize(a) + r_len);
                            SetKeyStrOffs(a, an, keySpace - GetSize(a));
                            MemCopy(a, GetKeyStrOffs(a, an), pg, GetKeyStrOffs(pg, r), r_len, 1);
                            an += 1;
                            SetKeyStrOid(a, an + bn, GetKeyStrOid(b, bn));
                        }
                        for (int i = 0; i < bn; i++, an++)
                        {
                            SetKeyStrSize(a, an, GetKeyStrSize(b, i));
                            SetKeyStrOffs(a, an, GetKeyStrOffs(b, i) - GetSize(a));
                            SetKeyStrOid(a, an, GetKeyStrOid(b, i));
                        }
                        SetSize(a, GetSize(a) + GetSize(b));
                        SetItemsCount(a, an);
                        MemCopy(a, keySpace - GetSize(a), b, keySpace - GetSize(b), GetSize(b), 1);
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        db.FreePage(GetKeyStrOid(pg, r + 1));
                        SetKeyStrOid(pg, r + 1, GetKeyStrOid(pg, r));
                        return RemoveByteArrayKey(pg, r);
                    }
                }
                else
                {
                    // page b is before a
                    Page b = db.GetPage(GetKeyStrOid(pg, r - 1));
                    int bn = GetItemsCount(b);
                    int merged_size = (an + bn) * strKeySize + GetSize(a) + GetSize(b);
                    if (height != 1)
                    {
                        merged_size += GetKeyStrSize(pg, r - 1) + strKeySize * 2;
                    }
                    if (merged_size > keySpace)
                    {
                        // reallocation of nodes between pages a and b
                        int i, j, k, len;
                        db.pool.Unfix(b);
                        b = db.PutPage(GetKeyStrOid(pg, r - 1));
                        int size_a = GetSize(a);
                        int size_b = GetSize(b);
                        int addSize, subSize;
                        if (height != 1)
                        {
                            addSize = GetKeyStrSize(pg, r - 1);
                            subSize = GetKeyStrSize(b, bn - 1);
                        }
                        else
                        {
                            addSize = subSize = GetKeyStrSize(b, bn - 1);
                        }
                        i = 0;
                        int prevDelta = (an * strKeySize + size_a) - (bn * strKeySize + size_b);
                        while (true)
                        {
                            i += 1;
                            int delta = ((an + i) * strKeySize + size_a + addSize) - ((bn - i) * strKeySize + size_b - subSize);
                            if (delta >= 0)
                            {
                                if (delta >= -prevDelta)
                                {
                                    i -= 1;
                                }
                                break;
                            }
                            prevDelta = delta;
                            size_a += addSize;
                            size_b -= subSize;
                            if (height != 1)
                            {
                                addSize = subSize;
                                subSize = GetKeyStrSize(b, bn - i - 1);
                            }
                            else
                            {
                                addSize = subSize = GetKeyStrSize(b, bn - i - 1);
                            }
                        }
                        int result = Btree.op_done;
                        if (i > 0)
                        {
                            k = i;
                            Assert.That(i < bn);
                            if (height != 1)
                            {
                                SetSize(b, GetSize(b) - GetKeyStrSize(b, bn - k));
                                MemCopy(a, i, a, 0, an + 1, strKeySize);
                                k -= 1;
                                SetKeyStrOid(a, k, GetKeyStrOid(b, bn));
                                len = GetKeyStrSize(pg, r - 1);
                                SetKeyStrSize(a, k, len);
                                SetSize(a, GetSize(a) + len);
                                SetKeyStrOffs(a, k, keySpace - GetSize(a));
                                MemCopy(a, GetKeyStrOffs(a, k), pg, GetKeyStrOffs(pg, r - 1), len, 1);
                            }
                            else
                            {
                                MemCopy(a, i, a, 0, an, strKeySize);
                            }
                            for (j = 0; j < k; j++)
                            {
                                len = GetKeyStrSize(b, bn - k + j);
                                SetSize(a, GetSize(a) + len);
                                SetSize(b, GetSize(b) - len);
                                SetKeyStrOffs(a, j, keySpace - GetSize(a));
                                SetKeyStrSize(a, j, len);
                                SetKeyStrOid(a, j, GetKeyStrOid(b, bn - k + j));
                                MemCopy(a, GetKeyStrOffs(a, j), b, GetKeyStrOffs(b, bn - k + j), len, 1);
                            }
                            an += i;
                            SetItemsCount(a, an);
                            rem.GetByteArray(b, bn - k - 1);
                            result = ReplaceByteArrayKey(db, pg, r - 1, rem, height);
                            SetItemsCount(b, CompactifyByteArrays(b, -i));
                        }
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        return result;
                    }
                    else
                    {
                        // merge page b to a
                        if (height != 1)
                        {
                            MemCopy(a, bn + 1, a, 0, an + 1, strKeySize);
                            int len = GetKeyStrSize(pg, r - 1);
                            SetKeyStrSize(a, bn, len);
                            SetSize(a, GetSize(a) + len);
                            SetKeyStrOffs(a, bn, keySpace - GetSize(a));
                            SetKeyStrOid(a, bn, GetKeyStrOid(b, bn));
                            MemCopy(a, GetKeyStrOffs(a, bn), pg, GetKeyStrOffs(pg, r - 1), len, 1);
                            an += 1;
                        }
                        else
                        {
                            MemCopy(a, bn, a, 0, an, strKeySize);
                        }
                        for (int i = 0; i < bn; i++)
                        {
                            SetKeyStrOid(a, i, GetKeyStrOid(b, i));
                            SetKeyStrSize(a, i, GetKeyStrSize(b, i));
                            SetKeyStrOffs(a, i, GetKeyStrOffs(b, i) - GetSize(a));
                        }
                        an += bn;
                        SetItemsCount(a, an);
                        SetSize(a, GetSize(a) + GetSize(b));
                        MemCopy(a, keySpace - GetSize(a), b, keySpace - GetSize(b), GetSize(b), 1);
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        db.FreePage(GetKeyStrOid(pg, r - 1));
                        return RemoveByteArrayKey(pg, r - 1);
                    }
                }
            }
            else
            {
                // scalar types
                Page a = db.PutPage(GetReference(pg, maxItems - r - 1));
                int an = GetItemsCount(a);
                int itemSize = ClassDescriptor.Sizeof[type];
                if (r < nItems)
                {
                    // exists greater page
                    Page b = db.GetPage(GetReference(pg, maxItems - r - 2));
                    int bn = GetItemsCount(b);
                    Assert.That(bn >= an);
                    if (height != 1)
                    {
                        MemCopy(a, an, pg, r, 1, itemSize);
                        an += 1;
                        bn += 1;
                    }
                    int merged_size = (an + bn) * (4 + itemSize);
                    if (merged_size > keySpace)
                    {
                        // reallocation of nodes between pages a and b
                        int i = bn - ((an + bn) >> 1);
                        db.pool.Unfix(b);
                        b = db.PutPage(GetReference(pg, maxItems - r - 2));
                        MemCopy(a, an, b, 0, i, itemSize);
                        MemCopy(b, 0, b, i, bn - i, itemSize);
                        MemCopy(a, maxItems - an - i, b, maxItems - i, i, 4);
                        MemCopy(b, maxItems - bn + i, b, maxItems - bn, bn - i, 4);
                        MemCopy(pg, r, a, an + i - 1, 1, itemSize);
                        SetItemsCount(b, GetItemsCount(b) - i);
                        SetItemsCount(a, GetItemsCount(a) + i);
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        return Btree.op_done;
                    }
                    else
                    {
                        // merge page b to a
                        MemCopy(a, an, b, 0, bn, itemSize);
                        MemCopy(a, maxItems - an - bn, b, maxItems - bn, bn, 4);
                        db.FreePage(GetReference(pg, maxItems - r - 2));
                        MemCopy(pg, maxItems - nItems, pg, maxItems - nItems - 1, nItems - r - 1, 4);
                        MemCopy(pg, r, pg, r + 1, nItems - r - 1, itemSize);
                        SetItemsCount(a, GetItemsCount(a) + bn);
                        SetItemsCount(pg, nItems - 1);
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        return nItems * (itemSize + 4) < keySpace / 2 ? Btree.op_underflow : Btree.op_done;
                    }
                }
                else
                {
                    // page b is before a
                    Page b = db.GetPage(GetReference(pg, maxItems - r));
                    int bn = GetItemsCount(b);
                    Assert.That(bn >= an);
                    if (height != 1)
                    {
                        an += 1;
                        bn += 1;
                    }
                    int merged_size = (an + bn) * (4 + itemSize);
                    if (merged_size > keySpace)
                    {
                        // reallocation of nodes between pages a and b
                        int i = bn - ((an + bn) >> 1);
                        db.pool.Unfix(b);
                        b = db.PutPage(GetReference(pg, maxItems - r));
                        MemCopy(a, i, a, 0, an, itemSize);
                        MemCopy(a, 0, b, bn - i, i, itemSize);
                        MemCopy(a, maxItems - an - i, a, maxItems - an, an, 4);
                        MemCopy(a, maxItems - i, b, maxItems - bn, i, 4);
                        if (height != 1)
                        {
                            MemCopy(a, i - 1, pg, r - 1, 1, itemSize);
                        }
                        MemCopy(pg, r - 1, b, bn - i - 1, 1, itemSize);
                        SetItemsCount(b, GetItemsCount(b) - i);
                        SetItemsCount(a, GetItemsCount(a) + i);
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        return Btree.op_done;
                    }
                    else
                    {
                        // merge page b to a
                        MemCopy(a, bn, a, 0, an, itemSize);
                        MemCopy(a, 0, b, 0, bn, itemSize);
                        MemCopy(a, maxItems - an - bn, a, maxItems - an, an, 4);
                        MemCopy(a, maxItems - bn, b, maxItems - bn, bn, 4);
                        if (height != 1)
                        {
                            MemCopy(a, bn - 1, pg, r - 1, 1, itemSize);
                        }
                        db.FreePage(GetReference(pg, maxItems - r));
                        SetReference(pg, maxItems - r, GetReference(pg, maxItems - r - 1));
                        SetItemsCount(a, GetItemsCount(a) + bn);
                        SetItemsCount(pg, nItems - 1);
                        db.pool.Unfix(a);
                        db.pool.Unfix(b);
                        return nItems * (itemSize + 4) < keySpace / 2 ? Btree.op_underflow : Btree.op_done;
                    }
                }
            }
        }
コード例 #2
0
ファイル: BtreePage.cs プロジェクト: kjk/tenderbase
        internal static int InsertStrKey(StorageImpl db, Page pg, int r, BtreeKey ins, int height)
        {
            int nItems = GetItemsCount(pg);
            int size = GetSize(pg);
            int n = (height != 0) ? nItems + 1 : nItems;
            // insert before e[r]
            char[] sval = (char[]) ins.key.oval;
            int len = sval.Length;
            if (size + len * 2 + (n + 1) * strKeySize <= keySpace)
            {
                MemCopy(pg, r + 1, pg, r, n - r, strKeySize);
                size += len * 2;
                SetKeyStrOffs(pg, r, keySpace - size);
                SetKeyStrSize(pg, r, len);
                SetKeyStrOid(pg, r, ins.oid);
                SetKeyStrChars(pg, keySpace - size, sval);
                nItems += 1;
            }
            else
            {
                // page is full then divide page
                int pageId = db.AllocatePage();
                Page b = db.PutPage(pageId);
                int moved = 0;
                int inserted = len * 2 + strKeySize;
                int prevDelta = (1 << 31) + 1;

                for (int bn = 0, i = 0; ; bn += 1)
                {
                    int addSize, subSize;
                    int j = nItems - i - 1;
                    int keyLen = GetKeyStrSize(pg, i);
                    if (bn == r)
                    {
                        keyLen = len;
                        inserted = 0;
                        addSize = len;
                        if (height == 0)
                        {
                            subSize = 0;
                            j += 1;
                        }
                        else
                        {
                            subSize = GetKeyStrSize(pg, i);
                        }
                    }
                    else
                    {
                        addSize = subSize = keyLen;
                        if (height != 0)
                        {
                            if (i + 1 != r)
                            {
                                subSize += GetKeyStrSize(pg, i + 1);
                                j -= 1;
                            }
                            else
                            {
                                inserted = 0;
                            }
                        }
                    }
                    int delta = (moved + addSize * 2 + (bn + 1) * strKeySize) - (j * strKeySize + size - subSize * 2 + inserted);
                    if (delta >= -prevDelta)
                    {
                        if (height == 0)
                        {
                            ins.GetStr(b, bn - 1);
                        }
                        else
                        {
                            Assert.That("String fits in the B-Tree page", moved + (bn + 1) * strKeySize <= keySpace);
                            if (bn != r)
                            {
                                ins.GetStr(pg, i);
                                SetKeyStrOid(b, bn, GetKeyStrOid(pg, i));
                                size -= keyLen * 2;
                                i += 1;
                            }
                            else
                            {
                                SetKeyStrOid(b, bn, ins.oid);
                            }
                        }
                        nItems = CompactifyStrings(pg, i);
                        if (bn < r || (bn == r && height == 0))
                        {
                            MemCopy(pg, r - i + 1, pg, r - i, n - r, strKeySize);
                            size += len * 2;
                            nItems += 1;
                            Assert.That("String fits in the B-Tree page", size + (n - i + 1) * strKeySize <= keySpace);
                            SetKeyStrOffs(pg, r - i, keySpace - size);
                            SetKeyStrSize(pg, r - i, len);
                            SetKeyStrOid(pg, r - i, ins.oid);
                            SetKeyStrChars(pg, keySpace - size, sval);
                        }
                        SetItemsCount(b, bn);
                        SetSize(b, moved);
                        SetSize(pg, size);
                        SetItemsCount(pg, nItems);
                        ins.oid = pageId;
                        db.pool.Unfix(b);
                        return Btree.op_overflow;
                    }
                    moved += keyLen * 2;
                    prevDelta = delta;
                    Assert.That("String fits in the B-Tree page", moved + (bn + 1) * strKeySize <= keySpace);
                    SetKeyStrSize(b, bn, keyLen);
                    SetKeyStrOffs(b, bn, keySpace - moved);
                    if (bn == r)
                    {
                        SetKeyStrOid(b, bn, ins.oid);
                        SetKeyStrChars(b, keySpace - moved, sval);
                    }
                    else
                    {
                        SetKeyStrOid(b, bn, GetKeyStrOid(pg, i));
                        MemCopy(b, keySpace - moved, pg, GetKeyStrOffs(pg, i), keyLen * 2, 1);
                        size -= keyLen * 2;
                        i += 1;
                    }
                }
            }
            SetItemsCount(pg, nItems);
            SetSize(pg, size);
            return size + strKeySize * (nItems + 1) < keySpace / 2 ? Btree.op_underflow : Btree.op_done;
        }