mirror of
https://github.com/logos-storage/easylibstorage.git
synced 2026-02-09 11:23:06 +00:00
feat: add e_storage_spr, fix memory management for resp
This commit is contained in:
parent
b2d4337f9a
commit
0f131937a8
@ -1,4 +1,4 @@
|
||||
# easystorage
|
||||
# libeasystorage
|
||||
|
||||
A simplified, higher level C wrapper around [libstorage](https://github.com/status-im/logos-storage-nim) built to showcase it. Allows filesharing in the Logos Storage network. Comes with an example console application to interact with it.
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "easystorage.h"
|
||||
#include "libstorage.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -17,8 +18,11 @@ typedef struct {
|
||||
size_t len;
|
||||
progress_callback pcb;
|
||||
int bytes_done;
|
||||
bool unreferenced;
|
||||
} resp;
|
||||
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static resp *resp_alloc(void) {
|
||||
resp *r = calloc(1, sizeof(resp));
|
||||
r->ret = -1;
|
||||
@ -27,7 +31,7 @@ static resp *resp_alloc(void) {
|
||||
|
||||
static void resp_destroy(resp *r) {
|
||||
if (!r) return;
|
||||
free(r->msg);
|
||||
if (r->msg) free(r->msg);
|
||||
free(r);
|
||||
}
|
||||
|
||||
@ -42,9 +46,12 @@ static void on_complete(int ret, const char *msg, size_t len, void *userData) {
|
||||
resp *r = userData;
|
||||
if (!r) return;
|
||||
|
||||
free(r->msg);
|
||||
r->msg = NULL;
|
||||
r->len = 0;
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (r->unreferenced) {
|
||||
resp_destroy(r);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg && len > 0) {
|
||||
r->msg = malloc(len + 1);
|
||||
@ -56,6 +63,8 @@ static void on_complete(int ret, const char *msg, size_t len, void *userData) {
|
||||
}
|
||||
|
||||
r->ret = ret;
|
||||
r->unreferenced = true;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
// Callback for operations that report progress before completing.
|
||||
@ -63,19 +72,23 @@ static void on_progress(int ret, const char *msg, size_t len, void *userData) {
|
||||
resp *r = userData;
|
||||
if (!r) return;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
if (r->unreferenced) {
|
||||
resp_destroy(r);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == RET_PROGRESS) {
|
||||
r->bytes_done += (int) len;
|
||||
if (r->pcb) {
|
||||
r->pcb(0, r->bytes_done, ret);
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return; // don't set r->ret yet — still in progress
|
||||
}
|
||||
|
||||
// Final callback (RET_OK or RET_ERR)
|
||||
free(r->msg);
|
||||
r->msg = NULL;
|
||||
r->len = 0;
|
||||
|
||||
if (msg && len > 0) {
|
||||
r->msg = malloc(len + 1);
|
||||
if (r->msg) {
|
||||
@ -86,11 +99,15 @@ static void on_progress(int ret, const char *msg, size_t len, void *userData) {
|
||||
}
|
||||
|
||||
r->ret = ret;
|
||||
r->unreferenced = true;
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
// Dispatches an async call, waits for completion, extracts the result.
|
||||
// Returns RET_OK/RET_ERR. If dispatch_ret != RET_OK, returns RET_ERR immediately.
|
||||
// If out is non-NULL and the response has a message, *out receives a strdup'd copy.
|
||||
// If **out is non-NULL, allocates a buffer and copies the content of
|
||||
// resp->msg onto it, which the caller must then free.
|
||||
static int call_wait(int dispatch_ret, resp *r, char **out) {
|
||||
if (dispatch_ret != RET_OK) {
|
||||
resp_destroy(r);
|
||||
@ -99,13 +116,19 @@ static int call_wait(int dispatch_ret, resp *r, char **out) {
|
||||
|
||||
resp_wait(r);
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
int result = (r->ret == RET_OK) ? RET_OK : RET_ERR;
|
||||
|
||||
if (out) {
|
||||
*out = r->msg ? strdup(r->msg) : NULL;
|
||||
}
|
||||
|
||||
resp_destroy(r);
|
||||
if (r->unreferenced) {
|
||||
resp_destroy(r);
|
||||
} else {
|
||||
r->unreferenced = true;
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -160,19 +183,22 @@ STORAGE_NODE e_storage_new(node_config config) {
|
||||
}
|
||||
|
||||
int e_storage_start(STORAGE_NODE node) {
|
||||
if (!node) return RET_ERR;
|
||||
if (!node)
|
||||
return RET_ERR;
|
||||
resp *r = resp_alloc();
|
||||
return call_wait(storage_start(node, (StorageCallback) on_complete, r), r, NULL);
|
||||
}
|
||||
|
||||
int e_storage_stop(STORAGE_NODE node) {
|
||||
if (!node) return RET_ERR;
|
||||
if (!node)
|
||||
return RET_ERR;
|
||||
resp *r = resp_alloc();
|
||||
return call_wait(storage_stop(node, (StorageCallback) on_complete, r), r, NULL);
|
||||
}
|
||||
|
||||
int e_storage_destroy(STORAGE_NODE node) {
|
||||
if (!node) return RET_ERR;
|
||||
if (!node)
|
||||
return RET_ERR;
|
||||
|
||||
// Close first (tolerate failure)
|
||||
resp *r = resp_alloc();
|
||||
@ -183,8 +209,21 @@ int e_storage_destroy(STORAGE_NODE node) {
|
||||
return call_wait(storage_destroy(node, (StorageCallback) on_complete, r), r, NULL);
|
||||
}
|
||||
|
||||
char *e_storage_spr(STORAGE_NODE node) {
|
||||
if (!node)
|
||||
return NULL;
|
||||
resp *r = resp_alloc();
|
||||
char *spr = NULL;
|
||||
int ret = call_wait(storage_spr(node, (StorageCallback) on_complete, r), r, &spr);
|
||||
if (ret != RET_OK) {
|
||||
return NULL;
|
||||
}
|
||||
return spr;
|
||||
}
|
||||
|
||||
char *e_storage_upload(STORAGE_NODE node, const char *filepath, progress_callback cb) {
|
||||
if (!node || !filepath) return NULL;
|
||||
if (!node || !filepath)
|
||||
return NULL;
|
||||
|
||||
// Init upload session
|
||||
resp *r = resp_alloc();
|
||||
@ -216,14 +255,15 @@ char *e_storage_upload(STORAGE_NODE node, const char *filepath, progress_callbac
|
||||
}
|
||||
|
||||
int e_storage_download(STORAGE_NODE node, const char *cid, const char *filepath, progress_callback cb) {
|
||||
if (!node || !cid || !filepath) return RET_ERR;
|
||||
if (!node || !cid || !filepath)
|
||||
return RET_ERR;
|
||||
|
||||
// Init download
|
||||
resp *r = resp_alloc();
|
||||
int ret =
|
||||
call_wait(storage_download_init(node, cid, DEFAULT_CHUNK_SIZE, false, (StorageCallback) on_complete, r), r,
|
||||
NULL);
|
||||
if (ret != RET_OK) return RET_ERR;
|
||||
int ret = call_wait(storage_download_init(node, cid, DEFAULT_CHUNK_SIZE, false, (StorageCallback) on_complete, r),
|
||||
r, NULL);
|
||||
if (ret != RET_OK)
|
||||
return RET_ERR;
|
||||
|
||||
// Stream to file with progress
|
||||
r = resp_alloc();
|
||||
|
||||
@ -20,6 +20,9 @@ int e_storage_start(STORAGE_NODE node);
|
||||
int e_storage_stop(STORAGE_NODE node);
|
||||
int e_storage_destroy(STORAGE_NODE node);
|
||||
|
||||
// Retrieves the node's SPR (caller must free), or NULL on failure.
|
||||
char *e_storage_spr(STORAGE_NODE node);
|
||||
|
||||
// Uploads a file. Returns CID string on success (caller must free), or NULL on failure.
|
||||
char *e_storage_upload(STORAGE_NODE node, const char *filepath, progress_callback cb);
|
||||
|
||||
|
||||
@ -18,7 +18,8 @@ void *storage_new(const char *configJson, StorageCallback callback, void *userDa
|
||||
}
|
||||
|
||||
int storage_start(void *ctx, StorageCallback callback, void *userData) {
|
||||
if (!ctx) return RET_ERR;
|
||||
if (!ctx)
|
||||
return RET_ERR;
|
||||
if (callback) {
|
||||
callback(RET_OK, "started", 7, userData);
|
||||
}
|
||||
@ -26,7 +27,8 @@ int storage_start(void *ctx, StorageCallback callback, void *userData) {
|
||||
}
|
||||
|
||||
int storage_stop(void *ctx, StorageCallback callback, void *userData) {
|
||||
if (!ctx) return RET_ERR;
|
||||
if (!ctx)
|
||||
return RET_ERR;
|
||||
if (callback) {
|
||||
callback(RET_OK, "stopped", 7, userData);
|
||||
}
|
||||
@ -34,7 +36,8 @@ int storage_stop(void *ctx, StorageCallback callback, void *userData) {
|
||||
}
|
||||
|
||||
int storage_close(void *ctx, StorageCallback callback, void *userData) {
|
||||
if (!ctx) return RET_ERR;
|
||||
if (!ctx)
|
||||
return RET_ERR;
|
||||
if (callback) {
|
||||
callback(RET_OK, "closed", 6, userData);
|
||||
}
|
||||
@ -42,7 +45,8 @@ int storage_close(void *ctx, StorageCallback callback, void *userData) {
|
||||
}
|
||||
|
||||
int storage_destroy(void *ctx, StorageCallback callback, void *userData) {
|
||||
if (!ctx) return RET_ERR;
|
||||
if (!ctx)
|
||||
return RET_ERR;
|
||||
if (callback) {
|
||||
callback(RET_OK, "destroyed", 9, userData);
|
||||
}
|
||||
@ -50,7 +54,8 @@ int storage_destroy(void *ctx, StorageCallback callback, void *userData) {
|
||||
}
|
||||
|
||||
int storage_upload_init(void *ctx, const char *filepath, size_t chunkSize, StorageCallback callback, void *userData) {
|
||||
if (!ctx) return RET_ERR;
|
||||
if (!ctx)
|
||||
return RET_ERR;
|
||||
// Return a fake session ID
|
||||
const char *session_id = "mock-session-123";
|
||||
if (callback) {
|
||||
@ -60,7 +65,8 @@ int storage_upload_init(void *ctx, const char *filepath, size_t chunkSize, Stora
|
||||
}
|
||||
|
||||
int storage_upload_file(void *ctx, const char *sessionId, StorageCallback callback, void *userData) {
|
||||
if (!ctx) return RET_ERR;
|
||||
if (!ctx)
|
||||
return RET_ERR;
|
||||
// Fire a progress callback first, then final OK with CID
|
||||
if (callback) {
|
||||
callback(RET_PROGRESS, "chunk", 5, userData);
|
||||
@ -72,7 +78,8 @@ int storage_upload_file(void *ctx, const char *sessionId, StorageCallback callba
|
||||
|
||||
int storage_download_init(void *ctx, const char *cid, size_t chunkSize, bool local, StorageCallback callback,
|
||||
void *userData) {
|
||||
if (!ctx) return RET_ERR;
|
||||
if (!ctx)
|
||||
return RET_ERR;
|
||||
if (callback) {
|
||||
callback(RET_OK, "init", 4, userData);
|
||||
}
|
||||
@ -81,10 +88,27 @@ int storage_download_init(void *ctx, const char *cid, size_t chunkSize, bool loc
|
||||
|
||||
int storage_download_stream(void *ctx, const char *cid, size_t chunkSize, bool local, const char *filepath,
|
||||
StorageCallback callback, void *userData) {
|
||||
if (!ctx) return RET_ERR;
|
||||
if (!ctx)
|
||||
return RET_ERR;
|
||||
if (callback) {
|
||||
callback(RET_PROGRESS, "data", 4, userData);
|
||||
callback(RET_OK, "done", 4, userData);
|
||||
}
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
int storage_spr(void *ctx, StorageCallback callback, void *userData) {
|
||||
const char *resp = "spr:"
|
||||
"CiUIAhIhAjWYLRhJho1LoZbaxILgJVTrHptSiejsvLKAqlumo4c4EgIDARpJCicAJQgCEiECNZgtGEmGjUuhltrEguAlVOs"
|
||||
"em1KJ6Oy8soCqW6ajhzgQsdDzywYaCwoJBLtZShSRAh-"
|
||||
"aGgsKCQS7WUoUkQIfmipHMEUCIQChjWYvEJE4bE55vtwyAuFvHoGqvpRouXasY7CaIg6nCQIgeCxTWoFmSCNEgMYinM7HuG"
|
||||
"oCKfoRkcC1pAycNnk-z3A";
|
||||
if (!ctx)
|
||||
return RET_ERR;
|
||||
|
||||
if (callback) {
|
||||
callback(RET_OK, resp, strlen(resp), userData);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
@ -114,6 +114,19 @@ static void test_download_null(void) {
|
||||
assert(ret == RET_ERR);
|
||||
}
|
||||
|
||||
static void test_get_should_get_node_spr(void) {
|
||||
node_config cfg = default_config();
|
||||
STORAGE_NODE node = e_storage_new(cfg);
|
||||
assert(node != NULL);
|
||||
assert(e_storage_start(node) == RET_OK);
|
||||
|
||||
const char *sprprefix = "spr:CiUIAhIhA";
|
||||
const char *spr = e_storage_spr(node);
|
||||
assert(spr != NULL);
|
||||
assert(strlen(spr) > 0);
|
||||
assert(strncmp(sprprefix, spr, strlen(sprprefix)) == 0);
|
||||
}
|
||||
|
||||
static void test_full_lifecycle(void) {
|
||||
node_config cfg = default_config();
|
||||
cfg.bootstrap_node = "spr:node1";
|
||||
@ -148,6 +161,7 @@ int main(void) {
|
||||
RUN_TEST(test_upload_null);
|
||||
RUN_TEST(test_download);
|
||||
RUN_TEST(test_download_null);
|
||||
RUN_TEST(test_get_should_get_node_spr);
|
||||
RUN_TEST(test_full_lifecycle);
|
||||
|
||||
printf("\n%d/%d tests passed.\n", tests_passed, tests_run);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user