From fa7025f77490c063aa3f896d967a0c90cfef42be Mon Sep 17 00:00:00 2001 From: William Miles Date: Fri, 9 Sep 2022 14:59:56 +1000 Subject: [PATCH] Split out sized buffer type and added tests. Modified reader to better utilise sized buffer. Debugged and working at least as well as previous. --- .gitignore | 2 +- NavmeshList/NavmeshList.vcxproj | 5 +- Navmesher.sln | 40 ++--- SizedBuffer/SizedBuffer.h | 81 ++++++++++ SizedBuffer/SizedBuffer.vcxproj | 138 ++++++++++++++++ SizedBuffer/SizedBuffer.vcxproj.filters | 27 ++++ SizedBuffer/SizedBufferTest.c | 147 +++++++++++++++++ espReader/ESPReader.h | 31 ++-- espReader/Reader.c | 207 ++++++++++++------------ espReader/espReader.vcxproj | 7 +- 10 files changed, 539 insertions(+), 146 deletions(-) create mode 100644 SizedBuffer/SizedBuffer.h create mode 100644 SizedBuffer/SizedBuffer.vcxproj create mode 100644 SizedBuffer/SizedBuffer.vcxproj.filters create mode 100644 SizedBuffer/SizedBufferTest.c diff --git a/.gitignore b/.gitignore index 7973d5d..a0a8eb0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore -NavmeshList/Skyrim.esm +*.esm # User-specific files *.rsuser diff --git a/NavmeshList/NavmeshList.vcxproj b/NavmeshList/NavmeshList.vcxproj index b79e729..6d85c46 100644 --- a/NavmeshList/NavmeshList.vcxproj +++ b/NavmeshList/NavmeshList.vcxproj @@ -409,7 +409,8 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ../ESPReader + ..\SizedBuffer;..\ESPReader + Disabled Console @@ -427,7 +428,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ../ESPReader + ..\SizedBuffer;..\ESPReader Console diff --git a/Navmesher.sln b/Navmesher.sln index edd55df..dea6d61 100644 --- a/Navmesher.sln +++ b/Navmesher.sln @@ -7,8 +7,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ESPReader", "espReader\espR EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NavmeshList", "NavmeshList\NavmeshList.vcxproj", "{4CE7E7FD-7FFF-4C42-AB16-C9C2B01E5D56}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mph_gen", "mph_gen\mph_gen.vcxproj", "{385E9D8D-F3F4-4FD4-B182-D06F877CB090}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libz-static", "zlib-win-build\build-VS2022\libz-static\libz-static.vcxproj", "{B56D17BC-072B-42F3-844A-870A07AFBAAA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F2B93142-BC5A-4D28-8AAA-24D52FA59514}" @@ -16,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SizedBuffer", "SizedBuffer\SizedBuffer.vcxproj", "{CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Itanium = Debug|Itanium @@ -65,24 +65,6 @@ Global {4CE7E7FD-7FFF-4C42-AB16-C9C2B01E5D56}.ReleaseWithoutAsm|x64.Build.0 = Release|x64 {4CE7E7FD-7FFF-4C42-AB16-C9C2B01E5D56}.ReleaseWithoutAsm|x86.ActiveCfg = Release|Win32 {4CE7E7FD-7FFF-4C42-AB16-C9C2B01E5D56}.ReleaseWithoutAsm|x86.Build.0 = Release|Win32 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Debug|Itanium.ActiveCfg = Debug|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Debug|Itanium.Build.0 = Debug|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Debug|x64.ActiveCfg = Debug|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Debug|x64.Build.0 = Debug|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Debug|x86.ActiveCfg = Debug|Win32 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Debug|x86.Build.0 = Debug|Win32 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Release|Itanium.ActiveCfg = Release|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Release|Itanium.Build.0 = Release|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Release|x64.ActiveCfg = Release|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Release|x64.Build.0 = Release|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Release|x86.ActiveCfg = Release|Win32 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.Release|x86.Build.0 = Release|Win32 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.ReleaseWithoutAsm|Itanium.Build.0 = Release|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.ReleaseWithoutAsm|x64.Build.0 = Release|x64 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.ReleaseWithoutAsm|x86.ActiveCfg = Release|Win32 - {385E9D8D-F3F4-4FD4-B182-D06F877CB090}.ReleaseWithoutAsm|x86.Build.0 = Release|Win32 {B56D17BC-072B-42F3-844A-870A07AFBAAA}.Debug|Itanium.ActiveCfg = Debug|x64 {B56D17BC-072B-42F3-844A-870A07AFBAAA}.Debug|Itanium.Build.0 = Debug|x64 {B56D17BC-072B-42F3-844A-870A07AFBAAA}.Debug|x64.ActiveCfg = Debug|x64 @@ -101,6 +83,24 @@ Global {B56D17BC-072B-42F3-844A-870A07AFBAAA}.ReleaseWithoutAsm|x64.Build.0 = Release|x64 {B56D17BC-072B-42F3-844A-870A07AFBAAA}.ReleaseWithoutAsm|x86.ActiveCfg = Release|Win32 {B56D17BC-072B-42F3-844A-870A07AFBAAA}.ReleaseWithoutAsm|x86.Build.0 = Release|Win32 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Debug|Itanium.ActiveCfg = Debug|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Debug|Itanium.Build.0 = Debug|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Debug|x64.ActiveCfg = Debug|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Debug|x64.Build.0 = Debug|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Debug|x86.ActiveCfg = Debug|Win32 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Debug|x86.Build.0 = Debug|Win32 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Release|Itanium.ActiveCfg = Release|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Release|Itanium.Build.0 = Release|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Release|x64.ActiveCfg = Release|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Release|x64.Build.0 = Release|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Release|x86.ActiveCfg = Release|Win32 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.Release|x86.Build.0 = Release|Win32 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.ReleaseWithoutAsm|Itanium.Build.0 = Release|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.ReleaseWithoutAsm|x64.Build.0 = Release|x64 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.ReleaseWithoutAsm|x86.ActiveCfg = Release|Win32 + {CB1C8F66-5B90-4DE7-890B-F6430DAAF25F}.ReleaseWithoutAsm|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SizedBuffer/SizedBuffer.h b/SizedBuffer/SizedBuffer.h new file mode 100644 index 0000000..cf6b57f --- /dev/null +++ b/SizedBuffer/SizedBuffer.h @@ -0,0 +1,81 @@ +#pragma once + +// Guards for C++ usage +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* A sized buffer abstraction. + * + * Functions that return int's will return 0 on success and nonzero otherwise. + */ + +typedef struct sized_buf SizedBuf; + +struct sized_buf +{ + char *data; + size_t size; +}; + +// Returns 0 if the given buffer has at least size bytes available +inline int sb_check(const SizedBuf sb, const size_t size) +{ + return !(sb.size >= size); +} + +// Moves the buffer size bytes forward, makes no checks +inline void sb_update(SizedBuf *const sb, const size_t size) +{ + sb->data += size; + sb->size -= size; +} + +// Copies size bytes of data from the source to the buffer +inline int sb_copyin(SizedBuf *const sb, const char *const source, const size_t size) +{ + int ret = sb_check(*sb, size); + if (!ret) { + memcpy(sb->data, source, size); + sb_update(sb, size); + } + return ret; +} + +// "Recasts" size bytes of memory and returns a pointer to said memory in out +inline int sb_recast(SizedBuf *const sb, const size_t size, const void **const out) +{ + int ret = sb_check(*sb, size); + if (!ret) { + *out = sb->data; + sb_update(sb, size); + } + return ret; +} + +// "Recasts" size bytes of memory without actually consuming the buffer +inline int sb_peek(const SizedBuf sb, const size_t size, const void **const out) +{ + *out = sb.data; + return sb_check(sb, size); +} + +// Returns a pointer just after the end of the sized buffer +inline char *sb_end(const SizedBuf sb) +{ + return sb.data + sb.size; +} + +inline bool sb_empty(const SizedBuf sb) +{ + return sb.size == 0; +} + +// End C++ guard +#ifdef __cplusplus +} +#endif + diff --git a/SizedBuffer/SizedBuffer.vcxproj b/SizedBuffer/SizedBuffer.vcxproj new file mode 100644 index 0000000..ad56421 --- /dev/null +++ b/SizedBuffer/SizedBuffer.vcxproj @@ -0,0 +1,138 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {cb1c8f66-5b90-4de7-890b-f6430daaf25f} + SizedBuffer + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/SizedBuffer/SizedBuffer.vcxproj.filters b/SizedBuffer/SizedBuffer.vcxproj.filters new file mode 100644 index 0000000..35bab92 --- /dev/null +++ b/SizedBuffer/SizedBuffer.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/SizedBuffer/SizedBufferTest.c b/SizedBuffer/SizedBufferTest.c new file mode 100644 index 0000000..fe10ae5 --- /dev/null +++ b/SizedBuffer/SizedBufferTest.c @@ -0,0 +1,147 @@ +#include "SizedBuffer.h" + +#undef NDBEUG +#include +#define NDEBUG +#include +#include +#include + + +void test_sb_check(void) { + SizedBuf t = { .size = 0 }; + assert(sb_check(t, 0) == 0); + assert(sb_check(t, 1) != 0); + assert(sb_check(t, SIZE_MAX) != 0); + + t.size = 100; + assert(sb_check(t, 99) == 0); + assert(sb_check(t, 100) == 0); + assert(sb_check(t, 101) != 0); + assert(sb_check(t, SIZE_MAX) != 0); + + t.size = SIZE_MAX; + assert(sb_check(t, 0) == 0); + assert(sb_check(t, 1) == 0); + assert(sb_check(t, SIZE_MAX) == 0); +} + +void test_sb_update(void) { + SizedBuf t; + + t = (SizedBuf){ .data = 0, .size = 0 }; + sb_update(&t, 0); + assert(t.data == 0 && t.size == 0); + + t.size = 100; + sb_update(&t, 25); + assert(t.data == (void *)25 && t.size == 75); + + sb_update(&t, 75); + assert(t.data == (void *)100 && t.size == 0); +} + +void test_sb_copyin(void) { +#define TEST_SIZE 128 + char buf[TEST_SIZE] = { 0 }; + SizedBuf sb = { .data = buf, .size = TEST_SIZE }; + + char test_buf[TEST_SIZE] = { 0 }; + memset(test_buf, 0xff, TEST_SIZE); + + int ret = 0; + + assert(sb_copyin(&sb, NULL, SIZE_MAX) != 0); + assert(sb_copyin(&sb, NULL, TEST_SIZE + 1) != 0); + assert(sb_copyin(&sb, test_buf, TEST_SIZE) == 0); + assert(memcmp(buf, test_buf, TEST_SIZE) == 0); + assert(sb.size == 0); + assert(sb.data == buf + TEST_SIZE); +#undef TEST_SIZE +} + +void test_sb_recast(void) { +#define TEST_SIZE 33 + char *const start = NULL; + SizedBuf t = { .data = start, .size = TEST_SIZE }; + void *out = NULL; + + assert(sb_recast(&t, SIZE_MAX, &out) != 0); + assert(sb_recast(&t, TEST_SIZE + 1, &out) != 0); + assert(sb_recast(&t, 13, &out) == 0); + assert(t.data == start + 13); + assert(t.size == TEST_SIZE - 13); + assert(out == NULL); + assert(sb_recast(&t, 8, &out) == 0); + assert(t.data == (void *)(13 + 8)); + assert(t.size == TEST_SIZE - 13 - 8); + assert(out == (void *)13); +#undef TEST_SIZE +} + +void test_sb_end(void) { + SizedBuf sb = { .data = NULL, .size = SIZE_MAX }; + assert(sb_end(sb) == (void *)SIZE_MAX); + + sb = (SizedBuf){ .data = (void *)0xdeadbeefull, .size = 0x1337 }; + assert(sb_end(sb) == (void *)(0xdeadbeefull + 0x1337ull)); +} + +void test_sb_empty(void) { + SizedBuf sb = { .data = NULL, .size = SIZE_MAX }; + assert(!sb_empty(sb)); + sb.size = 1; + assert(!sb_empty(sb)); + sb.size = 0; + assert(sb_empty(sb)); +} + +void test_sb_peek(void) { +#define TEST_SIZE 128 + SizedBuf sb = { .data = NULL, .size = TEST_SIZE }; + void *test = (void *)0xdeadbeefull; + assert(sb_peek(sb, SIZE_MAX, &test) != 0); + assert(sb_peek(sb, TEST_SIZE + 1, &test) != 0); + assert(sb_peek(sb, TEST_SIZE, &test) == 0); + assert(sb.data == test); + assert(sb.size == TEST_SIZE); + test = (void *)'FOUR'; + assert(sb_peek(sb, 0, &test) == 0); + assert(sb.data == test); + assert(sb.size == TEST_SIZE); +#undef TEST_SIZE +} + +typedef struct { + void (*test)(void); + const char *const name; +} Test; + +#define TEST(x) { x, #x } +Test tests[] = { + TEST(test_sb_check), + TEST(test_sb_update), + TEST(test_sb_copyin), + TEST(test_sb_recast), + TEST(test_sb_end), + TEST(test_sb_empty), + TEST(test_sb_peek), +}; +#undef TEST + +int main() { +#ifdef DISABLE_STDOUT + fclose(stdout); +#endif + + // run all tests + size_t num_tests = sizeof(tests) / sizeof(tests[0]); + for (size_t i = 0; i < num_tests; i++) { + tests[i].test(); + fprintf(stderr, "Test passed: %s\n", tests[i].name); + } + + fprintf(stderr, "All tests passed.\n"); + + return 0; +} \ No newline at end of file diff --git a/espReader/ESPReader.h b/espReader/ESPReader.h index dcdecca..2c5abf5 100644 --- a/espReader/ESPReader.h +++ b/espReader/ESPReader.h @@ -24,10 +24,13 @@ */ #include +#include #undef NDEBUG #include #define NDEBUG + #include "msh.h" +#include "SizedBuffer.h" // Guards for C++ usage #ifdef __cplusplus @@ -76,6 +79,7 @@ extern "C" { typedef struct field Field; typedef struct meta_node MetaNode; typedef struct meta_tree MetaTree; + typedef struct esp_stats ESPStats; // @@ -113,11 +117,6 @@ extern "C" { char _pad[4]; }; - struct sized_buf { - char *data; - size_t size; - }; - // // === ENUMS === // @@ -334,13 +333,6 @@ extern "C" { return rth2rt[uint32_t_msh(type, RT_HASH_BITS, RT_HASH_SEED)]; } - // Updates a sized_buf after using `size` bytes - inline void sized_buf_update(struct sized_buf *sb, size_t size) { - assert(sb->size >= size); - sb->data += size; - sb->size -= size; - } - /* `espr_walk` walks through the tree structure of the esp/esm binary * data starting at `data` of `size` bytes. * @@ -351,7 +343,7 @@ extern "C" { * increasing in terms of memory location within the buffer. */ void espr_walk( - struct sized_buf esp, + SizedBuf esp, struct walker_callbacks cb, void *from_parent ); @@ -359,13 +351,13 @@ extern "C" { /* `espr_print` prints the header of every group and record in the given * esp/esm binary data. */ - void espr_print(struct sized_buf esp); + void espr_print(SizedBuf esp); /* Calculates the number of groups and records in the esp/esm file and * the size of the esp/esm if all of the compressed records were * decompressed. */ - struct esp_stats espr_stats(struct sized_buf esp); + struct esp_stats espr_stats(SizedBuf esp); // Calculates the number of formid's in an esm/esp from the stats inline uint32_t espr_formid_count(struct esp_stats stats) { @@ -387,10 +379,7 @@ extern "C" { * as it does so. buf_size should be the value returned from * `espr_decompressed_size`, and `buf` should be at least of that size. */ - void espr_decompress( - struct sized_buf esp, - struct sized_buf decom - ); + void espr_decompress(SizedBuf esp, SizedBuf decom); /* Constructs a MetaNode tree in `tree` over the esp/esm data in `in`. * @@ -398,13 +387,13 @@ extern "C" { * data, and also allow for modifications that add, remove, or change * the size of groups/records/fields. */ - MetaTree espr_create_tree(struct sized_buf in, struct sized_buf tree); + MetaTree espr_create_tree(SizedBuf in, SizedBuf tree); void espr_meta_walk(MetaTree tree, struct meta_callbacks cb); void espr_meta_node_walk(MetaNode *m, struct meta_callbacks cb); - void espr_serialize(MetaTree tree, struct sized_buf out); + void espr_serialize(MetaTree tree, SizedBuf out); // End C++ guard #ifdef __cplusplus diff --git a/espReader/Reader.c b/espReader/Reader.c index 4298f1f..d401909 100644 --- a/espReader/Reader.c +++ b/espReader/Reader.c @@ -38,13 +38,9 @@ const int year_offset = 9; void asserts(void); // Tree walkers -char *walk_concat( - struct sized_buf tree, - struct walker_callbacks cb, - void *from_parent -); -char *walk_group(char *data, struct walker_callbacks cb, void *from_parent); -char *walk_record(char *data, struct walker_callbacks cb, void *from_parent); +void walk_concat(SizedBuf *tree, struct walker_callbacks cb, void *from_parent); +void walk_group(SizedBuf *tree, struct walker_callbacks cb, void *from_parent); +void walk_record(SizedBuf *tree, struct walker_callbacks cb, void *from_parent); // Header printers void print_group_header(Group *header); @@ -118,21 +114,23 @@ void asserts(void) } void espr_walk( - struct sized_buf esp, + SizedBuf esp, struct walker_callbacks cb, void *from_parent ) { // check assertions that cannot be checked at compile time asserts(); - char *data_start = esp.data; - // check that we are at the start of the file - const Type4 type = *(const Type4 *)esp.data; - assert(type.uint == rt[TES4]); + { + Type4 *type = NULL; + int err = sb_peek(esp, sizeof(Type4), &type); + assert(err == 0); + assert(type->uint == rt[TES4]); + } - esp.data = walk_concat(esp, cb, from_parent); - assert(esp.data == data_start + esp.size); + walk_concat(&esp, cb, from_parent); + assert(sb_empty(esp)); } /* Unknown data will be some concatenation of groups and records. @@ -140,27 +138,22 @@ void espr_walk( * `walk_concat` will call the appropriate walking function * for each segment of unknown data in this concatenation. */ -char *walk_concat( - struct sized_buf tree, - struct walker_callbacks cb, - void *from_parent -) { - const char *end = tree.data + tree.size; - while (tree.data != end) { - assert(tree.data < end); - - const Type4 *type = (Type4 *)tree.data; +void walk_concat(SizedBuf *tree, struct walker_callbacks cb, void *from_parent) +{ + while (!sb_empty(*tree)) { + const Type4 *type = NULL; + int err = sb_peek(*tree, sizeof(Type4), &type); + assert(err == 0); // check valid type assert(rt[rt_hash(type->uint)] == type->uint); // only need to distinguish between groups and records if (type->uint == rt[GRUP]) - tree.data = walk_group(tree.data, cb, from_parent); + walk_group(tree, cb, from_parent); else - tree.data = walk_record(tree.data, cb, from_parent); + walk_record(tree, cb, from_parent); } - return tree.data; } /* Walk a group record. Group records are containers for any other type of @@ -169,18 +162,25 @@ char *walk_concat( * This function will also call `cb` with the node constructed from this group * record. */ -char *walk_group(char *data, struct walker_callbacks cb, void *from_parent) +void walk_group(SizedBuf *tree, struct walker_callbacks cb, void *from_parent) { - Group *const header = (Group *const)data; + Group *header = NULL; + { + int err = sb_peek(*tree, sizeof(Group), &header); + assert(err == 0); + } - // The size in the group header includes the size of the header - char *data_start = data + sizeof(Group); - char *data_end = data + header->size; - size_t data_size = data_end - data_start; + SizedBuf group = { .size = header->size }; + { + int err = sb_recast(tree, group.size, &group.data); + assert(err == 0); + err = sb_recast(&group, sizeof(Group), &header); + assert(err == 0); + } Node n = { .header.group = header, - .data = data_start, + .data = group.data, .type = NT_GROUP }; void *carry = NULL; @@ -191,27 +191,30 @@ char *walk_group(char *data, struct walker_callbacks cb, void *from_parent) cb.pre(n, cb.data, &carry, from_parent, &to_children); // Walk through the concatenation of data inside the group. - struct sized_buf tree = { data_start, data_size }; - data = walk_concat(tree, cb, to_children); - assert(data == data_end); + walk_concat(&group, cb, to_children); + assert(sb_empty(group)); // Post-walk callback if (cb.post) cb.post(n, cb.data, carry); - - return data; } -char *walk_record(char *data, struct walker_callbacks cb, void *from_parent) +void walk_record(SizedBuf *tree, struct walker_callbacks cb, void *from_parent) { - Record *header = (Record *)data; - assert(header->type.uint != rt[GRUP]); + Record *header = NULL; + char *data = NULL; + { + int err = sb_recast(tree, sizeof(Record), &header); + assert(err == 0); + err = sb_recast(tree, header->size, &data); + assert(err == 0); + } - char *data_start = data + sizeof(Record); + assert(header->type.uint != rt[GRUP]); Node n = { .header.record = header, - .data = data_start, + .data = data, .type = NT_RECORD }; void *carry = NULL; @@ -226,17 +229,12 @@ char *walk_record(char *data, struct walker_callbacks cb, void *from_parent) if (cb.pre) cb.pre(n, cb.data, &carry, from_parent, &to_children); - // Update data ptr based on record size. - data += sizeof(Record) + header->size; - // Post-walk callback if (cb.post) cb.post(n, cb.data, carry); - - return data; } -void espr_print(struct sized_buf esp) +void espr_print(SizedBuf esp) { struct walker_callbacks cb = { .pre = print_cb }; espr_walk(esp, cb, NULL); @@ -265,9 +263,9 @@ void print_cb( } } -struct esp_stats espr_stats(struct sized_buf esp) +ESPStats espr_stats(SizedBuf esp) { - struct esp_stats stats = { 0 }; + ESPStats stats = { 0 }; struct walker_callbacks cb = { .pre = stats_cb, .data = &stats }; espr_walk(esp, cb, NULL); return stats; @@ -310,7 +308,7 @@ void stats_cb( } } -void espr_decompress(struct sized_buf esp, struct sized_buf decom) +void espr_decompress(SizedBuf esp, SizedBuf decom) { struct walker_callbacks cb = { .pre = decompress_pre, @@ -334,7 +332,7 @@ void espr_decompress(struct sized_buf esp, struct sized_buf decom) */ void decompress_pre( Node n, - void *decom_ptr, + void *out_buf, void **carry_out, void *from_parent, void **to_children @@ -342,28 +340,32 @@ void decompress_pre( (void)from_parent; (void)to_children; - struct sized_buf *d = decom_ptr; + SizedBuf *d = out_buf; switch (n.type) { case NT_RECORD: // compressed record if (n.header.record->flags & COMPRESSED_FLAG) { - // copy header - memcpy(d->data, n.header.record, sizeof(Record)); - - // copied header reference - Record *header = (Record *)d->data; - - // update decom struct - sized_buf_update(d, sizeof(Record)); + Record *copied_header = NULL; + { + // Acces for copied header + int err = sb_peek(*d, sizeof(Record), &copied_header); + assert(err == 0); + // copy header + err = sb_copyin(d, (char *)n.header.record, sizeof(Record)); + assert(err == 0); + } // decompress directly into buffer // first 4 bytes are the decompressed size const uint32_t dc_size = *((uint32_t *)n.data); uint32_t to_copy = dc_size; - uint32_t cur_size = - n.header.record->size - sizeof(uint32_t); + uint32_t cur_size = n.header.record->size - sizeof(uint32_t); char *data_start = n.data + sizeof(uint32_t); + + // check that we have enough space in the buffer + sb_check(*d, dc_size); + int ret = uncompress( (Bytef *)d->data, (uLongf *)&to_copy, @@ -373,32 +375,27 @@ void decompress_pre( assert(ret == Z_OK); assert(to_copy == dc_size); - // update decom struct - sized_buf_update(d, dc_size); + // update the buffer + sb_update(d, dc_size); // update header data size - header->size = dc_size; + copied_header->size = dc_size; // unset compressed flag - header->flags &= ~COMPRESSED_FLAG; + copied_header->flags &= ~COMPRESSED_FLAG; } else { // copy record - size_t record_size = sizeof(Record) + n.header.record->size; - memcpy(d->data, n.header.record, record_size); - - // update decom - sized_buf_update(d, record_size); + size_t record_size = sizeof(Record) + + n.header.record->size; + sb_copyin(d, (char *)n.header.record, record_size); } break; case NT_GROUP: - // copy header, contents will be copied while walking - memcpy(d->data, n.header.group, sizeof(Group)); - // save copied header location for post-walk group size recalc *carry_out = (void *)d->data; - // update decom - sized_buf_update(d, sizeof(Group)); + // copy header, contents will be copied while walking + sb_copyin(d, (char *)n.header.group, sizeof(Group)); break; default: @@ -412,9 +409,9 @@ void decompress_pre( * based on the difference between the current destination pointer and the * group header pointer. */ -void decompress_post(Node n, void *decom_ptr, void *carry_in) +void decompress_post(Node n, void *out, void *carry_in) { - struct sized_buf *d = decom_ptr; + SizedBuf *d = out; // only need to handle group resize if (n.type == NT_GROUP) { @@ -630,15 +627,16 @@ Timestamp convert_ts(uint16_t ts) return (Timestamp) { year, month, day }; } -MetaTree espr_create_tree(struct sized_buf in, struct sized_buf tree) +MetaTree espr_create_tree(SizedBuf in, SizedBuf tree) { // create root node - MetaNode *root = (MetaNode *)tree.data; + MetaNode *root = NULL; + { + int err = sb_recast(&tree, sizeof(MetaNode), &root); + assert(err == 0); + } *root = (MetaNode){ 0 }; - // update tree - sized_buf_update(&tree, sizeof(MetaNode)); - // walk struct walker_callbacks cb = { .pre = create_tree_cb, .data = &tree }; espr_walk(in, cb, root); @@ -656,9 +654,12 @@ void create_tree_cb( (void)carry_out; // add new metanode to tree - struct sized_buf *tree = data; - MetaNode *m = (MetaNode *)tree->data; - sized_buf_update(tree, sizeof(MetaNode)); + SizedBuf *tree = data; + MetaNode *m = NULL; + { + int err = sb_recast(tree, sizeof(MetaNode), &m); + assert(err == 0); + } // parent passes their MetaNode to children MetaNode *p = from_parent; @@ -692,13 +693,13 @@ void espr_meta_node_walk(MetaNode *m, struct meta_callbacks cb) { } } -void espr_serialize(MetaTree tree, struct sized_buf out) { +void espr_serialize(MetaTree tree, SizedBuf out) { struct meta_callbacks cb = { .pre = serialize_cb, .data = &out }; espr_meta_walk(tree, cb); } void serialize_cb(MetaNode *m, void *data) { - struct sized_buf *out = data; + SizedBuf *out = data; // exit on empty node if (!m->n.data) @@ -707,19 +708,25 @@ void serialize_cb(MetaNode *m, void *data) { switch (m->n.type) { case NT_GROUP: // only serialize the header of groups - assert(out->size >= sizeof(Group)); - memcpy(out->data, m->n.header.group, sizeof(Group)); - sized_buf_update(out, sizeof(Group)); - break; + { + int err = sb_copyin(out, (char *)m->n.header.group, sizeof(Group)); + assert(err == 0); + } + break; case NT_RECORD: size_t data_size = m->n.header.record->size; - assert(out->size >= data_size + sizeof(Record)); + char *header = (char *)m->n.header.record; // serialize header and data separately as they may be // discontiguous - memcpy(out->data, m->n.header.record, sizeof(Record)); - sized_buf_update(out, sizeof(Record)); - memcpy(out->data, m->n.data, data_size); - sized_buf_update(out, data_size); + { + int err = sb_copyin(out, header, sizeof(Record)); + assert(err == 0); + err = sb_copyin(out, m->n.data, data_size); + assert(err == 0); + } + break; + default: + assert(false); } } diff --git a/espReader/espReader.vcxproj b/espReader/espReader.vcxproj index 4f5460c..ad77202 100644 --- a/espReader/espReader.vcxproj +++ b/espReader/espReader.vcxproj @@ -111,7 +111,7 @@ true stdcpp17 stdc11 - ..\zlib-win-build + ..\zlib-win-build;..\SizedBuffer Disabled @@ -129,7 +129,7 @@ NDEBUG;_LIB;%(PreprocessorDefinitions) true stdc11 - ..\zlib-win-build + ..\zlib-win-build;..\SizedBuffer @@ -148,6 +148,9 @@ + + {cb1c8f66-5b90-4de7-890b-f6430daaf25f} + {b56d17bc-072b-42f3-844a-870a07afbaaa}