From 1463c1f606c6720e3ef7cbfe75b55c695167e01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sun, 1 May 2022 01:19:38 +0800 Subject: [PATCH] add page --- CMakeLists.txt | 3 ++- README.md | 3 +++ api/dbfile.md | 2 +- {src => include}/binary.h | 37 +++++++++++++++++++++++++++++++++ include/page.h | 33 ++++++++++++++++++++++++++++++ option.txt | 1 + src/page.c | 38 ++++++++++++++++++++++++++++++++++ tests/binary_test.c | 43 ++++++++++++++++++++++++++++++++------- 8 files changed, 151 insertions(+), 9 deletions(-) rename {src => include}/binary.h (55%) create mode 100644 include/page.h create mode 100644 option.txt create mode 100644 src/page.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 33c7109..289cec0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,8 @@ if(${isBigEndian}) add_definitions(-DWORDS_BIGENDIAN) endif() -option(BUILD_TEST "Whether to build tests" OFF) +include(option.txt) + if(${BUILD_TEST}) message(STATUS "Building tests...") enable_testing() diff --git a/README.md b/README.md index 20b88fd..892887a 100644 --- a/README.md +++ b/README.md @@ -13,5 +13,8 @@ Most part of this README and the docs in `api` folder is written in Chinese. If - [ ] 按索引[删除表项](/api/table.md#表项的删除) - [ ] 对非主键(无[unique约束](/api/types.md#类型修饰符))进行[索引](/api/index.md) +## 关于并发 +不支持并发,如需要并发,请自行加锁以免数据损坏。 + ## Thanks - [asciiflow](https://asciiflow.com/) \ No newline at end of file diff --git a/api/dbfile.md b/api/dbfile.md index 9de7697..ff623b9 100644 --- a/api/dbfile.md +++ b/api/dbfile.md @@ -34,7 +34,7 @@ #### 新建 空闲块不会被主动新建,而是源于分配时富余的部分以及删除后的剩余。 #### 合并 -受限于记录长度的数字为uint16,一块未被使用的空闲空间大小最大为`65535`,一般则不使其超过`32768`。如果确有连续的超过`32768`字节的空闲,应当划分为多个块。特别地,如果确有连续的超过`32768`字节的空闲且其后的空间不足`4096`字节,应当适当减小前一块的大小,使后一块大小超过`4096`字节。 +受限于记录长度的数字为uint16,一块未被使用的空闲空间大小最大为`65535`,实际使用时不使其超过`4096`。如果确有连续的超过`4096`字节的空闲,应当划分为多个块。块应当是`4k`对齐的。 #### 使用 使用时优先从第一个块遍历,比较其大小以及页对齐是否符合要求。当使用后仍有剩余,对于小于10字节的块,直接舍弃不用;否则更新块大小与相关链表指针。 ### 表 diff --git a/src/binary.h b/include/binary.h similarity index 55% rename from src/binary.h rename to include/binary.h index d8f5078..8c14514 100644 --- a/src/binary.h +++ b/include/binary.h @@ -1,3 +1,6 @@ +#ifndef _BINARY_H_ +#define _BINARY_H_ + #include #include #include @@ -48,19 +51,53 @@ #define putle16(buf, x) (*(uint16_t*)(buf) = _byteswap_ushort((uint16_t)(x))) #define putle32(buf, x) (*(uint32_t*)(buf) = _byteswap_ulong((uint32_t)(x))) #define putle64(buf, x) (*(uint64_t*)(buf) = _byteswap_uint64((uint64_t)(x))) + + #define readle16(fd, num) ((~read((fd), &(num), 2))?((num)=_byteswap_ushort((uint16_t)(num))):EOF) + #define readle32(fd, num) ((~read((fd), &(num), 4))?((num)=_byteswap_ulong((uint32_t)(num))):EOF) + #define readle64(fd, num) ((~read((fd), &(num), 8))?((num)=_byteswap_uint64((uint64_t)(num))):EOF) + + #define le16(buf) _byteswap_ushort(*(uint16_t*)(buf)) + #define le32(buf) _byteswap_ulong(*(uint32_t*)(buf)) + #define le64(buf) _byteswap_uint64(*(uint64_t*)(buf)) #else #define putle16(buf, x) (*(uint16_t*)(buf) = (uint16_t)(x)) #define putle32(buf, x) (*(uint32_t*)(buf) = (uint32_t)(x)) #define putle64(buf, x) (*(uint64_t*)(buf) = (uint64_t)(x)) + + #define readle16(fd, num) read((fd), &(num), 2) + #define readle32(fd, num) read((fd), &(num), 4) + #define readle64(fd, num) read((fd), &(num), 8) + + #define le16(buf) (*(uint16_t*)(buf)) + #define le32(buf) (*(uint32_t*)(buf)) + #define le64(buf) (*(uint64_t*)(buf)) #endif #else #ifdef WORDS_BIGENDIAN #define putle16(buf, x) (*(uint16_t*)(buf) = __builtin_bswap16((uint16_t)(x))) #define putle32(buf, x) (*(uint32_t*)(buf) = __builtin_bswap32((uint32_t)(x))) #define putle64(buf, x) (*(uint64_t*)(buf) = __builtin_bswap64((uint64_t)(x))) + + #define readle16(fd, num) ((~read((fd), &(num), 2))?((num)=__builtin_bswap16((uint16_t)(num))):EOF) + #define readle32(fd, num) ((~read((fd), &(num), 4))?((num)=__builtin_bswap32((uint32_t)(num))):EOF) + #define readle64(fd, num) ((~read((fd), &(num), 8))?((num)=__builtin_bswap64((uint64_t)(num))):EOF) + + #define le16(buf) __builtin_bswap16(*(uint16_t*)(buf)) + #define le32(buf) __builtin_bswap32(*(uint32_t*)(buf)) + #define le64(buf) __builtin_bswap64(*(uint64_t*)(buf)) #else #define putle16(buf, x) (*(uint16_t*)(buf) = (uint16_t)(x)) #define putle32(buf, x) (*(uint32_t*)(buf) = (uint32_t)(x)) #define putle64(buf, x) (*(uint64_t*)(buf) = (uint64_t)(x)) + + #define readle16(fd, num) read((fd), &(num), 2) + #define readle32(fd, num) read((fd), &(num), 4) + #define readle64(fd, num) read((fd), &(num), 8) + + #define le16(buf) (*(uint16_t*)(buf)) + #define le32(buf) (*(uint32_t*)(buf)) + #define le64(buf) (*(uint64_t*)(buf)) #endif #endif + +#endif \ No newline at end of file diff --git a/include/page.h b/include/page.h new file mode 100644 index 0000000..b310a3d --- /dev/null +++ b/include/page.h @@ -0,0 +1,33 @@ +#ifndef _PAGE_H_ +#define _PAGE_H_ + +#include + +#ifndef PAGESZ + #define PAGESZ 4096 +#endif + +// 获取文件中的第一个空闲页,并将其 mmap 到内存 +// 如果现有空闲均无整页,则新分配一页 +// 返回: +// EOF 错误 +void* alloc_page(int fd); + +// 释放分配的页,将其标记为空闲以备使用 +// 返回: +// EOF 错误 +int free_page(int fd, void* page); + +// 获取文件中的第一个满足 size 大小的块 +// 返回: +// EOF 错误 +void* alloc_block(int fd, size_t size); + +// 释放块,并将其标记为空闲以备使用 +// 值得注意的是,只有在本页最后一块 +// 也被释放后,这一页才会被真正释放 +// 返回: +// EOF 错误 +int free_block(int fd, void* page); + +#endif \ No newline at end of file diff --git a/option.txt b/option.txt new file mode 100644 index 0000000..601b328 --- /dev/null +++ b/option.txt @@ -0,0 +1 @@ +option(BUILD_TEST "Whether to build tests" OFF) \ No newline at end of file diff --git a/src/page.c b/src/page.c new file mode 100644 index 0000000..0fb46fa --- /dev/null +++ b/src/page.c @@ -0,0 +1,38 @@ +// page.c +// 管理文件中的空闲块 + +#define _GNU_SOURCE /* See feature_test_macros(7) */ + +#include +#include +#include +#include + +#include "../include/binary.h" +#include "../include/page.h" + +uint8_t nullpage[PAGESZ]; + +void* alloc_page(int fd) { + uint64_t ptr, prev_ptr = 8; + void* page; + if(lseek(fd, 8, SEEK_SET) < 0) return EOF; + // 对于 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); // 从空闲块链表移除本块 + return page; + } + 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); +} diff --git a/tests/binary_test.c b/tests/binary_test.c index 1379f86..1d40ec6 100644 --- a/tests/binary_test.c +++ b/tests/binary_test.c @@ -1,12 +1,41 @@ -#include "../src/binary.h" +#include +#include +#include "../include/binary.h" int main() { - char buf[8]; + unsigned char buf[8]; putle16(buf, 1); - if(buf[0] != 1) return 1; - putle32(buf, 2); - if(buf[0] != 2) return 2; - putle64(buf, 3); - if(buf[0] != 3) return 3; + if(buf[0] != 1 || le16(buf) != 0x01) return 1; + putle32(buf+1, 2); + if(buf[1] != 2 || le32(buf+1) != 0x02) return 2; + putle64(buf, 0xff0000); + if(buf[2] != 0xff || le64(buf) != 0xff0000) return 3; + + int fd = open("binary_test_tmp.bin", O_WRONLY | O_CREAT | O_TRUNC, 0644); + if(fd < 0) { + perror("create"); + return 4; + } + write(fd, "\001\002\003\004\005\006\a\b", 8); + close(fd); + fd = open("binary_test_tmp.bin", O_RDONLY | O_NONBLOCK); + if(fd < 0) { + perror("open"); + return 5; + } + uint64_t ptr = 0; + readle16(fd, ptr); + printf("%04x\n", (uint16_t)ptr); + if((uint16_t)ptr != 0x0201) return 6; + lseek(fd, 0, SEEK_SET); + readle32(fd, ptr); + printf("%08x\n", (uint32_t)ptr); + if((uint32_t)ptr != 0x04030201) return 7; + lseek(fd, 0, SEEK_SET); + readle64(fd, ptr); + printf("%016llx\n", (uint64_t)ptr); + if((uint64_t)ptr != 0x0807060504030201) return 8; + close(fd); + remove("binary_test_tmp.bin"); return 0; }