summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cc4group.c61
-rw-r--r--src/cc4group.h14
-rw-r--r--src/cppc4group.cpp29
-rw-r--r--src/cppc4group.hpp11
4 files changed, 111 insertions, 4 deletions
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<Private>&& 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> CppC4Group::openAsChild(const std::string& path)
+{
+ auto g = cc4group.openAsChild(p->g, path.c_str());
+ if(g == nullptr)
+ {
+ return {};
+ }
+ else
+ {
+ return {{std::make_unique<Private>(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<Private> p;
+ // this is only needed for internal purposes
+ CppC4Group(std::unique_ptr<Private>&& 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<CppC4Group> openAsChild(const std::string& path);
};