mirror of https://github.com/GNOME/gimp.git
306 lines
5.2 KiB
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);
|
|
}
|
|
|
|
|