diff --git a/api/dbfile.md b/api/dbfile.md index ff623b9..a4ecef1 100644 --- a/api/dbfile.md +++ b/api/dbfile.md @@ -3,23 +3,21 @@ 由于文件内普遍以uint64作为指针,因此理论最大支持文件大小为`16384PB`,在现有条件下完全足够使用。 ## 文件头 -文件最开头填充了固定的8字节ascii编码`FUMIDB\1\0`。前6字节必须为`FUMIDB`,表明本文件为`fumidb`数据库文件格式。7, 8字节`\1\0`作为数据库的版本,是一个小端的uint16,在这里表示第1版,之后将依次递增。今后可能会在文件头增加更多字段,由于其后的`ptr of first table`不一定要求其指向的第一个表头紧跟文件头,字段的增加将不影响之前版本的解析。在目前实现时,为`possible other header data`空出16字节。 +文件最开头填充了固定的8字节ascii编码`FUMIDB\1\0`。前6字节必须为`FUMIDB`,表明本文件为`fumidb`数据库文件格式。7, 8字节`\1\0`作为数据库的版本,是一个小端的uint16,在这里表示第1版,之后将依次递增。今后可能会在文件头增加更多字段。 ``` 0 8 16 ┌───────────────────┬───────────────────┐ │ FUMIDB\1\0 │ ptr of unused blk │ ├───────────────────┼───────────────────┤ -│ ptr of first table│ ptr of next table │ +│ ptr of next table │ ...... ...... │ ├───────────────────┴───────────────────┤ │ possible other header data │ ├───────────────────────────────────────┤ -│ first table head │ -├───────────────────────────────────────┤ │ ...... ...... ...... │ ├───────────────────────────────────────┤ │ some possible padding to fit 4096Byte │ ├───────────────────┬───────────────────┤ -│ ptr of next table │ second table head │ +│ ptr of next table │ first table head │ ├───────────────────┴───────────────────┤ │ ...... ...... ...... │ ├───────────────────────────────────────┤ @@ -34,12 +32,12 @@ #### 新建 空闲块不会被主动新建,而是源于分配时富余的部分以及删除后的剩余。 #### 合并 -受限于记录长度的数字为uint16,一块未被使用的空闲空间大小最大为`65535`,实际使用时不使其超过`4096`。如果确有连续的超过`4096`字节的空闲,应当划分为多个块。块应当是`4k`对齐的。 +受限于记录长度的数字为uint16,一块未被使用的空闲空间大小最大为`65535`,实际使用时不使其超过`4096`。如果确有连续的超过`4096`字节的空闲,应当划分为多个块。`4096`字节的块应当是`4k`对齐的。 #### 使用 使用时优先从第一个块遍历,比较其大小以及页对齐是否符合要求。当使用后仍有剩余,对于小于10字节的块,直接舍弃不用;否则更新块大小与相关链表指针。 ### 表 #### 新建 -在新建表时将计算表头大小,优先选取一块未被使用的足够大的对齐部分写入表头。当找不到时,在文件末尾附加表头(留出新的对齐)。接下来将上一个表头开头的`下一个表头的指针`指向新表头的开头,然后建立相应数据结构,填充表头字段。 +在新建表时将计算表头大小,优先选取一块未被使用的足够大的`4k`对齐部分写入表头。当找不到时,在文件末尾附加表头(留出新的对齐)。接下来将上一个表头开头的`下一个表头的指针`指向新表头的开头,然后建立相应数据结构,填充表头字段。 #### 修改 一旦创建数据表,将不支持修改。可以先删除表再重新创建,但这样数据将会丢失。 #### 删除 diff --git a/api/table.md b/api/table.md index b1a6662..101cf10 100644 --- a/api/table.md +++ b/api/table.md @@ -1,6 +1,6 @@ # 数据表格式 ## 表头 -如下所示,其中行类型列表的No.1自动成为主键,强制应用`unique`类型修饰符;`data blocks`可以为任意数据,如索引,表项等。由于数据块均为定长,增加时直接添加或重用已删除区块,修改时直接覆盖,删除时直接在索引中移除该项,将块首地址附加到已删除块的链表即可。 +如下所示,加上文件中附加的`ptr of next table`,表头永远是`4k`对齐的,其中行类型列表的No.1自动成为主键,强制应用`unique`类型修饰符;`data blocks`可以为任意数据,如索引,表项等。由于数据块均为定长,增加时直接添加或重用已删除区块,修改时直接覆盖,删除时直接在索引中移除该项,将块首地址附加到已删除块的链表即可。 ``` ┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐ │ 0 - 7 │ 8 - 15 │ 16 -- 23 │ 24 -- 31 │ 32 -- 39 │ 40 -- 47 │ 48 -- 55 │ 56 -- 63 │ diff --git a/include/file.h b/include/file.h new file mode 100644 index 0000000..15fc1c5 --- /dev/null +++ b/include/file.h @@ -0,0 +1,24 @@ +#ifndef _FILE_H_ +#define _FILE_H_ + +#include + +// 初始化并写入数据库文件头 +int init_file_header_page(int fd); + +// 获得数据库版本 +uint16_t get_db_version(int fd); + +// 设置 ptr of unused blk 字段 +int set_first_unused_block(int fd, uint64_t ptr); + +// 获得 ptr of unused blk 字段 +uint64_t get_first_unused_block(int fd); + +// 设置 ptr of next table 字段 +int set_first_table(int fd, uint64_t ptr); + +// 获得 ptr of next table 字段 +uint64_t get_first_table(int fd); + +#endif \ No newline at end of file diff --git a/include/page.h b/include/page.h index b310a3d..2cee124 100644 --- a/include/page.h +++ b/include/page.h @@ -7,27 +7,61 @@ #define PAGESZ 4096 #endif -// 获取文件中的第一个空闲页,并将其 mmap 到内存 +// 获取文件中的第一个空闲页,并将其读到内存 // 如果现有空闲均无整页,则新分配一页 +// 返回指针的 -8 字节记录了本页在文件的偏移 +// 因此实际上分配了 PAGESZ+8 的空间 // 返回: -// EOF 错误 +// NULL 错误,参见 errno +// page 指针 void* alloc_page(int fd); -// 释放分配的页,将其标记为空闲以备使用 +// 刷新一页 // 返回: -// EOF 错误 +// EOF lseek 错误,参见 errno +// 0/1 write size != PAGESZ +int sync_page(int fd, void* page); + +// 刷新并释放 page +// 返回: +// EOF lseek 错误,参见 errno +// 0/1 write size != PAGESZ +int unmount_page(int fd, void* page); + +// 释放分配的页,将其标记为空闲以备使用 +// 同时将数据刷新到文件 +// 返回: +// EOF 错误,参见 errno +// 0 成功 int free_page(int fd, void* page); -// 获取文件中的第一个满足 size 大小的块 +// 获取文件中的第一个满足 size 大小的块的位置 +// 返回的块大小一定是 size,实际上为 size + 8 + 2 +// 开头的 8+2 字节记录了真正的块位置与大小以备释放 +// 返回的 ptr 已经跳过这 10 个字节 // 返回: -// EOF 错误 -void* alloc_block(int fd, size_t size); +// NULL 错误,参见 errno +// blk 指针 +void* alloc_block(int fd, uint16_t size); + +// 刷新 block 到文件 +// 返回: +// EOF lseek 错误或 size 过大,参见 errno +// 0/1 write size != PAGESZ +int sync_block(int fd, void* blk); + +// 刷新 block 到文件并释放 +// 返回: +// EOF lseek 错误或 size 过大,参见 errno +// 0/1 write size != PAGESZ +int unmount_block(int fd, void* blk); // 释放块,并将其标记为空闲以备使用 -// 值得注意的是,只有在本页最后一块 -// 也被释放后,这一页才会被真正释放 +// 如果该块前后也有空闲且不跨过4096 +// 将会被合并 // 返回: -// EOF 错误 -int free_block(int fd, void* page); +// EOF 错误,参见 errno +// 0 成功 +int free_block(int fd, void* blk); #endif \ No newline at end of file diff --git a/src/file.c b/src/file.c new file mode 100644 index 0000000..687a8be --- /dev/null +++ b/src/file.c @@ -0,0 +1,49 @@ +#include +#include +#include + +#include "../include/binary.h" +#include "../include/page.h" +#include "../include/file.h" + +uint8_t header[PAGESZ] = {'F', 'U', 'M', 'I', 'D', 'B', 1, 0}; + +int init_file_header_page(int fd) { + lseek(fd, 0, SEEK_SET); + return write(fd, header, PAGESZ) != PAGESZ; +} + +uint16_t get_db_version(int fd) { + uint16_t v; + if(lseek(fd, 6, SEEK_SET) < 0) return EOF; + readle16(fd, v); + return v; +} + +int set_first_unused_block(int fd, uint64_t ptr) { + uint8_t buf[8]; + if(lseek(fd, 8, SEEK_SET) < 0) return EOF; + putle64(buf, ptr); + return write(fd, buf, 8) != 8; +} + +uint64_t get_first_unused_block(int fd) { + uint64_t ptr; + if(lseek(fd, 8, SEEK_SET) < 0) return EOF; + readle64(fd, ptr); + return ptr; +} + +int set_first_table(int fd, uint64_t ptr) { + uint8_t buf[8]; + if(lseek(fd, 16, SEEK_SET) < 0) return EOF; + putle64(buf, ptr); + return write(fd, buf, 8) != 8; +} + +uint64_t get_first_table(int fd) { + uint64_t ptr; + if(lseek(fd, 16, SEEK_SET) < 0) return EOF; + readle64(fd, ptr); + return ptr; +} diff --git a/src/page.c b/src/page.c index 0fb46fa..6b9d0a2 100644 --- a/src/page.c +++ b/src/page.c @@ -1,12 +1,11 @@ // page.c // 管理文件中的空闲块 -#define _GNU_SOURCE /* See feature_test_macros(7) */ - #include +#include #include #include -#include +#include #include "../include/binary.h" #include "../include/page.h" @@ -14,25 +13,239 @@ uint8_t nullpage[PAGESZ]; void* alloc_page(int fd) { - uint64_t ptr, prev_ptr = 8; + uint64_t ptr = 8, prev_ptr = 0, prev_prev_ptr = 0; void* page; - if(lseek(fd, 8, SEEK_SET) < 0) return EOF; + uint8_t buf[8]; // 对于 page,只关心位于第一页 8~15 字节的 ptr of unused blk - readle64(fd, ptr); while(ptr) { if(!(ptr%PAGESZ)) { // 找到符合要求的页 - page = mmap(NULL, PAGESZ, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, ptr); - if(page < 0) return page; - if(lseek(fd, prev_ptr, SEEK_SET) < 0) return EOF; - write(fd, page, 8); // 从空闲块链表移除本块 + if(lseek(fd, ptr, SEEK_SET) < 0) return NULL; + if(read(fd, buf, 8) != 8) return NULL; + page = malloc(PAGESZ+8); + if(page == NULL) return NULL; + putle64(page, ptr); + page += 8; + if(lseek(fd, prev_ptr, SEEK_SET) < 0) return NULL; + if(write(fd, buf, 8) != 8) return NULL; // 从空闲块链表移除本块 return page; } + if(prev_prev_ptr && ptr < prev_ptr) { // 不符合顺序,进行一次调整 + lseek(fd, prev_prev_ptr, SEEK_SET); + putle64(buf, ptr); + if(write(fd, buf, 8) != 8) return NULL; // 1->next = 3 + if(lseek(fd, ptr, SEEK_SET) < 0) return NULL; + prev_prev_ptr = ptr; // new1 = 3 + readle64(fd, ptr); // ptr(new3) = 3->next + lseek(fd, -8, SEEK_SET); + putle64(buf, prev_ptr); // new2 = 2 + if(write(fd, buf, 8) != 8) return NULL; // 3->next = 2 + lseek(fd, prev_ptr, SEEK_SET); + putle64(buf, ptr); + if(write(fd, buf, 8) != 8) return NULL; // 2->next = ptr + continue; + } + prev_prev_ptr = prev_ptr; + prev_ptr = ptr; + if(lseek(fd, ptr, SEEK_SET) < 0) return NULL; + readle64(fd, ptr); + } + ptr = lseek(fd, 0, SEEK_END); + if(ptr < 0) return NULL; + if(ptr%PAGESZ) { // 文件没有页对齐 + errno = ESPIPE; + return NULL; + } + if(write(fd, nullpage, PAGESZ) != PAGESZ) return NULL; + page = malloc(PAGESZ+8); + if(page == NULL) return NULL; + putle64(page, ptr); + return page+8; +} + +int sync_page(int fd, void* page) { + uint64_t ptr = le64(page-8); + if(lseek(fd, ptr, SEEK_SET) < 0) return EOF; + return write(fd, page, PAGESZ) != PAGESZ; +} + +int unmount_page(int fd, void* page) { + int r = sync_page(fd, page); + free(page-8); + return r; +} + +int free_page(int fd, void* page) { + uint64_t ptr = 8, prev_ptr = 0, prev_prev_ptr = 0, page_ptr = le64(page-8); + uint8_t buf[8]; + while(ptr && ptr < page_ptr) { + if(prev_ptr == ptr) return EOF; + if(prev_prev_ptr && ptr < prev_ptr) { // 不符合顺序,进行一次调整 + lseek(fd, prev_prev_ptr, SEEK_SET); + putle64(buf, ptr); + if(write(fd, buf, 8) != 8) return EOF; // 1->next = 3 + if(lseek(fd, ptr, SEEK_SET) < 0) return EOF; + prev_prev_ptr = ptr; // new1 = 3 + readle64(fd, ptr); // ptr(new3) = 3->next + lseek(fd, -8, SEEK_SET); + putle64(buf, prev_ptr); // new2 = 2 + if(write(fd, buf, 8) != 8) return EOF; // 3->next = 2 + lseek(fd, prev_ptr, SEEK_SET); + putle64(buf, ptr); + if(write(fd, buf, 8) != 8) return EOF; // 2->next = ptr + continue; + } + prev_prev_ptr = prev_ptr; prev_ptr = ptr; if(lseek(fd, ptr, SEEK_SET) < 0) return EOF; readle64(fd, ptr); } - ptr = lseek(fd, 0, SEEK_END); - if(ptr < 0 || !(ptr%PAGESZ)) return EOF; - if(write(fd, nullpage, PAGESZ) != PAGESZ) return EOF; - return mmap(NULL, PAGESZ, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, ptr); + putle64(page, ptr); + putle16(page+8, PAGESZ); + putle64(buf, page_ptr); + lseek(fd, prev_ptr, SEEK_SET); + if(write(fd, buf, 8) != 8) return EOF; // 将本页附加到链表 + sync_page(fd, page); + free(page-8); + return 0; +} + +void* alloc_block(int fd, uint16_t size) { + uint64_t ptr = 8, prev_ptr = 0, prev_prev_ptr = 0; + void* blk; + uint8_t buf[8]; + uint16_t blksz; + + if(size > PAGESZ) return NULL; + // 对于 page,只关心位于第一页 8~15 字节的 ptr of unused blk + while(ptr) { + if(lseek(fd, ptr, SEEK_SET) < 0) return NULL; + if(read(fd, buf, 8) != 8) return NULL; + readle16(fd, blksz); + if(blksz >= size) { // 找到符合要求的块 + blk = malloc(size+10); + if(blk == NULL) return NULL; + if(blksz - size > 10) { // 分裂块 + lseek(fd, ptr+size, SEEK_SET); + write(fd, buf, 8); + blksz -= size; + putle16(buf, blksz); + write(fd, buf, 2); + putle64(buf, ptr+size); + } + putle64(blk, ptr); + putle16(blk+8, size); + blk += 10; + if(lseek(fd, prev_ptr, SEEK_SET) < 0) return NULL; + if(write(fd, buf, 8) != 8) return NULL; // 从空闲块链表移除本块 + return blk; + } + if(prev_prev_ptr && ptr < prev_ptr) { // 不符合顺序,进行一次调整 + lseek(fd, prev_prev_ptr, SEEK_SET); + putle64(buf, ptr); + if(write(fd, buf, 8) != 8) return NULL; // 1->next = 3 + if(lseek(fd, ptr, SEEK_SET) < 0) return NULL; + prev_prev_ptr = ptr; // new1 = 3 + readle64(fd, ptr); // ptr(new3) = 3->next + lseek(fd, -8, SEEK_SET); + putle64(buf, prev_ptr); // new2 = 2 + if(write(fd, buf, 8) != 8) return NULL; // 3->next = 2 + lseek(fd, prev_ptr, SEEK_SET); + putle64(buf, ptr); + if(write(fd, buf, 8) != 8) return NULL; // 2->next = ptr + continue; + } + prev_prev_ptr = prev_ptr; + prev_ptr = ptr; + if(lseek(fd, ptr, SEEK_SET) < 0) return NULL; + readle64(fd, ptr); + } + ptr = lseek(fd, 0, SEEK_END); + if(ptr < 0) return NULL; + if(ptr%PAGESZ) { // 文件没有页对齐 + errno = ESPIPE; + return NULL; + } + if(write(fd, nullpage, PAGESZ) != PAGESZ) return NULL; + if(PAGESZ-size > 10) { // 回收冗余 + if(lseek(fd, prev_ptr, SEEK_SET) < 0) return NULL; + putle64(buf, ptr+size); + if(write(fd, buf, 8) != 8) return NULL; + if(lseek(fd, ptr+size+8, SEEK_SET) < 0) return NULL; + putle16(buf, PAGESZ - size); + if(write(fd, buf, 2) != 2) return NULL; + } + blk = malloc(size+10); + if(blk == NULL) return NULL; + putle64(blk, ptr); + putle16(blk+8, size); + return blk+10; +} + +int sync_block(int fd, void* blk) { + uint64_t off = le64(blk-10); + uint16_t size = le16(blk-2); + if(size > PAGESZ) { + errno = EFBIG; + return EOF; + } + if(lseek(fd, off, SEEK_SET) < 0) return EOF; + return write(fd, blk, size) != size; +} + +int unmount_block(int fd, void* blk) { + int r = sync_block(fd, blk); + free(blk-10); + return r; +} + +int free_block(int fd, void* blk) { + uint64_t ptr = 8, prev_ptr = 0, prev_prev_ptr = 0, off = le64(blk-10); + uint8_t buf[8]; + uint16_t size = le16(blk-2), sz; + while(ptr && ptr < off) { + if(prev_ptr == ptr) return EOF; + if(prev_prev_ptr && ptr < prev_ptr) { // 不符合顺序,进行一次调整 + lseek(fd, prev_prev_ptr, SEEK_SET); + putle64(buf, ptr); + if(write(fd, buf, 8) != 8) return EOF; // 1->next = 3 + if(lseek(fd, ptr, SEEK_SET) < 0) return EOF; + prev_prev_ptr = ptr; // new1 = 3 + readle64(fd, ptr); // ptr(new3) = 3->next + lseek(fd, -8, SEEK_SET); + putle64(buf, prev_ptr); // new2 = 2 + if(write(fd, buf, 8) != 8) return EOF; // 3->next = 2 + lseek(fd, prev_ptr, SEEK_SET); + putle64(buf, ptr); + if(write(fd, buf, 8) != 8) return EOF; // 2->next = ptr + continue; + } + prev_prev_ptr = prev_ptr; + prev_ptr = ptr; + if(lseek(fd, ptr, SEEK_SET) < 0) return EOF; + readle64(fd, ptr); + } + putle64(blk, ptr); + putle16(blk+8, size); + putle64(buf, off); + lseek(fd, prev_ptr, SEEK_SET); + if(write(fd, buf, 8) != 8) return EOF; // 将本页附加到链表 + readle16(fd, sz); + if(prev_ptr+sz == off && prev_ptr%PAGESZ < off%PAGESZ) { // 可以和前一块合并 + lseek(fd, prev_ptr, SEEK_SET); + write(fd, blk, 8); + putle16(buf, size+sz); + write(fd, buf, 2); + free(blk-10); + return 0; + } + if(off+size == ptr && off%PAGESZ < ptr%PAGESZ) { // 可以和后一块合并 + if(lseek(fd, ptr, SEEK_SET) < 0) return EOF; + readle64(fd, prev_ptr); + readle16(fd, sz); + putle64(blk, prev_ptr); + putle16(blk, size+sz); + } + sync_block(fd, blk); + free(blk-10); + return 0; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ba0ee0a..a3c4272 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.0.0) project(fumidb_test VERSION 1.0) -add_executable(binary_test binary_test.c) +add_executable(binary_test binary_test.c) +add_executable(page_test page_test.c ../src/page.c ../src/file.c) -add_test(test_binary binary_test COMMAND binary_test) \ No newline at end of file +add_test(test_binary binary_test COMMAND binary_test) +add_test(test_page page_test COMMAND page_test) \ No newline at end of file diff --git a/tests/page_test.c b/tests/page_test.c new file mode 100644 index 0000000..8f5e195 --- /dev/null +++ b/tests/page_test.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include "../include/binary.h" +#include "../include/file.h" +#include "../include/page.h" + +void* pages[16]; +uint8_t nullpage[PAGESZ]; + +int main() { + int fd = open("page_test_tmp.bin", O_RDWR | O_CREAT | O_TRUNC, 0644); + if(fd < 0) { + perror("create"); + return 1; + } + if(init_file_header_page(fd) < 0) return 2; + for(int i = 0; i < 16; i++) { + void* page = alloc_page(fd); + if(page == NULL) { + perror("alloc_page"); + return 3; + } + pages[i] = page; + } + puts("free!"); + free_page(fd, pages[15]); + if(get_first_unused_block(fd) != 16*PAGESZ) return 5; + free_page(fd, pages[12]); + if(get_first_unused_block(fd) != 13*PAGESZ) return 6; + free_page(fd, pages[1]); + if(get_first_unused_block(fd) != 2*PAGESZ) return 7; + free_page(fd, pages[10]); + if(get_first_unused_block(fd) != 2*PAGESZ) return 8; + free_page(fd, pages[9]); + if(get_first_unused_block(fd) != 2*PAGESZ) return 8; + pages[1] = alloc_page(fd); + if(le64(pages[1]-8) != (uint64_t)(2*PAGESZ)) { + printf("1: %016llx != %016llx\n", le64(pages[1]-8), (uint64_t)(2*PAGESZ)); + return 9; + } + pages[9] = alloc_page(fd); + if(le64(pages[9]-8) != (uint64_t)(10*PAGESZ)) { + printf("9: %016llx != %016llx\n", le64(pages[9]-8), (uint64_t)(11*PAGESZ)); + return 10; + } + pages[10] = alloc_page(fd); + if(le64(pages[10]-8) != (uint64_t)(11*PAGESZ)) { + printf("10: %016llx != %016llx\n", le64(pages[10]-8), (uint64_t)(11*PAGESZ)); + return 11; + } + pages[12] = alloc_page(fd); + if(le64(pages[12]-8) != (uint64_t)(13*PAGESZ)) return 12; + pages[15] = alloc_page(fd); + if(le64(pages[15]-8) != (uint64_t)(16*PAGESZ)) return 13; + for(int i = 0; i < 16; i++) { + if(free_page(fd, pages[i])) { + perror("free_page"); + return 14; + } + } + close(fd); + + fd = open("page_test_tmp.bin", O_RDWR, 0644); + if(fd < 0) { + perror("open"); + return 15; + } + uint8_t* blk1 = alloc_block(fd, 40); + uint8_t* blk2 = alloc_block(fd, 22); + uint8_t* blk3 = alloc_block(fd, 33); + uint8_t* blk4 = alloc_block(fd, 4095); + memcpy(blk1, "hello world!", 13); + sync_block(fd, blk1); + lseek(fd, PAGESZ, SEEK_SET); + read(fd, blk2, 13); + if(strcmp((const char *)blk2, (const char *)blk1)) { + return 16; + } + sync_block(fd, blk2); + lseek(fd, PAGESZ+40, SEEK_SET); + read(fd, blk3, 13); + if(strcmp((const char *)blk3, (const char *)blk1)) { + return 17; + } + sync_block(fd, blk3); + lseek(fd, PAGESZ+40+22, SEEK_SET); + read(fd, blk4+222, 13); + sync_block(fd, blk4); + if(strcmp((const char *)&blk4[222], (const char *)blk1)) { + return 18; + } + memset(blk1, 0, 40); + lseek(fd, PAGESZ*2+222, SEEK_SET); + read(fd, blk1, 13); + if(strcmp((const char *)blk1, (const char *)blk2)) { + return 19; + } + if(free_block(fd, blk1)) { + perror("free_block(fd, blk1)"); + return 20; + } + if(free_block(fd, blk2)) { + perror("free_block(fd, blk2)"); + return 21; + } + if(free_block(fd, blk3)) { + perror("free_block(fd, blk3)"); + return 22; + } + if(free_block(fd, blk4)) { + perror("free_block(fd, blk4)"); + return 23; + } + blk1 = alloc_block(fd, 40+22+33); + memcpy(blk1+44, "hello world!", 13); + sync_block(fd, blk1); + lseek(fd, PAGESZ+44, SEEK_SET); + char buf[13]; + read(fd, buf, 13); + if(strcmp((const char *)&blk1[44], (const char *)buf)) { + return 24; + } + close(fd); + + fd = open("page_test_tmp.bin", O_RDWR | O_CREAT | O_TRUNC, 0644); + if(fd < 0) { + perror("create"); + return 25; + } + if(init_file_header_page(fd) < 0) return 26; + blk1 = alloc_block(fd, 40); + blk2 = alloc_block(fd, 22); + blk3 = alloc_block(fd, 33); + blk4 = alloc_block(fd, 4095); + memcpy(blk1, "hello world!", 13); + sync_block(fd, blk1); + lseek(fd, PAGESZ, SEEK_SET); + read(fd, blk2, 13); + if(strcmp((const char *)blk2, (const char *)blk1)) { + return 16; + } + sync_block(fd, blk2); + lseek(fd, PAGESZ+40, SEEK_SET); + read(fd, blk3, 13); + if(strcmp((const char *)blk3, (const char *)blk1)) { + return 17; + } + sync_block(fd, blk3); + lseek(fd, PAGESZ+40+22, SEEK_SET); + read(fd, blk4+222, 13); + sync_block(fd, blk4); + if(strcmp((const char *)&blk4[222], (const char *)blk1)) { + return 18; + } + memset(blk1, 0, 40); + lseek(fd, PAGESZ*2+222, SEEK_SET); + read(fd, blk1, 13); + if(strcmp((const char *)blk1, (const char *)blk2)) { + return 19; + } + if(free_block(fd, blk1)) { + perror("free_block(fd, blk1)"); + return 20; + } + if(free_block(fd, blk2)) { + perror("free_block(fd, blk2)"); + return 21; + } + if(free_block(fd, blk3)) { + perror("free_block(fd, blk3)"); + return 22; + } + if(free_block(fd, blk4)) { + perror("free_block(fd, blk4)"); + return 23; + } + blk1 = alloc_block(fd, 40+22+33); + memcpy(blk1+44, "hello world!", 13); + sync_block(fd, blk1); + lseek(fd, PAGESZ+44, SEEK_SET); + memset(buf, 0, 13); + read(fd, buf, 13); + if(strcmp((const char *)&blk1[44], (const char *)buf)) { + return 24; + } + close(fd); + + remove("page_test_tmp.bin"); + return 0; +}