2007-06-07 21:53:09 +00:00
|
|
|
#include "config.h"
|
2008-04-13 04:02:57 +00:00
|
|
|
#include <stasis/page.h>
|
|
|
|
#include <stasis/page/slotted.h>
|
2004-07-30 01:28:39 +00:00
|
|
|
#include <assert.h>
|
2007-06-07 21:53:09 +00:00
|
|
|
/** @todo should page implementations provide readLSN / writeLSN??? */
|
2007-06-11 21:36:57 +00:00
|
|
|
#include <stasis/truncation.h>
|
2008-12-02 19:38:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
Run sanity checks to make sure page is in a consistent state.
|
|
|
|
|
|
|
|
If SLOTTED_PAGE_SANITY_CHECKS and SLOTTED_PAGE_CHECK_FOR_OVERLAP
|
|
|
|
are defined at compile time then this method will be more thorough
|
|
|
|
and more expensive.
|
|
|
|
*/
|
2007-06-07 21:53:09 +00:00
|
|
|
static inline void slottedFsck(const Page const * page) {
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2007-06-01 21:32:33 +00:00
|
|
|
assertlocked(page->rwlatch);
|
2007-06-07 21:53:09 +00:00
|
|
|
|
2006-07-22 00:01:42 +00:00
|
|
|
Page dummy;
|
|
|
|
|
|
|
|
dummy.id = -1;
|
|
|
|
dummy.memAddr = 0;
|
|
|
|
|
2007-10-02 00:18:33 +00:00
|
|
|
const short page_type = *stasis_page_type_cptr(page);
|
|
|
|
const short numslots = *numslots_cptr(page);
|
|
|
|
const short freespace = *freespace_cptr(page);
|
|
|
|
const short freelist = *freelist_cptr(page);
|
2006-07-22 00:01:42 +00:00
|
|
|
|
|
|
|
const long slotListStart = (long)slot_length_ptr(&dummy, numslots-1);
|
|
|
|
assert(slotListStart < PAGE_SIZE && slotListStart >= 0);
|
2007-06-07 21:53:09 +00:00
|
|
|
assert(page_type == SLOTTED_PAGE ||
|
2009-03-15 07:21:37 +00:00
|
|
|
page_type == BOUNDARY_TAG_PAGE ||
|
|
|
|
page_type == SLOTTED_LSN_FREE_PAGE);
|
2006-07-22 00:01:42 +00:00
|
|
|
assert(numslots >= 0);
|
|
|
|
assert(numslots * SLOTTED_PAGE_OVERHEAD_PER_RECORD < PAGE_SIZE);
|
|
|
|
assert(freespace >= 0);
|
|
|
|
assert(freespace <= slotListStart);
|
|
|
|
assert(freelist >= INVALID_SLOT);
|
|
|
|
assert(freelist < numslots);
|
2007-06-07 21:53:09 +00:00
|
|
|
|
|
|
|
#ifdef SLOTTED_PAGE_SANITY_CHECKS
|
|
|
|
|
2007-01-22 20:55:25 +00:00
|
|
|
// Check integrity of freelist. All free slots less than
|
|
|
|
// numslots should be on it, in order.
|
2006-07-22 00:01:42 +00:00
|
|
|
|
|
|
|
short * slot_offsets = alloca(numslots * sizeof(short));
|
|
|
|
short * slot_lengths = alloca(numslots * sizeof(short));
|
|
|
|
for(int i = 0; i < numslots; i++) {
|
|
|
|
slot_offsets[i] = *slot_ptr(page, i);
|
|
|
|
slot_lengths[i] = *slot_length_ptr(page, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
short foundEndOfList = 0;
|
|
|
|
|
|
|
|
if(freelist != INVALID_SLOT) {
|
|
|
|
assert(slot_offsets[freelist] == INVALID_SLOT);
|
|
|
|
} else {
|
|
|
|
foundEndOfList = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(short i = 0; i < numslots; i++) {
|
|
|
|
const short slot_length = slot_lengths[i];
|
|
|
|
const short slot_offset = slot_offsets[i];
|
|
|
|
if(slot_offset == INVALID_SLOT) {
|
|
|
|
if(slot_length == INVALID_SLOT) {
|
|
|
|
assert(!foundEndOfList);
|
|
|
|
foundEndOfList = 1;
|
|
|
|
} else {
|
|
|
|
assert (slot_offsets[slot_length] == INVALID_SLOT);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assert(slot_offset + slot_length <= freespace);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is the free list terminated?
|
|
|
|
assert(foundEndOfList);
|
|
|
|
|
|
|
|
#ifdef SLOTTED_PAGE_CHECK_FOR_OVERLAP
|
|
|
|
|
|
|
|
const byte UNUSED = 0xFF;
|
|
|
|
const byte PAGE_HEADER = 0xFE;
|
|
|
|
const byte SLOTTED_HEADER = 0xFD;
|
|
|
|
// const byte SLOT_LIST = 0xFC;
|
|
|
|
const byte FREE_SPACE = 0xFB;
|
|
|
|
|
|
|
|
const unsigned short S_SLOT_LIST = 0xFCFC;
|
|
|
|
|
|
|
|
byte image[PAGE_SIZE];
|
|
|
|
for(short i = 0; i < PAGE_SIZE; i++) {
|
|
|
|
image[i] = UNUSED;
|
|
|
|
}
|
|
|
|
for(short i = USABLE_SIZE_OF_PAGE; i < PAGE_SIZE; i++) {
|
|
|
|
image[i] = PAGE_HEADER;
|
|
|
|
}
|
|
|
|
for(short i = USABLE_SIZE_OF_PAGE - SLOTTED_PAGE_HEADER_OVERHEAD; i < USABLE_SIZE_OF_PAGE; i++) {
|
|
|
|
image[i] = SLOTTED_HEADER;
|
|
|
|
}
|
|
|
|
for(short i = *freespace_ptr(page); i < slotListStart; i++) {
|
|
|
|
image[i] = FREE_SPACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
dummy.memAddr = image;
|
|
|
|
|
|
|
|
for(short i = 0; i < *numslots_ptr(page); i++) {
|
|
|
|
*slot_ptr(&dummy, i) = S_SLOT_LIST;
|
|
|
|
*slot_length_ptr(&dummy, i) = S_SLOT_LIST;
|
|
|
|
}
|
|
|
|
for(short i = 0; i < *numslots_ptr(page); i++) {
|
|
|
|
short slot_offset = *slot_ptr(page, i);
|
|
|
|
if(slot_offset != INVALID_SLOT) {
|
|
|
|
const unsigned char ci = i % 0xFF;
|
2007-10-03 01:53:51 +00:00
|
|
|
short slot_len = stasis_record_type_to_size(*slot_length_ptr(page, i));
|
2006-07-22 00:01:42 +00:00
|
|
|
|
|
|
|
for(short j = 0; j < slot_len; j++) {
|
|
|
|
assert(image[slot_offset + j] == 0xFF);
|
|
|
|
image[slot_offset + j] = ci;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // SLOTTED_PAGE_CHECK_FOR_OVERLAP
|
2006-08-10 23:59:13 +00:00
|
|
|
#endif // SLOTTED_PAGE_SANITY_CHECKS
|
2006-07-22 00:01:42 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2004-07-30 01:28:39 +00:00
|
|
|
/**
|
2008-12-02 19:38:49 +00:00
|
|
|
|
|
|
|
Move all of the records to the beginning of the page in order to
|
2004-07-30 01:28:39 +00:00
|
|
|
increase the available free space.
|
|
|
|
|
|
|
|
The caller of this function must have a writelock on the page.
|
|
|
|
*/
|
2005-01-29 01:17:37 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static void slottedCompact(Page * page) {
|
2007-06-01 21:32:33 +00:00
|
|
|
assertlocked(page->rwlatch);
|
2006-07-22 00:01:42 +00:00
|
|
|
Page bufPage;
|
|
|
|
byte buffer[PAGE_SIZE];
|
|
|
|
bufPage.memAddr = buffer;
|
|
|
|
|
|
|
|
// Copy external headers into bufPage.
|
|
|
|
|
|
|
|
memcpy(&buffer[USABLE_SIZE_OF_PAGE], &(page->memAddr[USABLE_SIZE_OF_PAGE]), PAGE_SIZE - USABLE_SIZE_OF_PAGE);
|
|
|
|
|
|
|
|
// Now, build new slotted page in the bufPage struct.
|
|
|
|
|
|
|
|
*freespace_ptr(&bufPage) = 0;
|
|
|
|
// numslots_ptr will be set later.
|
|
|
|
*freelist_ptr(&bufPage) = INVALID_SLOT;
|
|
|
|
|
|
|
|
const short numSlots = *numslots_ptr(page);
|
|
|
|
short lastFreeSlot = INVALID_SLOT;
|
|
|
|
short lastFreeSlotBeforeUsedSlot = INVALID_SLOT;
|
|
|
|
short lastUsedSlot = -1;
|
|
|
|
|
|
|
|
// Rebuild free list.
|
|
|
|
|
|
|
|
for(short i = 0; i < numSlots; i++) {
|
|
|
|
if(*slot_ptr(page, i) == INVALID_SLOT) {
|
|
|
|
if(lastFreeSlot == INVALID_SLOT) {
|
|
|
|
*freelist_ptr(&bufPage) = i;
|
|
|
|
} else {
|
|
|
|
*slot_length_ptr(&bufPage, lastFreeSlot) = i;
|
|
|
|
}
|
|
|
|
*slot_ptr(&bufPage, i) = INVALID_SLOT;
|
|
|
|
lastFreeSlot = i;
|
|
|
|
} else {
|
|
|
|
lastUsedSlot = i;
|
|
|
|
lastFreeSlotBeforeUsedSlot = lastFreeSlot;
|
|
|
|
|
|
|
|
short logicalSize = *slot_length_ptr(page, i);
|
2007-10-03 01:53:51 +00:00
|
|
|
short physicalSize = stasis_record_type_to_size(logicalSize);
|
2006-07-22 00:01:42 +00:00
|
|
|
|
|
|
|
memcpy(&(buffer[*freespace_ptr(&bufPage)]), record_ptr(page, i), physicalSize);
|
|
|
|
|
|
|
|
*slot_ptr(&bufPage, i) = *freespace_ptr(&bufPage);
|
|
|
|
*slot_length_ptr(&bufPage, i) = logicalSize;
|
|
|
|
|
|
|
|
(*freespace_ptr(&bufPage)) += physicalSize;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Truncate linked list, and update numslots_ptr.
|
|
|
|
*slot_length_ptr(&bufPage, lastFreeSlotBeforeUsedSlot) = INVALID_SLOT;
|
|
|
|
*numslots_ptr(&bufPage) = lastUsedSlot+1;
|
|
|
|
|
|
|
|
memcpy(page->memAddr, buffer, PAGE_SIZE);
|
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
slottedFsck(page);
|
2006-07-22 00:01:42 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2004-08-17 01:46:17 +00:00
|
|
|
void slottedPageInit() {
|
2006-07-22 00:01:42 +00:00
|
|
|
#ifdef SLOTTED_PAGE_CHECK_FOR_OVERLAP
|
|
|
|
printf("slotted.c: Using expensive page sanity checking.\n");
|
|
|
|
#endif
|
2004-08-17 01:46:17 +00:00
|
|
|
}
|
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
void slottedPageDeinit() {
|
2004-08-17 01:46:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-02 00:18:33 +00:00
|
|
|
void stasis_slotted_initialize_page(Page * page) {
|
2007-06-07 21:53:09 +00:00
|
|
|
assertlocked(page->rwlatch);
|
2007-11-12 07:27:49 +00:00
|
|
|
stasis_page_cleanup(page);
|
2007-10-02 00:18:33 +00:00
|
|
|
*stasis_page_type_ptr(page) = SLOTTED_PAGE;
|
2004-07-30 01:28:39 +00:00
|
|
|
*freespace_ptr(page) = 0;
|
|
|
|
*numslots_ptr(page) = 0;
|
|
|
|
*freelist_ptr(page) = INVALID_SLOT;
|
|
|
|
}
|
2006-04-05 00:38:11 +00:00
|
|
|
|
2008-12-02 19:38:49 +00:00
|
|
|
/**
|
|
|
|
Check to see how many bytes can fit in a given slot. This
|
|
|
|
makes it possible for callers to guarantee the safety
|
|
|
|
of a subsequent call to really_do_ralloc().
|
2006-04-05 00:38:11 +00:00
|
|
|
*/
|
2007-06-07 21:53:09 +00:00
|
|
|
static size_t slottedFreespaceForSlot(Page * page, int slot) {
|
2007-06-01 21:32:33 +00:00
|
|
|
assertlocked(page->rwlatch);
|
2006-04-05 00:38:11 +00:00
|
|
|
size_t slotOverhead;
|
|
|
|
|
2006-08-04 23:45:27 +00:00
|
|
|
if(slot == INVALID_SLOT) {
|
2006-07-21 01:07:09 +00:00
|
|
|
slotOverhead = (*freelist_ptr(page) == INVALID_SLOT) ? SLOTTED_PAGE_OVERHEAD_PER_RECORD : 0;
|
|
|
|
} else if(slot < *numslots_ptr(page)) {
|
2006-04-05 00:38:11 +00:00
|
|
|
slotOverhead = 0;
|
|
|
|
} else {
|
2006-07-21 01:07:09 +00:00
|
|
|
// slotOverhead = SLOTTED_PAGE_OVERHEAD_PER_RECORD * (*numslots_ptr(page) - slot);
|
|
|
|
slotOverhead = SLOTTED_PAGE_OVERHEAD_PER_RECORD * ((slot+1) - *numslots_ptr(page));
|
2006-04-05 00:38:11 +00:00
|
|
|
}
|
|
|
|
// end_of_free_space points to the beginning of the slot header at the bottom of the page header.
|
|
|
|
byte* end_of_free_space = (byte*)slot_length_ptr(page, (*numslots_ptr(page))-1);
|
2006-04-05 02:52:40 +00:00
|
|
|
|
2006-04-05 00:38:11 +00:00
|
|
|
// start_of_free_space points to the first unallocated byte in the page
|
|
|
|
// (ignoring space that could be reclaimed by compaction)
|
|
|
|
byte* start_of_free_space = (byte*)(page->memAddr + *freespace_ptr(page));
|
2006-04-05 02:52:40 +00:00
|
|
|
|
2006-04-05 00:38:11 +00:00
|
|
|
assert(end_of_free_space >= start_of_free_space);
|
|
|
|
|
|
|
|
if(end_of_free_space < start_of_free_space + slotOverhead) {
|
2006-04-05 02:52:40 +00:00
|
|
|
// The regions would overlap after allocation. There is no free space.
|
2006-04-05 00:38:11 +00:00
|
|
|
return 0;
|
|
|
|
} else {
|
2006-04-05 02:52:40 +00:00
|
|
|
// The regions would not overlap. There might be free space.
|
2006-04-05 00:38:11 +00:00
|
|
|
return (size_t) (end_of_free_space - start_of_free_space - slotOverhead);
|
|
|
|
}
|
|
|
|
}
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2008-12-02 19:38:49 +00:00
|
|
|
/**
|
|
|
|
Allocate data on a page after deciding which recordid to allocate,
|
|
|
|
and making sure there is enough freespace.
|
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
Allocation is complicated without locking. Consider this situation:
|
2006-04-05 02:52:40 +00:00
|
|
|
|
|
|
|
(1) *numslot_ptr(page) is 10
|
2006-06-13 22:45:30 +00:00
|
|
|
(2) An aborting transcation calls really_do_ralloc(page) with rid.slot = 12
|
2006-04-05 02:52:40 +00:00
|
|
|
(3) *numslot_ptr(page) must be incremented to 12. Now, what happens to 11?
|
|
|
|
- If 11 was also deleted by a transaction that could abort, we should lock it so that it won't be reused.
|
|
|
|
(4) This function adds it to the freelist to avoid leaking space. (Therefore, Talloc() can return recordids that will
|
|
|
|
be reused by aborting transactions...)
|
2006-06-20 00:03:38 +00:00
|
|
|
|
2007-06-01 21:06:18 +00:00
|
|
|
For now, we make sure that we don't alloc off a page that another active
|
|
|
|
transaction dealloced from.
|
|
|
|
|
2008-04-17 06:29:34 +00:00
|
|
|
@param page A pointer to the page.
|
2006-06-20 00:03:38 +00:00
|
|
|
@param rid Recordid with 'internal' size. The size should have already been translated to a type if necessary.
|
2006-04-05 02:52:40 +00:00
|
|
|
*/
|
2006-06-13 22:45:30 +00:00
|
|
|
static void really_do_ralloc(Page * page, recordid rid) {
|
2007-06-01 21:32:33 +00:00
|
|
|
assertlocked(page->rwlatch);
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2006-03-28 20:15:31 +00:00
|
|
|
short freeSpace;
|
2005-01-31 01:29:52 +00:00
|
|
|
|
2006-04-05 02:52:40 +00:00
|
|
|
// Compact the page if we don't have enough room.
|
2007-10-03 01:53:51 +00:00
|
|
|
if(slottedFreespaceForSlot(page, rid.slot) < stasis_record_type_to_size(rid.size)) {
|
2004-08-17 01:46:17 +00:00
|
|
|
slottedCompact(page);
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2006-04-05 02:52:40 +00:00
|
|
|
// Make sure we have enough enough free space for the new record
|
2007-10-03 01:53:51 +00:00
|
|
|
assert (slottedFreespaceForSlot(page, rid.slot) >= stasis_record_type_to_size(rid.size));
|
2004-07-30 01:28:39 +00:00
|
|
|
}
|
2006-03-20 23:11:46 +00:00
|
|
|
|
2006-04-05 00:38:11 +00:00
|
|
|
freeSpace = *freespace_ptr(page);
|
2006-04-05 02:52:40 +00:00
|
|
|
|
|
|
|
// Remove this entry from the freelist (if necessary) slottedCompact
|
|
|
|
// assumes that this does not change the order of items in the list.
|
|
|
|
// If it did, then slottedCompact could leaks slot id's (or worse!)
|
|
|
|
if(rid.slot < *numslots_ptr(page) && *slot_ptr(page,rid.slot) == INVALID_SLOT) {
|
|
|
|
short next = *freelist_ptr(page);
|
|
|
|
short last = INVALID_SLOT;
|
|
|
|
// special case: is the slot physically before us the predecessor?
|
|
|
|
if(rid.slot > 0) {
|
|
|
|
if(*slot_length_ptr(page, rid.slot-1) == rid.slot && *slot_ptr(page, rid.slot-1) == INVALID_SLOT) {
|
|
|
|
next = rid.slot;
|
|
|
|
last = rid.slot-1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while(next != INVALID_SLOT && next != rid.slot) {
|
|
|
|
last = next;
|
2006-07-22 00:01:42 +00:00
|
|
|
assert(next < *numslots_ptr(page));
|
2006-07-21 01:07:09 +00:00
|
|
|
short next_slot_ptr = *slot_ptr(page, next);
|
|
|
|
assert(next_slot_ptr == INVALID_SLOT);
|
2006-04-05 02:52:40 +00:00
|
|
|
next = *slot_length_ptr(page, next);
|
|
|
|
}
|
|
|
|
if(next == rid.slot) {
|
|
|
|
if(last == INVALID_SLOT) {
|
|
|
|
*freelist_ptr(page) = *slot_length_ptr(page, rid.slot);
|
|
|
|
} else {
|
|
|
|
*slot_length_ptr(page, last) = *slot_length_ptr(page, rid.slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert any slots that come between the previous numslots_ptr()
|
|
|
|
// and the slot we're allocating onto the freelist. In order to
|
|
|
|
// promote the reuse of free slot numbers, we go out of our way to make sure
|
|
|
|
// that we put them in the list in increasing order. (Note: slottedCompact's
|
|
|
|
// correctness depends on this behavior!)
|
2006-07-21 01:07:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
if(rid.slot > *numslots_ptr(page)) {
|
|
|
|
short lastSlot;
|
|
|
|
short numSlots = *numslots_ptr(page);
|
|
|
|
if(*freelist_ptr(page) == INVALID_SLOT) {
|
|
|
|
|
|
|
|
*freelist_ptr(page) = numSlots;
|
|
|
|
lastSlot = numSlots;
|
|
|
|
|
|
|
|
*slot_ptr(page, lastSlot) = INVALID_SLOT;
|
|
|
|
// will set slot_length_ptr on next iteration.
|
|
|
|
|
|
|
|
|
|
|
|
(*numslots_ptr(page))++;
|
|
|
|
} else {
|
|
|
|
lastSlot = INVALID_SLOT;
|
|
|
|
short next = *freelist_ptr(page);
|
|
|
|
while(next != INVALID_SLOT) {
|
|
|
|
lastSlot = next;
|
|
|
|
next = *slot_length_ptr(page, lastSlot);
|
|
|
|
assert(lastSlot < *numslots_ptr(page));
|
|
|
|
assert(*slot_ptr(page, lastSlot) == INVALID_SLOT);
|
|
|
|
}
|
|
|
|
*slot_ptr(page, lastSlot) = INVALID_SLOT;
|
|
|
|
|
2006-04-05 02:52:40 +00:00
|
|
|
}
|
|
|
|
|
2006-07-21 01:07:09 +00:00
|
|
|
// lastSlot now contains the tail of the free list. We can start adding slots to the list starting at *numslots_ptr.
|
|
|
|
|
|
|
|
while(*numslots_ptr(page) < rid.slot) {
|
|
|
|
*slot_length_ptr(page, lastSlot) = *numslots_ptr(page);
|
|
|
|
lastSlot = *numslots_ptr(page);
|
|
|
|
*slot_ptr(page, lastSlot) = INVALID_SLOT;
|
|
|
|
(*numslots_ptr(page))++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Terminate the end of the list.
|
2006-07-22 00:01:42 +00:00
|
|
|
assert(lastSlot < *numslots_ptr(page));
|
2006-07-21 01:07:09 +00:00
|
|
|
*slot_length_ptr(page, lastSlot) = INVALID_SLOT;
|
|
|
|
|
2006-04-05 02:52:40 +00:00
|
|
|
}
|
2006-07-21 01:07:09 +00:00
|
|
|
|
2006-04-05 02:52:40 +00:00
|
|
|
if(*numslots_ptr(page) == rid.slot) {
|
2006-07-21 01:07:09 +00:00
|
|
|
*numslots_ptr(page) = rid.slot+1;
|
|
|
|
}
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2006-07-22 00:01:42 +00:00
|
|
|
assert(*numslots_ptr(page) > rid.slot);
|
|
|
|
|
2004-07-30 01:28:39 +00:00
|
|
|
DEBUG("Num slots %d\trid.slot %d\n", *numslots_ptr(page), rid.slot);
|
2006-04-05 02:52:40 +00:00
|
|
|
|
|
|
|
// Reserve space for this record and record the space's offset in
|
|
|
|
// the slot header.
|
2006-07-22 00:01:42 +00:00
|
|
|
|
|
|
|
assert(rid.slot < *numslots_ptr(page));
|
2007-10-03 01:53:51 +00:00
|
|
|
*freespace_ptr(page) = freeSpace + stasis_record_type_to_size(rid.size);
|
2004-07-30 01:28:39 +00:00
|
|
|
*slot_ptr(page, rid.slot) = freeSpace;
|
2006-03-20 23:11:46 +00:00
|
|
|
|
2006-06-17 00:25:09 +00:00
|
|
|
*slot_length_ptr(page, rid.slot) = rid.size;
|
2006-07-22 00:01:42 +00:00
|
|
|
|
2004-07-30 01:28:39 +00:00
|
|
|
}
|
2006-07-22 00:01:42 +00:00
|
|
|
|
2008-12-02 19:38:49 +00:00
|
|
|
// --------------------------------------------------------------------------
|
2007-06-07 21:53:09 +00:00
|
|
|
// PUBLIC API IS BELOW THIS LINE
|
2008-12-02 19:38:49 +00:00
|
|
|
// --------------------------------------------------------------------------
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static inline void sanityCheck(Page * p, recordid rid) {
|
|
|
|
assert(p->id == rid.page);
|
|
|
|
assert(rid.size < BLOB_THRESHOLD_SIZE); // Caller deals with this now!
|
|
|
|
slottedFsck(p);
|
|
|
|
}
|
2004-08-21 00:03:30 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static const byte* slottedRead (int xid, Page *p, recordid rid) {
|
|
|
|
sanityCheck(p, rid);
|
2004-08-21 00:03:30 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
return record_ptr(p, rid.slot);
|
|
|
|
}
|
2004-08-21 00:03:30 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static byte* slottedWrite(int xid, Page *p, recordid rid) {
|
|
|
|
sanityCheck(p, rid);
|
2006-07-25 01:05:02 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
return record_ptr(p, rid.slot);
|
|
|
|
}
|
|
|
|
static int slottedGetType(int xid, Page *p, recordid rid) {
|
|
|
|
//sanityCheck(p, rid); <-- Would fail if rid.size is a blob
|
|
|
|
assert(p->id == rid.page);
|
|
|
|
slottedFsck(p);
|
|
|
|
if(rid.slot >= *numslots_ptr(p)) { return INVALID_SLOT; }
|
|
|
|
if(*slot_ptr(p, rid.slot) == INVALID_SLOT) { return INVALID_SLOT; }
|
|
|
|
int ret = *slot_length_ptr(p, rid.slot);
|
|
|
|
return ret >= 0 ? NORMAL_SLOT : ret;
|
|
|
|
}
|
|
|
|
static void slottedSetType(int xid, Page *p, recordid rid, int type) {
|
|
|
|
sanityCheck(p, rid);
|
|
|
|
|
|
|
|
int old_type = *slot_length_ptr(p, rid.slot);
|
|
|
|
assert(rid.slot < *numslots_ptr(p));
|
|
|
|
assert(old_type != INVALID_SLOT);
|
|
|
|
|
|
|
|
if(type == NORMAL_SLOT) {
|
|
|
|
// set slot_length_ptr to the physical length.
|
2007-10-03 01:53:51 +00:00
|
|
|
*slot_length_ptr(p, rid.slot) = stasis_record_type_to_size(old_type);
|
2007-06-07 21:53:09 +00:00
|
|
|
} else {
|
|
|
|
// Changing to a special slot type; make sure doing so doesn't change
|
|
|
|
// the record size.
|
2007-10-03 01:53:51 +00:00
|
|
|
assert(stasis_record_type_to_size(type) == stasis_record_type_to_size(old_type));
|
2007-06-07 21:53:09 +00:00
|
|
|
*slot_length_ptr(p, rid.slot) = type;
|
|
|
|
}
|
|
|
|
}
|
2004-08-21 00:03:30 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static int slottedGetLength(int xid, Page *p, recordid rid) {
|
|
|
|
assert(p->id == rid.page);
|
|
|
|
slottedFsck(p);
|
2007-08-20 16:04:44 +00:00
|
|
|
if( slottedGetType(xid, p, rid) == INVALID_SLOT)
|
2007-06-07 21:53:09 +00:00
|
|
|
return INVALID_SLOT;
|
|
|
|
else
|
2007-10-03 01:53:51 +00:00
|
|
|
return stasis_record_type_to_size(*slot_length_ptr(p, rid.slot));
|
2007-06-07 21:53:09 +00:00
|
|
|
}
|
2004-08-21 00:03:30 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static recordid slottedNext(int xid, Page *p, recordid rid) {
|
|
|
|
sanityCheck(p, rid);
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
short n = *numslots_ptr(p);
|
|
|
|
rid.slot ++;
|
2007-08-20 16:04:44 +00:00
|
|
|
while(rid.slot < n && slottedGetType(xid,p,rid)==INVALID_SLOT) {
|
|
|
|
rid.slot++;
|
|
|
|
}
|
|
|
|
if(rid.slot != n) {
|
|
|
|
rid.size = *slot_length_ptr(p, rid.slot);
|
2007-07-18 20:09:14 +00:00
|
|
|
return rid;
|
|
|
|
} else {
|
|
|
|
return NULLRID;
|
|
|
|
}
|
2007-06-07 21:53:09 +00:00
|
|
|
}
|
2006-06-20 21:40:21 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static recordid slottedFirst(int xid, Page *p) {
|
|
|
|
slottedFsck(p);
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
recordid rid = { p->id, -1, 0 };
|
|
|
|
return slottedNext(xid, p, rid);
|
|
|
|
}
|
2005-02-22 03:10:54 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static int notSupported(int xid, Page * p) { return 0; }
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static int slottedFreespace(int xid, Page * p) {
|
|
|
|
slottedFsck(p);
|
2006-07-21 01:07:09 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
return slottedFreespaceForSlot(p, INVALID_SLOT);
|
2004-07-30 01:28:39 +00:00
|
|
|
}
|
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static recordid slottedPreRalloc(int xid, Page * p, int type) {
|
|
|
|
assert(type != INVALID_SLOT);
|
|
|
|
slottedFsck(p);
|
2006-07-22 00:01:42 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
recordid rid;
|
|
|
|
rid.page = p->id;
|
|
|
|
rid.slot = *numslots_ptr(p);
|
|
|
|
rid.size = type;
|
2006-07-22 00:01:42 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
if(*freelist_ptr(p) != INVALID_SLOT) {
|
|
|
|
rid.slot = *freelist_ptr(p);
|
2006-07-22 00:01:42 +00:00
|
|
|
}
|
2004-10-19 21:16:37 +00:00
|
|
|
|
2007-10-03 01:53:51 +00:00
|
|
|
if(slottedFreespaceForSlot(p, rid.slot) < stasis_record_type_to_size(type)) {
|
2007-06-07 21:53:09 +00:00
|
|
|
rid = NULLRID;
|
2004-10-19 21:16:37 +00:00
|
|
|
}
|
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
return rid;
|
2004-10-19 21:16:37 +00:00
|
|
|
}
|
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static void slottedPostRalloc(int xid, Page * p, recordid rid) {
|
|
|
|
sanityCheck(p, rid);
|
2004-07-30 01:28:39 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
really_do_ralloc(p, rid);
|
|
|
|
}
|
2006-06-17 00:25:09 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
static void slottedFree(int xid, Page * p, recordid rid) {
|
|
|
|
sanityCheck(p, rid);
|
2006-03-20 23:11:46 +00:00
|
|
|
|
2007-10-03 01:53:51 +00:00
|
|
|
if(*freespace_ptr(p) == *slot_ptr(p, rid.slot) + stasis_record_type_to_size(rid.size)) {
|
|
|
|
(*freespace_ptr(p)) -= stasis_record_type_to_size(rid.size);
|
2007-06-07 21:53:09 +00:00
|
|
|
}
|
2006-03-20 23:11:46 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
assert(rid.slot < *numslots_ptr(p));
|
|
|
|
if(rid.slot == *numslots_ptr(p)-1) {
|
|
|
|
(*numslots_ptr(p))--;
|
|
|
|
assert(slottedGetType(xid,p,rid)==INVALID_SLOT);
|
|
|
|
} else {
|
|
|
|
*slot_ptr(p, rid.slot) = INVALID_SLOT;
|
|
|
|
*slot_length_ptr(p, rid.slot) = *freelist_ptr(p);
|
|
|
|
*freelist_ptr(p) = rid.slot;
|
|
|
|
assert(slottedGetType(xid,p,rid)==INVALID_SLOT);
|
2004-07-30 01:28:39 +00:00
|
|
|
}
|
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
slottedFsck(p);
|
2004-07-30 01:28:39 +00:00
|
|
|
}
|
|
|
|
|
2006-07-22 00:01:42 +00:00
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
// XXX dereferenceRID
|
2006-07-22 00:01:42 +00:00
|
|
|
|
2008-12-02 19:38:49 +00:00
|
|
|
static void slottedLoaded(Page *p) {
|
2007-10-02 00:18:33 +00:00
|
|
|
p->LSN = *stasis_page_lsn_ptr(p);
|
2008-12-02 19:38:49 +00:00
|
|
|
// @todo arrange for pagefsck to run on load/flush, but nowhere else.
|
|
|
|
slottedFsck(p);
|
2007-07-18 20:09:14 +00:00
|
|
|
}
|
2008-12-02 19:38:49 +00:00
|
|
|
static void slottedFlushed(Page *p) {
|
2007-10-02 00:18:33 +00:00
|
|
|
*stasis_page_lsn_ptr(p) = p->LSN;
|
2007-07-18 20:09:14 +00:00
|
|
|
slottedFsck(p);
|
2007-06-07 21:53:09 +00:00
|
|
|
}
|
2008-12-02 19:38:49 +00:00
|
|
|
static void slottedCleanup(Page *p) { }
|
2007-06-07 21:53:09 +00:00
|
|
|
|
|
|
|
page_impl slottedImpl() {
|
|
|
|
static page_impl pi = {
|
|
|
|
SLOTTED_PAGE,
|
|
|
|
slottedRead,
|
|
|
|
slottedWrite,
|
2007-07-18 20:09:14 +00:00
|
|
|
0,// readDone
|
|
|
|
0,// writeDone
|
2007-06-07 21:53:09 +00:00
|
|
|
slottedGetType,
|
|
|
|
slottedSetType,
|
|
|
|
slottedGetLength,
|
|
|
|
slottedFirst,
|
|
|
|
slottedNext,
|
|
|
|
notSupported, // is block supported
|
2007-10-02 00:18:33 +00:00
|
|
|
stasis_block_first_default_impl,
|
|
|
|
stasis_block_next_default_impl,
|
|
|
|
stasis_block_done_default_impl,
|
2007-06-07 21:53:09 +00:00
|
|
|
slottedFreespace,
|
|
|
|
slottedCompact,
|
|
|
|
slottedPreRalloc,
|
|
|
|
slottedPostRalloc,
|
2007-07-18 20:09:14 +00:00
|
|
|
slottedFree,
|
2007-06-07 21:53:09 +00:00
|
|
|
0, //XXX page_impl_dereference_identity,
|
2007-07-18 20:09:14 +00:00
|
|
|
slottedLoaded,
|
|
|
|
slottedFlushed,
|
2007-08-20 21:58:20 +00:00
|
|
|
slottedCleanup
|
2007-06-07 21:53:09 +00:00
|
|
|
};
|
|
|
|
return pi;
|
2004-07-30 01:28:39 +00:00
|
|
|
}
|
2004-10-19 21:16:37 +00:00
|
|
|
|
2008-12-02 19:38:49 +00:00
|
|
|
page_impl boundaryTagImpl() {
|
2007-06-07 21:53:09 +00:00
|
|
|
page_impl p = slottedImpl();
|
|
|
|
p.page_type = BOUNDARY_TAG_PAGE;
|
|
|
|
return p;
|
2004-10-19 21:16:37 +00:00
|
|
|
}
|