static void walShmBarrier(Wal wal) { if (wal.ExclusiveMode_ != MODE.HEAPMEMORY) { wal.DBFile.ShmBarrier(); } }
static RC walHashGet(Wal wal, Pid id, ht_slot[] hashOut, Pid[] idsOut, out uint zeroOut) { object ids; RC rc = walIndexPage(wal, id, ref ids); Debug.Assert(rc == RC.OK || id > 0); if (rc == RC.OK) { Pid zero; var hash = (ht_slot)ids[HASHTABLE_NPAGE]; if (id == 0) { ids = &ids[WALINDEX_HDR_SIZE / sizeof(Pid)]; zero = 0; } else { zero = (uint)(HASHTABLE_NPAGE_ONE + (id - 1) * HASHTABLE_NPAGE); } idsOut = &ids[-1]; hashOut = hash; zeroOut = zero; } return(rc); }
static void walUnlockShared(Wal wal, int lockIdx) { if (wal.ExclusiveMode_ != MODE.NORMAL) { return; } wal.DBFile.ShmLock(lockIdx, 1, VFile.SHM.UNLOCK | VFile.SHM.SHARED); WALTRACE("WAL%p: release SHARED-%s\n", wal, walLockName(lockIdx)); }
static void walUnlockExclusive(Wal wal, int lockIdx, int n) { if (wal.ExclusiveMode_ != MODE.NORMAL) { return; } wal.DBFile.ShmLock(lockIdx, n, VFile.SHM.UNLOCK | VFile.SHM.EXCLUSIVE); WALTRACE("WAL%p: release EXCLUSIVE-%s cnt=%d\n", wal, walLockName(lockIdx), n); }
static uint walFramePgno(Wal wal, uint frame) { int hash = walFramePage(frame); if (hash == 0) { return(wal.WiData[0][WALINDEX_HDR_SIZE / sizeof(uint) + frame - 1]); } return(wal.WiData[hash][(frame - 1 - HASHTABLE_NPAGE_ONE) % HASHTABLE_NPAGE]); }
static void walCleanupHash(Wal wal) { Debug.Assert(wal.WriteLock); if (wal.Header.MaxFrame == 0) { return; } // Obtain pointers to the hash-table and page-number array containing the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed // that the page said hash-table and array reside on is already mapped. Debug.Assert(wal.WiData.Length > walFramePage(wal.Header.MaxFrame)); Debug.Assert(wal.WiData[walFramePage(wal.Header.MaxFrame)] != 0); ht_slot[] hash = null; // Pointer to hash table to clear Pid[] ids = null; // Page number array for hash table int zero = 0; // frame == (aHash[x]+iZero) walHashGet(wal, walFramePage(wal.Header.MaxFrame), ref hash, ref ids, ref zero); // Zero all hash-table entries that correspond to frame numbers greater than pWal->hdr.mxFrame. int limit = wal.Header.MaxFrame - zero; // Zero values greater than this Debug.Assert(limit > 0); for (int i = 0; i < HASHTABLE_NSLOT; i++) { if (hash[i] > limit) { hash[i] = 0; } } // Zero the entries in the aPgno array that correspond to frames with frame numbers greater than pWal->hdr.mxFrame. int bytes = (int)((char *)hash - (char *)&ids[limit + 1]); // Number of bytes to zero in aPgno[] _memset((void *)&ids[limit + 1], 0, bytes); #if ENABLE_EXPENSIVE_ASSERT // Verify that the every entry in the mapping region is still reachable via the hash table even after the cleanup. int key; // Hash key if (limit) { for (int i = 1; i <= limit; i++) { for (key = walHash(ids[i]); hash[key]; key = walNextHash(key)) { if (hash[key] == i) { break; } } _assert(hash[key] == i); } } #endif }
static RC walLockExclusive(Wal wal, int lockIdx, int n) { if (wal.ExclusiveMode_ != MODE.NORMAL) { return(RC.OK); } RC rc = wal.DBFile.ShmLock(lockIdx, n, VFile.SHM.LOCK | VFile.SHM.EXCLUSIVE); WALTRACE("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", wal, walLockName(lockIdx), n, rc != 0 ? "failed" : "ok"); return(rc); }
static RC walLockShared(Wal wal, int lockIdx) { if (wal.ExclusiveMode_ != MODE.NORMAL) { return(RC.OK); } RC rc = wal.DBFile.ShmLock(lockIdx, 1, VFile.SHM.LOCK | VFile.SHM.SHARED); WALTRACE("WAL%p: acquire SHARED-%s %s\n", wal, walLockName(lockIdx), rc != 0 ? "failed" : "ok"); return(rc); }
static void walIndexWriteHdr(Wal wal) { var header = walIndexHeader(wal, 0); Debug.Assert(wal.WriteLock != 0); wal.Header.IsInit = true; wal.Header.Version = WALINDEX_MAX_VERSION; walChecksumBytes(true, wal.Header, null, wal.Header.Checksum); wal.Header = header[1].copy(); walShmBarrier(wal); wal.Header = header[0].copy(); }
static void walEncodeFrame(Wal wal, Pid id, uint truncate, byte[] data, byte[] frame) { var checksum = wal.Header.FrameChecksum; Debug.Assert(WAL_FRAME_HDRSIZE == 24); ConvertEx.Put4(frame, 0, id); ConvertEx.Put4(frame, 4, truncate); Buffer.BlockCopy(frame, 8, wal.Header.Salt, 0, 8); bool nativeChecksum = true; // (wal.Header.BigEndianChecksum == TYPE_BIGENDIAN); // True for native byte-order checksums walChecksumBytes(nativeChecksum, frame, 8, checksum, checksum); walChecksumBytes(nativeChecksum, data, (int)wal.SizePage, checksum, checksum); ConvertEx.Put4(frame, 16, checksum[0]); ConvertEx.Put4(frame, 20, checksum[1]); }
static RC walIndexPage(Wal wal, Pid id, ref object idOut) { // Enlarge the pWal->apWiData[] array if required if (wal.WiData.Length <= id) { var bytes = (int)(1 * (id + 1)); var newWiData = SysEx.Realloc <object>(1, wal.WiData, bytes); if (newWiData != null) { idOut = null; return(RC.NOMEM); } Array.Clear(newWiData, wal.WiData.Length, (int)(id + 1 - wal.WiData.Length)); wal.WiData = newWiData; } // Request a pointer to the required page from the VFS var rc = RC.OK; if (wal.WiData[id] == null) { if (wal.ExclusiveMode_ == Wal.MODE.HEAPMEMORY) { wal.WiData[id] = C._alloc <Pid>(4, WALINDEX_PGSZ, true); if (wal.WiData[id] != null) { rc = RC.NOMEM; } } else { rc = wal.DBFile.ShmMap((int)id, WALINDEX_PGSZ, (int)wal.WriteLock, wal.WiData, (int)id); if (rc == RC.READONLY) { wal.ReadOnly_ |= READONLY.SHM_RDONLY; rc = RC.OK; } } } idOut = wal.WiData[id]; Debug.Assert(id == 0 || idOut != null || rc != RC.OK); return(rc); }
static bool walDecodeFrame(Wal wal, Pid idOut, uint truncateOut, byte[] data, byte[] frame) { var checksum = wal.Header.FrameChecksum; Debug.Assert(WAL_FRAME_HDRSIZE == 24); // A frame is only valid if the salt values in the frame-header match the salt values in the wal-header. var testFrame = new byte[8]; Buffer.BlockCopy(frame, 8, testFrame, 0, 8); if (Enumerable.SequenceEqual(wal.Header.Salt, testFrame)) { return(false); } // A frame is only valid if the page number is creater than zero. Pid id = ConvertEx.Get4(frame, 0); // Page number of the frame if (id == 0) { return(false); } // A frame is only valid if a checksum of the WAL header, all prior frams, the first 16 bytes of this frame-header, // and the frame-data matches the checksum in the last 8 bytes of this frame-header. bool nativeChecksum = true; // (wal.Header.BigEndianChecksum == TYPE_BIGENDIAN); // True for native byte-order checksums walChecksumBytes(nativeChecksum, frame, 8, checksum, checksum); walChecksumBytes(nativeChecksum, data, (int)wal.SizePage, checksum, checksum); if (checksum[0] != ConvertEx.Get4(frame, 16) || checksum[1] != ConvertEx.Get4(frame, 20)) // Checksum failed. { return(false); } // If we reach this point, the frame is valid. Return the page number and the new database size. idOut = id; truncateOut = ConvertEx.Get4(frame, 4); return(true); }
static void walShmBarrier(Wal wal) { if (wal.ExclusiveMode_ != MODE.HEAPMEMORY) wal.DBFile.ShmBarrier(); }
static RC walLockShared(Wal wal, int lockIdx) { if (wal.ExclusiveMode_ != MODE.NORMAL) return RC.OK; RC rc = wal.DBFile.ShmLock(lockIdx, 1, VFile.SHM.LOCK | VFile.SHM.SHARED); WALTRACE("WAL%p: acquire SHARED-%s %s\n", wal, walLockName(lockIdx), rc != 0 ? "failed" : "ok"); return rc; }
static RC walLockExclusive(Wal wal, int lockIdx, int n) { if (wal.ExclusiveMode_ != MODE.NORMAL) return RC.OK; RC rc = wal.DBFile.ShmLock(lockIdx, n, VFile.SHM.LOCK | VFile.SHM.EXCLUSIVE); WALTRACE("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", wal, walLockName(lockIdx), n, rc != 0 ? "failed" : "ok"); return rc; }
static RC walIndexPage(Wal wal, Pid id, ref object idOut) { // Enlarge the pWal->apWiData[] array if required if (wal.WiData.Length <= id) { var bytes = (int)(1 * (id + 1)); var newWiData = SysEx.Realloc<object>(1, wal.WiData, bytes); if (newWiData != null) { idOut = null; return RC.NOMEM; } Array.Clear(newWiData, wal.WiData.Length, (int)(id + 1 - wal.WiData.Length)); wal.WiData = newWiData; } // Request a pointer to the required page from the VFS var rc = RC.OK; if (wal.WiData[id] == null) { if (wal.ExclusiveMode_ == Wal.MODE.HEAPMEMORY) { wal.WiData[id] = SysEx.Alloc<Pid>(4, WALINDEX_PGSZ, true); if (wal.WiData[id] != null) rc = RC.NOMEM; } else { rc = wal.DBFile.ShmMap((int)id, WALINDEX_PGSZ, (int)wal.WriteLock, wal.WiData, (int)id); if (rc == RC.READONLY) { wal.ReadOnly_ |= READONLY.SHM_RDONLY; rc = RC.OK; } } } idOut = wal.WiData[id]; Debug.Assert(id == 0 || idOut != null || rc != RC.OK); return rc; }
static WalIndexHeader[] walIndexHeader(Wal wal, int idx) { Debug.Assert(wal.WiData.Length > 0 && wal.WiData[0] != null); return (WalIndexHeader[])wal.WiData[0]; }
static RC walHashGet(Wal wal, Pid id, ht_slot[] hashOut, Pid[] idsOut, out uint zeroOut) { object ids; RC rc = walIndexPage(wal, id, ref ids); Debug.Assert(rc == RC.OK || id > 0); if (rc == RC.OK) { Pid zero; var hash = (ht_slot)ids[HASHTABLE_NPAGE]; if (id == 0) { ids = &ids[WALINDEX_HDR_SIZE / sizeof(Pid)]; zero = 0; } else zero = (uint)(HASHTABLE_NPAGE_ONE + (id - 1) * HASHTABLE_NPAGE); idsOut = &ids[-1]; hashOut = hash; zeroOut = zero; } return rc; }
static uint walFramePgno(Wal wal, uint frame) { int hash = walFramePage(frame); if (hash == 0) return wal.WiData[0][WALINDEX_HDR_SIZE / sizeof(uint) + frame - 1]; return wal.WiData[hash][(frame - 1 - HASHTABLE_NPAGE_ONE) % HASHTABLE_NPAGE]; }
static bool walDecodeFrame(Wal wal, Pid idOut, uint truncateOut, byte[] data, byte[] frame) { var checksum = wal.Header.FrameChecksum; Debug.Assert(WAL_FRAME_HDRSIZE == 24); // A frame is only valid if the salt values in the frame-header match the salt values in the wal-header. var testFrame = new byte[8]; Buffer.BlockCopy(frame, 8, testFrame, 0, 8); if (Enumerable.SequenceEqual(wal.Header.Salt, testFrame)) return false; // A frame is only valid if the page number is creater than zero. Pid id = ConvertEx.Get4(frame, 0); // Page number of the frame if (id == 0) return false; // A frame is only valid if a checksum of the WAL header, all prior frams, the first 16 bytes of this frame-header, // and the frame-data matches the checksum in the last 8 bytes of this frame-header. bool nativeChecksum = true; // (wal.Header.BigEndianChecksum == TYPE_BIGENDIAN); // True for native byte-order checksums walChecksumBytes(nativeChecksum, frame, 8, checksum, checksum); walChecksumBytes(nativeChecksum, data, (int)wal.SizePage, checksum, checksum); if (checksum[0] != ConvertEx.Get4(frame, 16) || checksum[1] != ConvertEx.Get4(frame, 20)) // Checksum failed. return false; // If we reach this point, the frame is valid. Return the page number and the new database size. idOut = id; truncateOut = ConvertEx.Get4(frame, 4); return true; }
static void walCleanupHash(Wal wal) { Debug.Assert(wal.WriteLock); if (wal.Header.MaxFrame == 0) return; // Obtain pointers to the hash-table and page-number array containing the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed // that the page said hash-table and array reside on is already mapped. Debug.Assert(wal.WiData.Length > walFramePage(wal.Header.MaxFrame)); Debug.Assert(wal.WiData[walFramePage(wal.Header.MaxFrame)] != 0); ht_slot[] hash = null; // Pointer to hash table to clear Pid[] ids = null; // Page number array for hash table int zero = 0; // frame == (aHash[x]+iZero) walHashGet(wal, walFramePage(wal.Header.MaxFrame), ref hash, ref ids, ref zero); // Zero all hash-table entries that correspond to frame numbers greater than pWal->hdr.mxFrame. int limit = wal.Header.MaxFrame - zero; // Zero values greater than this Debug.Assert(limit > 0); for (int i = 0; i < HASHTABLE_NSLOT; i++) if (hash[i] > limit) hash[i] = 0; // Zero the entries in the aPgno array that correspond to frames with frame numbers greater than pWal->hdr.mxFrame. int bytes = (int)((char*)hash - (char*)&ids[limit + 1]); // Number of bytes to zero in aPgno[] _memset((void*)&ids[limit + 1], 0, bytes); #if ENABLE_EXPENSIVE_ASSERT // Verify that the every entry in the mapping region is still reachable via the hash table even after the cleanup. int key; // Hash key if (limit) for (int i = 1; i <= limit; i++) { for (key = walHash(ids[i]); hash[key]; key = walNextHash(key)) if (hash[key] == i) break; _assert(hash[key] == i); } #endif }
static WalCheckpointInfo walCkptInfo(Wal wal, int idx) { Debug.Assert(wal.WiData.Length > 0 && wal.WiData[0] != null); return((WalCheckpointInfo)wal.WiData[0]); }
static void walUnlockExclusive(Wal wal, int lockIdx, int n) { if (wal.ExclusiveMode_ != MODE.NORMAL) return; wal.DBFile.ShmLock(lockIdx, n, VFile.SHM.UNLOCK | VFile.SHM.EXCLUSIVE); WALTRACE("WAL%p: release EXCLUSIVE-%s cnt=%d\n", wal, walLockName(lockIdx), n); }
static WalIndexHeader[] walIndexHeader(Wal wal, int idx) { Debug.Assert(wal.WiData.Length > 0 && wal.WiData[0] != null); return((WalIndexHeader[])wal.WiData[0]); }
static void walUnlockShared(Wal wal, int lockIdx) { if (wal.ExclusiveMode_ != MODE.NORMAL) return; wal.DBFile.ShmLock(lockIdx, 1, VFile.SHM.UNLOCK | VFile.SHM.SHARED); WALTRACE("WAL%p: release SHARED-%s\n", wal, walLockName(lockIdx)); }
static WalCheckpointInfo walCkptInfo(Wal wal, int idx) { Debug.Assert(wal.WiData.Length > 0 && wal.WiData[0] != null); return (WalCheckpointInfo)wal.WiData[0]; }