From afaebd0407af3f88dfa9c46b74754a1d96f92ab5 Mon Sep 17 00:00:00 2001 From: Markus Mittendrein Date: Wed, 20 Mar 2019 17:47:51 +0100 Subject: Add openAsChild, currently without writing support --- src/cc4group.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/cc4group.h | 14 ++++++++++++- src/cppc4group.cpp | 29 ++++++++++++++++++++++++++ src/cppc4group.hpp | 11 +++++++++- 4 files changed, 111 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/cc4group.c b/src/cc4group.c index b00cc45..f257d6b 100644 --- a/src/cc4group.c +++ b/src/cc4group.c @@ -98,6 +98,9 @@ struct CC4Group_t { char* lastFormattedMessage; } error; + + size_t referenceCounter; + CC4Group* parent; }; static bool cc4group_applyMemoryManagementStart(CC4Group_MemoryManagement const management, const uint8_t** data, size_t size) @@ -951,6 +954,9 @@ static void cc4group_init(CC4Group* const this) { assert(this); + this->referenceCounter = 1; + this->parent = NULL; + this->uncompressedData = NULL; this->uncompressedSize = 0; @@ -1144,6 +1150,16 @@ static bool cc4group_openExisting(CC4Group* const this, const char* const path) return cc4group_uncompressGroupFromFile(this, path); } +static void cc4group_unreference(CC4Group* const this) +{ + assert(this); + + if(--this->referenceCounter == 0) + { + cc4group_delete(this); + } +} + static void cc4group_delete(CC4Group* const this) { assert(this); @@ -1578,6 +1594,12 @@ static bool cc4group_saveIt(CC4Group* const this, const char* const path, bool c assert(this); assert(path); + if(this->parent != NULL) + { + SET_MESSAGE_ERROR("Saving is currently not implemented for groups opened with openAsChild"); + return false; + } + bool success = false; int file = open(path, O_WRONLY | O_BINARY | O_CREAT | (overwrite ? O_TRUNC : O_EXCL), 0644); @@ -2047,6 +2069,38 @@ static bool cc4group_setExecutable(CC4Group* const this, bool const executable, return true; } +static CC4Group* cc4group_openAsChild(CC4Group* const this, const char* const path) +{ + assert(this); + + const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, false); + + if(entry == NULL) + { + return NULL; + } + + CC4Group* child = cc4group_new(); + if(child == NULL) + { + SET_ERRNO_ERROR("malloc: allocating the child group object"); + return NULL; + } + + memcpy(&child->root.core, &entry->core, sizeof(child->root.core)); + child->root.data = entry->data; + child->root.core.Directory = 1; + child->root.core.FileName[0] = '\0'; + child->root.children = entry->children; + child->parent = this; + + ++this->referenceCounter; + + CleanUpJobListAppend(child->cleanupJobs, (CC4Group_CleanupJob){.func = (CC4Group_CleanupFunc)cc4group_unreference, .data = this}); + + return child; +} + CC4Group_API cc4group = { .MemoryManagement = { .Take = Take, @@ -2065,7 +2119,7 @@ CC4Group_API cc4group = { .new = cc4group_new, - .delete = cc4group_delete, + .delete = cc4group_unreference, .create = cc4group_create, @@ -2107,5 +2161,8 @@ CC4Group_API cc4group = { .getErrorMessage = cc4group_getErrorMessage, .getErrorCode = cc4group_getErrorCode, .getErrorMethod = cc4group_getErrorMethod, - .getErrorCauser = cc4group_getErrorCauser + .getErrorCauser = cc4group_getErrorCauser, + + + .openAsChild = cc4group_openAsChild }; diff --git a/src/cc4group.h b/src/cc4group.h index 8b86d9c..207ef15 100644 --- a/src/cc4group.h +++ b/src/cc4group.h @@ -29,7 +29,7 @@ // the directory separator used for all entry paths in cc4group is "/" _regardless of the platform_. yes, you better watch out Windows users! // in contrast to paths names are only the the name of the entry itself, not the whole path // - to free all resources used by the group when it is not needed anymore, call cc4group.delete with the group pointer and discard it afterwards (i.e. don't use it anymore) -// - all functions, except cc4group.new, cc4group.delete and cc4group.setTmpMemoryStrategy (where the latter can't fail) follow the same scheme +// - all functions, except cc4group.new, cc4group.openAsChild, cc4group.delete and cc4group.setTmpMemoryStrategy (where the latter two can't fail) follow the same scheme // all of them can fail, either caused by wrong arguments or by things outside of the applications control // they all return _true_ if everything went well and _false_ if any error occured // information about the error (incredibly useful for debugging or asking for help with problems) can be obtained by using one the cc4group.getError* functions @@ -188,6 +188,8 @@ typedef struct { CC4Group* (*new)(void); // destructs the group object and frees all memory used by group, like the operator delete + // the group object may be freed at a later time if cc4group.openAsChild was used on it and the child is not deleted yet + // child relations are tracked through reference counting, thus the group will be really destructed when all referencing child groups are gone void (*delete)(CC4Group* const this); @@ -316,6 +318,16 @@ typedef struct { // returns human readable information during which operation the error occured const char* (*getErrorCauser)(const CC4Group* const this); + + + // returns a new CC4Group object that represents a subgroup denoted by path + // path must point to a directory + // in any error case NULL will be returned + // the returned group will share most resources with its parent (the one passed as this) + // the parent may be deleted anytime, but it will stay in memory until the child is deleted as well (using reference counting) + // because both share most of their data, modifications of the child will affect the parent and the opposite direction is also possible if the modifications involve the subgroup + // saving through child groups is not implemented yet, it will result in an error + CC4Group* (*openAsChild)(CC4Group* const this, const char* const path); } const CC4Group_API; diff --git a/src/cppc4group.cpp b/src/cppc4group.cpp index 1b4022e..950a714 100644 --- a/src/cppc4group.cpp +++ b/src/cppc4group.cpp @@ -25,6 +25,11 @@ struct CppC4Group::Private { } + Private(CC4Group* g) : g{g} + { + + } + ~Private() { cc4group.delete_(g); @@ -66,6 +71,17 @@ CppC4Group::CppC4Group() : p{new CppC4Group::Private{}} } +CppC4Group::CppC4Group(CppC4Group&& other) : p{std::move(other.p)} +{ + +} + +CppC4Group::CppC4Group(std::unique_ptr&& p) : p{std::move(p)} +{ + +} + + CppC4Group::~CppC4Group() { @@ -281,3 +297,16 @@ bool CppC4Group::setEntryData(const std::string& path, const void*const data, co { return cc4group.setEntryData(p->g, path.c_str(), data, size, convertMemoryManagement(management)); } + +std::optional CppC4Group::openAsChild(const std::string& path) +{ + auto g = cc4group.openAsChild(p->g, path.c_str()); + if(g == nullptr) + { + return {}; + } + else + { + return {{std::make_unique(g)}}; + } +} \ No newline at end of file diff --git a/src/cppc4group.hpp b/src/cppc4group.hpp index a1bb28a..c30af44 100644 --- a/src/cppc4group.hpp +++ b/src/cppc4group.hpp @@ -5,7 +5,7 @@ // although, when dealing with raw memory (because cc4group can't know the type of the stored data) also users of this wrapper have to deal with some void* // because most methods behave exactly the same, please look at cc4group.h for their description (they are named exactly the same with a few exceptions) // in case their are real differences, they will be noted in this header also -// until now, all examples only use cc4group's C-API, but as a C++-programmer you should have no problems reading them anyway +// until now, all except one example only use cc4group's C-API, but as a C++-programmer you should have no problems reading them anyway // one general difference to the C-API that is not mentioned at the individual methods is that functions actually returning data return an std::optional instead of a bool... // ...the optional will be populated normally and empty in error cases @@ -22,6 +22,9 @@ class CppC4Group { std::unique_ptr p; + // this is only needed for internal purposes + CppC4Group(std::unique_ptr&& p); + public: // this struct is used for the getEntryData-method to return them in a uniform way (instead of using reference- or pointer-arguments) struct Data { @@ -100,6 +103,9 @@ public: // the constructor will automatically construct an internal CC4Group, so no new-equivalent method is needed CppC4Group(); + // the move constructor is needed to move the returned group out of the optional from openAsChild + CppC4Group(CppC4Group&& other); + // the destructor will automatically delete the internal CC4Group, so also no extra method needed ~CppC4Group(); @@ -138,4 +144,7 @@ public: bool createFile(const std::string& path); bool setEntryData(const std::string& path, const void* const data = nullptr, const size_t size = 0, const MemoryManagement management = Copy); + + // to get the child group out of the optional in case of success, construct a new CppC4Group with the move constructor: CppC4Group child{std::move(*optionalChild)}; + std::optional openAsChild(const std::string& path); }; -- cgit v1.2.3-54-g00ecf