Esempio n. 1
0
        internal void assemblePage(int nCell, byte[] apCell, int[] aSize)
        {
            Debug.Assert(this.NOverflows == 0);
            Debug.Assert(MutexEx.Held(this.Shared.Mutex));
            Debug.Assert(nCell >= 0 && nCell <= (int)Btree.MX_CELL(this.Shared) && (int)Btree.MX_CELL(this.Shared) <= 10921);
            Debug.Assert(Pager.IsPageWriteable(this.DbPage));
            // Check that the page has just been zeroed by zeroPage()
            Debug.Assert(this.Cells == 0);
            //
            var data    = this.Data;                   // Pointer to data for pPage
            int hdr     = this.HeaderOffset;           // Offset of header on pPage
            var nUsable = (int)this.Shared.UsableSize; // Usable size of page

            Debug.Assert(ConvertEx.Get2nz(data, hdr + 5) == nUsable);
            var pCellptr = this.CellOffset + nCell * 2; // Address of next cell pointer
            var cellbody = nUsable;                     // Address of next cell body

            for (var i = nCell - 1; i >= 0; i--)
            {
                var sz = (ushort)aSize[i];
                pCellptr -= 2;
                cellbody -= sz;
                ConvertEx.Put2(data, pCellptr, cellbody);
                Buffer.BlockCopy(apCell, 0, data, cellbody, sz);
            }
            ConvertEx.Put2(data, hdr + 3, nCell);
            ConvertEx.Put2(data, hdr + 5, cellbody);
            this.FreeBytes -= (ushort)(nCell * 2 + nUsable - cellbody);
            this.Cells      = (ushort)nCell;
        }
Esempio n. 2
0
        internal void insertCell(int i, byte[] pCell, int sz, byte[] pTemp, Pgno iChild, ref RC pRC)
        {
            var nSkip = (iChild != 0 ? 4 : 0);

            if (pRC != RC.OK)
            {
                return;
            }
            Debug.Assert(i >= 0 && i <= this.Cells + this.NOverflows);
            Debug.Assert(this.Cells <= Btree.MX_CELL(this.Shared) && Btree.MX_CELL(this.Shared) <= 10921);
            Debug.Assert(this.NOverflows <= this.Overflows.Length);
            Debug.Assert(MutexEx.Held(this.Shared.Mutex));
            // The cell should normally be sized correctly.  However, when moving a malformed cell from a leaf page to an interior page, if the cell size
            // wanted to be less than 4 but got rounded up to 4 on the leaf, then size might be less than 8 (leaf-size + pointer) on the interior node.  Hence
            // the term after the || in the following assert().
            Debug.Assert(sz == cellSizePtr(pCell) || (sz == 8 && iChild > 0));
            if (this.NOverflows != 0 || sz + 2 > this.FreeBytes)
            {
                if (pTemp != null)
                {
                    Buffer.BlockCopy(pCell, nSkip, pTemp, nSkip, sz - nSkip);
                    pCell = pTemp;
                }
                if (iChild != 0)
                {
                    ConvertEx.Put4L(pCell, iChild);
                }
                var j = this.NOverflows++;
                Debug.Assert(j < this.Overflows.Length);
                this.Overflows[j].Cell  = pCell;
                this.Overflows[j].Index = (ushort)i;
            }
            else
            {
                var rc = Pager.Write(this.DbPage);
                if (rc != RC.OK)
                {
                    pRC = rc;
                    return;
                }
                Debug.Assert(Pager.IsPageWriteable(this.DbPage));
                var data       = this.Data;                   // The content of the whole page
                var cellOffset = (int)this.CellOffset;        // Address of first cell pointer in data[]
                var end        = cellOffset + 2 * this.Cells; // First byte past the last cell pointer in data[]
                var ins        = cellOffset + 2 * i;          // Index in data[] where new cell pointer is inserted
                int idx        = 0;                           // Where to write new cell content in data[]
                rc = allocateSpace(sz, ref idx);
                if (rc != RC.OK)
                {
                    pRC = rc;
                    return;
                }
                // The allocateSpace() routine guarantees the following two properties if it returns success
                Debug.Assert(idx >= end + 2);
                Debug.Assert(idx + sz <= (int)this.Shared.UsableSize);
                this.Cells++;
                this.FreeBytes -= (ushort)(2 + sz);
                Buffer.BlockCopy(pCell, nSkip, data, idx + nSkip, sz - nSkip);
                if (iChild != 0)
                {
                    ConvertEx.Put4L(data, (uint)idx, iChild);
                }
                for (var j = end; j > ins; j -= 2)
                {
                    data[j + 0] = data[j - 2];
                    data[j + 1] = data[j - 1];
                }
                ConvertEx.Put2(data, ins, idx);
                ConvertEx.Put2(data, this.HeaderOffset + 3, this.Cells);
#if !SQLITE_OMIT_AUTOVACUUM
                if (this.Shared.AutoVacuum)
                {
                    // The cell may contain a pointer to an overflow page. If so, write the entry for the overflow page into the pointer map.
                    ptrmapPutOvflPtr(pCell, ref pRC);
                }
#endif
            }
        }
Esempio n. 3
0
        internal RC btreeInitPage()
        {
            Debug.Assert(this.Shared != null);
            Debug.Assert(MutexEx.Held(this.Shared.Mutex));
            Debug.Assert(this.ID == Pager.GetPageID(this.DbPage));
            Debug.Assert(this == Pager.sqlite3PagerGetExtra <MemPage>(this.DbPage));
            Debug.Assert(this.Data == Pager.sqlite3PagerGetData(this.DbPage));
            if (!this.HasInit)
            {
                var pBt  = this.Shared;       // The main btree structure
                var hdr  = this.HeaderOffset; // Offset to beginning of page header
                var data = this.Data;
                if (decodeFlags(data[hdr]) != 0)
                {
                    return(SysEx.SQLITE_CORRUPT_BKPT());
                }
                Debug.Assert(pBt.PageSize >= 512 && pBt.PageSize <= 65536);
                this.MaskPage   = (ushort)(pBt.PageSize - 1);
                this.NOverflows = 0;
                var    usableSize = (int)pBt.UsableSize;   // Amount of usable space on each page
                ushort cellOffset;                         // Offset from start of page to first cell pointer
                this.CellOffset = (cellOffset = (ushort)(hdr + 12 - 4 * this.Leaf));
                var top = ConvertEx.Get2nz(data, hdr + 5); // First byte of the cell content area
                this.Cells = (ushort)(ConvertEx.Get2(data, hdr + 3));
                if (this.Cells > Btree.MX_CELL(pBt))
                {
                    // To many cells for a single page.  The page must be corrupt
                    return(SysEx.SQLITE_CORRUPT_BKPT());
                }
                // A malformed database page might cause us to read past the end of page when parsing a cell.
                // The following block of code checks early to see if a cell extends past the end of a page boundary and causes SQLITE_CORRUPT to be
                // returned if it does.
                var iCellFirst = cellOffset + 2 * this.Cells; // First allowable cell or freeblock offset
                var iCellLast  = usableSize - 4;              // Last possible cell or freeblock offset
#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK
                if (pPage.leaf == 0)
                {
                    iCellLast--;
                }
                for (var i = 0; i < pPage.nCell; i++)
                {
                    pc = (ushort)ConvertEx.get2byte(data, cellOffset + i * 2);
                    if (pc < iCellFirst || pc > iCellLast)
                    {
                        return(SysEx.SQLITE_CORRUPT_BKPT());
                    }
                    var sz = cellSizePtr(pPage, data, pc);
                    if (pc + sz > usableSize)
                    {
                        return(SysEx.SQLITE_CORRUPT_BKPT());
                    }
                }
                if (pPage.leaf == 0)
                {
                    iCellLast++;
                }
#endif
                // Compute the total free space on the page
                var pc    = (ushort)ConvertEx.Get2(data, hdr + 1); // Address of a freeblock within pPage.aData[]
                var nFree = (ushort)(data[hdr + 7] + top);         // Number of unused bytes on the page
                while (pc > 0)
                {
                    if (pc < iCellFirst || pc > iCellLast)
                    {
                        // Start of free block is off the page
                        return(SysEx.SQLITE_CORRUPT_BKPT());
                    }
                    var next = (ushort)ConvertEx.Get2(data, pc);
                    var size = (ushort)ConvertEx.Get2(data, pc + 2);
                    if ((next > 0 && next <= pc + size + 3) || pc + size > usableSize)
                    {
                        // Free blocks must be in ascending order. And the last byte of the free-block must lie on the database page.
                        return(SysEx.SQLITE_CORRUPT_BKPT());
                    }
                    nFree = (ushort)(nFree + size);
                    pc    = next;
                }
                // At this point, nFree contains the sum of the offset to the start of the cell-content area plus the number of free bytes within
                // the cell-content area. If this is greater than the usable-size of the page, then the page must be corrupted. This check also
                // serves to verify that the offset to the start of the cell-content area, according to the page header, lies within the page.
                if (nFree > usableSize)
                {
                    return(SysEx.SQLITE_CORRUPT_BKPT());
                }
                this.FreeBytes = (ushort)(nFree - iCellFirst);
                this.HasInit   = true;
            }
            return(RC.OK);
        }