Ejemplo n.º 1
0
        /*
        ** Create a new bitmap object able to handle bits between 0 and iSize,
        ** inclusive.  Return a pointer to the new object.  Return NULL if
        ** malloc fails.
        */
        private static Bitvec sqlite3BitvecCreate(uint iSize)
        {
            Bitvec p;

            //Debug.Assert( sizeof(p)==BITVEC_SZ );
            p = new Bitvec(); //sqlite3MallocZero( sizeof(p) );
            if (p != null)
            {
                p.iSize = iSize;
            }
            return(p);
        }
Ejemplo n.º 2
0
        /*
        ** Clear the i-th bit.
        **
        ** pBuf must be a pointer to at least BITVEC_SZ bytes of temporary storage
        ** that BitvecClear can use to rebuilt its hash table.
        */
        private static void sqlite3BitvecClear(Bitvec p, uint i, uint[] pBuf)
        {
            if (p == null)
            {
                return;
            }
            Debug.Assert(i > 0);
            i--;
            while (p.iDivisor != 0)
            {
                var bin = i / p.iDivisor;
                i = i % p.iDivisor;
                p = p.u.apSub[bin];
                if (null == p)
                {
                    return;
                }
            }

            if (p.iSize <= BITVEC_NBIT)
            {
                p.u.aBitmap[i / BITVEC_SZELEM] &= (byte)~(1 << (int)(i & (BITVEC_SZELEM - 1)));
            }
            else
            {
                uint j;
                var  aiValues = pBuf;
                Array.Copy(p.u.aHash, aiValues, p.u.aHash.Length); //memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
                p.u.aHash = new uint[aiValues.Length];             // memset(p->u.aHash, 0, sizeof(p->u.aHash));
                p.nSet    = 0;
                for (j = 0; j < BITVEC_NINT; j++)
                {
                    if (aiValues[j] != 0 && aiValues[j] != i + 1)
                    {
                        var h = BITVEC_HASH(aiValues[j] - 1);
                        p.nSet++;
                        while (p.u.aHash[h] != 0)
                        {
                            h++;
                            if (h >= BITVEC_NINT)
                            {
                                h = 0;
                            }
                        }

                        p.u.aHash[h] = aiValues[j];
                    }
                }
            }
        }
Ejemplo n.º 3
0
 /*
 ** Destroy a bitmap object.  Reclaim all memory used.
 */
 static void sqlite3BitvecDestroy(ref Bitvec p)
 {
     if (p == null)
     {
         return;
     }
     if (p.iDivisor != 0)
     {
         u32 i;
         for (i = 0; i < BITVEC_NPTR; i++)
         {
             sqlite3BitvecDestroy(ref p.u.apSub[i]);
         }
     }
     //sqlite3_free( ref p );
 }
Ejemplo n.º 4
0
        /*
        ** Check to see if the i-th bit is set.  Return true or false.
        ** If p is NULL (if the bitmap has not been created) or if
        ** i is out of range, then return false.
        */
        private static int sqlite3BitvecTest(Bitvec p, uint i)
        {
            if (p == null || i == 0)
            {
                return(0);
            }
            if (i > p.iSize)
            {
                return(0);
            }
            i--;
            while (p.iDivisor != 0)
            {
                var bin = i / p.iDivisor;
                i = i % p.iDivisor;
                p = p.u.apSub[bin];
                if (null == p)
                {
                    return(0);
                }
            }

            if (p.iSize <= BITVEC_NBIT)
            {
                return((p.u.aBitmap[i / BITVEC_SZELEM] & (1 << (int)(i & (BITVEC_SZELEM - 1)))) != 0 ? 1 : 0);
            }

            var h = BITVEC_HASH(i++);

            while (p.u.aHash[h] != 0)
            {
                if (p.u.aHash[h] == i)
                {
                    return(1);
                }
                h = (h + 1) % BITVEC_NINT;
            }

            return(0);
        }
Ejemplo n.º 5
0
        /*
        ** This routine runs an extensive test of the Bitvec code.
        **
        ** The input is an array of integers that acts as a program
        ** to test the Bitvec.  The integers are opcodes followed
        ** by 0, 1, or 3 operands, depending on the opcode.  Another
        ** opcode follows immediately after the last operand.
        **
        ** There are 6 opcodes numbered from 0 through 5.  0 is the
        ** "halt" opcode and causes the test to end.
        **
        **    0          Halt and return the number of errors
        **    1 N S X    Set N bits beginning with S and incrementing by X
        **    2 N S X    Clear N bits beginning with S and incrementing by X
        **    3 N        Set N randomly chosen bits
        **    4 N        Clear N randomly chosen bits
        **    5 N S X    Set N bits from S increment X in array only, not in bitvec
        **
        ** The opcodes 1 through 4 perform set and clear operations are performed
        ** on both a Bitvec object and on a linear array of bits obtained from malloc.
        ** Opcode 5 works on the linear array only, not on the Bitvec.
        ** Opcode 5 is used to deliberately induce a fault in order to
        ** confirm that error detection works.
        **
        ** At the conclusion of the test the linear array is compared
        ** against the Bitvec object.  If there are any differences,
        ** an error is returned.  If they are the same, zero is returned.
        **
        ** If a memory allocation error occurs, return -1.
        */
        private static int sqlite3BitvecBuiltinTest(uint sz, int[] aOp)
        {
            Bitvec pBitvec = null;

            byte[] pV = null;
            var    rc = -1;
            int    i, nx, pc, op;

            uint[] pTmpSpace;

            /* Allocate the Bitvec to be tested and a linear array of
            ** bits to act as the reference */
            pBitvec   = sqlite3BitvecCreate(sz);
            pV        = sqlite3_malloc((int)(sz + 7) / 8 + 1);
            pTmpSpace = new uint[BITVEC_SZ]; // sqlite3_malloc( BITVEC_SZ );
            if (pBitvec == null || pV == null || pTmpSpace == null)
            {
                goto bitvec_end;
            }
            Array.Clear(pV, 0, (int)(sz + 7) / 8 + 1);  // memset( pV, 0, ( sz + 7 ) / 8 + 1 );

            /* NULL pBitvec tests */
            sqlite3BitvecSet(null, 1);
            sqlite3BitvecClear(null, 1, pTmpSpace);

            /* Run the program */
            pc = 0;
            while ((op = aOp[pc]) != 0)
            {
                switch (op)
                {
                case 1:
                case 2:
                case 5:
                {
                    nx           = 4;
                    i            = aOp[pc + 2] - 1;
                    aOp[pc + 2] += aOp[pc + 3];
                    break;
                }

                case 3:
                case 4:
                default:
                {
                    nx = 2;
                    long i64Temp = 0;
                    sqlite3_randomness(sizeof(long), ref i64Temp);
                    i = (int)i64Temp;
                    break;
                }
                }

                if (--aOp[pc + 1] > 0)
                {
                    nx = 0;
                }
                pc += nx;
                i   = (int)((i & 0x7fffffff) % sz);
                if ((op & 1) != 0)
                {
                    SETBIT(pV, i + 1);
                    if (op != 5)
                    {
                        if (sqlite3BitvecSet(pBitvec, (uint)i + 1) != 0)
                        {
                            goto bitvec_end;
                        }
                    }
                }
                else
                {
                    CLEARBIT(pV, i + 1);
                    sqlite3BitvecClear(pBitvec, (uint)i + 1, pTmpSpace);
                }
            }

            /* Test to make sure the linear array exactly matches the
            ** Bitvec object.  Start with the assumption that they do
            ** match (rc==0).  Change rc to non-zero if a discrepancy
            ** is found.
            */
            rc = sqlite3BitvecTest(null, 0) + sqlite3BitvecTest(pBitvec, sz + 1)
                 + sqlite3BitvecTest(pBitvec, 0)
                 + (int)(sqlite3BitvecSize(pBitvec) - sz);
            for (i = 1; i <= sz; i++)
            {
                if (TESTBIT(pV, i) != sqlite3BitvecTest(pBitvec, (uint)i))
                {
                    rc = i;
                    break;
                }
            }

            /* Free allocated structure */
bitvec_end:
            //sqlite3_free( ref pTmpSpace );
            //sqlite3_free( ref pV );
            sqlite3BitvecDestroy(ref pBitvec);
            return(rc);
        }
Ejemplo n.º 6
0
 /*
 ** Return the value of the iSize parameter specified when Bitvec *p
 ** was created.
 */
 private static uint sqlite3BitvecSize(Bitvec p)
 {
     return(p.iSize);
 }
Ejemplo n.º 7
0
        /*
        ** Set the i-th bit.  Return 0 on success and an error code if
        ** anything goes wrong.
        **
        ** This routine might cause sub-bitmaps to be allocated.  Failing
        ** to get the memory needed to hold the sub-bitmap is the only
        ** that can go wrong with an insert, assuming p and i are valid.
        **
        ** The calling function must ensure that p is a valid Bitvec object
        ** and that the value for "i" is within range of the Bitvec object.
        ** Otherwise the behavior is undefined.
        */
        private static int sqlite3BitvecSet(Bitvec p, uint i)
        {
            uint h;

            if (p == null)
            {
                return(SQLITE_OK);
            }
            Debug.Assert(i > 0);
            Debug.Assert(i <= p.iSize);
            i--;
            while (p.iSize > BITVEC_NBIT && p.iDivisor != 0)
            {
                var bin = i / p.iDivisor;
                i = i % p.iDivisor;
                if (p.u.apSub[bin] == null)
                {
                    p.u.apSub[bin] = sqlite3BitvecCreate(p.iDivisor);
                    if (p.u.apSub[bin] == null)
                    {
                        return(SQLITE_NOMEM);
                    }
                }

                p = p.u.apSub[bin];
            }

            if (p.iSize <= BITVEC_NBIT)
            {
                p.u.aBitmap[i / BITVEC_SZELEM] |= (byte)(1 << (int)(i & (BITVEC_SZELEM - 1)));
                return(SQLITE_OK);
            }

            h = BITVEC_HASH(i++);
            /* if there wasn't a hash collision, and this doesn't */
            /* completely fill the hash, then just add it without */
            /* worring about sub-dividing and re-hashing. */
            if (0 == p.u.aHash[h])
            {
                if (p.nSet < BITVEC_NINT - 1)
                {
                    goto bitvec_set_end;
                }
                goto bitvec_set_rehash;
            }

            /* there was a collision, check to see if it's already */
            /* in hash, if not, try to find a spot for it */
            do
            {
                if (p.u.aHash[h] == i)
                {
                    return(SQLITE_OK);
                }
                h++;
                if (h >= BITVEC_NINT)
                {
                    h = 0;
                }
            } while (p.u.aHash[h] != 0);

            /* we didn't find it in the hash.  h points to the first */
            /* available free spot. check to see if this is going to */
            /* make our hash too "full".  */
bitvec_set_rehash:
            if (p.nSet >= BITVEC_MXHASH)
            {
                uint j;
                int  rc;
                var  aiValues = new uint[BITVEC_NINT]; // = sqlite3StackAllocRaw(0, sizeof(p->u.aHash));
                if (aiValues == null)
                {
                    return(SQLITE_NOMEM);
                }

                Buffer.BlockCopy(p.u.aHash, 0, aiValues, 0,
                                 aiValues.Length * sizeof(uint)); // memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
                p.u.apSub  = new Bitvec[BITVEC_NPTR];             //memset(p->u.apSub, 0, sizeof(p->u.apSub));
                p.iDivisor = (uint)((p.iSize + BITVEC_NPTR - 1) / BITVEC_NPTR);
                rc         = sqlite3BitvecSet(p, i);
                for (j = 0; j < BITVEC_NINT; j++)
                {
                    if (aiValues[j] != 0)
                    {
                        rc |= sqlite3BitvecSet(p, aiValues[j]);
                    }
                }
                //sqlite3StackFree( null, aiValues );
                return(rc);
            }

bitvec_set_end:
            p.nSet++;
            p.u.aHash[h] = i;
            return(SQLITE_OK);
        }
Ejemplo n.º 8
0
/*
** Return the value of the iSize parameter specified when Bitvec *p
** was created.
*/
static u32 sqlite3BitvecSize( Bitvec p )
{
  return p.iSize;
}
Ejemplo n.º 9
0
/*
** Destroy a bitmap object.  Reclaim all memory used.
*/
static void sqlite3BitvecDestroy( ref Bitvec p )
{
  if ( p == null )
    return;
  if ( p.iDivisor != 0 )
  {
    u32 i;
    for ( i = 0; i < BITVEC_NPTR; i++ )
    {
      sqlite3BitvecDestroy( ref p.u.apSub[i] );
    }
  }
  //sqlite3_free( ref p );
}
Ejemplo n.º 10
0
/*
** Clear the i-th bit.
**
** pBuf must be a pointer to at least BITVEC_SZ bytes of temporary storage
** that BitvecClear can use to rebuilt its hash table.
*/
static void sqlite3BitvecClear( Bitvec p, u32 i, u32[] pBuf )
{
  if ( p == null )
    return;
  Debug.Assert( i > 0 );
  i--;
  while ( p.iDivisor != 0 )
  {
    u32 bin = i / p.iDivisor;
    i = i % p.iDivisor;
    p = p.u.apSub[bin];
    if ( null == p )
    {
      return;
    }
  }
  if ( p.iSize <= BITVEC_NBIT )
  {
    p.u.aBitmap[i / BITVEC_SZELEM] &= (byte)~( ( 1 << (int)( i & ( BITVEC_SZELEM - 1 ) ) ) );
  }
  else
  {
    u32 j;
    u32[] aiValues = pBuf;
    Array.Copy( p.u.aHash, aiValues, p.u.aHash.Length );//memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
    p.u.aHash = new u32[aiValues.Length];// memset(p->u.aHash, 0, sizeof(p->u.aHash));
    p.nSet = 0;
    for ( j = 0; j < BITVEC_NINT; j++ )
    {
      if ( aiValues[j] != 0 && aiValues[j] != ( i + 1 ) )
      {
        u32 h = BITVEC_HASH( aiValues[j] - 1 );
        p.nSet++;
        while ( p.u.aHash[h] != 0 )
        {
          h++;
          if ( h >= BITVEC_NINT )
            h = 0;
        }
        p.u.aHash[h] = aiValues[j];
      }
    }
  }
}
Ejemplo n.º 11
0
/*
** Set the i-th bit.  Return 0 on success and an error code if
** anything goes wrong.
**
** This routine might cause sub-bitmaps to be allocated.  Failing
** to get the memory needed to hold the sub-bitmap is the only
** that can go wrong with an insert, assuming p and i are valid.
**
** The calling function must ensure that p is a valid Bitvec object
** and that the value for "i" is within range of the Bitvec object.
** Otherwise the behavior is undefined.
*/
static int sqlite3BitvecSet( Bitvec p, u32 i )
{
  u32 h;
  if ( p == null )
    return SQLITE_OK;
  Debug.Assert( i > 0 );
  Debug.Assert( i <= p.iSize );
  i--;
  while ( ( p.iSize > BITVEC_NBIT ) && p.iDivisor != 0 )
  {
    u32 bin = i / p.iDivisor;
    i = i % p.iDivisor;
    if ( p.u.apSub[bin] == null )
    {
      p.u.apSub[bin] = sqlite3BitvecCreate( p.iDivisor );
      //if ( p.u.apSub[bin] == null )
      //  return SQLITE_NOMEM;
    }
    p = p.u.apSub[bin];
  }
  if ( p.iSize <= BITVEC_NBIT )
  {
    p.u.aBitmap[i / BITVEC_SZELEM] |= (byte)( 1 << (int)( i & ( BITVEC_SZELEM - 1 ) ) );
    return SQLITE_OK;
  }
  h = BITVEC_HASH( i++ );
  /* if there wasn't a hash collision, and this doesn't */
  /* completely fill the hash, then just add it without */
  /* worring about sub-dividing and re-hashing. */
  if ( 0 == p.u.aHash[h] )
  {
    if ( p.nSet < ( BITVEC_NINT - 1 ) )
    {
      goto bitvec_set_end;
    }
    else
    {
      goto bitvec_set_rehash;
    }
  }
  /* there was a collision, check to see if it's already */
  /* in hash, if not, try to find a spot for it */
  do
  {
    if ( p.u.aHash[h] == i )
      return SQLITE_OK;
    h++;
    if ( h >= BITVEC_NINT )
      h = 0;
  } while ( p.u.aHash[h] != 0 );
/* we didn't find it in the hash.  h points to the first */
/* available free spot. check to see if this is going to */
/* make our hash too "full".  */
bitvec_set_rehash:
  if ( p.nSet >= BITVEC_MXHASH )
  {
    u32 j;
    int rc;
    u32[] aiValues = new u32[BITVEC_NINT];// = sqlite3StackAllocRaw(0, sizeof(p->u.aHash));
    //if ( aiValues == null )
    //{
    //  return SQLITE_NOMEM;
    //}
    //else
    {

      Buffer.BlockCopy( p.u.aHash, 0, aiValues, 0, aiValues.Length * ( sizeof( u32 ) ) );// memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
      p.u.apSub = new Bitvec[BITVEC_NPTR];//memset(p->u.apSub, 0, sizeof(p->u.apSub));
      p.iDivisor = (u32)( ( p.iSize + BITVEC_NPTR - 1 ) / BITVEC_NPTR );
      rc = sqlite3BitvecSet( p, i );
      for ( j = 0; j < BITVEC_NINT; j++ )
      {
        if ( aiValues[j] != 0 )
          rc |= sqlite3BitvecSet( p, aiValues[j] );
      }
      //sqlite3StackFree( null, aiValues );
      return rc;
    }
  }
bitvec_set_end:
  p.nSet++;
  p.u.aHash[h] = i;
  return SQLITE_OK;
}
Ejemplo n.º 12
0
/*
** Check to see if the i-th bit is set.  Return true or false.
** If p is NULL (if the bitmap has not been created) or if
** i is out of range, then return false.
*/
static int sqlite3BitvecTest( Bitvec p, u32 i )
{
  if ( p == null || i == 0 )
    return 0;
  if ( i > p.iSize )
    return 0;
  i--;
  while ( p.iDivisor != 0 )
  {
    u32 bin = i / p.iDivisor;
    i = i % p.iDivisor;
    p = p.u.apSub[bin];
    if ( null == p )
    {
      return 0;
    }
  }
  if ( p.iSize <= BITVEC_NBIT )
  {
    return ( ( p.u.aBitmap[i / BITVEC_SZELEM] & ( 1 << (int)( i & ( BITVEC_SZELEM - 1 ) ) ) ) != 0 ) ? 1 : 0;
  }
  else
  {
    u32 h = BITVEC_HASH( i++ );
    while ( p.u.aHash[h] != 0 )
    {
      if ( p.u.aHash[h] == i )
        return 1;
      h = ( h + 1 ) % BITVEC_NINT;
    }
    return 0;
  }
}
Ejemplo n.º 13
0
/*
** Create a new bitmap object able to handle bits between 0 and iSize,
** inclusive.  Return a pointer to the new object.  Return NULL if
** malloc fails.
*/
static Bitvec sqlite3BitvecCreate( u32 iSize )
{
  Bitvec p;
  //Debug.Assert( sizeof(p)==BITVEC_SZ );
  p = new Bitvec();//sqlite3MallocZero( sizeof(p) );
  if ( p != null )
  {
    p.iSize = iSize;
  }
  return p;
}
Ejemplo n.º 14
0
 /*
 ** Return the value of the iSize parameter specified when Bitvec *p
 ** was created.
 */
 static u32 sqlite3BitvecSize(Bitvec p)
 {
     return(p.iSize);
 }
Ejemplo n.º 15
0
    /*
    ** Read a single page from either the journal file (if isMainJrnl==1) or
    ** from the sub-journal (if isMainJrnl==0) and playback that page.
    ** The page begins at offset *pOffset into the file. The *pOffset
    ** value is increased to the start of the next page in the journal.
    **
    ** The isMainJrnl flag is true if this is the main rollback journal and
    ** false for the statement journal.  The main rollback journal uses
    ** checksums - the statement journal does not.
    **
    ** If the page number of the page record read from the (sub-)journal file
    ** is greater than the current value of Pager.dbSize, then playback is
    ** skipped and SQLITE_OK is returned.
    **
    ** If pDone is not NULL, then it is a record of pages that have already
    ** been played back.  If the page at *pOffset has already been played back
    ** (if the corresponding pDone bit is set) then skip the playback.
    ** Make sure the pDone bit corresponding to the *pOffset page is set
    ** prior to returning.
    **
    ** If the page record is successfully read from the (sub-)journal file
    ** and played back, then SQLITE_OK is returned. If an IO error occurs
    ** while reading the record from the (sub-)journal file or while writing
    ** to the database file, then the IO error code is returned. If data
    ** is successfully read from the (sub-)journal file but appears to be
    ** corrupted, SQLITE_DONE is returned. Data is considered corrupted in
    ** two circumstances:
    **
    **   * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or
    **   * If the record is being rolled back from the main journal file
    **     and the checksum field does not match the record content.
    **
    ** Neither of these two scenarios are possible during a savepoint rollback.
    **
    ** If this is a savepoint rollback, then memory may have to be dynamically
    ** allocated by this function. If this is the case and an allocation fails,
    ** SQLITE_NOMEM is returned.
    */
    static int pager_playback_one_page(
    Pager pPager,                /* The pager being played back */
    int isMainJrnl,              /* True for main rollback journal. False for Stmt jrnl */
    int isUnsync,                /* True if reading from unsynced main journal */
    ref i64 pOffset,             /* Offset of record to playback */
    int isSavepnt,               /* True for a savepoint rollback */
    Bitvec pDone                 /* Bitvec of pages already played back */
    )
    {
      int rc;
      PgHdr pPg;                   /* An existing page in the cache */
      Pgno pgno = 0;               /* The page number of a page in journal */
      u32 cksum = 0;               /* Checksum used for sanity checking */
      byte[] aData;                /* Temporary storage for the page */
      sqlite3_file jfd;            /* The file descriptor for the journal file */

      Debug.Assert( ( isMainJrnl & ~1 ) == 0 );   /* isMainJrnl is 0 or 1 */
      Debug.Assert( ( isSavepnt & ~1 ) == 0 );    /* isSavepnt is 0 or 1 */
      Debug.Assert( isMainJrnl != 0 || pDone != null );        /* pDone always used on sub-journals */
      Debug.Assert( isSavepnt != 0 || pDone == null );    /* pDone never used on non-savepoint */

      aData = pPager.pTmpSpace;
      Debug.Assert( aData != null );         /* Temp storage must have already been allocated */

      /* Read the page number and page data from the journal or sub-journal
      ** file. Return an error code to the caller if an IO error occurs.
      */
      jfd = isMainJrnl != 0 ? pPager.jfd : pPager.sjfd;

      rc = read32bits( jfd, pOffset, ref pgno );
      if ( rc != SQLITE_OK ) return rc;
      rc = sqlite3OsRead( jfd, aData, pPager.pageSize, ( pOffset ) + 4 );
      if ( rc != SQLITE_OK ) return rc;
      pOffset += pPager.pageSize + 4 + isMainJrnl * 4;

      /* Sanity checking on the page.  This is more important that I originally
      ** thought.  If a power failure occurs while the journal is being written,
      ** it could cause invalid data to be written into the journal.  We need to
      ** detect this invalid data (with high probability) and ignore it.
      */
      if ( pgno == 0 || pgno == PAGER_MJ_PGNO( pPager ) )
      {
        Debug.Assert( 0 == isSavepnt );
        return SQLITE_DONE;
      }
      if ( pgno > pPager.dbSize || sqlite3BitvecTest( pDone, pgno ) != 0 )
      {
        return SQLITE_OK;
      }
      if ( isMainJrnl != 0 )
      {
        rc = read32bits( jfd, ( pOffset ) - 4, ref cksum );
        if ( rc != 0 ) return rc;
        if ( 0 == isSavepnt && pager_cksum( pPager, aData ) != cksum )
        {
          return SQLITE_DONE;
        }
      }

      if ( pDone != null && ( rc = sqlite3BitvecSet( pDone, pgno ) ) != SQLITE_OK )
      {
        return rc;
      }

      Debug.Assert( pPager.state == PAGER_RESERVED || pPager.state >= PAGER_EXCLUSIVE );

      /* If the pager is in RESERVED state, then there must be a copy of this
      ** page in the pager cache. In this case just update the pager cache,
      ** not the database file. The page is left marked dirty in this case.
      **
      ** An exception to the above rule: If the database is in no-sync mode
      ** and a page is moved during an incremental vacuum then the page may
      ** not be in the pager cache. Later: if a malloc() or IO error occurs
      ** during a Movepage() call, then the page may not be in the cache
      ** either. So the condition described in the above paragraph is not
      ** Debug.Assert()able.
      **
      ** If in EXCLUSIVE state, then we update the pager cache if it exists
      ** and the main file. The page is then marked not dirty.
      **
      ** Ticket #1171:  The statement journal might contain page content that is
      ** different from the page content at the start of the transaction.
      ** This occurs when a page is changed prior to the start of a statement
      ** then changed again within the statement.  When rolling back such a
      ** statement we must not write to the original database unless we know
      ** for certain that original page contents are synced into the main rollback
      ** journal.  Otherwise, a power loss might leave modified data in the
      ** database file without an entry in the rollback journal that can
      ** restore the database to its original form.  Two conditions must be
      ** met before writing to the database files. (1) the database must be
      ** locked.  (2) we know that the original page content is fully synced
      ** in the main journal either because the page is not in cache or else
      ** the page is marked as needSync==0.
      **
      ** 2008-04-14:  When attempting to vacuum a corrupt database file, it
      ** is possible to fail a statement on a database that does not yet exist.
      ** Do not attempt to write if database file has never been opened.
      */
      pPg = pager_lookup( pPager, pgno );
      Debug.Assert( pPg != null ||
#if SQLITE_OMIT_MEMORYDB
0==MEMDB
#else
 pPager.memDb == 0
#endif
 );


      PAGERTRACE( "PLAYBACK %d page %d hash(%08x) %s\n",
      PAGERID( pPager ), pgno, pager_datahash( pPager.pageSize, aData ),
      ( isMainJrnl != 0 ? "main-journal" : "sub-journal" )
      );
      if ( ( pPager.state >= PAGER_EXCLUSIVE )
      && ( pPg == null || 0 == ( pPg.flags & PGHDR_NEED_SYNC ) )
      && isOpen( pPager.fd )
      && 0 == isUnsync
      )
      {
        i64 ofst = ( pgno - 1 ) * (i64)pPager.pageSize;
        rc = sqlite3OsWrite( pPager.fd, (u8[])aData, pPager.pageSize, ofst );
        if ( pgno > pPager.dbFileSize )
        {
          pPager.dbFileSize = (u32)pgno;
        }
        if ( pPager.pBackup != null )
        {
          if ( CODEC1( pPager, aData, pgno, SQLITE_DECRYPT ) ) rc = SQLITE_NOMEM; // CODEC1( pPager, aData, pgno, 3, rc = SQLITE_NOMEM );
          sqlite3BackupUpdate( pPager.pBackup, pgno, (u8[])aData );
          if ( CODEC2( pPager, aData, pgno, SQLITE_ENCRYPT_READ_CTX,ref aData ) ) rc = SQLITE_NOMEM;//CODEC2( pPager, aData, pgno, 7, rc = SQLITE_NOMEM, aData);
        }
      }
      else if ( 0 == isMainJrnl && pPg == null )
      {
        /* If this is a rollback of a savepoint and data was not written to
        ** the database and the page is not in-memory, there is a potential
        ** problem. When the page is next fetched by the b-tree layer, it
        ** will be read from the database file, which may or may not be
        ** current.
        **
        ** There are a couple of different ways this can happen. All are quite
        ** obscure. When running in synchronous mode, this can only happen
        ** if the page is on the free-list at the start of the transaction, then
        ** populated, then moved using sqlite3PagerMovepage().
        **
        ** The solution is to add an in-memory page to the cache containing
        ** the data just read from the sub-journal. Mark the page as dirty
        ** and if the pager requires a journal-sync, then mark the page as
        ** requiring a journal-sync before it is written.
        */
        Debug.Assert( isSavepnt != 0 );
        if ( ( rc = sqlite3PagerAcquire( pPager, (u32)pgno, ref pPg, 1 ) ) != SQLITE_OK )
        {
          return rc;
        }
        pPg.flags &= ~PGHDR_NEED_READ;
        sqlite3PcacheMakeDirty( pPg );
      }
      if ( pPg != null )
      {
        /* No page should ever be explicitly rolled back that is in use, except
        ** for page 1 which is held in use in order to keep the lock on the
        ** database active. However such a page may be rolled back as a result
        ** of an internal error resulting in an automatic call to
        ** sqlite3PagerRollback().
        */
        byte[] pData = pPg.pData;
        Buffer.BlockCopy( aData, 0, pData, 0, pPager.pageSize );// memcpy(pData, (u8[])aData, pPager.pageSize);
        pPager.xReiniter( pPg );
        if ( isMainJrnl != 0 && ( 0 == isSavepnt || pOffset <= pPager.journalHdr ) )
        {
          /* If the contents of this page were just restored from the main
          ** journal file, then its content must be as they were when the
          ** transaction was first opened. In this case we can mark the page
          ** as clean, since there will be no need to write it out to the.
          **
          ** There is one exception to this rule. If the page is being rolled
          ** back as part of a savepoint (or statement) rollback from an
          ** unsynced portion of the main journal file, then it is not safe
          ** to mark the page as clean. This is because marking the page as
          ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is
          ** already in the journal file (recorded in Pager.pInJournal) and
          ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to
          ** again within this transaction, it will be marked as dirty but
          ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially
          ** be written out into the database file before its journal file
          ** segment is synced. If a crash occurs during or following this,
          ** database corruption may ensue.
          */

          sqlite3PcacheMakeClean( pPg );
        }
#if SQLITE_CHECK_PAGES
pPg.pageHash = pager_pagehash(pPg);
#endif
        /* If this was page 1, then restore the value of Pager.dbFileVers.
** Do this before any decoding. */
        if ( pgno == 1 )
        {
          Buffer.BlockCopy( pData, 24, pPager.dbFileVers, 0, pPager.dbFileVers.Length ); //memcpy(pPager.dbFileVers, ((u8*)pData)[24], sizeof(pPager.dbFileVers));
        }

        /* Decode the page just read from disk */
        if ( CODEC1( pPager, pData, pPg.pgno, SQLITE_DECRYPT ) ) rc = SQLITE_NOMEM; //CODEC1(pPager, pData, pPg.pgno, 3, rc=SQLITE_NOMEM);
        sqlite3PcacheRelease( pPg );
      }
      return rc;
    }