#include "pnode.h" #include #include 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); }