Files
mlpack/fastlib2/fastlib/fx/datanode.c
T
2008-05-18 03:53:22 +00:00

268 lines
5.8 KiB
C

// Copyright 2007 Georgia Institute of Technology. All rights reserved.
/**
* @file datanode.c
*
* Implementation for path-based string data storage.
*/
#include "datanode.h"
static void datanode__reset(struct datanode *node)
{
node->mod_type = 0;
node->val_type = 0;
node->meta = NULL;
node->key = NULL;
node->val = NULL;
node->first_child = NULL;
node->last_child = NULL;
node->next = NULL;
node->parent = NULL;
}
void datanode_init(struct datanode *node, const char *key)
{
datanode__reset(node);
node->key = strdup(key);
}
void datanode_destroy(struct datanode *node)
{
struct datanode *child = node->first_child;
while (child) {
struct datanode *next = child->next;
datanode_destroy(child);
free(child);
child = next;
}
free(node->key);
free(node->val);
datanode__reset(node);
}
struct datanode *datanode_lookup_expert(struct datanode *node, char *path,
int create)
{
char *end = path + strlen(path);
*end = '/'; /* Note: not null-terminated! */
while (path < end) {
char *slash = strchr(path, '/');
*slash = '\0'; /* Demark end of current query */
/* Match special paths: "", ".", ".." */
if (unlikely(*path == '\0' || strcmp(path, ".") == 0)) {
path = slash + 1;
continue;
} else if (unlikely(strcmp(path, "..") == 0)) {
if (node->parent) {
node = node->parent;
}
path = slash + 1;
continue;
}
/* Seek query in node's children, moving it to the end
* - moving things to end gets them out of the way
* - but check end first for fast repeat lookups
*/
if (!node->last_child
|| likely(strcmp(path, node->last_child->key) != 0)) {
struct datanode *child;
struct datanode **prev;
/* Note: last child can't match, already checked it
* - important because use of node->last_child would fail
*/
for (prev = &node->first_child, child = node->first_child; child;
prev = &child->next, child = child->next) {
if (unlikely(strcmp(path, child->key) == 0)) {
*prev = child->next;
child->next = NULL;
node->last_child->next = child;
node->last_child = child;
break;
}
}
/* Create query if not found */
if (unlikely(!child)) {
if (!create) {
return NULL;
}
child = malloc(sizeof(struct datanode));
datanode__reset(child);
child->key = malloc((slash - path + 1) * sizeof(char));
strcpy(child->key, path);
child->parent = node;
*prev = child; /* Note: handles empty lists, too */
node->last_child = child;
}
}
node = node->last_child;
path = slash + 1;
}
return node;
}
struct datanode *datanode_lookup(struct datanode *node, const char *path,
int create)
{
struct datanode *retval;
char *buf = strdup(path);
retval = datanode_lookup_expert(node, buf, create);
free(buf);
return retval;
}
static int datanode__exists_impl(struct datanode *node)
{
struct datanode *child;
if (node->val) {
return 1;
}
for (child = node->first_child; child; child = child->next) {
if (datanode__exists_impl(child)) {
return 1;
}
}
return 0;
}
int datanode_exists(struct datanode *node, const char *path)
{
node = datanode_lookup(node, path, 0);
return node && datanode__exists_impl(node);
}
void datanode_copy(struct datanode *dest, struct datanode *src,
int overwrite)
{
struct datanode *child;
if (!dest->val || overwrite) {
dest->mod_type = src->mod_type;
dest->val_type = src->val_type;
dest->meta = src->meta;
/* TODO: consider clearing val if val_type < 0 */
if (src->val && src->val_type >= 0) {
free(dest->val);
dest->val = strdup(src->val);
}
}
for (child = src->first_child; child; child = child->next) {
datanode_copy(datanode_lookup_expert(dest, child->key, 1), child,
overwrite);
}
}
static void datanode__write_path(struct datanode *node, FILE *stream)
{
if (node->parent) {
datanode__write_path(node->parent, stream);
putc('/', stream);
}
hex_to_stream(stream, node->key, "_.-+");
}
void datanode_write(struct datanode *node, FILE *stream,
const char *type_char)
{
struct datanode *child;
if (node->val && node->val_type >= 0) {
datanode__write_path(node, stream);
if (type_char) {
fprintf(stream, ":%c", type_char[node->mod_type]);
}
putc(' ', stream);
hex_to_stream(stream, node->val, "_.-+");
putc('\n', stream);
}
for (child = node->first_child; child; child = child->next) {
datanode_write(child, stream, type_char);
}
}
void datanode_read(struct datanode *node, FILE *stream,
const char *type_char, int overwrite)
{
char buf[LINE_MAX];
while (fgets(buf, sizeof(buf), stream) != NULL) {
struct datanode *entry;
char *key;
char *val;
int type;
int len;
/* Strip whitespace, skipping blanks and comments */
for (key = buf; isspace(*key); ++key) {}
if (*key == '\0' || *key == '#') {
continue;
}
for (len = strlen(key); isspace(key[len-1]); --len) {}
key[len] = '\n'; /* Note: not null-terminated! */
/* Demark the end of key and beginning of value */
for (val = key; !isspace(*val); ++val) {}
key[len] = '\0'; /* Restore null-termination */
if (val - key == len) {
val = "";
} else {
len = val - key;
*val = '\0';
while (isspace(*++val)) {}
}
/* Identify the node type, if provided */
type = 0;
if (len > 2 && key[len-2] == ':') {
if (type_char) {
type = strchr(type_char, key[len-1]) - type_char;
}
key[len-2] = '\0';
} else if (key[len-1] == ':') {
key[len-1] = '\0';
}
unhex_in_place(key);
unhex_in_place(val);
entry = datanode_lookup_expert(node, key, 1);
if (!entry->val || overwrite) {
if (type_char) {
entry->mod_type = type;
}
free(entry->val);
entry->val = strdup(val);
}
}
}