diff --git a/.gitignore b/.gitignore index c6127b3..4d3ceeb 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ modules.order Module.symvers Mkfile.old dkms.conf + +build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f744169 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.0.0) +project(simple-protobuf) + +include(TestBigEndian) +test_big_endian(isBigEndian) +if (${isBigEndian}) + add_library(spb SHARED protobuf_be.c) +else() + add_library(spb SHARED protobuf_le.c) +endif() + +add_executable(test test.c) +target_link_libraries(test spb) \ No newline at end of file diff --git a/README.md b/README.md index 3411da6..88e5221 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,41 @@ # simple-protobuf -A subset of Google's ProtoBuf. + +English | [简体中文](README_ZH.md) + +A simplified variant of Google's ProtoBuf. + +# format +A simple-protobuf file can be filled into a C struct directly, but no pointer is supported. + +The sp file format is shown below. + +```bash +[struct_len] + n*[[type][data_len][data]] +``` + +1. `type` is an one-byte number indicating the type length(2^type^bytes) of this struct item. +2. `*_len` is a length-variable LE number(less than 2^57^) indicating the length of `*`. +3. `data` contains `data_len` bytes of the data. + +# usage +You can read `test.c` to find out the detailed usage. + +## Save a struct +### 1. Create items type info array + +This array has type `uint8_t`, which indicates the length of each struct item. Note that the length of each item must be 2^n^. + +### 2. Align items type info array + +You can use function `align_struct` to align each item to the proper size. + +If you are sure that your struct has been aligned, you won't need to run `align_struct`. + +### 3. Call set_pb to save + + +## Read into a struct + +### 1. Open file + +### 2. Call get_pb to read \ No newline at end of file diff --git a/README_ZH.md b/README_ZH.md new file mode 100644 index 0000000..f99fc15 --- /dev/null +++ b/README_ZH.md @@ -0,0 +1,37 @@ +# simple-protobuf + +[English](README.md) | 简体中文 + +Google ProtoBuf的简化变体。 + +# 数据格式 +本程序生成的文件与C结构体一一映射,但该结构体不可包含指针,具体如下所示。 + +```bash +[struct_len] + n*[[type][data_len][data]] +``` + +1. `type` 指明该项的类型,亦即其在结构体中实际占用的空间的对数。 +2. `*_len` 是一个小于 2^57^ 的变长数字。 +3. `data` 是 `data_len` 长度的数据。 + +# 用法 +比较简单,详见`test.c`。 + +## 保存结构体 +### 1. 创建 items_type 数组 + +是结构体所有项的`type`组成的数组。 + +### 2. 类型对齐 + +接下来需要用`align_struct`进行对齐。如果您已经将数组对齐或结构体本身是对齐的,则无需此步骤。 + +### 3. 调用 set_pb 保存 + + +## 读入结构体 + +### 1. 打开文件 + +### 2. 调用 get_pb 读取 diff --git a/protobuf_le.c b/protobuf_le.c new file mode 100644 index 0000000..c8e0b83 --- /dev/null +++ b/protobuf_le.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include "simple_protobuf.h" + +//#define DEBUG + +static uint64_t read_num(FILE* fp) { + uint8_t c; + uint64_t n = 0; + uint8_t i = 0; + do { + c = fgetc(fp); + n |= (c & 0x7f) << (7 * i++); + } while(c & 0x80); + return n; +} + +static int write_num(FILE* fp, uint64_t n) { + char* c = (char*)(&n); + int i = 0; + while(*c) { + int ch = *c & 0x7f; + if(c[1]) ch |= 0x80; + fputc(ch, fp); + n >>= 7; + i++; + } + return i; +} + +SIMPLE_PB* get_pb(FILE* fp) { + uint64_t struct_len = read_num(fp); + if(struct_len > 1) { + SIMPLE_PB* spb = malloc(struct_len + sizeof(uint64_t)); + #ifdef DEBUG + printf("Malloc %llu + %lu bytes.\n", struct_len, sizeof(uint64_t)); + #endif + if(spb) { + spb->len = struct_len; + char* p = spb->target; + char* end = p + struct_len; + memset(p, 0, struct_len); + while(p < end) { + uint64_t offset = 1u << fgetc(fp); + uint64_t data_len = read_num(fp); + #ifdef DEBUG + printf("Offset: %llu, data_len: %llu.\n", offset, data_len); + #endif + fread(p, data_len, 1, fp); + p += offset; + } + return spb; + } + } + return NULL; +} + +int set_pb(FILE* fp, uint8_t* items_type, uint64_t struct_len, void* target) { + uint64_t offset = 0; + uint32_t i = 0; + char* p = (char*)target; + write_num(fp, struct_len); + while(offset < struct_len) { + uint8_t type = items_type[i++]; + uint64_t data_len = 1u << type; + fputc(type, fp); + char* this = p + offset; + offset += data_len; + if(data_len > 1) while(!this[data_len - 1]) data_len--; + write_num(fp, data_len); + fwrite(this, data_len, 1, fp); + } + return i; +} \ No newline at end of file diff --git a/simple_protobuf.h b/simple_protobuf.h new file mode 100644 index 0000000..851eff9 --- /dev/null +++ b/simple_protobuf.h @@ -0,0 +1,43 @@ +#ifndef _SIMPLE_PROTOBUF_H_ +#define _SIMPLE_PROTOBUF_H_ + +#include + +struct SIMPLE_PB { + uint64_t len; + char target[]; +}; +typedef struct SIMPLE_PB SIMPLE_PB; + +SIMPLE_PB* get_pb(FILE* fp); +int set_pb(FILE* fp, uint8_t* items_type, uint64_t struct_len, void* target); + +uint8_t first_set(uint64_t n) { + uint8_t i = 0; + while(!(n & 1)) { + n >>= 1; + i++; + } + return i; +} + +void align_struct(uint8_t* items_type, uint64_t items_cnt, uint64_t struct_size) { + uint64_t sum = 0; + uint64_t min_cnt = 0; + uint8_t min = 255; + for(uint64_t i = 0; i < items_cnt; i++) { + sum += 1u << items_type[i]; + if(min > items_type[i]) min = items_type[i]; + } + while(sum < struct_size) { + uint8_t new_min = 255; + sum = 0; + for(uint64_t i = 0; i < items_cnt; i++) { + if(items_type[i] == min) items_type[i]++; + if(new_min > items_type[i]) new_min = items_type[i]; + sum += 1u << items_type[i]; + } + } +} + +#endif \ No newline at end of file diff --git a/test.c b/test.c new file mode 100644 index 0000000..1d65574 --- /dev/null +++ b/test.c @@ -0,0 +1,45 @@ +#include +#include +#include "simple_protobuf.h" + +struct TEST { + uint8_t a; + uint16_t b; + uint32_t c; + uint64_t d; + char e[256]; +}; + +struct TEST t; +uint64_t items_len[5] = {sizeof(t.a), sizeof(t.b), sizeof(t.c), sizeof(t.d), sizeof(t.e)}; +uint8_t types_len[5]; + +int main() { + t.a = 0xAB; + t.b = 63345; + t.c = 1234567890; + t.d = 1234567898765435432ULL; + strcpy(t.e, "Hello world! This is a message from simple protobuf."); + for(int i = 0; i < 5; i++) { + types_len[i] = first_set(items_len[i]); + printf("Item %d has type %d with size %llu\n", i, types_len[i], items_len[i]); + } + align_struct(types_len, 5, sizeof(struct TEST)); + for(int i = 0; i < 5; i++) { + printf("Item %d's type after align: %u\n", i, types_len[i]); + } + FILE* fp = fopen("test.sp", "wb"); + if(fp) { + set_pb(fp, types_len, sizeof(struct TEST), &t); + memset(&t, 0, sizeof(struct TEST)); + fclose(fp); + puts("Write file succeed."); + fp = NULL; + fp = fopen("test.sp", "rb"); + if(fp) { + SIMPLE_PB* spb = get_pb(fp); + memcpy(&t, spb->target, sizeof(struct TEST)); + printf("a:%u\nb:%u\nc:%u\nd:%llu\ne:%s\n", t.a, t.b, t.c, t.d, t.e); + } else perror("[SPB]"); + } else perror("[SPB]"); +} \ No newline at end of file