mirror of
https://github.com/fumiama/fumidb.git
synced 2026-06-05 08:40:30 +08:00
finish page & file
This commit is contained in:
@@ -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`对齐部分写入表头。当找不到时,在文件末尾附加表头(留出新的对齐)。接下来将上一个表头开头的`下一个表头的指针`指向新表头的开头,然后建立相应数据结构,填充表头字段。
|
||||
#### 修改
|
||||
一旦创建数据表,将不支持修改。可以先删除表再重新创建,但这样数据将会丢失。
|
||||
#### 删除
|
||||
|
||||
@@ -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 │
|
||||
|
||||
24
include/file.h
Normal file
24
include/file.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef _FILE_H_
|
||||
#define _FILE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// 初始化并写入数据库文件头
|
||||
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
|
||||
@@ -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
|
||||
49
src/file.c
Normal file
49
src/file.c
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
241
src/page.c
241
src/page.c
@@ -1,12 +1,11 @@
|
||||
// page.c
|
||||
// 管理文件中的空闲块
|
||||
|
||||
#define _GNU_SOURCE /* See feature_test_macros(7) */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
add_test(test_binary binary_test COMMAND binary_test)
|
||||
add_test(test_page page_test COMMAND page_test)
|
||||
191
tests/page_test.c
Normal file
191
tests/page_test.c
Normal file
@@ -0,0 +1,191 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user