Replace the custom rbtree by a std::map

This commit is contained in:
Jocelyn Turcotte 2017-08-23 19:16:12 +02:00
parent 72e44ce3d7
commit d66c2b5fae
18 changed files with 153 additions and 1721 deletions

View file

@ -51,36 +51,6 @@
#include "csync_rename.h"
#include "c_jhash.h"
static int _key_cmp(const void *key, const void *data) {
uint64_t a;
csync_file_stat_t *b;
a = *(uint64_t *) (key);
b = (csync_file_stat_t *) data;
if (a < b->phash) {
return -1;
} else if (a > b->phash) {
return 1;
}
return 0;
}
static int _data_cmp(const void *key, const void *data) {
csync_file_stat_t *a, *b;
a = (csync_file_stat_t *) key;
b = (csync_file_stat_t *) data;
if (a->phash < b->phash) {
return -1;
} else if (a->phash > b->phash) {
return 1;
}
return 0;
}
csync_s::csync_s(const char *localUri, const char *db_file) {
size_t len = 0;
@ -91,9 +61,6 @@ csync_s::csync_s(const char *localUri, const char *db_file) {
local.uri = c_strndup(localUri, len);
c_rbtree_create(&local.tree, _key_cmp, _data_cmp);
c_rbtree_create(&remote.tree, _key_cmp, _data_cmp);
statedb.file = c_strdup(db_file);
}
@ -137,7 +104,7 @@ int csync_update(CSYNC *ctx) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
"Update detection for local replica took %.2f seconds walking %zu files.",
c_secdiff(finish, start), c_rbtree_size(ctx->local.tree));
c_secdiff(finish, start), ctx->local.files.size());
csync_memstat_check();
/* update detection for remote replica */
@ -157,7 +124,7 @@ int csync_update(CSYNC *ctx) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
"Update detection for remote replica took %.2f seconds "
"walking %zu files.",
c_secdiff(finish, start), c_rbtree_size(ctx->remote.tree));
c_secdiff(finish, start), ctx->remote.files.size());
csync_memstat_check();
ctx->status |= CSYNC_STATUS_UPDATE;
@ -195,7 +162,7 @@ int csync_reconcile(CSYNC *ctx) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
"Reconciliation for local replica took %.2f seconds visiting %zu files.",
c_secdiff(finish, start), c_rbtree_size(ctx->local.tree));
c_secdiff(finish, start), ctx->local.files.size());
if (rc < 0) {
if (!CSYNC_STATUS_IS_OK(ctx->status_code)) {
@ -215,7 +182,7 @@ int csync_reconcile(CSYNC *ctx) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
"Reconciliation for remote replica took %.2f seconds visiting %zu files.",
c_secdiff(finish, start), c_rbtree_size(ctx->remote.tree));
c_secdiff(finish, start), ctx->remote.files.size());
if (rc < 0) {
if (!CSYNC_STATUS_IS_OK(ctx->status_code)) {
@ -236,17 +203,11 @@ out:
/*
* local visitor which calls the user visitor with repacked stat info.
*/
static int _csync_treewalk_visitor(void *obj, void *data) {
static int _csync_treewalk_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
int rc = 0;
csync_file_stat_t *cur = NULL;
CSYNC *ctx = NULL;
csync_treewalk_visit_func *visitor = NULL;
_csync_treewalk_context *twctx = NULL;
c_rbtree_t *other_tree = NULL;
c_rbnode_t *other_node = NULL;
cur = (csync_file_stat_t *) obj;
ctx = (CSYNC *) data;
csync_s::FileMap *other_tree = nullptr;
if (ctx == NULL) {
return -1;
@ -255,51 +216,33 @@ static int _csync_treewalk_visitor(void *obj, void *data) {
/* we need the opposite tree! */
switch (ctx->current) {
case LOCAL_REPLICA:
other_tree = ctx->remote.tree;
other_tree = &ctx->remote.files;
break;
case REMOTE_REPLICA:
other_tree = ctx->local.tree;
other_tree = &ctx->local.files;
break;
default:
break;
}
other_node = c_rbtree_find(other_tree, &cur->phash);
csync_s::FileMap::const_iterator other_file_it = other_tree->find(cur->path);
if (!other_node) {
if (other_file_it == other_tree->cend()) {
/* Check the renamed path as well. */
int len;
uint64_t h = 0;
char *renamed_path = csync_rename_adjust_path(ctx, cur->path);
if (!c_streq(renamed_path, cur->path)) {
len = strlen( renamed_path );
h = c_jhash64((uint8_t *) renamed_path, len, 0);
other_node = c_rbtree_find(other_tree, &h);
}
SAFE_FREE(renamed_path);
QByteArray renamed_path = csync_rename_adjust_path(ctx, cur->path);
if (renamed_path != cur->path)
other_file_it = other_tree->find(renamed_path);
}
if (!other_node) {
if (other_file_it == other_tree->cend()) {
/* Check the source path as well. */
int len;
uint64_t h = 0;
char *renamed_path = csync_rename_adjust_path_source(ctx, cur->path);
if (!c_streq(renamed_path, cur->path)) {
len = strlen( renamed_path );
h = c_jhash64((uint8_t *) renamed_path, len, 0);
other_node = c_rbtree_find(other_tree, &h);
}
SAFE_FREE(renamed_path);
QByteArray renamed_path = csync_rename_adjust_path_source(ctx, cur->path);
if (renamed_path != cur->path)
other_file_it = other_tree->find(renamed_path);
}
csync_file_stat_t *other = other_node ? (csync_file_stat_t*)other_node->data : NULL;
csync_file_stat_t *other = (other_file_it != other_tree->cend()) ? other_file_it->second.get() : NULL;
if (obj == NULL || data == NULL) {
ctx->status_code = CSYNC_STATUS_PARAM_ERROR;
return -1;
}
ctx->status_code = CSYNC_STATUS_OK;
twctx = (_csync_treewalk_context*) ctx->callbacks.userdata;
@ -331,28 +274,24 @@ static int _csync_treewalk_visitor(void *obj, void *data) {
* which calls the local _csync_treewalk_visitor in this module.
* The user visitor is called from there.
*/
static int _csync_walk_tree(CSYNC *ctx, c_rbtree_t *tree, csync_treewalk_visit_func *visitor, int filter)
static int _csync_walk_tree(CSYNC *ctx, csync_s::FileMap *tree, csync_treewalk_visit_func *visitor, int filter)
{
_csync_treewalk_context tw_ctx;
int rc = -1;
int rc = 0;
if (ctx == NULL) {
errno = EBADF;
return rc;
}
if (visitor == NULL || tree == NULL) {
ctx->status_code = CSYNC_STATUS_PARAM_ERROR;
return rc;
}
tw_ctx.userdata = ctx->callbacks.userdata;
tw_ctx.user_visitor = visitor;
tw_ctx.instruction_filter = filter;
ctx->callbacks.userdata = &tw_ctx;
rc = c_rbtree_walk(tree, (void*) ctx, _csync_treewalk_visitor);
for (auto &pair : *tree) {
if (_csync_treewalk_visitor(pair.second.get(), ctx) < 0) {
rc = -1;
break;
}
}
if( rc < 0 ) {
if( ctx->status_code == CSYNC_STATUS_OK )
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_TREE_ERROR);
@ -367,18 +306,9 @@ static int _csync_walk_tree(CSYNC *ctx, c_rbtree_t *tree, csync_treewalk_visit_f
*/
int csync_walk_remote_tree(CSYNC *ctx, csync_treewalk_visit_func *visitor, int filter)
{
c_rbtree_t *tree = NULL;
int rc = -1;
if(ctx != NULL) {
ctx->status_code = CSYNC_STATUS_OK;
ctx->current = REMOTE_REPLICA;
tree = ctx->remote.tree;
}
/* all error handling in the called function */
rc = _csync_walk_tree(ctx, tree, visitor, filter);
return rc;
ctx->status_code = CSYNC_STATUS_OK;
ctx->current = REMOTE_REPLICA;
return _csync_walk_tree(ctx, &ctx->remote.files, visitor, filter);
}
/*
@ -386,25 +316,9 @@ int csync_walk_remote_tree(CSYNC *ctx, csync_treewalk_visit_func *visitor, int
*/
int csync_walk_local_tree(CSYNC *ctx, csync_treewalk_visit_func *visitor, int filter)
{
c_rbtree_t *tree = NULL;
int rc = -1;
if (ctx != NULL) {
ctx->status_code = CSYNC_STATUS_OK;
ctx->current = LOCAL_REPLICA;
tree = ctx->local.tree;
}
/* all error handling in the called function */
rc = _csync_walk_tree(ctx, tree, visitor, filter);
return rc;
}
static void _tree_destructor(void *data) {
csync_file_stat_t *freedata = NULL;
freedata = (csync_file_stat_t *) data;
delete freedata;
ctx->status_code = CSYNC_STATUS_OK;
ctx->current = LOCAL_REPLICA;
return _csync_walk_tree(ctx, &ctx->local.files, visitor, filter);
}
int csync_s::reinitialize() {
@ -419,26 +333,12 @@ int csync_s::reinitialize() {
}
statedb.db = NULL;
/* destroy the rbtrees */
if (c_rbtree_size(local.tree) > 0) {
c_rbtree_destroy(local.tree, _tree_destructor);
}
if (c_rbtree_size(remote.tree) > 0) {
c_rbtree_destroy(remote.tree, _tree_destructor);
}
/* free memory */
c_rbtree_free(local.tree);
c_rbtree_free(remote.tree);
remote.read_from_db = 0;
read_remote_from_db = true;
db_is_empty = false;
/* Create new trees */
c_rbtree_create(&local.tree, _key_cmp, _data_cmp);
c_rbtree_create(&remote.tree, _key_cmp, _data_cmp);
local.files.clear();
remote.files.clear();
status = CSYNC_STATUS_INIT;
SAFE_FREE(error_string);
@ -454,19 +354,6 @@ csync_s::~csync_s() {
}
statedb.db = NULL;
/* destroy the rbtrees */
if (c_rbtree_size(local.tree) > 0) {
c_rbtree_destroy(local.tree, _tree_destructor);
}
if (c_rbtree_size(remote.tree) > 0) {
c_rbtree_destroy(remote.tree, _tree_destructor);
}
/* free memory */
c_rbtree_free(local.tree);
c_rbtree_free(remote.tree);
SAFE_FREE(statedb.file);
SAFE_FREE(local.uri);
SAFE_FREE(error_string);

View file

@ -36,6 +36,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <sqlite3.h>
#include <map>
#include "config_csync.h"
#include "std/c_lib.h"
@ -69,6 +70,14 @@ enum csync_replica_e {
* @brief csync public structure
*/
struct OCSYNC_EXPORT csync_s {
class FileMap : public std::map<QByteArray, std::unique_ptr<csync_file_stat_t>> {
public:
csync_file_stat_t *findFile(const QByteArray &key) const {
auto it = find(key);
return it != end() ? it->second.get() : nullptr;
}
};
struct {
csync_auth_callback auth_function = nullptr;
void *userdata = nullptr;
@ -76,8 +85,8 @@ struct OCSYNC_EXPORT csync_s {
void *update_callback_userdata = nullptr;
/* hooks for checking the white list (uses the update_callback_userdata) */
int (*checkSelectiveSyncBlackListHook)(void*, const char*) = nullptr;
int (*checkSelectiveSyncNewFolderHook)(void*, const char* /* path */, const char* /* remotePerm */) = nullptr;
int (*checkSelectiveSyncBlackListHook)(void*, const QByteArray &) = nullptr;
int (*checkSelectiveSyncNewFolderHook)(void*, const QByteArray &/* path */, const QByteArray &/* remotePerm */) = nullptr;
csync_vio_opendir_hook remote_opendir_hook = nullptr;
@ -105,17 +114,17 @@ struct OCSYNC_EXPORT csync_s {
} statedb;
struct {
std::map<std::string, std::string> folder_renamed_to; // map from->to
std::map<std::string, std::string> folder_renamed_from; // map to->from
std::map<QByteArray, QByteArray> folder_renamed_to; // map from->to
std::map<QByteArray, QByteArray> folder_renamed_from; // map to->from
} renames;
struct {
char *uri = nullptr;
c_rbtree_t *tree = nullptr;
FileMap files;
} local;
struct {
c_rbtree_t *tree = nullptr;
FileMap files;
bool read_from_db = false;
QByteArray root_perms; /* Permission of the root folder. (Since the root folder is not in the db tree, we need to keep a separate entry.) */
} remote;
@ -151,6 +160,13 @@ struct OCSYNC_EXPORT csync_s {
csync_s(const char *localUri, const char *db_file);
~csync_s();
int reinitialize();
// For some reason MSVC references the copy constructor and/or the assignment operator
// if a class is exported. This is a problem since unique_ptr isn't copyable.
// Explicitly disable them to fix the issue.
// https://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/e39ab33d-1aaf-4125-b6de-50410d9ced1d
csync_s(const csync_s &) = delete;
csync_s &operator=(const csync_s &) = delete;
};
/*

View file

@ -37,33 +37,28 @@
/* Check if a file is ignored because one parent is ignored.
* return the node of the ignored directoy if it's the case, or NULL if it is not ignored */
static c_rbnode_t *_csync_check_ignored(c_rbtree_t *tree, const char *path, int pathlen) {
uint64_t h = 0;
c_rbnode_t *node = NULL;
static csync_file_stat_t *_csync_check_ignored(csync_s::FileMap *tree, const QByteArray &path) {
/* compute the size of the parent directory */
int parentlen = pathlen - 1;
while (parentlen > 0 && path[parentlen] != '/') {
int parentlen = path.size() - 1;
while (parentlen > 0 && path.at(parentlen) != '/') {
parentlen--;
}
if (parentlen <= 0) {
return NULL;
return nullptr;
}
h = c_jhash64((uint8_t *) path, parentlen, 0);
node = c_rbtree_find(tree, &h);
if (node) {
csync_file_stat_t *n = (csync_file_stat_t*)node->data;
if (n->instruction == CSYNC_INSTRUCTION_IGNORE) {
QByteArray parentPath = path.left(parentlen);
csync_file_stat_t *fs = tree->findFile(parentPath);
if (fs) {
if (fs->instruction == CSYNC_INSTRUCTION_IGNORE) {
/* Yes, we are ignored */
return node;
return fs;
} else {
/* Not ignored */
return NULL;
return nullptr;
}
} else {
/* Try if the parent itself is ignored */
return _csync_check_ignored(tree, path, parentlen);
return _csync_check_ignored(tree, parentPath);
}
}
@ -83,7 +78,7 @@ static bool _csync_is_collision_safe_hash(const char *checksum_header)
/**
* The main function in the reconcile pass.
*
* It's called for each entry in the local and remote rbtrees by
* It's called for each entry in the local and remote files by
* csync_reconcile()
*
* Before the reconcile phase the trees already know about changes
@ -107,52 +102,37 @@ static bool _csync_is_collision_safe_hash(const char *checksum_header)
* (timestamp is newer), it is not overwritten. If both files, on the
* source and the destination, have been changed, the newer file wins.
*/
static int _csync_merge_algorithm_visitor(void *obj, void *data) {
csync_file_stat_t *cur = NULL;
csync_file_stat_t *other = NULL;
static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
std::unique_ptr<csync_file_stat_t> tmp;
uint64_t h = 0;
int len = 0;
CSYNC *ctx = NULL;
c_rbtree_t *tree = NULL;
c_rbnode_t *node = NULL;
cur = (csync_file_stat_t *) obj;
ctx = (CSYNC *) data;
csync_s::FileMap *other_tree = nullptr;
/* we need the opposite tree! */
switch (ctx->current) {
case LOCAL_REPLICA:
tree = ctx->remote.tree;
other_tree = &ctx->remote.files;
break;
case REMOTE_REPLICA:
tree = ctx->local.tree;
other_tree = &ctx->local.files;
break;
default:
break;
}
node = c_rbtree_find(tree, &cur->phash);
csync_file_stat_t *other = other_tree->findFile(cur->path);;
if (!node) {
if (!other) {
/* Check the renamed path as well. */
char *renamed_path = csync_rename_adjust_path(ctx, cur->path);
if (renamed_path != cur->path) {
len = strlen( renamed_path );
h = c_jhash64((uint8_t *) renamed_path, len, 0);
node = c_rbtree_find(tree, &h);
}
SAFE_FREE(renamed_path);
other = other_tree->findFile(csync_rename_adjust_path(ctx, cur->path));
}
if (!node) {
if (!other) {
/* Check if it is ignored */
node = _csync_check_ignored(tree, cur->path, cur->path.size());
other = _csync_check_ignored(other_tree, cur->path);
/* If it is ignored, other->instruction will be IGNORE so this one will also be ignored */
}
/* file only found on current replica */
if (node == NULL) {
if (!other) {
switch(cur->instruction) {
/* file has been modified */
case CSYNC_INSTRUCTION_EVAL:
@ -187,26 +167,20 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
}
if( tmp ) {
len = strlen( tmp->path );
if( len > 0 ) {
h = c_jhash64((uint8_t *) tmp->path.constData(), len, 0);
if( !tmp->path.isEmpty() ) {
/* First, check that the file is NOT in our tree (another file with the same name was added) */
node = c_rbtree_find(ctx->current == REMOTE_REPLICA ? ctx->remote.tree : ctx->local.tree, &h);
if (node) {
csync_s::FileMap *our_tree = ctx->current == REMOTE_REPLICA ? &ctx->remote.files : &ctx->local.files;
if (our_tree->findFile(tmp->path)) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Origin found in our tree : %s", tmp->path.constData());
} else {
/* Find the temporar file in the other tree. */
node = c_rbtree_find(tree, &h);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "PHash of temporary opposite (%s): %" PRIu64 " %s",
tmp->path.constData() , h, node ? "found": "not found" );
if (node) {
other = (csync_file_stat_t*)node->data;
} else {
/* the renamed file could not be found in the opposite tree. That is because it
* is not longer existing there, maybe because it was renamed or deleted.
* The journal is cleaned up later after propagation.
*/
}
/* Find the temporar file in the other tree.
* If the renamed file could not be found in the opposite tree, that is because it
* is not longer existing there, maybe because it was renamed or deleted.
* The journal is cleaned up later after propagation.
*/
other = other_tree->findFile(tmp->path);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Temporary opposite (%s) %s",
tmp->path.constData() , other ? "found": "not found" );
}
}
@ -250,7 +224,6 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
/*
* file found on the other replica
*/
other = (csync_file_stat_t *) node->data;
switch (cur->instruction) {
case CSYNC_INSTRUCTION_UPDATE_METADATA:
@ -395,25 +368,26 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
}
int csync_reconcile_updates(CSYNC *ctx) {
int rc;
c_rbtree_t *tree = NULL;
csync_s::FileMap *tree = nullptr;
switch (ctx->current) {
case LOCAL_REPLICA:
tree = ctx->local.tree;
tree = &ctx->local.files;
break;
case REMOTE_REPLICA:
tree = ctx->remote.tree;
tree = &ctx->remote.files;
break;
default:
break;
}
rc = c_rbtree_walk(tree, (void *) ctx, _csync_merge_algorithm_visitor);
if( rc < 0 ) {
ctx->status_code = CSYNC_STATUS_RECONCILE_ERROR;
for (auto &pair : *tree) {
if (_csync_merge_algorithm_visitor(pair.second.get(), ctx) < 0) {
ctx->status_code = CSYNC_STATUS_RECONCILE_ERROR;
return -1;
}
}
return rc;
return 0;
}
/* vim: set ts=8 sw=2 et cindent: */

View file

@ -21,45 +21,43 @@
#include "csync_private.h"
#include "csync_rename.h"
#include <string>
#include <vector>
#include <algorithm>
static std::string _parentDir(const std::string &path) {
static QByteArray _parentDir(const QByteArray &path) {
int len = path.length();
while(len > 0 && path[len-1]!='/') len--;
while(len > 0 && path[len-1]=='/') len--;
return path.substr(0, len);
while(len > 0 && path.at(len-1)!='/') len--;
while(len > 0 && path.at(len-1)=='/') len--;
return path.left(len);
}
void csync_rename_record(CSYNC* ctx, const char* from, const char* to)
void csync_rename_record(CSYNC* ctx, const QByteArray &from, const QByteArray &to)
{
ctx->renames.folder_renamed_to[from] = to;
ctx->renames.folder_renamed_from[to] = from;
}
char* csync_rename_adjust_path(CSYNC* ctx, const char* path)
QByteArray csync_rename_adjust_path(CSYNC* ctx, const QByteArray &path)
{
for (std::string p = _parentDir(path); !p.empty(); p = _parentDir(p)) {
std::map< std::string, std::string >::iterator it = ctx->renames.folder_renamed_to.find(p);
for (QByteArray p = _parentDir(path); !p.isEmpty(); p = _parentDir(p)) {
std::map< QByteArray, QByteArray >::iterator it = ctx->renames.folder_renamed_to.find(p);
if (it != ctx->renames.folder_renamed_to.end()) {
std::string rep = it->second + (path + p.length());
return c_strdup(rep.c_str());
QByteArray rep = it->second + path.mid(p.length());
return rep;
}
}
return c_strdup(path);
return path;
}
char* csync_rename_adjust_path_source(CSYNC* ctx, const char* path)
QByteArray csync_rename_adjust_path_source(CSYNC* ctx, const QByteArray &path)
{
for (std::string p = _parentDir(path); !p.empty(); p = _parentDir(p)) {
std::map< std::string, std::string >::iterator it = ctx->renames.folder_renamed_from.find(p);
for (QByteArray p = _parentDir(path); !p.isEmpty(); p = _parentDir(p)) {
std::map< QByteArray, QByteArray >::iterator it = ctx->renames.folder_renamed_from.find(p);
if (it != ctx->renames.folder_renamed_from.end()) {
std::string rep = it->second + (path + p.length());
return c_strdup(rep.c_str());
QByteArray rep = it->second + path.mid(p.length());
return rep;
}
}
return c_strdup(path);
return path;
}
bool csync_rename_count(CSYNC *ctx) {

View file

@ -23,9 +23,9 @@
#include "csync.h"
/* Return the final destination path of a given patch in case of renames */
char OCSYNC_EXPORT *csync_rename_adjust_path(CSYNC *ctx, const char *path);
QByteArray OCSYNC_EXPORT csync_rename_adjust_path(CSYNC *ctx, const QByteArray &path);
/* Return the source of a given path in case of renames */
char OCSYNC_EXPORT *csync_rename_adjust_path_source(CSYNC *ctx, const char *path);
void OCSYNC_EXPORT csync_rename_record(CSYNC *ctx, const char *from, const char *to);
QByteArray OCSYNC_EXPORT csync_rename_adjust_path_source(CSYNC *ctx, const QByteArray &path);
void OCSYNC_EXPORT csync_rename_record(CSYNC *ctx, const QByteArray &from, const QByteArray &to);
/* Return the amount of renamed item recorded */
bool OCSYNC_EXPORT csync_rename_count(CSYNC *ctx);

View file

@ -486,18 +486,15 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
}
/* store into result list. */
if (c_rbtree_insert(ctx->remote.tree, (void *) st.release()) < 0) {
st.reset();
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
break;
}
QByteArray path = st->path;
ctx->remote.files[path] = std::move(st);
cnt++;
}
} while( rc == SQLITE_ROW );
ctx->statedb.lastReturnValue = rc;
if( rc != SQLITE_DONE ) {
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
ctx->status_code = CSYNC_STATUS_STATEDB_LOAD_ERROR;
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "%" PRId64 " entries read below path %s from db.", cnt, path);
}

View file

@ -383,18 +383,13 @@ out:
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "file: %s, instruction: %s <<=", fs->path.constData(),
csync_instruction_str(fs->instruction));
QByteArray path = fs->path;
switch (ctx->current) {
case LOCAL_REPLICA:
if (c_rbtree_insert(ctx->local.tree, (void *) fs.release()) < 0) {
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
return -1;
}
ctx->local.files[path] = std::move(fs);
break;
case REMOTE_REPLICA:
if (c_rbtree_insert(ctx->remote.tree, (void *) fs.release()) < 0) {
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
return -1;
}
ctx->remote.files[path] = std::move(fs);
break;
default:
break;

View file

@ -17,7 +17,6 @@ set(CSTDLIB_LINK_LIBRARIES
set(cstdlib_SRCS
c_alloc.c
c_path.c
c_rbtree.c
c_string.c
c_time.c
c_utf8.cpp

View file

@ -24,7 +24,6 @@
#include "c_macro.h"
#include "c_alloc.h"
#include "c_path.h"
#include "c_rbtree.h"
#include "c_string.h"
#include "c_time.h"
#include "c_private.h"

View file

@ -1,743 +0,0 @@
/*
* cynapses libc functions
*
* Copyright (c) 2003-2004 by Andrew Suffield <asuffield@debian.org>
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This code was originally released under GPL but Andrew Suffield agreed to
* change it to LGPL.
*/
/*
* static function don't have NULL pointer checks, segfaults are intended.
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include "c_alloc.h"
#include "c_rbtree.h"
#define NIL &_sentinel /* all leafs are sentinels */
static c_rbnode_t _sentinel = {NULL, NIL, NIL, NULL, NULL, BLACK};
void c_rbtree_create(c_rbtree_t **rbtree, c_rbtree_compare_func *key_compare, c_rbtree_compare_func *data_compare) {
assert(rbtree);
assert(key_compare);
assert(data_compare);
c_rbtree_t *tree = NULL;
tree = c_malloc(sizeof(*tree));
tree->root = NIL;
tree->key_compare = key_compare;
tree->data_compare = data_compare;
tree->size = 0;
*rbtree = tree;
}
static c_rbnode_t *_rbtree_subtree_dup(const c_rbnode_t *node, c_rbtree_t *new_tree, c_rbnode_t *new_parent) {
c_rbnode_t *new_node = NULL;
new_node = (c_rbnode_t*) c_malloc(sizeof(c_rbnode_t));
new_node->tree = new_tree;
new_node->data = node->data;
new_node->color = node->color;
new_node->parent = new_parent;
if (node->left == NIL) {
new_node->left = NIL;
} else {
new_node->left = _rbtree_subtree_dup(node->left, new_tree, new_node);
}
if (node->right == NIL) {
new_node->right = NIL;
} else {
new_node->right = _rbtree_subtree_dup(node->right, new_tree, new_node);
}
return new_node;
}
c_rbtree_t *c_rbtree_dup(const c_rbtree_t *tree) {
c_rbtree_t *new_tree = NULL;
new_tree = (c_rbtree_t*) c_malloc(sizeof(c_rbtree_t));
new_tree->key_compare = tree->key_compare;
new_tree->data_compare = tree->data_compare;
new_tree->size = tree->size;
new_tree->root = _rbtree_subtree_dup(tree->root, new_tree, NULL);
return new_tree;
}
static int _rbtree_subtree_free(c_rbnode_t *node) {
assert(node);
if (node->left != NIL) {
if (_rbtree_subtree_free(node->left) < 0) {
/* TODO: set errno? ECANCELED? */
return -1;
}
}
if (node->right != NIL) {
if (_rbtree_subtree_free(node->right) < 0) {
/* TODO: set errno? ECANCELED? */
return -1;
}
}
SAFE_FREE(node);
return 0;
}
int c_rbtree_free(c_rbtree_t *tree) {
if (tree == NULL) {
errno = EINVAL;
return -1;
}
if (tree->root != NIL) {
_rbtree_subtree_free(tree->root);
}
SAFE_FREE(tree);
return 0;
}
static int _rbtree_subtree_walk(c_rbnode_t *node, void *data, c_rbtree_visit_func *visitor) {
assert(node);
assert(data);
assert(visitor);
if (node == NIL) {
return 0;
}
if (_rbtree_subtree_walk(node->left, data, visitor) < 0) {
return -1;
}
if ((*visitor)(node->data, data) < 0) {
return -1;
}
if (_rbtree_subtree_walk(node->right, data, visitor) < 0) {
return -1;
}
return 0;
}
int c_rbtree_walk(c_rbtree_t *tree, void *data, c_rbtree_visit_func *visitor) {
if (tree == NULL || data == NULL || visitor == NULL) {
errno = EINVAL;
return -1;
}
if (_rbtree_subtree_walk(tree->root, data, visitor) < 0) {
return -1;
}
return 0;
}
static c_rbnode_t *_rbtree_subtree_head(c_rbnode_t *node) {
assert(node);
if (node == NIL) {
return node;
}
while (node->left != NIL) {
node = node->left;
}
return node;
}
static c_rbnode_t *_rbtree_subtree_tail(c_rbnode_t *node) {
assert(node);
if (node == NIL) {
return node;
}
while (node->right != NIL) {
node = node->right;
}
return node;
}
c_rbnode_t *c_rbtree_head(c_rbtree_t *tree) {
c_rbnode_t *node = NULL;
if (tree == NULL) {
errno = EINVAL;
return NULL;
}
node = _rbtree_subtree_head(tree->root);
return node != NIL ? node : NULL;
}
c_rbnode_t *c_rbtree_tail(c_rbtree_t *tree) {
c_rbnode_t *node = NULL;
if (tree == NULL) {
errno = EINVAL;
return NULL;
}
node = _rbtree_subtree_tail(tree->root);
return node != NIL ? node : NULL;
}
c_rbnode_t *c_rbtree_node_next(c_rbnode_t *node) {
c_rbnode_t *parent = NULL;
if (node == NULL) {
errno = EINVAL;
return NULL;
}
if (node->right != NIL) {
c_rbnode_t *next = NULL;
next = _rbtree_subtree_head(node->right);
return next != NIL ? next : NULL;
}
parent = node->parent;
while (parent && node == parent->right) {
node = parent;
parent = node->parent;
}
return parent != NULL ? parent : NULL;
}
c_rbnode_t *c_rbtree_node_prev(c_rbnode_t *node) {
c_rbnode_t *parent = NULL;
if (node == NULL) {
return NULL;
}
if (node->left != NIL) {
c_rbnode_t *prev = NULL;
prev = _rbtree_subtree_tail(node->left);
return prev != NIL ? prev : NULL;
}
parent = node->parent;
while (parent && node == parent->left) {
node = parent;
parent = node->parent;
}
return parent != NULL ? parent : NULL;
}
c_rbnode_t *c_rbtree_find(c_rbtree_t *tree, const void *key) {
int cmp = 0;
c_rbnode_t *node = NULL;
if (tree == NULL) {
errno = EINVAL;
return NULL;
}
node = tree->root;
while (node != NIL) {
cmp = tree->key_compare(key, node->data);
if (cmp == 0) {
return node;
}
if (cmp < 0) {
node = node->left;
} else {
node = node->right;
}
}
return NULL;
}
static void _rbtree_subtree_left_rotate(c_rbnode_t *x) {
c_rbnode_t *y = NULL;
assert(x);
y = x->right;
/* establish x-right link */
x->right = y->left;
if (y->left != NIL) {
y->left->parent = x;
}
/* establish y->parent link */
if (y != NIL) {
y->parent = x->parent;
}
if (x->parent) {
if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
} else {
x->tree->root = y;
}
/* link x and y */
y->left = x;
if (x != NIL) {
x->parent = y;
}
}
/* rotat node x to the right */
static void _rbtree_subtree_right_rotate(c_rbnode_t *x) {
c_rbnode_t *y = NULL;
assert(x);
y = x->left;
/* establish x->left link */
x->left = y->right;
if (y->right != NIL) {
y->right->parent = x;
}
/* establish y->parent link */
if (y != NIL) {
y->parent = x->parent;
}
if (x->parent) {
if (x == x->parent->right) {
x->parent->right = y;
} else {
x->parent->left = y;
}
} else {
x->tree->root = y;
}
/* link x and y */
y->right = x;
if (x != NIL) {
x->parent = y;
}
}
int c_rbtree_insert(c_rbtree_t *tree, void *data) {
int cmp = 0;
c_rbnode_t *current = NULL;
c_rbnode_t *parent = NULL;
c_rbnode_t *x = NULL;
if (tree == NULL) {
errno = EINVAL;
return -1;
}
/* First we do a classic binary tree insert */
current = tree->root;
parent = NULL;
while (current != NIL) {
cmp = tree->data_compare(data, current->data);
parent = current;
if (cmp == 0) {
return 1;
} else if (cmp < 0) {
current = current->left;
} else {
current = current->right;
}
}
x = (c_rbnode_t *) c_malloc(sizeof(c_rbnode_t));
x->tree = tree;
x->data = data;
x->parent = parent;
x->left = NIL;
x->right = NIL;
x->color = RED;
if (parent) {
/* Note that cmp still contains the comparison of data with
* parent->data, from the last pass through the loop above
*/
if (cmp < 0) {
parent->left = x;
} else {
parent->right = x;
}
} else {
tree->root = x;
}
/* Insert fixup - check red-black properties */
while (x != tree->root && x->parent->color == RED) {
/* we have a violation */
if (x->parent == x->parent->parent->left) {
c_rbnode_t *y = NULL;
y = x->parent->parent->right;
if (y->color == RED) {
x->parent->color = BLACK;
y->color = BLACK;
x->parent->parent->color = RED;
x = x->parent->parent;
} else {
/* uncle is back */
if (x == x->parent->right) {
/* make x a left child */
x = x->parent;
_rbtree_subtree_left_rotate(x);
}
x->parent->color = BLACK;
x->parent->parent->color = RED;
_rbtree_subtree_right_rotate(x->parent->parent);
}
} else {
c_rbnode_t *y = NULL;
y = x->parent->parent->left;
if (y->color == RED) {
x->parent->color = BLACK;
y->color = BLACK;
x->parent->parent->color = RED;
x = x->parent->parent;
} else {
/* uncle is back */
if (x == x->parent->left) {
x = x->parent;
_rbtree_subtree_right_rotate(x);
}
x->parent->color = BLACK;
x->parent->parent->color = RED;
_rbtree_subtree_left_rotate(x->parent->parent);
}
}
} /* end while */
tree->root->color = BLACK;
tree->size++;
return 0;
}
int c_rbtree_node_delete(c_rbnode_t *node) {
c_rbtree_t *tree;
c_rbnode_t *y;
c_rbnode_t *x;
if (node == NULL || node == NIL) {
errno = EINVAL;
return -1;
}
tree = node->tree;
if (node->left == NIL || node->right == NIL) {
/* y has a NIL node as a child */
y = node;
} else {
/* find tree successor with a NIL node as a child */
y = node;
while(y->left != NIL) {
y = y->left;
}
}
/* x is y's only child */
if (y->left != NIL) {
x = y->left;
} else {
x = y->right;
}
/* remove y from the parent chain */
x->parent = y->parent;
if (y->parent) {
if (y == y->parent->left) {
y->parent->left = x;
} else {
y->parent->right = x;
}
} else {
y->tree->root = x;
}
/* If y is not the node we're deleting, splice it in place of that
* node
*
* The traditional code would call for us to simply copy y->data, but
* that would invalidate the wrong pointer - there might be external
* references to this node, and we must preserve its address.
*/
if (y != node) {
/* Update y */
y->parent = node->parent;
y->left = node->left;
y->right = node->right;
/* Update the children and the parent */
if (y->left != NIL) {
y->left->parent = y;
}
if (y->right != NIL) {
y->right->parent = y;
}
if (y->parent != NULL) {
if (node == y->parent->left) {
y->parent->left = y;
} else {
y->parent->right = y;
}
} else {
y->tree->root = y;
}
}
if (y->color == BLACK) {
while (x != y->tree->root && x->color == BLACK) {
if (x == x->parent->left) {
c_rbnode_t *w = NULL;
w = x->parent->right;
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
_rbtree_subtree_left_rotate(x->parent);
w = x->parent->right;
}
if (w->left->color == BLACK && w->right->color == BLACK) {
w->color = RED;
x = x->parent;
} else {
if (w->right->color == BLACK) {
w->left->color = BLACK;
w->color = RED;
_rbtree_subtree_right_rotate(w);
w = x->parent->right;
}
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
_rbtree_subtree_left_rotate(x->parent);
x = y->tree->root;
}
} else {
c_rbnode_t *w = NULL;
w = x->parent->left;
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
_rbtree_subtree_right_rotate(x->parent);
w = x->parent->left;
}
if (w->right->color == BLACK && w->left->color == BLACK) {
w->color = RED;
x = x->parent;
} else {
if (w->left->color == BLACK) {
w->right->color = BLACK;
w->color = RED;
_rbtree_subtree_left_rotate(w);
w = x->parent->left;
}
w->color = x->parent->color;
x->parent->color = BLACK;
w->left->color = BLACK;
_rbtree_subtree_right_rotate(x->parent);
x = y->tree->root;
}
}
}
x->color = BLACK;
} /* end if: y->color == BLACK */
/* node has now been spliced out of the tree */
SAFE_FREE(y);
tree->size--;
return 0;
}
static int _rbtree_subtree_check_black_height(c_rbnode_t *node) {
int left = 0;
int right = 0;
assert(node);
if (node == NIL) {
return 0;
}
left = _rbtree_subtree_check_black_height(node->left);
right = _rbtree_subtree_check_black_height(node->right);
if (left != right) {
return -1;
}
return left + (node->color == BLACK);
}
int c_rbtree_check_sanity(c_rbtree_t *tree) {
c_rbnode_t *node = NULL;
c_rbnode_t *prev = NULL;
c_rbnode_t *next = NULL;
c_rbnode_t *tail = NULL;
size_t size = 0;
if (tree == NULL) {
errno = EINVAL;
return -1;
}
if (! tree->key_compare || ! tree->data_compare) {
errno = EINVAL;
return -2;
}
/* Iterate the tree */
tail = c_rbtree_tail(tree);
for (node = c_rbtree_head(tree); node; node = next) {
if (node->tree != tree) {
return -4;
}
/* We should never see a nil while iterating */
if (node == NIL) {
return -5;
}
/* node == tree-root iff node->parent == NIL */
if (node == tree->root) {
if (node->parent != NULL) {
return -6;
}
} else {
if (node->parent == NULL) {
return -7;
}
}
/* Invertability of the iterate functions */
if (prev != c_rbtree_node_prev(node)) {
return -8;
}
/* Check the iteration sequence */
if (prev) {
if (tree->data_compare(prev->data, node->data) > 0) {
return -9;
}
/* And the other way around, to make sure data_compare is stable */
if (tree->data_compare(node->data, prev->data) < 0) {
return -10;
}
}
/* The binary tree property */
if (node->left != NIL) {
if (tree->data_compare(node->left->data, node->data) > 0) {
return -11;
}
if (tree->data_compare(node->data, node->left->data) < 0) {
return -11;
}
}
if (node->right != NIL) {
if (tree->data_compare(node->data, node->right->data) > 0) {
return -12;
}
if (tree->data_compare(node->right->data, node->data) < 0) {
return -13;
}
}
/* Red-black tree property 3: red nodes have black children */
if (node->color == RED) {
if (node->left->color == RED) {
return -14;
}
if (node->right->color == RED) {
return -15;
}
}
/* next == NULL if node == tail */
next = c_rbtree_node_next(node);
if (next) {
if (node == tail) {
return -16;
}
} else {
if (node != tail) {
return -17;
}
}
prev = node;
size++;
} /* end for loop */
if (size != tree->size) {
return -18;
}
if (_rbtree_subtree_check_black_height(tree->root) < 0) {
return -19;
}
return 0;
}

View file

@ -1,318 +0,0 @@
/*
* cynapses libc functions
*
* Copyright (c) 2003-2004 by Andrew Suffield <asuffield@debian.org>
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file c_rbtree.h
*
* @brief Interface of the cynapses libc red-black tree implementation
*
* A red-black tree is a type of self-balancing binary search tree. It is
* complex, but has good worst-case running time for its operations and is
* efficient in practice: it can search, insert, and delete in O(log n)
* time, where n is the number of elements in the tree.
*
* In red-black trees, the leaf nodes are not relevant and do not contain
* data. Therefore we use a sentinal node to save memory. All references
* from internal nodes to leaf nodes instead point to the sentinel node.
*
* In a red-black tree each node has a color attribute, the value of which
* is either red or black. In addition to the ordinary requirements imposed
* on binary search trees, the following additional requirements of any
* valid red-black tree apply:
*
* 1. A node is either red or black.
* 2. The root is black.
* 3. All leaves are black, even when the parent is black
* (The leaves are the null children.)
* 4. Both children of every red node are black.
* 5. Every simple path from a node to a descendant leaf contains the same
* number of black nodes, either counting or not counting the null black
* nodes. (Counting or not counting the null black nodes does not affect
* the structure as long as the choice is used consistently.).
*
* These constraints enforce a critical property of red-black trees: that the
* longest path from the root to a leaf is no more than twice as long as the
* shortest path from the root to a leaf in that tree. The result is that the
* tree is roughly balanced. Since operations such as inserting, deleting, and
* finding values requires worst-case time proportional to the height of the
* tree, this theoretical upper bound on the height allows red-black trees to
* be efficient in the worst-case, unlike ordinary binary search trees.
*
* http://en.wikipedia.org/wiki/Red-black_tree
*
* @defgroup cynRBTreeInternals cynapses libc red-black tree functions
* @ingroup cynLibraryAPI
*
* @{
*/
#ifndef _C_RBTREE_H
#define _C_RBTREE_H
#ifdef __cplusplus
extern "C" {
#endif
/* Forward declarations */
struct c_rbtree_s; typedef struct c_rbtree_s c_rbtree_t;
struct c_rbnode_s; typedef struct c_rbnode_s c_rbnode_t;
/**
* Define the two colors for the red-black tree
*/
enum xrbcolor_e { BLACK = 0, RED }; typedef enum xrbcolor_e xrbcolor_t;
/**
* @brief Callback function to compare a key with the data from a
* red-black tree node.
*
* @param key key as a generic pointer
* @param data data as a generic pointer
*
* @return It returns an integer less than, equal to, or greater than zero
* depending on the key or data you use. The function is similar
* to strcmp().
*/
typedef int c_rbtree_compare_func(const void *key, const void *data);
/**
* @brief Visit function for the c_rbtree_walk() function.
*
* This function will be called by c_rbtree_walk() for every node. It is up to
* the developer what the function does. The fist parameter is a node object
* the second can be of any kind.
*
* @param obj The node data that will be passed by c_rbtree_walk().
* @param data Generic data pointer.
*
* @return 0 on success, < 0 on error. You should set errno.
*
*/
typedef int c_rbtree_visit_func(void *, void *);
/**
* Structure that represents a red-black tree
*/
struct c_rbtree_s {
c_rbnode_t *root;
c_rbtree_compare_func *key_compare;
c_rbtree_compare_func *data_compare;
size_t size;
};
/**
* Structure that represents a node of a red-black tree
*/
struct c_rbnode_s {
c_rbtree_t *tree;
c_rbnode_t *left;
c_rbnode_t *right;
c_rbnode_t *parent;
void *data;
xrbcolor_t color;
};
/**
* @brief Create the red-black tree
*
* @param rbtree The pointer to assign the allocated memory.
*
* @param key_compare Callback function to compare a key with the data
* inside a reb-black tree node.
*
* @param data_compare Callback function to compare a key as data with thee
* data inside a red-black tree node.
*/
void c_rbtree_create(c_rbtree_t **rbtree, c_rbtree_compare_func *key_compare, c_rbtree_compare_func *data_compare);
/**
* @brief Duplicate a red-black tree.
*
* @param tree Tree to duplicate.
*
* @return Pointer to a new allocated duplicated rbtree. NULL if an error
* occurred.
*/
c_rbtree_t *c_rbtree_dup(const c_rbtree_t *tree);
/**
* @brief Free the structure of a red-black tree.
*
* You should call c_rbtree_destroy() before you call this function.
*
* @param tree The tree to free.
*
* @return 0 on success, less than 0 if an error occurred.
*/
int c_rbtree_free(c_rbtree_t *tree);
/**
* @brief Destroy the content and the nodes of an red-black tree.
*
* This is far from the most efficient way to walk a tree, but it is
* the *safest* way to destroy a tree - the destructor can do almost
* anything (as long as it does not create an infinite loop) to the
* tree structure without risk.
*
* If for some strange reason you need a faster destructor (think
* twice - speed and memory deallocation don't mix well) then consider
* stashing an llist of dataects and destroying that instead, and just
* using c_rbtree_free() on your tree.
*
* @param T The tree to destroy.
* @param DESTRUCTOR The destructor to call on a node to destroy.
*/
#define c_rbtree_destroy(T, DESTRUCTOR) \
do { \
if (T) { \
c_rbnode_t *_c_rbtree_temp; \
while ((_c_rbtree_temp = c_rbtree_head(T))) { \
(DESTRUCTOR)(_c_rbtree_temp->data); \
if (_c_rbtree_temp == c_rbtree_head(T)) { \
c_rbtree_node_delete(_c_rbtree_temp); \
} \
} \
} \
SAFE_FREE(T); \
} while (0);
/**
* @brief Inserts a node into a red black tree.
*
* @param tree The red black tree to insert the node.
* @param data The data to insert into the tree.
*
* @return 0 on success, 1 if a duplicate has been found and < 0 if an error
* occurred with errno set.
* EINVAL if a null pointer has been passed as the tree.
* ENOMEM if there is no memory left.
*/
int c_rbtree_insert(c_rbtree_t *tree, void *data);
/**
* @brief Find a node in a red-black tree.
*
* c_rbtree_find() is searching for the given key in a red-black tree and
* returns the node if the key has been found.
*
* @param tree The tree to search.
* @param key The key to search for.
*
* @return If the key was found the node will be returned. On error NULL
* will be returned.
*/
c_rbnode_t *c_rbtree_find(c_rbtree_t *tree, const void *key);
/**
* @brief Get the head of the red-black tree.
*
* @param tree The tree to get the head for.
*
* @return The head node. NULL if an error occurred.
*/
c_rbnode_t *c_rbtree_head(c_rbtree_t *tree);
/**
* @brief Get the tail of the red-black tree.
*
* @param tree The tree to get the tail for.
*
* @return The tail node. NULL if an error occurred.
*/
c_rbnode_t *c_rbtree_tail(c_rbtree_t *tree);
/**
* @brief Get the size of the red-black tree.
*
* @param T The tree to get the size from.
*
* @return The size of the red-black tree.
*/
#define c_rbtree_size(T) (T) == NULL ? 0 : ((T)->size)
/**
* @brief Walk over a red-black tree.
*
* Walk over a red-black tree calling a visitor function for each node found.
*
* @param tree Tree to walk.
* @param data Data which should be passed to the visitor function.
* @param visitor Visitor function. This will be called for each node passed.
*
* @return 0 on sucess, less than 0 if an error occurred.
*/
int c_rbtree_walk(c_rbtree_t *tree, void *data, c_rbtree_visit_func *visitor);
/**
* @brief Delete a node in a red-black tree.
*
* @param node Node which should be deleted.
*
* @return 0 on success, -1 if an error occurred.
*/
int c_rbtree_node_delete(c_rbnode_t *node);
/**
* @brief Get the next node.
*
* @param node The node of which you want the next node.
*
* @return The next node, NULL if an error occurred.
*/
c_rbnode_t *c_rbtree_node_next(c_rbnode_t *node);
/**
* @brief Get the previous node.
*
* @param node The node of which you want the previous node.
*
* @return The previous node, NULL if an error occurred.
*/
c_rbnode_t *c_rbtree_node_prev(c_rbnode_t *node);
/**
* @brief Get the data of a node.
*
* @param N The node to get the data from.
*
* @return The data, NULL if an error occurred.
*/
#define c_rbtree_node_data(N) ((N) ? ((N)->data) : NULL)
/**
* @brief Perform a sanity check for a red-black tree.
*
* This is mostly for testing purposes.
*
* @param tree The tree to check.
*
* @return 0 on success, less than 0 if an error occurred.
*/
int c_rbtree_check_sanity(c_rbtree_t *tree);
/**
* }@
*/
#ifdef __cplusplus
}
#endif
#endif /* _C_RBTREE_H */

View file

@ -59,7 +59,7 @@ static bool findPathInList(const QStringList &list, const QString &path)
return pathSlash.startsWith(*it);
}
bool DiscoveryJob::isInSelectiveSyncBlackList(const char *path) const
bool DiscoveryJob::isInSelectiveSyncBlackList(const QByteArray &path) const
{
if (_selectiveSyncBlackList.isEmpty()) {
// If there is no black list, everything is allowed
@ -73,24 +73,23 @@ bool DiscoveryJob::isInSelectiveSyncBlackList(const char *path) const
// Also try to adjust the path if there was renames
if (csync_rename_count(_csync_ctx)) {
QScopedPointer<char, QScopedPointerPodDeleter> adjusted(
csync_rename_adjust_path_source(_csync_ctx, path));
if (strcmp(adjusted.data(), path) != 0) {
return findPathInList(_selectiveSyncBlackList, QString::fromUtf8(adjusted.data()));
QByteArray adjusted = csync_rename_adjust_path_source(_csync_ctx, path);
if (adjusted != path) {
return findPathInList(_selectiveSyncBlackList, QString::fromUtf8(adjusted));
}
}
return false;
}
int DiscoveryJob::isInSelectiveSyncBlackListCallback(void *data, const char *path)
int DiscoveryJob::isInSelectiveSyncBlackListCallback(void *data, const QByteArray &path)
{
return static_cast<DiscoveryJob *>(data)->isInSelectiveSyncBlackList(path);
}
bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString &path, const char *remotePerm)
bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString &path, const QByteArray &remotePerm)
{
if (_syncOptions._confirmExternalStorage && std::strchr(remotePerm, 'M')) {
if (_syncOptions._confirmExternalStorage && remotePerm.contains('M')) {
// 'M' in the permission means external storage.
/* Note: DiscoverySingleDirectoryJob::directoryListingIteratedSlot make sure that only the
@ -145,7 +144,7 @@ bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString &path, const char *
}
}
int DiscoveryJob::checkSelectiveSyncNewFolderCallback(void *data, const char *path, const char *remotePerm)
int DiscoveryJob::checkSelectiveSyncNewFolderCallback(void *data, const QByteArray &path, const QByteArray &remotePerm)
{
return static_cast<DiscoveryJob *>(data)->checkSelectiveSyncNewFolder(QString::fromUtf8(path), remotePerm);
}

View file

@ -210,10 +210,10 @@ class DiscoveryJob : public QObject
* return true if the given path should be ignored,
* false if the path should be synced
*/
bool isInSelectiveSyncBlackList(const char *path) const;
static int isInSelectiveSyncBlackListCallback(void *, const char *);
bool checkSelectiveSyncNewFolder(const QString &path, const char *remotePerm);
static int checkSelectiveSyncNewFolderCallback(void *data, const char *path, const char *remotePerm);
bool isInSelectiveSyncBlackList(const QByteArray &path) const;
static int isInSelectiveSyncBlackListCallback(void *, const QByteArray &);
bool checkSelectiveSyncNewFolder(const QString &path, const QByteArray &remotePerm);
static int checkSelectiveSyncNewFolderCallback(void *data, const QByteArray &path, const QByteArray &remotePerm);
// Just for progress
static void update_job_update_callback(bool local,

View file

@ -128,9 +128,6 @@ QString SyncEngine::csyncErrorToString(CSYNC_STATUS err)
case CSYNC_STATUS_NO_MODULE:
errStr = tr("<p>The %1 plugin for csync could not be loaded.<br/>Please verify the installation!</p>").arg(qApp->applicationName());
break;
case CSYNC_STATUS_TREE_ERROR:
errStr = tr("CSync got an error while processing internal trees.");
break;
case CSYNC_STATUS_PARAM_ERROR:
errStr = tr("CSync fatal parameter error.");
break;
@ -347,7 +344,7 @@ int SyncEngine::treewalkRemote(csync_file_stat_t *file, csync_file_stat_t *other
* Called on each entry in the local and remote trees by
* csync_walk_local_tree()/csync_walk_remote_tree().
*
* It merges the two csync rbtrees into a single map of SyncFileItems.
* It merges the two csync file trees into a single map of SyncFileItems.
*
* See doc/dev/sync-algorithm.md for an overview.
*/
@ -359,7 +356,7 @@ int SyncEngine::treewalkFile(csync_file_stat_t *file, csync_file_stat_t *other,
QTextCodec::ConverterState utf8State;
static QTextCodec *codec = QTextCodec::codecForName("UTF-8");
ASSERT(codec);
QString fileUtf8 = codec->toUnicode(file->path, qstrlen(file->path), &utf8State);
QString fileUtf8 = codec->toUnicode(file->path, file->path.size(), &utf8State);
QString renameTarget;
QString key = fileUtf8;
@ -368,7 +365,7 @@ int SyncEngine::treewalkFile(csync_file_stat_t *file, csync_file_stat_t *other,
qCWarning(lcEngine) << "File ignored because of invalid utf-8 sequence: " << file->path;
instruction = CSYNC_INSTRUCTION_IGNORE;
} else {
renameTarget = codec->toUnicode(file->rename_path, qstrlen(file->rename_path), &utf8State);
renameTarget = codec->toUnicode(file->rename_path, file->rename_path.size(), &utf8State);
if (utf8State.invalidChars > 0 || utf8State.remainingChars > 0) {
qCWarning(lcEngine) << "File ignored because of invalid utf-8 sequence in the rename_path: " << file->path << file->rename_path;
instruction = CSYNC_INSTRUCTION_IGNORE;
@ -937,7 +934,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
_backInTimeFiles = 0;
bool walkOk = true;
_remotePerms.clear();
_remotePerms.reserve(c_rbtree_size(_csync_ctx->remote.tree));
_remotePerms.reserve(_csync_ctx->remote.files.size());
_seenFiles.clear();
_temporarilyUnavailablePaths.clear();
_renamedFolders.clear();

View file

@ -23,7 +23,6 @@ set(TEST_TARGET_LIBRARIES ${TORTURE_LIBRARY})
add_cmocka_test(check_std_c_alloc std_tests/check_std_c_alloc.c ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_std_c_jhash std_tests/check_std_c_jhash.c ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_std_c_path std_tests/check_std_c_path.c ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_std_c_rbtree std_tests/check_std_c_rbtree.c ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_std_c_str std_tests/check_std_c_str.c ${TEST_TARGET_LIBRARIES})
add_cmocka_test(check_std_c_time std_tests/check_std_c_time.c ${TEST_TARGET_LIBRARIES})

View file

@ -146,19 +146,18 @@ static void check_csync_statedb_drop_tables(void **state)
static void check_csync_statedb_insert_metadata(void **state)
{
CSYNC *csync = (CSYNC*)*state;
csync_file_stat_t *st;
std::unique_ptr<csync_file_stat_t> st;
int i, rc = 0;
// rc = csync_statedb_create_tables(csync->statedb.db);
assert_int_equal(rc, 0);
for (i = 0; i < 100; i++) {
st = new csync_file_stat_t;
st.reset(new csync_file_stat_t);
st->path = QString("file_%1").arg(i).toUtf8();
st->phash = i;
rc = c_rbtree_insert(csync->local.tree, (void *) st);
assert_int_equal(rc, 0);
csync->local.files[st->path] = std::move(st);
}
// rc = csync_statedb_insert_metadata(csync, csync->statedb.db);
@ -168,15 +167,15 @@ static void check_csync_statedb_insert_metadata(void **state)
static void check_csync_statedb_write(void **state)
{
CSYNC *csync = (CSYNC*)*state;
csync_file_stat_t *st;
int i, rc;
std::unique_ptr<csync_file_stat_t> st;
int i, rc = 0;
for (i = 0; i < 100; i++) {
st = new csync_file_stat_t;
st.reset(new csync_file_stat_t);
st->path = QString("file_%1").arg(i).toUtf8();
st->phash = i;
rc = c_rbtree_insert(csync->local.tree, (void *) st);
csync->local.files[st->path] = std::move(st);
assert_int_equal(rc, 0);
}

View file

@ -227,7 +227,7 @@ static void check_csync_detect_update(void **state)
assert_int_equal(rc, 0);
/* the instruction should be set to new */
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
st = csync->local.files.begin()->second.get();
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW);
/* create a statedb */
@ -250,7 +250,7 @@ static void check_csync_detect_update_db_none(void **state)
assert_int_equal(rc, 0);
/* the instruction should be set to new */
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
st = csync->local.files.begin()->second.get();
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW);
@ -271,7 +271,7 @@ static void check_csync_detect_update_db_eval(void **state)
assert_int_equal(rc, 0);
/* the instruction should be set to new */
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
st = csync->local.files.begin()->second.get();
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW);
/* create a statedb */
@ -296,7 +296,7 @@ static void check_csync_detect_update_db_rename(void **state)
/* the instruction should be set to rename */
/*
* temporarily broken.
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
st = csync->local.files.begin()->second.get();
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_RENAME);
st->instruction = CSYNC_INSTRUCTION_UPDATED;
@ -318,7 +318,7 @@ static void check_csync_detect_update_db_new(void **state)
assert_int_equal(rc, 0);
/* the instruction should be set to new */
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
st = csync->local.files.begin()->second.get();
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW);

View file

@ -1,366 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <time.h>
#include "torture.h"
#include "std/c_alloc.h"
#include "std/c_rbtree.h"
typedef struct test_s {
int key;
int number;
} test_t;
static int data_cmp(const void *key, const void *data) {
test_t *a, *b;
a = (test_t *) key;
b = (test_t *) data;
if (a->key < b->key) {
return -1;
} else if (a->key > b->key) {
return 1;
}
return 0;
}
static int key_cmp(const void *key, const void *data) {
int a;
test_t *b;
a = POINTER_TO_INT(key);
b = (test_t *) data;
if (a < b->key) {
return -1;
} else if (a > b->key) {
return 1;
}
return 0;
}
static int visitor(void *obj, void *data) {
test_t *a;
test_t *b;
a = (test_t *) obj;
b = (test_t *) data;
if (a->key == b->key) {
a->number = 42;
}
return 0;
}
static void destructor(void *data) {
test_t *freedata = NULL;
freedata = (test_t *) data;
SAFE_FREE(freedata);
}
static int setup(void **state) {
c_rbtree_t *tree = NULL;
c_rbtree_create(&tree, key_cmp, data_cmp);
*state = tree;
return 0;
}
static int setup_complete_tree(void **state) {
c_rbtree_t *tree = NULL;
int i = 0;
int rc;
c_rbtree_create(&tree, key_cmp, data_cmp);
for (i = 0; i < 100; i++) {
test_t *testdata = NULL;
testdata = c_malloc(sizeof(test_t));
assert_non_null(testdata);
testdata->key = i;
rc = c_rbtree_insert(tree, (void *) testdata);
assert_int_equal(rc, 0);
}
*state = tree;
return 0;
}
static int teardown(void **state) {
c_rbtree_t *tree = *state;
c_rbtree_destroy(tree, destructor);
c_rbtree_free(tree);
*state = NULL;
return 0;
}
static void check_c_rbtree_create_free(void **state)
{
c_rbtree_t *tree = NULL;
int rc;
(void) state; /* unused */
c_rbtree_create(&tree, key_cmp, data_cmp);
assert_int_equal(tree->size, 0);
rc = c_rbtree_free(tree);
assert_int_equal(rc, 0);
}
static void check_c_rbtree_free_null(void **state)
{
int rc;
(void) state; /* unused */
rc = c_rbtree_free(NULL);
assert_int_equal(rc, -1);
}
static void check_c_rbtree_insert_delete(void **state)
{
c_rbtree_t *tree = NULL;
c_rbnode_t *node = NULL;
test_t *testdata = NULL;
int rc;
(void) state; /* unused */
c_rbtree_create(&tree, key_cmp, data_cmp);
testdata = malloc(sizeof(test_t));
testdata->key = 42;
rc = c_rbtree_insert(tree, (void *) testdata);
assert_int_equal(rc, 0);
node = c_rbtree_head(tree);
assert_non_null(node);
testdata = c_rbtree_node_data(node);
SAFE_FREE(testdata);
rc = c_rbtree_node_delete(node);
assert_int_equal(rc, 0);
c_rbtree_free(tree);
}
static void check_c_rbtree_insert_random(void **state)
{
c_rbtree_t *tree = *state;
int i = 0, rc;
for (i = 0; i < 100; i++) {
test_t *testdata = NULL;
testdata = malloc(sizeof(test_t));
assert_non_null(testdata);
testdata->key = i;
rc = c_rbtree_insert(tree, testdata);
assert_int_equal(rc, 0);
}
rc = c_rbtree_check_sanity(tree);
assert_int_equal(rc, 0);
}
static void check_c_rbtree_insert_duplicate(void **state)
{
c_rbtree_t *tree = *state;
test_t *testdata;
int rc;
testdata = malloc(sizeof(test_t));
assert_non_null(testdata);
testdata->key = 42;
rc = c_rbtree_insert(tree, (void *) testdata);
assert_int_equal(rc, 0);
/* add again */
testdata = malloc(sizeof(test_t));
assert_non_null(testdata);
testdata->key = 42;
/* check for duplicate */
rc = c_rbtree_insert(tree, (void *) testdata);
assert_int_equal(rc, 1);
free(testdata);
}
static void check_c_rbtree_find(void **state)
{
c_rbtree_t *tree = *state;
int rc, i = 42;
c_rbnode_t *node;
test_t *testdata;
rc = c_rbtree_check_sanity(tree);
assert_int_equal(rc, 0);
/* find the node with the key 42 */
node = c_rbtree_find(tree, (void *) &i);
assert_non_null(node);
testdata = (test_t *) c_rbtree_node_data(node);
assert_int_equal(testdata->key, 42);
}
static void check_c_rbtree_delete(void **state)
{
c_rbtree_t *tree = *state;
int rc, i = 42;
c_rbnode_t *node = NULL;
test_t *freedata = NULL;
rc = c_rbtree_check_sanity(tree);
assert_int_equal(rc, 0);
node = c_rbtree_find(tree, (void *) &i);
assert_non_null(node);
freedata = (test_t *) c_rbtree_node_data(node);
free(freedata);
rc = c_rbtree_node_delete(node);
assert_int_equal(rc, 0);
rc = c_rbtree_check_sanity(tree);
assert_int_equal(rc, 0);
}
static void check_c_rbtree_walk(void **state)
{
c_rbtree_t *tree = *state;
int rc, i = 42;
test_t *testdata;
c_rbnode_t *node;
rc = c_rbtree_check_sanity(tree);
assert_int_equal(rc, 0);
testdata = (test_t *) c_malloc(sizeof(test_t));
testdata->key = 42;
rc = c_rbtree_walk(tree, testdata, visitor);
assert_int_equal(rc, 0);
/* find the node with the key 42 */
node = c_rbtree_find(tree, (void *) &i);
assert_non_null(node);
free(testdata);
testdata = (test_t *) c_rbtree_node_data(node);
assert_int_equal(testdata->number, 42);
}
static void check_c_rbtree_walk_null(void **state)
{
c_rbtree_t *tree = *state;
int rc, i = 42;
test_t *testdata;
c_rbnode_t *node;
rc = c_rbtree_check_sanity(tree);
assert_int_equal(rc, 0);
testdata = (test_t *) malloc(sizeof(test_t));
testdata->key = 42;
rc = c_rbtree_walk(NULL, testdata, visitor);
assert_int_equal(rc, -1);
assert_int_equal(errno, EINVAL);
rc = c_rbtree_walk(tree, NULL, visitor);
assert_int_equal(rc, -1);
assert_int_equal(errno, EINVAL);
rc = c_rbtree_walk(tree, testdata, NULL);
assert_int_equal(rc, -1);
assert_int_equal(errno, EINVAL);
/* find the node with the key 42 */
node = c_rbtree_find(tree, (void *) &i);
assert_non_null(node);
free(testdata);
}
static void check_c_rbtree_dup(void **state)
{
c_rbtree_t *tree = *state;
c_rbtree_t *duptree = NULL;
int rc = -1;
duptree = c_rbtree_dup(tree);
assert_non_null(duptree);
rc = c_rbtree_check_sanity(duptree);
assert_int_equal(rc, 0);
c_rbtree_free(duptree);
}
#if 0
static void check_c_rbtree_x)
{
int rc = -1;
rc = c_rbtree_check_sanity(tree);
fail_unless(rc == 0, "c_rbtree_check_sanity failed with return code %d", rc);
}
#endif
int torture_run_tests(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(check_c_rbtree_create_free),
cmocka_unit_test(check_c_rbtree_free_null),
cmocka_unit_test(check_c_rbtree_insert_delete),
cmocka_unit_test_setup_teardown(check_c_rbtree_insert_random, setup, teardown),
cmocka_unit_test_setup_teardown(check_c_rbtree_insert_duplicate, setup, teardown),
cmocka_unit_test_setup_teardown(check_c_rbtree_find, setup_complete_tree, teardown),
cmocka_unit_test_setup_teardown(check_c_rbtree_delete, setup_complete_tree, teardown),
cmocka_unit_test_setup_teardown(check_c_rbtree_walk, setup_complete_tree, teardown),
cmocka_unit_test_setup_teardown(check_c_rbtree_walk_null, setup_complete_tree, teardown),
cmocka_unit_test_setup_teardown(check_c_rbtree_dup, setup_complete_tree, teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}