pcloudcc-lneely/pclsync/pfstasks_tree.c

167 lines
6.0 KiB
C

/*
* pfstasks_tree.c — pure tree operations for the fstask subsystem.
*
* All functions here are psql-free and operate only on in-memory psync_tree
* structures. Extracted so that unit tests can link this file alone without
* pulling in pfstasks.c's heavy dependencies (psql, pcrypto, pfs*, …).
*/
#include <stddef.h>
#include <string.h>
#include "pdbg.h"
#include "pfstasks_tree.h"
/* ------------------------------------------------------------------ */
/* Static helpers */
/* ------------------------------------------------------------------ */
/*
* BST search by name string at `nameoff`.
* If `taskid` != 0 and a name-equal node has a different taskid,
* walks prev/next siblings to find the matching one.
*/
static psync_tree *pfs_task_search_tree(psync_tree *tree, size_t nameoff,
const char *name, uint64_t taskid,
size_t taskidoff) {
int c;
while (tree) {
c = strcmp(name, ((char *)tree) + nameoff);
if (c < 0) tree = tree->left;
else if (c > 0) tree = tree->right;
else break;
}
if (!tree || !taskid ||
*((uint64_t *)(((char *)tree) + taskidoff)) == taskid)
return tree;
/* Walk siblings with the same name to find the matching taskid */
psync_tree *tn = ptree_get_prev(tree);
while (tn) {
if (strcmp(name, ((char *)tn) + nameoff)) break;
if (*((uint64_t *)(((char *)tn) + taskidoff)) == taskid) return tn;
tn = ptree_get_prev(tn);
}
tn = ptree_get_next(tree);
while (tn) {
if (strcmp(name, ((char *)tn) + nameoff)) break;
if (*((uint64_t *)(((char *)tn) + taskidoff)) == taskid) return tn;
tn = ptree_get_next(tn);
}
return NULL;
}
/*
* Linear in-order walk to find the first node whose uint64_t field at
* `taskidoff` equals `taskid`.
*/
static psync_tree *pfs_task_walk_tree(psync_tree *tree, uint64_t taskid,
size_t taskidoff) {
tree = ptree_get_first(tree);
while (tree) {
if (*((uint64_t *)(((char *)tree) + taskidoff)) == taskid) return tree;
tree = ptree_get_next(tree);
}
return NULL;
}
/* ------------------------------------------------------------------ */
/* Exported: tree insertion */
/* ------------------------------------------------------------------ */
void pfs_task_insert_into_tree(psync_tree **tree, size_t nameoff,
psync_tree *element) {
const char *name;
psync_tree *node;
int c;
if (!*tree) {
ptree_add_after(tree, NULL, element);
return;
}
name = ((char *)element) + nameoff;
node = *tree;
while (1) {
c = strcmp(name, ((char *)node) + nameoff);
if (c < 0) {
if (node->left)
node = node->left;
else {
ptree_add_before(tree, node, element);
return;
}
} else {
if (c == 0)
pdbg_logf(D_WARNING, "duplicate entry %s, should not happen",
name);
if (node->right)
node = node->right;
else {
ptree_add_after(tree, node, element);
return;
}
}
}
}
/* ------------------------------------------------------------------ */
/* Exported: find by name (+ optional taskid discriminator) */
/* ------------------------------------------------------------------ */
psync_fstask_mkdir_t *pfs_task_find_mkdir(psync_fstask_folder_t *folder,
const char *name, uint64_t taskid) {
return ptree_element(
pfs_task_search_tree(folder->mkdirs,
offsetof(psync_fstask_mkdir_t, name), name,
taskid, offsetof(psync_fstask_mkdir_t, taskid)),
psync_fstask_mkdir_t, tree);
}
psync_fstask_rmdir_t *pfs_task_find_rmdir(psync_fstask_folder_t *folder,
const char *name, uint64_t taskid) {
return ptree_element(
pfs_task_search_tree(folder->rmdirs,
offsetof(psync_fstask_rmdir_t, name), name,
taskid, offsetof(psync_fstask_rmdir_t, taskid)),
psync_fstask_rmdir_t, tree);
}
psync_fstask_creat_t *pfs_task_find_creat(psync_fstask_folder_t *folder,
const char *name, uint64_t taskid) {
return ptree_element(
pfs_task_search_tree(folder->creats,
offsetof(psync_fstask_creat_t, name), name,
taskid, offsetof(psync_fstask_creat_t, taskid)),
psync_fstask_creat_t, tree);
}
psync_fstask_unlink_t *pfs_task_find_unlink(psync_fstask_folder_t *folder,
const char *name, uint64_t taskid) {
return ptree_element(
pfs_task_search_tree(folder->unlinks,
offsetof(psync_fstask_unlink_t, name), name,
taskid, offsetof(psync_fstask_unlink_t, taskid)),
psync_fstask_unlink_t, tree);
}
/* ------------------------------------------------------------------ */
/* Exported: find by numeric ID */
/* ------------------------------------------------------------------ */
psync_fstask_mkdir_t *pfs_task_find_mkdir_by_folderid(
psync_fstask_folder_t *folder, psync_fsfolderid_t folderid) {
return ptree_element(
pfs_task_walk_tree(folder->mkdirs, folderid,
offsetof(psync_fstask_mkdir_t, folderid)),
psync_fstask_mkdir_t, tree);
}
psync_fstask_creat_t *pfs_task_find_creat_by_fileid(
psync_fstask_folder_t *folder, psync_fsfileid_t fileid) {
return ptree_element(
pfs_task_walk_tree(folder->creats, fileid,
offsetof(psync_fstask_creat_t, fileid)),
psync_fstask_creat_t, tree);
}