gimp/tools/gcg/pnode.c

306 lines
5.2 KiB
C

#include "pnode.h"
#include <stdio.h>
#include <stdarg.h>
static const gconstpointer p_node_magic_tag = &p_node_magic_tag;
#define BE_NODE(x) g_assert((x) && ((PNode*)(x))->magic == &p_node_magic_tag)
static GMemChunk* p_node_chunk;
static GList* p_float_list;
static GHashTable* p_str_hash;
typedef enum{
NODE_NIL,
NODE_DATA,
NODE_COLLECT
} PNodeType;
typedef struct{
gchar* str;
PNode* node;
} PBlock;
typedef struct _PRNode{
GQuark tag;
PNode* node;
} PRNode;
struct _PRoot{
GData* data;
GList* nodes;
};
struct _PNode{
gconstpointer magic;
guint ref_count;
GList* float_link;
PNodeType type;
union {
struct {
gint nblocks;
PBlock* blocks;
} b;
struct {
PNodeCreateFunc func;
GQuark tag;
} c;
} u;
};
static PNode p_nil_node = {
&p_node_magic_tag,
-1,
NULL,
NODE_NIL,
{{0,NULL}}
};
PNode* p_nil = &p_nil_node;
static PNode* p_make(PNodeType type){
PNode* node;
if(!p_node_chunk)
p_node_chunk = g_mem_chunk_create(PNode, 1024,
G_ALLOC_AND_FREE);
node = g_chunk_new(PNode, p_node_chunk);
node->ref_count = 0;
node->magic = &p_node_magic_tag;
node->float_link = p_float_list = g_list_prepend(p_float_list, node);
node->type = type;
return node;
}
void p_ref(PNode* node){
BE_NODE(node);
if(node==p_nil)
return;
if(!node->ref_count){
p_float_list = g_list_remove_link(p_float_list,
node->float_link);
node->float_link = NULL;
}
node->ref_count++;
}
void p_unref(PNode* node){
BE_NODE(node);
if(node==p_nil)
return;
node->ref_count--;
if(node->ref_count>0)
return;
switch(node->type){
case NODE_DATA:{
PBlock* bl = node->u.b.blocks;
gint i=node->u.b.nblocks;
while(i--){
g_free(bl[i].str);
p_unref(bl[i].node);
}
g_free(bl);
break;
}
case NODE_NIL:
break;
case NODE_COLLECT:
break;
}
g_mem_chunk_free(p_node_chunk, node);
}
void p_write(PNode* node, FILE* f, PRoot* r){
g_assert(f);
BE_NODE(node);
switch(node->type){
case NODE_DATA:{
gint i = 0, n = node->u.b.nblocks;
PBlock* bl = node->u.b.blocks;
for(i = 0; i < n; i++){
if(bl[i].str)
fputs(bl[i].str, f);
p_write(bl[i].node, f, r);
}
break;
}
case NODE_NIL:
break;
case NODE_COLLECT:
if(r){
GList* l = g_datalist_id_get_data(&r->data,
node->u.c.tag);
l = g_list_last(l);
if(node->u.c.func)
for(; l; l = l->prev)
p_write(node->u.c.func(l->data),
f, r);
else
for(; l; l = l->prev)
p_write(l->data, f, r);
}
break;
}
}
gchar* p_to_str(PNode* n, PRoot* pr){
FILE* f = tmpfile();
glong len;
gchar* buf;
p_write(n, f, pr);
len = ftell(f);
rewind(f);
buf = g_new(gchar, len+1);
fread(buf, len, 1, f);
buf[len]='\0';
fclose(f);
return buf;
}
static PNode* p_simple_string(gchar* str){
PNode* n = p_make(NODE_DATA);
n->u.b.nblocks = 1;
n->u.b.blocks = g_new(PBlock, 1);
n->u.b.blocks[0].str = str;
n->u.b.blocks[0].node = p_nil;
return n;
}
PNode* p_str(const gchar* str){
PNode* n;
if(!str)
return p_nil;
if(!p_str_hash)
p_str_hash = g_hash_table_new(g_str_hash, g_str_equal);
n = g_hash_table_lookup(p_str_hash, str);
if(n)
return n;
else
return p_simple_string(g_strdup(str));
}
PNode* p_prf(const gchar* format, ...){
PNode* n;
va_list args;
va_start(args, format);
n = p_simple_string(g_strdup_vprintf (format, args));
va_end(args);
return n;
}
PNode* p_fmt(const gchar* f, ...){
va_list args;
const gchar* b;
PNode* n = p_make(NODE_DATA);
gint i;
PBlock* bl;
va_start(args, f);
g_assert(f);
for(b = f, i = 0; *b; b++)
if(*b == '~')
i++;
if(b!=f && b[-1] != '~')
i++;
bl = g_new(PBlock, i);
n->u.b.blocks = bl;
n->u.b.nblocks = i;
for(b = f, i = 0; *b; i++){
gint idx=0;
PNode* p = p_nil;
while(b[idx] && b[idx] != '~')
idx++;
bl[i].str = g_strndup(b, idx);
if(b[idx] == '~'){
p = va_arg(args, PNode*);
BE_NODE(p);
p_ref(p);
idx++;
}
bl[i].node = p;
b = &b[idx];
}
return n;
}
PNode* p_for(GSList* lst, PNodeCreateFunc func, gpointer user_data){
PNode* n = p_make(NODE_DATA);
GSList* l = lst;
gint i = g_slist_length(l);
n->u.b.nblocks = i;
n->u.b.blocks = g_new(PBlock, i);
i = 0;
while(l){
PNode* p;
if(user_data == p_nil)
p = func(l->data);
else
p = func(l->data, user_data);
BE_NODE(p);
p_ref(p);
n->u.b.blocks[i].str = NULL;
n->u.b.blocks[i++].node = p;
l = l->next;
}
return n;
}
PNode* p_col(const gchar* tag, PNodeCreateFunc func){
PNode* n = p_make(NODE_COLLECT);
n->u.c.func = func;
n->u.c.tag = g_quark_from_string(tag);
return n;
}
PRoot* pr_new(void){
PRoot* pr = g_new(PRoot, 1);
pr->nodes = NULL;
g_datalist_init(&pr->data);
return pr;
}
/*
void pr_add(PRoot* pr, const gchar* tag, PNode* node){
PRNode* n;
g_assert(pr);
BE_NODE(node);
if(node == p_nil)
return;
n = g_new(PRNode, 1);
n->tag = g_quark_from_string(tag);
n->node = node;
pr->nodes = g_list_prepend(pr->nodes, n);
p_ref(node);
}
*/
void pr_put(PRoot* pr, const gchar* tag, gpointer datum){
GList* l = g_datalist_get_data(&pr->data, tag);
if(!g_list_find(l, datum))
g_datalist_set_data(&pr->data, tag,
g_list_prepend(l, datum));
}
void pr_free(PRoot* pr){
GList* l;
for(l=pr->nodes;l;l = l->next){
PRNode* n = l->data;
p_unref(n->node);
g_free(n);
}
g_list_free(pr->nodes);
g_free(pr);
}