aboutsummaryrefslogtreecommitdiff
path: root/system/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'system/kernel')
-rw-r--r--system/kernel/CONTENTS7
-rw-r--r--system/kernel/Makefile16
-rw-r--r--system/kernel/pdp_debug.c21
-rw-r--r--system/kernel/pdp_dpd_command.c87
-rw-r--r--system/kernel/pdp_list.c931
-rw-r--r--system/kernel/pdp_mem.c129
-rw-r--r--system/kernel/pdp_packet.c634
-rw-r--r--system/kernel/pdp_packet2.c623
-rw-r--r--system/kernel/pdp_post.c48
-rw-r--r--system/kernel/pdp_symbol.c196
-rw-r--r--system/kernel/pdp_type.c473
11 files changed, 3165 insertions, 0 deletions
diff --git a/system/kernel/CONTENTS b/system/kernel/CONTENTS
new file mode 100644
index 0000000..c2f7c8c
--- /dev/null
+++ b/system/kernel/CONTENTS
@@ -0,0 +1,7 @@
+debug debug stuff
+forth the forth system
+list the list implementation
+mem memory allocation stuf
+packet the packet memory manager
+type the type handling and conversion system
+symbol symbol implementation, with namespaces for forth, types, classes, ...
diff --git a/system/kernel/Makefile b/system/kernel/Makefile
new file mode 100644
index 0000000..08b21cd
--- /dev/null
+++ b/system/kernel/Makefile
@@ -0,0 +1,16 @@
+
+OBJECTS = pdp_packet.o pdp_type.o pdp_dpd_command.o \
+ pdp_list.o pdp_debug.o pdp_symbol.o \
+ pdp_mem.o pdp_post.o
+
+
+include ../../Makefile.config
+
+all: pdp_main_clean $(OBJECTS)
+
+pdp_main_clean:
+ rm -f pdp.o
+
+clean:
+ rm -f *~
+ rm -f *.o
diff --git a/system/kernel/pdp_debug.c b/system/kernel/pdp_debug.c
new file mode 100644
index 0000000..07f6541
--- /dev/null
+++ b/system/kernel/pdp_debug.c
@@ -0,0 +1,21 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include "pdp_post.h"
+
+int pdp_debug_sigtrap_on_assert;
+
+
+void pdp_assert_hook (char *condition, char *file, int line)
+{
+ pdp_post("PDP_ASSERT (%s) failed in file %s, line %u. ", condition, file, line);
+ pdp_post("%s.\n", pdp_debug_sigtrap_on_assert ? "sending SIGTRAP" : "continuing");
+
+ if (pdp_debug_sigtrap_on_assert) kill(getpid(), SIGTRAP);
+}
+
+
+void pdp_debug_setup(void)
+{
+ pdp_debug_sigtrap_on_assert = 1;
+}
diff --git a/system/kernel/pdp_dpd_command.c b/system/kernel/pdp_dpd_command.c
new file mode 100644
index 0000000..d812cf2
--- /dev/null
+++ b/system/kernel/pdp_dpd_command.c
@@ -0,0 +1,87 @@
+
+/*
+ * Pure Data Packet header file. DPD command class
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* this object implements a dpd command queue and command object */
+
+#include "pdp_dpd_command.h"
+#include "pdp_mem.h"
+
+void pdp_dpd_commandfactory_init(t_pdp_dpd_commandfactory *x, u32 size)
+{
+ x->nb_commands = 0;
+ x->command_size = size;
+ x->command = 0;
+}
+
+void _pdp_dpd_commandfactory_free(t_pdp_dpd_command *x)
+{
+ if (x) _pdp_dpd_commandfactory_free(x->next);
+ pdp_dealloc(x);
+}
+
+void pdp_dpd_commandfactory_free(t_pdp_dpd_commandfactory *x)
+{
+ _pdp_dpd_commandfactory_free(x->command);
+ x->command = 0;
+ x->nb_commands = 0;
+}
+
+
+/* factory method */
+t_pdp_dpd_command *pdp_dpd_commandfactory_get_new_command(t_pdp_dpd_commandfactory *x)
+{
+
+ t_pdp_dpd_command *c = x->command;
+ t_pdp_dpd_command *oldhead = c;
+
+ /* check if we can reuse */
+ while (c){
+ if (!c->used){
+ c->used = 1;
+ //post("reusing command %x", c, c->used);
+ return c;
+ }
+ //post("command %x is used %d", c, c->used);
+ c = c->next;
+ }
+
+ /* create a new command */
+ x->command = (t_pdp_dpd_command *)pdp_alloc(x->command_size);
+ x->command->next = oldhead;
+ x->command->used = 1;
+ x->nb_commands++;
+ //post("created command %x, nbcommands: %d", x->command, x->nb_commands);
+ return x->command;
+
+}
+
+
+/* (self)destructor */
+void pdp_dpd_command_suicide(void *x)
+{
+ t_pdp_dpd_command *c = (t_pdp_dpd_command *)x;
+ c->used = 0;
+ //post("command %x committed suicide %d", c, c->used);
+}
+
+
+
+
diff --git a/system/kernel/pdp_list.c b/system/kernel/pdp_list.c
new file mode 100644
index 0000000..deeb7f1
--- /dev/null
+++ b/system/kernel/pdp_list.c
@@ -0,0 +1,931 @@
+
+/*
+ * Pure Data Packet header file. List class
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* who can live without a list, hmm?
+
+ this is sorth of a compromise between lists, queues,
+ stacks, lisp and forth. list contain pdp atoms
+ (floats, ints, symbols, pointers, packets or lists) */
+
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "pdp_list.h"
+#include "pdp_symbol.h"
+#include "pdp_packet.h"
+#include "pdp_type.h"
+#include "pdp_types.h"
+#include "pdp_mem.h"
+#include "pdp_post.h"
+#include "pdp_debug.h"
+
+#define D if (0)
+
+
+t_pdp_fastalloc *_fast_atom_allocator;
+t_pdp_fastalloc *_fast_list_allocator;
+
+
+
+
+static void _pdp_list_dealloc(t_pdp_list *l)
+{
+ pdp_fastalloc_save_atom(_fast_list_allocator, l);
+}
+
+// allocator macros
+#define PDP_ATOM_ALLOC() pdp_fastalloc_new_atom(_fast_atom_allocator)
+#define PDP_ATOM_DEALLOC(x) pdp_fastalloc_save_atom(_fast_atom_allocator, x)
+#define PDP_LIST_ALLOC() pdp_fastalloc_new_atom(_fast_list_allocator)
+#define PDP_LIST_DEALLOC(x) _pdp_list_dealloc(x)
+//#define PDP_LIST_DEALLOC(x) pdp_fastalloc_save_atom(_fast_list_allocator, x)
+
+
+
+
+/* some private helper methods */
+
+/* list pool setup */
+void pdp_list_setup(void)
+{
+
+
+
+ /* create fast allocators */
+ _fast_atom_allocator = pdp_fastalloc_new(sizeof(t_pdp_atom));
+ _fast_list_allocator = pdp_fastalloc_new(sizeof(t_pdp_list));
+
+ /* testing code */
+ if (0){
+ char *next;
+ t_pdp_list *l = pdp_tree_from_cstring("( een twee (3 vier ()) vijf (6.0)", &next);
+ if (!l){
+ pdp_post("parse error:");
+ pdp_post(next);
+ }
+ else{
+ pdp_list_print(l);
+ }
+ exit(1);
+ }
+
+
+}
+
+
+
+/* create a list */
+t_pdp_list* pdp_list_new(int elements)
+{
+ t_pdp_atom *a = 0;
+ t_pdp_list *l = PDP_LIST_ALLOC();
+ l->elements = 0;
+
+
+ if (elements){
+ a = PDP_ATOM_ALLOC();
+ l->elements++;
+ a->t = a_undef;
+ a->w.w_int = 0;
+ a->next = 0;
+ elements--;
+ }
+ l->first = a;
+ l->last = a;
+
+ while (elements--){
+ a = PDP_ATOM_ALLOC();
+ l->elements++;
+ a->t = a_undef;
+ a->w.w_int = 0;
+ a->next = l->first;
+ l->first = a;
+ }
+
+ return l;
+}
+
+/* clear a list */
+void pdp_list_clear(t_pdp_list *l)
+{
+ t_pdp_atom *a = l->first;
+ t_pdp_atom *next_a;
+
+ while(a){
+ next_a = a->next;
+ PDP_ATOM_DEALLOC(a);
+ a = next_a;
+ }
+
+ l->first = 0;
+ l->last = 0;
+ l->elements = 0;
+
+}
+
+/* destroy a list */
+void pdp_list_free(t_pdp_list *l)
+{
+ if (l){
+ pdp_list_clear(l);
+ PDP_LIST_DEALLOC(l);
+ }
+}
+
+
+/* destroy a (sub)tree */
+void pdp_tree_free(t_pdp_list *l)
+{
+ if (l) {
+ pdp_tree_clear(l);
+ PDP_LIST_DEALLOC(l);
+ }
+}
+
+/* clear a tree */
+void pdp_tree_clear(t_pdp_list *l)
+{
+ t_pdp_atom *a = l->first;
+ t_pdp_atom *next_a;
+
+
+ while(a){
+ if (a->t == a_list){
+ pdp_tree_free(a->w.w_list);
+ }
+ next_a = a->next;
+ PDP_ATOM_DEALLOC(a);
+ a = next_a;
+ }
+
+ l->first = 0;
+ l->last = 0;
+ l->elements = 0;
+
+}
+
+/* BEGIN PARSER CODE */
+
+/* real whitespace handling */
+static inline int _is_whitespace(char c){return (c == ' ' || c == '\n' || c == '\t');}
+static inline void _skip_real_whitespace(char **c){while (_is_whitespace(**c)) (*c)++;}
+
+/* comment handling */
+static inline int _is_left_comment(char c) {return (c == '#');}
+static inline int _is_right_comment(char c) {return (c == '\n');}
+static inline void _skip_comment(char **c)
+{
+ if (!_is_left_comment(**c)) return;
+ (*c)++;
+ while (!_is_right_comment(**c)){
+ if (!**c) return; // no terminating newline
+ (*c)++;
+ }
+ (*c)++;
+}
+
+/* comment + whitespace handling */
+static inline void _skip_whitespace(char **c)
+{
+ char *prev_c;
+ /* skip comments and whitespace until the
+ pointer stops moving */
+ do {
+ prev_c = *c;
+ _skip_real_whitespace(c);
+ _skip_comment(c);
+ } while (prev_c != *c);
+}
+
+static inline int _is_left_separator(char c) {return (c == '(');}
+static inline int _is_right_separator(char c) {return (c == ')');}
+static inline int _is_terminator(char c) {return (c == 0);}
+
+/* the end of an atom is marked by a separator */
+static inline int _is_separator(char c) {return (_is_terminator(c)
+ || _is_left_separator(c)
+ || _is_right_separator(c)
+ || _is_whitespace(c));}
+
+
+/* parse a single pure atom from a zero terminated string
+ a pure atom is either a number (int or float) xor a symbol
+*/
+
+static inline void _parse_pure_atom(t_pdp_atom *a, char *c)
+{
+ char *next;
+
+ /* check if the string has a decimal point */
+ int has_decimal = 0;
+ char *c2;
+ for(c2 = c; *c2; c2++){
+ if (*c2 == '.') { has_decimal = 1; break; }
+ }
+
+ /* try parsing as a number (int or float) first */
+ if (has_decimal){ // try float
+ float f = strtod(c, &next);
+ if (next[0] == 0){ // got everything?
+ D pdp_post("parsing float %f", f);
+ a->t = a_float;
+ a->w = (t_pdp_word)f;
+ return;
+ }
+ }
+ else { // try int
+ int i = strtol(c, &next, 0);
+ if (next[0] == 0){ // got everything?
+ D pdp_post("parsing int %d", i);
+ a->t = a_int;
+ a->w = (t_pdp_word)i;
+ return;
+ }
+ }
+
+
+ /* number parsing failed: it's a symbol */
+ D pdp_post("parsing symbol %s", c);
+ a->t = a_symbol;
+ a->w = (t_pdp_word)pdp_gensym(c);
+
+}
+
+t_pdp_atom *pdp_atom_new(void){t_pdp_atom *a = PDP_ATOM_ALLOC(); a->next = 0; return a;}
+void pdp_atom_free(t_pdp_atom *a){PDP_ATOM_DEALLOC(a);}
+
+/* there are two parser methods: parse an atom and parse a list
+ both can call each other recursively.
+ the atoms and list are allocated with pdp_list_new and
+ pdp_atom_new respectively */
+
+t_pdp_atom *pdp_atom_from_cstring(char *chardef, char **next)
+{
+ t_pdp_atom *a = 0;
+
+ /* skip whitespace and check if there's anything left */
+ _skip_whitespace(&chardef);
+ if (!chardef[0] || _is_right_separator(*chardef)) goto done;
+
+
+ /* check if it's a list atom */
+ if(_is_left_separator(*chardef)){
+ t_pdp_list *l = pdp_tree_from_cstring(chardef, &chardef);
+ if (l){
+ a = pdp_atom_new();
+ a->t = a_list;
+ a->w.w_list = l;
+ }
+
+ }
+
+ /* we have a pure atom, copy it to a temp buffer */
+ else{
+ int n = 0;
+ while (!_is_separator(chardef[n])) n++;
+ if (!n) goto done;
+ else {
+ char tmp[n+1];
+ strncpy(tmp, chardef, n);
+ tmp[n] = 0;
+ a = pdp_atom_new();
+ _parse_pure_atom(a, tmp);
+ chardef += n;
+ }
+
+ }
+
+ done:
+ if (next) *next = chardef;
+ return a;
+
+}
+
+/* check if a tree (list of lists) matches a certain type syntax
+ types:
+
+ symbol -> a_sym;
+ int -> a_int;
+ float -> a_float;
+ packet -> a_packet;
+ list -> a_list;
+ ... -> zero or more times the preceeding elements in the list
+*/
+
+
+
+/* create a list from a character string */
+t_pdp_list *pdp_tree_from_cstring(char *chardef, char **next)
+{
+ t_pdp_list *l = pdp_list_new(0);
+ t_pdp_atom *a = 0;
+
+ D pdp_post ("creating list from char: %s", chardef);
+
+ /* find opening parenthesis and skip it*/
+ _skip_whitespace(&chardef);
+ if (!_is_left_separator(*chardef)) goto error; else chardef++;
+
+ /* chardef now points at the first atom, start adding atoms */
+ while(1){
+ a = pdp_atom_from_cstring(chardef, &chardef);
+ if (a)pdp_list_add_back_atom(l, a);
+ else break;
+ }
+
+ /* skip whitespace and find closing parenthesis */
+ _skip_whitespace(&chardef);
+ if (!_is_right_separator(*chardef)) goto error; else chardef++;
+ if (next) *next = chardef;
+ return l;
+
+ error:
+ /* end of string encountered: parse error */
+ D pdp_post("parse error: %s", chardef);
+ if (next) *next = chardef;
+ pdp_tree_free(l); //this will free all sublists too
+ return 0; // parse error
+
+
+
+}
+
+/* END PARSER CODE */
+
+// this assumes syntax's syntax is correct
+int pdp_tree_check_syntax(t_pdp_list *list, t_pdp_list *syntax)
+{
+
+ t_pdp_atom *la = 0;
+ t_pdp_atom *sa = 0;
+
+ t_pdp_symbol *ellipsis = pdp_gensym("...");
+ t_pdp_symbol *wildcard = pdp_gensym("*");
+
+ /* handle empty lists */
+ if (list->elements == 0){
+
+ /* check if syntax list is empty */
+ if (syntax->elements == 0) goto match;
+
+ /* check if syntax list has ellipsis */
+ if (syntax->last->t == a_symbol &&
+ syntax->last->w.w_symbol == ellipsis) goto match;
+
+ /* default: no match */
+ goto nomatch;
+ }
+
+
+ /* loop over list and syntax list */
+ for (la = list->first, sa = syntax->first;
+ la && sa;
+ la = la->next, sa = sa->next){
+
+ D pdp_post("pdp_tree_check_syntax: starting check");
+
+ checkatom:
+ /* what do we expect for this atom ? */
+ switch(sa->t){
+ case a_list:
+ D pdp_post("expecting list");
+ /* we need to recurse down the tree */
+ /* exit if the current list to check
+ does not have a sublist */
+ if (la->t != a_list) {
+ D pdp_post("not a list");
+ goto nomatch;
+ }
+
+ /* recurse and exit if no match */
+ D pdp_post("checking sublist");
+ if (!pdp_tree_check_syntax(la->w.w_list, sa->w.w_list)){
+ D pdp_post("sublist does not match");
+ goto nomatch;
+ }
+
+ break;
+
+ case a_symbol:
+
+ /* if ellipsis, rewind */
+ if (ellipsis == sa->w.w_symbol){
+ D pdp_post("got ellipsis");
+ /* check if we're not looping */
+ if (sa == syntax->first){
+ D pdp_post("ellipsis at start of list");
+ goto nomatch;
+ }
+ /* try again */
+ sa = syntax->first;
+ D pdp_post("ellipsis rewind");
+ goto checkatom;
+ }
+
+ else if (wildcard == sa->w.w_symbol){
+ D pdp_post("got wildcard");
+ }
+
+ /* ordinary atom: check type */
+ else{
+ D pdp_post("expecting %s", sa->w.w_symbol->s_name);
+ switch(la->t){
+
+ case a_int:
+ if (sa->w.w_symbol != pdp_gensym("int")) goto nomatch; break;
+ case a_float:
+ if (sa->w.w_symbol != pdp_gensym("float")) goto nomatch; break;
+ case a_symbol:
+ if (sa->w.w_symbol != pdp_gensym("symbol")) goto nomatch; break;
+ case a_packet:
+ if (sa->w.w_symbol != pdp_gensym("packet")) goto nomatch; break;
+ case a_list:
+ if (sa->w.w_symbol != pdp_gensym("list")) goto nomatch; break;
+
+ default:
+ goto nomatch;
+ }
+ D pdp_post("OK");
+ }
+
+ break;
+
+ default:
+ D pdp_post("syntax syntax error");
+ pdp_list_print(syntax);
+ goto nomatch; // incorrect syntax description
+ }
+
+ }
+
+ /* loop ended because one of the lists was finished */
+ /* only two cases can be valid: la == 0 and (sa == 0 or ellipsis) */
+
+ if (la != 0){
+ D pdp_post("not end of list -> no match");
+ goto nomatch;
+ }
+
+ if (sa == 0) goto match;
+
+ if (!(sa->t == a_symbol && sa->w.w_symbol == ellipsis)){
+ D pdp_post("syntax list not in ellipsis position -> no match");
+ goto nomatch;
+ }
+
+
+ /* exits */
+ match:
+ D pdp_post("pdp_tree_check_syntax: match");
+ return 1;
+ nomatch:
+ D pdp_post("pdp_tree_check_syntax: no match");
+ return 0;
+
+}
+
+
+
+/* traversal */
+void pdp_list_apply(t_pdp_list *l, t_pdp_atom_method m)
+{
+ t_pdp_atom *a;
+ if (!l) return;
+ for (a=l->first; a; a=a->next) m(a);
+}
+
+void pdp_tree_apply(t_pdp_list *l, t_pdp_atom_method m)
+{
+ t_pdp_atom *a;
+ if (!l) return;
+ for (a=l->first; a; a=a->next){
+ if (a->t == a_list) pdp_tree_apply(a->w.w_list, m);
+ else m(a);
+ }
+}
+
+void pdp_list_apply_word_method(t_pdp_list *l,
+ t_pdp_word_type type, t_pdp_word_method wm)
+{
+ t_pdp_atom *a;
+ if (!l) return;
+ for (a=l->first; a; a=a->next){
+ if (a->t == type) wm(a->w);
+ }
+}
+void pdp_list_apply_pword_method(t_pdp_list *l,
+ t_pdp_word_type type, t_pdp_pword_method pwm)
+{
+ t_pdp_atom *a;
+ if (!l) return;
+ for (a=l->first; a; a=a->next){
+ if (a->t == type) pwm(&a->w);
+ }
+}
+
+void pdp_tree_apply_word_method(t_pdp_list *l,
+ t_pdp_word_type type, t_pdp_word_method wm)
+{
+ t_pdp_atom *a;
+ if (!l) return;
+ for (a=l->first; a; a=a->next){
+ if (a->t == a_list) pdp_tree_apply_word_method(a->w.w_list, type, wm);
+ else if (a->t == type) wm(a->w);
+ }
+}
+void pdp_tree_apply_pword_method(t_pdp_list *l,
+ t_pdp_word_type type, t_pdp_pword_method pwm)
+{
+ t_pdp_atom *a;
+ if (!l) return;
+ for (a=l->first; a; a=a->next){
+ if (a->t == a_list) pdp_tree_apply_pword_method(a->w.w_list, type ,pwm);
+ else if (a->t == type) pwm(&a->w);
+ }
+}
+
+static void _atom_packet_mark_unused(t_pdp_atom *a)
+{
+ if (a->t == a_packet){
+ pdp_packet_mark_unused(a->w.w_packet);
+ a->w.w_packet = -1;
+ }
+}
+
+static void _atom_packet_copy_ro(t_pdp_atom *a)
+{
+ int p;
+ if (a->t == a_packet){
+ a->w.w_packet = pdp_packet_copy_ro(a->w.w_packet);
+ }
+}
+
+void pdp_tree_strip_packets (t_pdp_list *l)
+{
+ pdp_tree_apply(l, _atom_packet_mark_unused);
+}
+
+static void _pdp_tree_copy_ro_packets (t_pdp_list *l)
+{
+ pdp_tree_apply(l, _atom_packet_copy_ro);
+}
+
+t_pdp_list *pdp_tree_copy_ro(t_pdp_list *l)
+{
+ t_pdp_list *l2 = pdp_tree_copy(l);
+ _pdp_tree_copy_ro_packets(l2);
+ return l2;
+}
+
+static void _pdp_atomlist_fprint(FILE* f, t_pdp_atom *a);
+
+static void _pdp_atom_fprint(FILE* f, t_pdp_atom *a)
+{
+ if (!a){
+ fprintf(f, "<NULL ATOM>");
+ return;
+ }
+
+ switch(a->t){
+ /* generic atoms */
+ case a_symbol: fprintf(f, "%s",a->w.w_symbol->s_name); break;
+ case a_float: fprintf(f, "%f",a->w.w_float); break;
+ case a_int: fprintf(f, "%d",a->w.w_int); break;
+ case a_packet: fprintf(f, "#<pdp %d %s>",a->w.w_packet,
+ pdp_packet_get_description(a->w.w_packet)->s_name); break;
+ case a_pointer: fprintf(f, "#<0x%08x>", a->w.w_int); break;
+ case a_list: _pdp_atomlist_fprint(f, a->w.w_list->first); break;
+ case a_atom_pointer:
+ fprintf(f, "->");
+ _pdp_atom_fprint(f, a->w.w_atom_pointer);
+ break;
+ case a_undef: fprintf(f, "<undef>"); break;
+
+ /* forth atoms */
+ case a_forthword: fprintf(f, "#<forth word 0x%08x>", a->w.w_int); break;
+ case a_vmword: fprintf(f, "#<vm word 0x%08x>", a->w.w_int); break;
+ case a_vmmacro: fprintf(f, "#<vm macro 0x%08x>", a->w.w_int); break;
+
+
+ default: fprintf(f, "<unknown type>"); break;
+ }
+}
+
+/* debug */
+static void _pdp_atomlist_fprint(FILE* f, t_pdp_atom *a)
+{
+ fprintf(f, "(");
+ while (a){
+ _pdp_atom_fprint(f,a);
+ a = a->next;
+ if (a) fprintf(f, " ");
+ }
+ fprintf(f, ")");
+}
+
+void _pdp_list_fprint(FILE* f, t_pdp_list *l)
+{
+ _pdp_atomlist_fprint(f, l->first);
+ fprintf(f, "\n");
+}
+
+void pdp_list_print(t_pdp_list *l)
+{
+ _pdp_list_fprint(stderr, l);
+}
+
+void pdp_atom_print(t_pdp_atom *a)
+{
+ _pdp_atom_fprint(stderr, a);
+}
+
+/* public list operations */
+
+
+
+
+/* add a atom/word to the start of the list */
+void pdp_list_add_atom(t_pdp_list *l, t_pdp_atom *a)
+{
+ a->next = l->first;
+ l->first = a;
+ l->elements++;
+ if (!l->last) l->last = a;
+}
+
+void pdp_list_add(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w)
+{
+ t_pdp_atom *a = PDP_ATOM_ALLOC();
+ a->t = t;
+ a->w = w;
+ pdp_list_add_atom(l, a);
+}
+
+
+/* add a word to the end of the list */
+void pdp_list_add_back_atom(t_pdp_list *l, t_pdp_atom *a)
+{
+
+ l->elements++;
+ a->next = 0;
+ if (l->last){
+ l->last->next = a;
+ }
+ else{
+ l->first = a;
+ }
+ l->last = a;
+}
+
+void pdp_list_add_back(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w)
+{
+ t_pdp_atom *a = PDP_ATOM_ALLOC();
+ a->w = w;
+ a->t = t;
+ pdp_list_add_back_atom(l, a);
+}
+
+/* get list size */
+int pdp_list_size(t_pdp_list *l)
+{
+ return l->elements;
+}
+
+
+
+
+/* pop: return first item and remove */
+t_pdp_atom *pdp_list_pop_atom(t_pdp_list *l)
+{
+ t_pdp_atom *a = l->first;
+ if (!a) return a;
+
+ l->first = a->next;
+ l->elements--;
+ if (!l->first) l->last = 0;
+ a->next = 0; // detach
+ return a;
+}
+
+
+/* pop: return first item and remove */
+t_pdp_word pdp_list_pop(t_pdp_list *l)
+{
+ t_pdp_atom *a = pdp_list_pop_atom(l);
+ t_pdp_word w=a->w;
+ PDP_ATOM_DEALLOC(a);
+ return w;
+}
+
+
+
+
+/* pop from one list and push to other */
+void pdp_list_pop_push(t_pdp_list *source, t_pdp_list *dest)
+{
+ t_pdp_atom *a = source->first;
+
+ /* pop atom */
+ if (--(source->elements)){source->last = 0;}
+ source->first = a->next;
+
+ /* push atom */
+ a->next = dest->first;
+ if (dest->elements++) {dest->last = a;}
+ dest->first = a;
+
+ return;
+
+}
+
+
+/* return element at index */
+t_pdp_word pdp_list_index(t_pdp_list *l, int index)
+{
+ t_pdp_atom *a;
+ for (a = l->first; index--; a = a->next);
+ return a->w;
+}
+
+
+
+
+
+/* remove an element from a list */
+void pdp_list_remove(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w)
+{
+ t_pdp_atom head;
+ t_pdp_atom *a;
+ t_pdp_atom *kill_a;
+ head.next = l->first;
+
+ for(a = &head; a->next; a = a->next){
+ if (a->next->w.w_int == w.w_int && a->next->t == t){
+ kill_a = a->next; // element to be killed
+ a->next = a->next->next; // remove link
+ PDP_ATOM_DEALLOC(kill_a);
+ l->elements--;
+ l->first = head.next; // restore the start pointer
+ if (l->last == kill_a) { // restore the end pointer
+ l->last = (a != &head) ? a : 0;
+ }
+
+ break;
+ }
+ }
+
+}
+
+
+
+
+
+/* copy a list */
+t_pdp_list* pdp_tree_copy_reverse(t_pdp_list *list)
+{
+ t_pdp_list *newlist = pdp_list_new(0);
+ t_pdp_atom *a;
+ for (a = list->first; a; a = a->next)
+ if (a->t == a_list){
+ pdp_list_add(newlist, a->t,
+ (t_pdp_word)pdp_tree_copy_reverse(a->w.w_list));
+ }
+ else{
+ pdp_list_add(newlist, a->t, a->w);
+ }
+ return newlist;
+}
+t_pdp_list* pdp_list_copy_reverse(t_pdp_list *list)
+{
+ t_pdp_list *newlist = pdp_list_new(0);
+ t_pdp_atom *a;
+ for (a = list->first; a; a = a->next)
+ pdp_list_add(newlist, a->t, a->w);
+ return newlist;
+}
+
+t_pdp_list* pdp_tree_copy(t_pdp_list *list)
+{
+ t_pdp_list *newlist = pdp_list_new(list->elements);
+ t_pdp_atom *a_src = list->first;
+ t_pdp_atom *a_dst = newlist->first;
+
+ while(a_src){
+ a_dst->t = a_src->t;
+ if (a_dst->t == a_list){ //recursively copy sublists (tree copy)
+ a_dst->w.w_list = pdp_tree_copy(a_src->w.w_list);
+ }
+ else{
+ a_dst->w = a_src->w;
+ }
+ a_src = a_src->next;
+ a_dst = a_dst->next;
+ }
+
+ return newlist;
+}
+t_pdp_list* pdp_list_copy(t_pdp_list *list)
+{
+ t_pdp_list *newlist = pdp_list_new(list->elements);
+ t_pdp_atom *a_src = list->first;
+ t_pdp_atom *a_dst = newlist->first;
+
+ while(a_src){
+ a_dst->t = a_src->t;
+ a_dst->w = a_src->w;
+ a_src = a_src->next;
+ a_dst = a_dst->next;
+ }
+ return newlist;
+}
+
+void pdp_list_join (t_pdp_list *l, t_pdp_list *tail)
+{
+ if (tail->elements){
+ l->elements += tail->elements;
+ if (l->last){
+ l->last->next = tail->first;
+ l->last = tail->last;
+ }
+ else {
+ l->first = tail->first;
+ l->last = tail->last;
+ }
+ }
+ PDP_LIST_DEALLOC(tail); //delete the tail header
+}
+
+void pdp_list_cat (t_pdp_list *l, t_pdp_list *tail)
+{
+ t_pdp_list *tmp = pdp_list_copy(tail);
+ pdp_list_join(l, tmp);
+}
+
+
+/* in place reverse: atoms stay the same
+ they are just relinked. so pointers will stay accurate */
+void pdp_list_reverse(t_pdp_list *l)
+{
+ t_pdp_list tmp;
+ t_pdp_atom *a;
+ tmp.first = l->first;
+ tmp.last = l->last;
+ tmp.elements = l->elements;
+ l->first = 0;
+ l->last = 0;
+ l->elements = 0;
+ while (a = pdp_list_pop_atom(&tmp)){
+ pdp_list_add_atom(l, a);
+ }
+}
+
+void pdp_list_reverse_old(t_pdp_list *l)
+{
+ t_pdp_list *l2 = pdp_list_copy_reverse(l);
+ pdp_list_clear(l);
+ l->first = l2->first;
+ l->last = l2->last;
+ l->elements = l2->elements;
+ _pdp_list_dealloc(l2);
+}
+
+/* check if a list contains an element */
+int pdp_list_contains(t_pdp_list *list, t_pdp_word_type t, t_pdp_word w)
+{
+ t_pdp_atom *a;
+ for(a = list->first; a; a=a->next){
+ if (a->w.w_int == w.w_int && a->t == t) return 1;
+ }
+ return 0;
+}
+
+/* add a thing to the start of the list if it's not in there already */
+void pdp_list_add_to_set(t_pdp_list *list, t_pdp_word_type t, t_pdp_word w)
+{
+ if (!pdp_list_contains(list, t, w))
+ pdp_list_add(list, t, w);
+}
+
+
+
+
diff --git a/system/kernel/pdp_mem.c b/system/kernel/pdp_mem.c
new file mode 100644
index 0000000..33822ef
--- /dev/null
+++ b/system/kernel/pdp_mem.c
@@ -0,0 +1,129 @@
+/*
+ * Pure Data Packet system file: memory allocation
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include "pdp_mem.h"
+#include "pdp_debug.h"
+
+
+/* malloc wrapper that calls garbage collector */
+void *pdp_alloc(int size)
+{
+ void *ptr = malloc(size);
+
+ PDP_ASSERT(ptr);
+
+ return ptr;
+
+ //TODO: REPAIR THIS
+ //post ("malloc failed in a pdp module: running garbage collector.");
+ //pdp_pool_collect_garbage();
+ //return malloc(size);
+}
+
+
+void pdp_dealloc(void *stuff)
+{
+ free (stuff);
+}
+
+
+/* fast atom allocation object
+ well, this is not too fast yet, but will be later
+ when it suports linux futexes or atomic operations */
+
+//#include <pthread.h>
+
+/* private linked list struct */
+typedef struct _fastalloc
+{
+ struct _fastalloc * next;
+} t_fastalloc;
+
+
+
+
+static void _pdp_fastalloc_lock(t_pdp_fastalloc *x){pthread_mutex_lock(&x->mut);}
+static void _pdp_fastalloc_unlock(t_pdp_fastalloc *x){pthread_mutex_unlock(&x->mut);}
+
+static void _pdp_fastalloc_refill_freelist(t_pdp_fastalloc *x)
+{
+ t_fastalloc *atom;
+ unsigned int i;
+
+ PDP_ASSERT(x->freelist == 0);
+
+ /* get a new block
+ there is no means of freeing the data afterwards,
+ this is a fast implementation with the tradeoff of data
+ fragmentation "memory leaks".. */
+
+ x->freelist = pdp_alloc(x->block_elements * x->atom_size);
+
+ /* link all atoms together */
+ atom = x->freelist;
+ for (i=0; i<x->block_elements-1; i++){
+ atom->next = (t_fastalloc *)(((char *)atom) + x->atom_size);
+ atom = atom->next;
+ }
+ atom->next = 0;
+
+}
+
+void *pdp_fastalloc_new_atom(t_pdp_fastalloc *x)
+{
+ t_fastalloc *atom;
+
+ _pdp_fastalloc_lock(x);
+
+ /* get an atom from the freelist
+ or refill it and try again */
+ while (!(atom = x->freelist)){
+ _pdp_fastalloc_refill_freelist(x);
+ }
+
+ /* delete the element from the freelist */
+ x->freelist = x->freelist->next;
+ atom->next = 0;
+
+ _pdp_fastalloc_unlock(x);
+
+ return (void *)atom;
+
+}
+void pdp_fastalloc_save_atom(t_pdp_fastalloc *x, void *atom)
+{
+ _pdp_fastalloc_lock(x);
+ ((t_fastalloc *)atom)->next = x->freelist;
+ x->freelist = (t_fastalloc *)atom;
+ _pdp_fastalloc_unlock(x);
+}
+
+t_pdp_fastalloc *pdp_fastalloc_new(unsigned int size)
+{
+ t_pdp_fastalloc *x = pdp_alloc(sizeof(*x));
+ if (size < sizeof(t_fastalloc)) size = sizeof(t_fastalloc);
+ x->freelist = 0;
+ x->atom_size = size;
+ x->block_elements = PDP_FASTALLOC_BLOCK_ELEMENTS;
+ pthread_mutex_init(&x->mut, NULL);
+ return x;
+}
+
diff --git a/system/kernel/pdp_packet.c b/system/kernel/pdp_packet.c
new file mode 100644
index 0000000..0eb569a
--- /dev/null
+++ b/system/kernel/pdp_packet.c
@@ -0,0 +1,634 @@
+/*
+ * Pure Data Packet system implementation: Packet Manager
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include "pdp_post.h"
+#include "pdp_packet.h"
+#include "pdp_mem.h"
+#include "pdp_list.h"
+#include "pdp_type.h"
+#include "pdp_debug.h"
+
+
+/* packet implementation. contains class and packet (instance) handling
+
+ some notes on packet operations.
+ copy ro/rw and unregister are relatively straightforward
+ packet creation can be done in 2 ways in this interface:
+ create + reuse
+ however, these methods should only be called by specific factory
+ methods, so the user should only create packets using pdp_factory_newpacket
+
+ reuse or create is thus the responsability of the factory methods for
+ each packet type (class) implementation
+
+
+*/
+
+
+/* NOTE:
+ the packet pool methods are called within the pool locks. this probably
+ needs to change, because it will cause deadlocks for container packets (fobs) */
+
+
+/* new implementation: probably just a minor adjustment: add the reuse fifo attached
+ to type desc symbol name
+ need to check and possibly eliminate hacks for non-pure packets
+
+ pdp_packet_new:
+ LOCK
+ 1. check reuse fifo
+ 2. empty -> create packet+return (search array)
+ 3. element -> check if type is correct, yes->pop+return, no->goto 1.
+ UNLOCK
+ 4. wakeup
+
+ pdp_packet_mark_unused
+
+ 1. check refcount. if > 1 dec + exit
+ 2. if 1 put packet to sleep
+ 3. dec refcount
+ 4. add to reuse fifo (no fifo -> create)
+
+ pdp_packet_delete: analogous to mark_unused
+ pdp_packet_copy_ro/rw: analogous to new
+
+*/
+
+
+/* the pool */
+#define PDP_INITIAL_POOL_SIZE 64
+static int pdp_pool_size;
+static t_pdp** pdp_pool;
+
+/* mutex: protects the pool and reuse lists attached to symbols */
+static pthread_mutex_t pdp_pool_mutex;
+#define LOCK pthread_mutex_lock (&pdp_pool_mutex)
+#define UNLOCK pthread_mutex_unlock (&pdp_pool_mutex)
+
+/* the list of classes */
+static t_pdp_list *class_list;
+
+/* debug */
+void
+pdp_packet_print_debug(int packet)
+{
+ t_pdp *h = pdp_packet_header(packet);
+ pdp_post("debug info for packet %d", packet);
+ if (!h){
+ pdp_post("invalid packet");
+ }
+ else{
+ pdp_post ("\ttype: %d", h->type);
+ pdp_post ("\tdesc: %s", h->desc ? h->desc->s_name : "unknown");
+ pdp_post ("\tsize: %d", h->size);
+ pdp_post ("\tflags: %x", h->flags);
+ pdp_post ("\tusers: %d", h->users);
+ pdp_post ("\tclass: %x", h->theclass);
+ }
+}
+
+
+
+/* setup methods */
+
+void
+pdp_packet_setup(void)
+{
+
+ pdp_pool_size = PDP_INITIAL_POOL_SIZE;
+ pdp_pool = (t_pdp **)pdp_alloc(PDP_INITIAL_POOL_SIZE * sizeof(t_pdp *));
+ bzero(pdp_pool, pdp_pool_size * sizeof(t_pdp *));
+ class_list = pdp_list_new(0);
+ pthread_mutex_init(&pdp_pool_mutex, NULL);
+}
+
+/* class methods */
+t_pdp_class *pdp_class_new(t_pdp_symbol *type, t_pdp_factory_method create){
+ t_pdp_class *c = (t_pdp_class *)pdp_alloc(sizeof(t_pdp_class));
+ memset(c, 0, sizeof(t_pdp_class));
+ c->create = create;
+ c->type = type; // set type
+ pdp_list_add(class_list, a_pointer, (t_pdp_word)((void *)c));
+ return c;
+}
+
+/* the packet factory */
+int pdp_factory_newpacket(t_pdp_symbol *type)
+{
+ int p;
+ t_pdp_class *c;
+ t_pdp_atom *a = class_list->first;
+
+ /* try to reuse first
+ THINK: should this be the responsability of the type specific constructors,
+ or should a packet allways be reusable (solution: depends on what the cleanup method returns??)
+ */
+ p = pdp_packet_reuse(type);
+ if (-1 != p) return p;
+
+
+ /* call class constructor */
+ while(a){
+ c = (t_pdp_class *)(a->w.w_pointer);
+ if (c->type && pdp_type_description_match(type, c->type)){
+ //pdp_post("method %x, type %s", c->create, type->s_name);
+ return (c->create) ? (*c->create)(type) : -1;
+ }
+ a = a->next;
+ }
+ return -1;
+}
+
+static void
+_pdp_pool_expand_nolock(void){
+ int i;
+
+ /* double the size */
+ int new_pool_size = pdp_pool_size << 1;
+ t_pdp **new_pool = (t_pdp **)pdp_alloc(new_pool_size * sizeof(t_pdp *));
+ bzero(new_pool, new_pool_size * sizeof(t_pdp *));
+ memcpy(new_pool, pdp_pool, pdp_pool_size * sizeof(t_pdp *));
+ pdp_dealloc(pdp_pool);
+ pdp_pool = new_pool;
+ pdp_pool_size = new_pool_size;
+}
+
+
+
+
+/* private _pdp_packet methods */
+
+/* packets can only be created and destroyed using these 2 methods */
+/* it updates the mem usage and total packet count */
+
+static void
+_pdp_packet_dealloc_nolock(t_pdp *p)
+{
+ /* free memory */
+ pdp_dealloc (p);
+}
+
+static t_pdp*
+_pdp_packet_alloc_nolock(unsigned int datatype, unsigned int datasize)
+{
+ unsigned int totalsize = datasize + PDP_HEADER_SIZE;
+ t_pdp *p = (t_pdp *)pdp_alloc(totalsize);
+ if (p){
+ memset(p, 0, PDP_HEADER_SIZE); //initialize header to 0
+ p->type = datatype;
+ p->size = totalsize;
+ p->users = 1;
+ }
+ return p;
+}
+
+
+/* create a new packet and expand pool if necessary */
+static int
+_pdp_packet_create_nolock(unsigned int datatype, unsigned int datasize)
+{
+ int p = 0;
+ while(1){
+ for (; p < pdp_pool_size; p++){
+ if (!pdp_pool[p]){
+ /* found slot to store packet*/
+ t_pdp *header = _pdp_packet_alloc_nolock(datatype, datasize);
+ if (!header) return -1; // error allocating packet
+ pdp_pool[p] = header;
+ return p;
+ }
+ }
+ /* no slot found, expand pool */
+ _pdp_pool_expand_nolock();
+ }
+}
+
+
+void
+pdp_packet_destroy(void)
+{
+ int i = 0;
+ /* dealloc all the data in object stack */
+ pdp_post("DEBUG: pdp_packet_destroy: clearing object pool.");
+ while ((i < pdp_pool_size) && (pdp_pool[i])) _pdp_packet_dealloc_nolock(pdp_pool[i++]);
+}
+
+
+
+
+
+
+
+
+/* public pool operations: have to be thread safe so each entry point
+ locks the mutex */
+
+
+/* create a new packet.
+ this should only be used by type specific factory methods, and only if the
+ reuse method fails, since it will always create a new packet */
+int
+pdp_packet_create(unsigned int datatype, unsigned int datasize /*without header*/)
+{
+ int packet;
+ LOCK;
+ packet = _pdp_packet_create_nolock(datatype, datasize);
+ UNLOCK;
+ return packet;
+}
+
+
+/* return a new packet.
+ it tries to reuse a packet based on
+ 1. matching data size
+ 2. abscence of destructor (which SHOULD mean there are no enclosed references)
+
+ it obviously can't use the reuse fifo tagged to a symbolic type description
+
+ ALWAYS USE pdp_packet_reuse BEFORE calling pdp_packet_new if possible
+ use both ONLY IN CONSTRUCTORS !!!
+
+ use pdp_packet_factory to create packets as a "user"
+
+ this is a summary of all internal packet creation mechanisms:
+
+ -> pdp_packet_reuse, which uses symbolic type descriptions, and should work for all packet types
+ it returns an initialized container (meta = correct, data = garbage)
+
+ -> pdp_packet_new, which only works for non-pure packets, and reuses packets based on data type
+ it returns a pure packet (meta + data = garbage)
+
+ -> pdp_packet_create, like pdp_packet_new, only it always creates a new packet
+
+
+
+*/
+
+int
+pdp_packet_new(unsigned int datatype, unsigned int datasize)
+{
+ t_pdp *header;
+ int packet;
+ LOCK;
+ for (packet = 0; packet < pdp_pool_size; packet++){
+ header = pdp_pool[packet];
+ /* check data size */
+ if (header
+ && header->users == 0 // must be unused
+ && header->size == datasize + PDP_HEADER_SIZE // must be same size
+ && !(header->theclass && header->theclass->cleanup)){ // must be pure packet (no destructor)
+
+ /* ok, got one. initialize */
+ memset(header, 0, PDP_HEADER_SIZE);
+ header->users = 1;
+ header->type = datatype;
+ header->size = datasize + PDP_HEADER_SIZE;
+
+ UNLOCK; //EXIT1
+ return packet;
+ }
+ }
+
+ /* no usable non-pure packet found, create a new one */
+
+ UNLOCK; //EXIT2
+ return pdp_packet_create(datatype, datasize);
+
+
+
+}
+
+
+/* internal method to add a packet to a packet type
+ description symbol's unused packet fifo */
+void
+_pdp_packet_save_nolock(int packet)
+{
+ t_pdp *header = pdp_packet_header(packet);
+ t_pdp_symbol *s;
+ PDP_ASSERT(header);
+ PDP_ASSERT(header->users == 0);
+ PDP_ASSERT(header->desc);
+ s = header->desc;
+ if (!s->s_reusefifo) s->s_reusefifo = pdp_list_new(0);
+ pdp_list_add(s->s_reusefifo, a_packet, (t_pdp_word)packet);
+}
+
+/* this will revive a packet matching a certain type description
+ no wildcards are allowed */
+int
+pdp_packet_reuse(t_pdp_symbol *type_description)
+{
+ int packet = -1;
+ t_pdp *header = 0;
+ t_pdp_list *l = 0;
+ LOCK;
+ if (!type_description || !(l = type_description->s_reusefifo)) goto exit;
+ while(l->elements){
+ packet = pdp_list_pop(l).w_packet;
+ header = pdp_packet_header(packet);
+
+ /* check if reuse fifo is consistent (packet unused + correct type)
+ packet could be deleted and replaced with another one, or
+ revived without the index updated (it's a "hint cache") */
+
+ if (header->users == 0){
+ /* check if type matches */
+ if (pdp_type_description_match(header->desc, type_description)){
+ header->users++; // revive
+ goto exit;
+ }
+ /* if not, add the packet to the correct reuse fifo */
+ else{
+ _pdp_packet_save_nolock(packet);
+ }
+ }
+
+ /* remove dangling refs */
+ header = 0;
+ packet = -1;
+ }
+
+ exit:
+ UNLOCK;
+ if (header && header->theclass && header->theclass->wakeup){
+ header->theclass->wakeup(header); // revive if necessary
+ }
+ return packet;
+}
+
+/* find all unused packets in pool, marked as used (to protect from other reapers)
+ and return them as a list. non-pure packets are not revived */
+
+
+
+
+
+/* this returns a copy of a packet for read only access.
+ (increases refcount of the packet -> packet will become readonly if it was
+ writable, i.e. had rc=1 */
+
+int
+pdp_packet_copy_ro(int handle)
+{
+ t_pdp* header;
+
+ if (header = pdp_packet_header(handle)){
+ PDP_ASSERT(header->users); // consistency check
+ LOCK;
+ header->users++; // increment reference count
+ UNLOCK;
+ }
+ else handle = -1;
+ return handle;
+}
+
+/* clone a packet: create a new packet with the same
+ type as the source packet */
+
+int
+pdp_packet_clone_rw(int handle)
+{
+ t_pdp* header;
+ int new_handle = -1;
+
+
+ if (header = pdp_packet_header(handle)){
+ /* consistency checks */
+ PDP_ASSERT(header->users);
+ PDP_ASSERT(header->desc);
+
+ /* first try to reuse old packet */
+ new_handle = pdp_packet_reuse(header->desc);
+
+ /* if this failed, create a new one using the central packet factory method */
+ if (-1 == new_handle) new_handle = pdp_factory_newpacket(header->desc);
+
+ /* if the factory method failed cline it manually */
+ if (-1 == new_handle) {
+ t_pdp *new_header;
+ //pdp_post("WARNING: pdp_clone_rw: working around non-implemented factory method.");
+ new_handle = pdp_packet_new(header->type, header->size - PDP_HEADER_SIZE);
+ new_header = pdp_packet_header(new_handle);
+ if (new_header){
+ memcpy(new_header, header, PDP_HEADER_SIZE);
+ }
+ }
+ }
+
+ return new_handle;
+}
+
+/* return a copy of a packet (clone + copy data) */
+int
+pdp_packet_copy_rw(int handle)
+{
+ t_pdp *header, *new_header;
+ int new_handle = -1;
+
+ if (!(header = pdp_packet_header(handle))) return -1;
+
+ /* check if we are allowed to copy */
+ if (header->flags & PDP_FLAG_DONOTCOPY) return -1;
+
+ /* get target packet */
+ new_handle = pdp_packet_clone_rw(handle);
+ if (-1 == new_handle) return -1;
+ new_header = pdp_packet_header(new_handle);
+
+ /* if there is a copy method, use that one */
+ if (header->theclass && header->theclass->copy){
+ header->theclass->copy(header, new_header);
+ }
+
+ /* otherwize copy the data verbatim */
+ else {
+ memcpy(pdp_packet_data(new_handle),
+ pdp_packet_data(handle),
+ pdp_packet_data_size(handle));
+ }
+
+ return new_handle;
+
+}
+
+
+/* decrement refcount */
+void pdp_packet_mark_unused(int handle)
+{
+ t_pdp *header;
+ if (!(header = pdp_packet_header(handle))) return;
+
+ PDP_ASSERT(header->users); // consistency check
+
+ LOCK;
+
+ /* just decrement refcount */
+ if (header->users > 1){
+ header->users--;
+ }
+
+ /* put packet to sleep if refcount 1->0 */
+ else {
+ if (header->theclass && header->theclass->sleep){
+ /* call sleep method (if any) outside of lock
+ while the packet is still alive, so it won't be
+ acclaimed by another thread */
+ UNLOCK;
+ header->theclass->sleep(header);
+ LOCK;
+ }
+ /* clear refcount & save in fifo for later use */
+ header->users = 0;
+ if (header->desc) // sleep could have destructed packet..
+ _pdp_packet_save_nolock(handle);
+ }
+
+ UNLOCK;
+}
+
+
+
+/* delete a packet. rc needs to be == 1 */
+void pdp_packet_delete(int handle)
+{
+ t_pdp *header;
+ header = pdp_packet_header(handle);
+ PDP_ASSERT(header);
+ PDP_ASSERT(header->users == 1); // consistency check
+
+ LOCK;
+
+ if (header->theclass && header->theclass->cleanup){
+ /* call cleanup method (if any) outside of lock
+ while the packet is still alive, so it won't be
+ acclaimed by another thread */
+ UNLOCK;
+ header->theclass->cleanup(header);
+ LOCK;
+ }
+
+ /* delete the packet */
+ pdp_pool[handle] = 0;
+ _pdp_packet_dealloc_nolock(header);
+
+
+ UNLOCK;
+}
+
+
+
+
+
+
+
+/* public data access methods */
+
+t_pdp*
+pdp_packet_header(int handle)
+{
+ if ((handle >= 0) && (handle < pdp_pool_size)) return pdp_pool[handle];
+ else return 0;
+}
+
+void*
+pdp_packet_subheader(int handle)
+{
+ t_pdp* header = pdp_packet_header(handle);
+ if (!header) return 0;
+ return (void *)(&header->info.raw);
+}
+
+void*
+pdp_packet_data(int handle)
+{
+ t_pdp *h;
+ if ((handle >= 0) && (handle < pdp_pool_size))
+ {
+ h = pdp_pool[handle];
+ if (!h) return 0;
+ return (char *)(h) + PDP_HEADER_SIZE;
+ }
+ else return 0;
+}
+
+int
+pdp_packet_data_size(int handle)
+{
+ t_pdp *h;
+ if ((handle >= 0) && (handle < pdp_pool_size))
+ {
+ h = pdp_pool[handle];
+ if (!h) return 0;
+ return h->size - PDP_HEADER_SIZE;
+ }
+ else return 0;
+}
+
+
+
+
+int pdp_packet_writable(int packet) /* returns true if packet is writable */
+{
+ t_pdp *h = pdp_packet_header(packet);
+ if (!h) return 0;
+ return (h->users == 1);
+}
+
+void pdp_packet_replace_with_writable(int *packet) /* replaces a packet with a writable copy */
+{
+ int new_p;
+ if (!pdp_packet_writable(*packet)){
+ new_p = pdp_packet_copy_rw(*packet);
+ pdp_packet_mark_unused(*packet);
+ *packet = new_p;
+ }
+
+}
+
+/* pool stuff */
+
+int
+pdp_pool_collect_garbage(void)
+{
+ pdp_post("ERROR: garbage collector not implemented");
+ return 0;
+}
+
+void
+pdp_pool_set_max_mem_usage(int max)
+{
+ pdp_post("ERROR: mem limit not implemented");
+}
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/system/kernel/pdp_packet2.c b/system/kernel/pdp_packet2.c
new file mode 100644
index 0000000..3717a77
--- /dev/null
+++ b/system/kernel/pdp_packet2.c
@@ -0,0 +1,623 @@
+/*
+ * Pure Data Packet system implementation: Packet Manager
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include "pdp_post.h"
+#include "pdp_packet.h"
+#include "pdp_mem.h"
+#include "pdp_list.h"
+#include "pdp_type.h"
+#include "pdp_debug.h"
+
+
+/* packet implementation. contains class and packet (instance) handling
+
+ some notes on packet operations.
+ copy ro/rw and unregister are relatively straightforward
+ packet creation can be done in 2 ways in this interface:
+ create + reuse
+ however, these methods should only be called by specific factory
+ methods, so the user should only create packets using pdp_factory_newpacket
+
+ reuse or create is thus the responsability of the factory methods for
+ each packet type (class) implementation
+
+
+*/
+
+
+/* NOTE:
+ the packet pool methods are called within the pool locks. this probably
+ needs to change, because it will cause deadlocks for container packets (fobs) */
+
+
+/* new implementation: probably just a minor adjustment: add the reuse fifo attached
+ to type desc symbol name
+ need to check and possibly eliminate hacks for non-pure packets
+
+ pdp_packet_new:
+ LOCK
+ 1. check reuse fifo
+ 2. empty -> create packet+return (search array)
+ 3. element -> check if type is correct, yes->pop+return, no->goto 1.
+ UNLOCK
+ 4. wakeup
+
+ pdp_packet_mark_unused
+
+ 1. check refcount. if > 1 dec + exit
+ 2. if 1 put packet to sleep
+ 3. dec refcount
+ 4. add to reuse fifo (no fifo -> create)
+
+ pdp_packet_delete: analogous to mark_unused
+ pdp_packet_copy_ro/rw: analogous to new
+
+*/
+
+
+/* the pool */
+#define PDP_INITIAL_POOL_SIZE 64
+static int pdp_pool_size;
+static t_pdp** pdp_pool;
+
+/* mutex: protects the pool and reuse lists attached to symbols */
+static pthread_mutex_t pdp_pool_mutex;
+#define LOCK pthread_mutex_lock (&pdp_pool_mutex)
+#define UNLOCK pthread_mutex_unlock (&pdp_pool_mutex)
+
+/* the list of classes */
+static t_pdp_list *class_list;
+
+/* debug */
+void
+pdp_packet_print_debug(int packet)
+{
+ t_pdp *h = pdp_packet_header(packet);
+ pdp_post("debug info for packet %d", packet);
+ if (!h){
+ pdp_post("invalid packet");
+ }
+ else{
+ pdp_post ("\ttype: %d", h->type);
+ pdp_post ("\tdesc: %s", h->desc ? h->desc->s_name : "unknown");
+ pdp_post ("\tsize: %d", h->size);
+ pdp_post ("\tflags: %x", h->flags);
+ pdp_post ("\tusers: %d", h->users);
+ pdp_post ("\tclass: %x", h->theclass);
+ }
+}
+
+
+
+/* setup methods */
+
+void
+pdp_packet_setup(void)
+{
+
+ pdp_pool_size = PDP_INITIAL_POOL_SIZE;
+ pdp_pool = (t_pdp **)pdp_alloc(PDP_INITIAL_POOL_SIZE * sizeof(t_pdp *));
+ bzero(pdp_pool, pdp_pool_size * sizeof(t_pdp *));
+ class_list = pdp_list_new(0);
+ pthread_mutex_init(&pdp_pool_mutex, NULL);
+}
+
+/* class methods */
+t_pdp_class *pdp_class_new(t_pdp_symbol *type, t_pdp_factory_method create){
+ t_pdp_class *c = (t_pdp_class *)pdp_alloc(sizeof(t_pdp_class));
+ memset(c, 0, sizeof(t_pdp_class));
+ c->create = create;
+ c->type = type; // set type
+ pdp_list_add(class_list, a_pointer, (t_pdp_word)((void *)c));
+ return c;
+}
+
+/* the packet factory */
+int pdp_factory_newpacket(t_pdp_symbol *type)
+{
+ int p;
+ t_pdp_class *c;
+ t_pdp_atom *a = class_list->first;
+
+ /* try to reuse first
+ THINK: should this be the responsability of the type specific constructors,
+ or should a packet allways be reusable (solution: depends on what the cleanup method returns??)
+ */
+ p = pdp_packet_reuse(type);
+ if (-1 != p) return p;
+
+
+ /* call class constructor */
+ while(a){
+ c = (t_pdp_class *)(a->w.w_pointer);
+ if (c->type && pdp_type_description_match(type, c->type)){
+ //pdp_post("method %x, type %s", c->create, type->s_name);
+ return (c->create) ? (*c->create)(type) : -1;
+ }
+ a = a->next;
+ }
+ return -1;
+}
+
+static void
+_pdp_pool_expand_nolock(void){
+ int i;
+
+ /* double the size */
+ int new_pool_size = pdp_pool_size << 1;
+ t_pdp **new_pool = (t_pdp **)pdp_alloc(new_pool_size * sizeof(t_pdp *));
+ bzero(new_pool, new_pool_size * sizeof(t_pdp *));
+ memcpy(new_pool, pdp_pool, pdp_pool_size * sizeof(t_pdp *));
+ pdp_dealloc(pdp_pool);
+ pdp_pool = new_pool;
+ pdp_pool_size = new_pool_size;
+}
+
+
+
+
+/* private _pdp_packet methods */
+
+/* packets can only be created and destroyed using these 2 methods */
+/* it updates the mem usage and total packet count */
+
+static void
+_pdp_packet_dealloc_nolock(t_pdp *p)
+{
+ /* free memory */
+ pdp_dealloc (p);
+}
+
+static t_pdp*
+_pdp_packet_alloc_nolock(unsigned int datatype, unsigned int datasize)
+{
+ unsigned int totalsize = datasize + PDP_HEADER_SIZE;
+ t_pdp *p = (t_pdp *)pdp_alloc(totalsize);
+ if (p){
+ memset(p, 0, PDP_HEADER_SIZE); //initialize header to 0
+ p->type = datatype;
+ p->size = totalsize;
+ p->users = 1;
+ }
+ return p;
+}
+
+
+/* create a new packet and expand pool if necessary */
+static int
+_pdp_packet_create_nolock(unsigned int datatype, unsigned int datasize)
+{
+ int p = 0;
+ while(1){
+ for (; p < pdp_pool_size; p++){
+ if (!pdp_pool[p]){
+ /* found slot to store packet*/
+ t_pdp *header = _pdp_packet_alloc_nolock(datatype, datasize);
+ if (!header) return -1; // error allocating packet
+ pdp_pool[p] = header;
+ return p;
+ }
+ }
+ /* no slot found, expand pool */
+ _pdp_pool_expand_nolock();
+ }
+}
+
+
+void
+pdp_packet_destroy(void)
+{
+ int i = 0;
+ /* dealloc all the data in object stack */
+ pdp_post("DEBUG: pdp_packet_destroy: clearing object pool.");
+ while ((i < pdp_pool_size) && (pdp_pool[i])) _pdp_packet_dealloc_nolock(pdp_pool[i++]);
+}
+
+
+
+
+
+
+
+
+/* public pool operations: have to be thread safe so each entry point
+ locks the mutex */
+
+
+/* create a new packet.
+ this should only be used by type specific factory methods, and only if the
+ reuse method fails, since it will always create a new packet */
+int
+pdp_packet_create(unsigned int datatype, unsigned int datasize /*without header*/)
+{
+ int packet;
+ LOCK;
+ packet = _pdp_packet_create_nolock(datatype, datasize);
+ UNLOCK;
+ return packet;
+}
+
+
+/* return a new packet.
+ it tries to reuse a packet based on
+ 1. matching data size
+ 2. abscence of destructor (which SHOULD mean there are no enclosed references)
+
+ it obviously can't use the reuse fifo tagged to a symbolic type description
+
+ ALWAYS USE pdp_packet_reuse BEFORE calling pdp_packet_new if possible
+ use both ONLY IN CONSTRUCTORS !!!
+
+ use pdp_packet_factory to create packets as a "user"
+
+ this is a summary of all internal packet creation mechanisms:
+
+ -> pdp_packet_reuse, which uses symbolic type descriptions, and should work for all packet types
+ it returns an initialized container (meta = correct, data = garbage)
+
+ -> pdp_packet_new, which only works for non-pure packets, and reuses packets based on data type
+ it returns a pure packet (meta + data = garbage)
+
+ -> pdp_packet_create, like pdp_packet_new, only it always creates a new packet
+
+
+
+*/
+
+int
+pdp_packet_new(unsigned int datatype, unsigned int datasize)
+{
+ t_pdp *header;
+ int packet;
+ LOCK;
+ for (packet = 0; packet < pdp_pool_size; packet++){
+ header = pdp_pool[packet];
+ /* check data size */
+ if (header
+ && header->users == 0
+ && header->size == datasize + PDP_HEADER_SIZE
+ && !(header->theclass && header->theclass->cleanup)){
+
+ /* ok, got one. initialize */
+ memset(header, 0, PDP_HEADER_SIZE);
+ header->users = 1;
+ header->type = datatype;
+ header->size = datasize + PDP_HEADER_SIZE;
+
+ UNLOCK; //EXIT1
+ return packet;
+ }
+ }
+
+ /* no usable non-pure packet found, create a new one */
+
+ UNLOCK; //EXIT2
+ return pdp_packet_create(datatype, datasize);
+
+
+
+}
+
+
+/* internal method to add a packet to a packet type
+ description symbol's unused packet fifo */
+void
+_pdp_packet_save_nolock(int packet)
+{
+ t_pdp *header = pdp_packet_header(packet);
+ t_pdp_symbol *s;
+ PDP_ASSERT(header);
+ PDP_ASSERT(header->users == 0);
+ PDP_ASSERT(header->desc);
+ s = header->desc;
+ if (!s->s_reusefifo) s->s_reusefifo = pdp_list_new(0);
+ pdp_list_add(s->s_reusefifo, a_packet, (t_pdp_word)packet);
+}
+
+/* this will revive a packet matching a certain type description
+ no wildcards are allowed */
+int
+pdp_packet_reuse(t_pdp_symbol *type_description)
+{
+ int packet = -1;
+ t_pdp *header = 0;
+ t_pdp_list *l = 0;
+ LOCK;
+ if (!type_description || !(l = type_description->s_reusefifo)) goto exit;
+ while(l->elements){
+ packet = pdp_list_pop(l).w_packet;
+ header = pdp_packet_header(packet);
+
+ /* check if reuse fifo is consistent (packet unused + correct type)
+ packet could be deleted and replaced with another one, or
+ revived without the index updated (it's a "hint cache") */
+
+ if (header->users == 0){
+ /* check if type matches */
+ if (pdp_type_description_match(header->desc, type_description)){
+ header->users++; // revive
+ goto exit;
+ }
+ /* if not, add the packet to the correct reuse fifo */
+ else{
+ _pdp_packet_save_nolock(packet);
+ }
+ }
+
+ /* remove dangling refs */
+ header = 0;
+ packet = -1;
+ }
+
+ exit:
+ UNLOCK;
+ if (header && header->theclass && header->theclass->wakeup){
+ header->theclass->wakeup(header); // revive if necessary
+ }
+ return packet;
+}
+
+/* find all unused packets in pool, marked as used (to protect from other reapers)
+ and return them as a list. non-pure packets are not revived */
+
+
+
+
+
+/* this returns a copy of a packet for read only access.
+ (increases refcount of the packet -> packet will become readonly if it was
+ writable, i.e. had rc=1 */
+
+int
+pdp_packet_copy_ro(int handle)
+{
+ t_pdp* header;
+
+ if (header = pdp_packet_header(handle)){
+ PDP_ASSERT(header->users); // consistency check
+ LOCK;
+ header->users++; // increment reference count
+ UNLOCK;
+ }
+ else handle = -1;
+ return handle;
+}
+
+/* clone a packet: create a new packet with the same
+ type as the source packet */
+
+int
+pdp_packet_clone_rw(int handle)
+{
+ t_pdp* header;
+ int new_handle = -1;
+
+
+ if (header = pdp_packet_header(handle)){
+ /* consistency checks */
+ PDP_ASSERT(header->users);
+ PDP_ASSERT(header->desc);
+
+ /* first try to reuse old packet */
+ new_handle = pdp_packet_reuse(header->desc);
+
+ /* if this failed, create a new one using the central packet factory method */
+ if (-1 == new_handle) new_handle = pdp_factory_newpacket(header->desc);
+ }
+
+ return new_handle;
+}
+
+/* return a copy of a packet (clone + copy data) */
+int
+pdp_packet_copy_rw(int handle)
+{
+ t_pdp *header, *new_header;
+ int new_handle = -1;
+
+ if (!(header = pdp_packet_header(handle))) return -1;
+
+ /* check if we are allowed to copy */
+ if (header->flags & PDP_FLAG_DONOTCOPY) return -1;
+
+ /* get target packet */
+ new_handle = pdp_packet_clone_rw(handle);
+ if (-1 == new_handle) return -1;
+ new_header = pdp_packet_header(new_handle);
+
+ /* if there is a copy method, use that one */
+ if (header->theclass && header->theclass->copy){
+ header->theclass->copy(header, new_header);
+ }
+
+ /* otherwize copy the data verbatim */
+ else {
+ memcpy(pdp_packet_data(new_handle),
+ pdp_packet_data(handle),
+ pdp_packet_data_size(handle));
+ }
+
+ return new_handle;
+
+}
+
+
+/* decrement refcount */
+void pdp_packet_mark_unused(int handle)
+{
+ t_pdp *header;
+ if (!(header = pdp_packet_header(handle))) return;
+
+ PDP_ASSERT(header->users); // consistency check
+
+ LOCK;
+
+ /* just decrement refcount */
+ if (header->users > 1){
+ header->users--;
+ }
+
+ /* put packet to sleep if refcount 1->0 */
+ else {
+ if (header->theclass && header->theclass->sleep){
+ /* call sleep method (if any) outside of lock
+ while the packet is still alive, so it won't be
+ acclaimed by another thread */
+ UNLOCK;
+ header->theclass->sleep(header);
+ LOCK;
+ }
+ /* clear refcount & save in fifo for later use */
+ header->users = 0;
+ if (header->desc) // sleep could have destructed packet..
+ _pdp_packet_save_nolock(handle);
+ }
+
+ UNLOCK;
+}
+
+
+
+/* delete a packet. rc needs to be == 1 */
+void pdp_packet_delete(int handle)
+{
+ t_pdp *header;
+ header = pdp_packet_header(handle);
+ PDP_ASSERT(header);
+ PDP_ASSERT(header->users == 1); // consistency check
+
+ LOCK;
+
+ if (header->theclass && header->theclass->cleanup){
+ /* call cleanup method (if any) outside of lock
+ while the packet is still alive, so it won't be
+ acclaimed by another thread */
+ UNLOCK;
+ header->theclass->cleanup(header);
+ LOCK;
+ }
+
+ /* delete the packet */
+ pdp_pool[handle] = 0;
+ _pdp_packet_dealloc_nolock(header);
+
+
+ UNLOCK;
+}
+
+
+
+
+
+
+
+/* public data access methods */
+
+t_pdp*
+pdp_packet_header(int handle)
+{
+ if ((handle >= 0) && (handle < pdp_pool_size)) return pdp_pool[handle];
+ else return 0;
+}
+
+void*
+pdp_packet_subheader(int handle)
+{
+ t_pdp* header = pdp_packet_header(handle);
+ if (!header) return 0;
+ return (void *)(&header->info.raw);
+}
+
+void*
+pdp_packet_data(int handle)
+{
+ t_pdp *h;
+ if ((handle >= 0) && (handle < pdp_pool_size))
+ {
+ h = pdp_pool[handle];
+ if (!h) return 0;
+ return (char *)(h) + PDP_HEADER_SIZE;
+ }
+ else return 0;
+}
+
+int
+pdp_packet_data_size(int handle)
+{
+ t_pdp *h;
+ if ((handle >= 0) && (handle < pdp_pool_size))
+ {
+ h = pdp_pool[handle];
+ if (!h) return 0;
+ return h->size - PDP_HEADER_SIZE;
+ }
+ else return 0;
+}
+
+
+
+
+int pdp_packet_writable(int packet) /* returns true if packet is writable */
+{
+ t_pdp *h = pdp_packet_header(packet);
+ if (!h) return 0;
+ return (h->users == 1);
+}
+
+void pdp_packet_replace_with_writable(int *packet) /* replaces a packet with a writable copy */
+{
+ int new_p;
+ if (!pdp_packet_writable(*packet)){
+ new_p = pdp_packet_copy_rw(*packet);
+ pdp_packet_mark_unused(*packet);
+ *packet = new_p;
+ }
+
+}
+
+/* pool stuff */
+
+int
+pdp_pool_collect_garbage(void)
+{
+ pdp_post("ERROR: garbage collector not implemented");
+ return 0;
+}
+
+void
+pdp_pool_set_max_mem_usage(int max)
+{
+ pdp_post("ERROR: mem limit not implemented");
+}
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/system/kernel/pdp_post.c b/system/kernel/pdp_post.c
new file mode 100644
index 0000000..fb761d0
--- /dev/null
+++ b/system/kernel/pdp_post.c
@@ -0,0 +1,48 @@
+
+/*
+ * Pure Data Packet system file. pdp logging.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include "pdp_post.h"
+
+/* list printing should be moved here too */
+
+/* write a message to log (console) */
+void pdp_post_n(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+void pdp_post(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ putc('\n', stderr);
+}
+
+
diff --git a/system/kernel/pdp_symbol.c b/system/kernel/pdp_symbol.c
new file mode 100644
index 0000000..32e9e33
--- /dev/null
+++ b/system/kernel/pdp_symbol.c
@@ -0,0 +1,196 @@
+/*
+ * Pure Data Packet system implementation. : code implementing pdp's namespace (symbols)
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <string.h>
+#include <pthread.h>
+#include "pdp_symbol.h"
+#include "pdp_list.h"
+#include "pdp_debug.h"
+
+// some extra prototypes
+void *pdp_alloc(int size);
+void pdp_dealloc(void *data);
+
+// the symbol hash mutex
+static pthread_mutex_t pdp_hash_mutex;
+
+#define HASHSIZE 1024
+static t_pdp_symbol *pdp_symhash[HASHSIZE];
+
+
+#define LOCK pthread_mutex_lock(&pdp_hash_mutex)
+#define UNLOCK pthread_mutex_unlock(&pdp_hash_mutex)
+
+
+static void _pdp_symbol_init(t_pdp_symbol *s)
+{
+ memset(s, 0, sizeof(*s));
+ s->s_forth.t = a_undef;
+}
+
+
+/* shamelessly copied from pd src and made thread safe */
+t_pdp_symbol *_pdp_dogensym(char *s, t_pdp_symbol *oldsym)
+{
+ t_pdp_symbol **sym1, *sym2;
+ unsigned int hash1 = 0, hash2 = 0;
+ int length = 0;
+ char *s2 = s;
+ while (*s2)
+ {
+ hash1 += *s2;
+ hash2 += hash1;
+ length++;
+ s2++;
+ }
+ sym1 = pdp_symhash + (hash2 & (HASHSIZE-1));
+
+ /* lock hash */
+ LOCK;
+
+ while (sym2 = *sym1)
+ {
+ if (!strcmp(sym2->s_name, s)) goto gotit;
+ sym1 = &sym2->s_next;
+ }
+ if (oldsym){
+ sym2 = oldsym;
+ }
+ else
+ {
+ sym2 = (t_pdp_symbol *)pdp_alloc(sizeof(*sym2));
+ _pdp_symbol_init(sym2);
+ sym2->s_name = pdp_alloc(length+1);
+ sym2->s_next = 0;
+ strcpy(sym2->s_name, s);
+ }
+ *sym1 = sym2;
+
+ gotit:
+
+ /* unlock hash */
+ UNLOCK;
+ return (sym2);
+}
+
+t_pdp_symbol *pdp_gensym(char *s)
+{
+ return(_pdp_dogensym(s, 0));
+}
+
+
+/* connect a parsed typelist to a symbol type name
+ 1 = succes, 0 = error (symbol already connected) */
+int pdp_symbol_set_typelist(t_pdp_symbol *s, t_pdp_list *typelist)
+{
+ int status = 0;
+ LOCK;
+ if (!s->s_type){
+ s->s_type = typelist;
+ status = 1;
+ }
+ UNLOCK;
+ return status;
+}
+
+
+void pdp_symbol_apply_all(t_pdp_symbol_iterator it)
+{
+ int i;
+ for (i=0; i<HASHSIZE; i++){
+ t_pdp_symbol *s;
+ for (s = pdp_symhash[i]; s; s=s->s_next){
+ it(s);
+ }
+
+ }
+}
+
+t_pdp_symbol _pdp_sym_wildcard;
+t_pdp_symbol _pdp_sym_float;
+t_pdp_symbol _pdp_sym_int;
+t_pdp_symbol _pdp_sym_symbol;
+t_pdp_symbol _pdp_sym_packet;
+t_pdp_symbol _pdp_sym_pointer;
+t_pdp_symbol _pdp_sym_invalid;
+t_pdp_symbol _pdp_sym_list;
+t_pdp_symbol _pdp_sym_question_mark;
+t_pdp_symbol _pdp_sym_atom;
+t_pdp_symbol _pdp_sym_null;
+t_pdp_symbol _pdp_sym_quote_start;
+t_pdp_symbol _pdp_sym_quote_end;
+t_pdp_symbol _pdp_sym_return;
+t_pdp_symbol _pdp_sym_nreturn;
+t_pdp_symbol _pdp_sym_defstart;
+t_pdp_symbol _pdp_sym_defend;
+t_pdp_symbol _pdp_sym_if;
+t_pdp_symbol _pdp_sym_then;
+t_pdp_symbol _pdp_sym_local;
+t_pdp_symbol _pdp_sym_forth;
+t_pdp_symbol _pdp_sym_call;
+t_pdp_symbol _pdp_sym_push;
+t_pdp_symbol _pdp_sym_pop;
+
+static void _sym(char *name, t_pdp_symbol *s)
+{
+ t_pdp_symbol *realsym;
+ _pdp_symbol_init(s);
+ s->s_name = name;
+ realsym = _pdp_dogensym(name, s);
+ PDP_ASSERT(realsym == s); // if this fails, the symbol was already defined
+}
+
+void pdp_symbol_setup(void)
+{
+ // create mutexes
+ pthread_mutex_init(&pdp_hash_mutex, NULL);
+
+ // init symbol hash
+ memset(pdp_symhash, 0, HASHSIZE * sizeof(t_pdp_symbol *));
+
+ // setup predefined symbols (those that have direct pointer access for speedup)
+ _sym("*", &_pdp_sym_wildcard);
+ _sym("float", &_pdp_sym_float);
+ _sym("int", &_pdp_sym_int);
+ _sym("symbol", &_pdp_sym_symbol);
+ _sym("packet", &_pdp_sym_packet);
+ _sym("pointer", &_pdp_sym_pointer);
+ _sym("invalid", &_pdp_sym_invalid);
+ _sym("list", &_pdp_sym_list);
+ _sym("?", &_pdp_sym_question_mark);
+ _sym("atom", &_pdp_sym_atom);
+ _sym("null", &_pdp_sym_null);
+ _sym("[", &_pdp_sym_quote_start);
+ _sym("]", &_pdp_sym_quote_end);
+ _sym("ret", &_pdp_sym_return);
+ _sym("nret", &_pdp_sym_nreturn);
+ _sym(":", &_pdp_sym_defstart);
+ _sym(";", &_pdp_sym_defend);
+ _sym("if", &_pdp_sym_if);
+ _sym("then", &_pdp_sym_then);
+ _sym("local", &_pdp_sym_local);
+ _sym("forth", &_pdp_sym_forth);
+ _sym("call", &_pdp_sym_call);
+ _sym("push", &_pdp_sym_push);
+ _sym("pop", &_pdp_sym_pop);
+
+}
+
+
diff --git a/system/kernel/pdp_type.c b/system/kernel/pdp_type.c
new file mode 100644
index 0000000..0216d07
--- /dev/null
+++ b/system/kernel/pdp_type.c
@@ -0,0 +1,473 @@
+/*
+ * Pure Data Packet system implementation. : code for handling different packet types
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/* this file contains type handling routines */
+
+#include <stdarg.h>
+#include <string.h>
+#include <pthread.h>
+#include "pdp_list.h"
+#include "pdp_symbol.h"
+#include "pdp_packet.h"
+#include "pdp_post.h"
+#include "pdp_type.h"
+#include "pdp_mem.h"
+#include "pdp_debug.h"
+
+
+// debug
+#define D if (0)
+
+
+static t_pdp_list *conversion_list;
+
+#define INIT_MAX_CACHE_SIZE 32
+
+static t_pdp_list *cached_conversion_list;
+static int max_cache_size;
+
+/* mutex */
+static pthread_mutex_t pdp_conversion_mutex;
+static pthread_mutex_t pdp_cache_mutex;
+
+
+
+/* convert a type to a list */
+t_pdp_list *pdp_type_to_list(t_pdp_symbol *type)
+{
+#define TMPSIZE 1024
+
+ char *c = type->s_name;
+ char *lastname = c;
+ int n = 0;
+ char tmp[strlen(type->s_name)+1];
+ t_pdp_list *l = pdp_list_new(0);
+
+ while(*c){
+ if (*c == '/'){
+ strncpy(tmp, lastname, n);
+ tmp[n] = 0;
+ pdp_list_add_back(l, a_symbol, (t_pdp_word)pdp_gensym(tmp));
+ c++;
+ lastname = c;
+ n = 0;
+ }
+ else{
+ c++;
+ n++;
+ PDP_ASSERT(n < TMPSIZE);
+ }
+ }
+ pdp_list_add_back(l, a_symbol, (t_pdp_word)pdp_gensym(lastname));
+
+ return l;
+}
+
+
+/* get the description symbol. */
+t_pdp_symbol *pdp_packet_get_description(int packet)
+{
+ t_pdp *header = pdp_packet_header(packet);
+
+ if (!header) return pdp_gensym("invalid");
+ else if (!header->desc){
+
+ /* since every packet is obliged to have a description, this is an error */
+ pdp_post("ERROR: pdp_packet_get_description: packet %d has no description.", packet);
+ pdp_packet_print_debug(packet);
+ return pdp_gensym("unknown");
+ }
+ else return header->desc;
+}
+
+
+
+/* this runs a conversion program */
+int _pdp_type_run_conversion_program(t_pdp_conversion_program *program,
+ int packet, t_pdp_symbol *dest_template)
+{
+ /* run a conversion program:
+ treat the source packet as readonly, and cleanup intermediates, such
+ that the net result is the production of a new packet, with the
+ source packet intact. */
+
+ int p, tmp;
+ t_pdp_atom *a;
+ t_pdp_conversion_method m;
+
+ // run the first line of the program
+ a = program->first;
+ m = a->w.w_pointer;
+ D pdp_post("DEBUG: _pdp_type_run_conversion_program: method = %x", m);
+ p = m(packet, dest_template);
+ D pdp_post("DEBUG: _pdp_type_run_conversion_program:");
+ D pdp_post(" packet returned = %d, type = %s",
+ p, pdp_packet_get_description(p)->s_name);
+
+ // run the remaining lines + cleanup intermediates
+ for (a=a->next; a; a=a->next){
+ m = a->w.w_pointer;
+ D pdp_post("DEBUG: _pdp_type_run_conversion_program: next method ptr = %x", m);
+ tmp = m(p, dest_template);
+ pdp_packet_mark_unused(p);
+ p = tmp;
+ }
+ return p;
+}
+
+
+/* find a conversion program */
+t_pdp_conversion_program *
+_pdp_type_find_conversion_program(t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern)
+{
+ t_pdp_conversion *c;
+ t_pdp_atom *a;
+ t_pdp_conversion_program *retval = 0;
+
+ /* lock conversion list */
+ pthread_mutex_lock(&pdp_conversion_mutex);
+
+ for (a = conversion_list->first; a; a=a->next){
+ c = a->w.w_pointer;
+ /* can be a wildcard match */
+ if (pdp_type_description_match(src_pattern, c->src_pattern) &&
+ pdp_type_description_match(dst_pattern, c->dst_pattern)) {
+ /* found a program */
+ D pdp_post("DEBUG: _pdp_type_find_conversion_program: found: %s -> %s",
+ c->src_pattern->s_name, c->dst_pattern->s_name);
+ retval = c->program;
+ goto gotit;
+ }
+ }
+
+ /* no conversion program was found */
+ retval = 0;
+ gotit:
+
+ /* lock conversion list */
+ pthread_mutex_unlock(&pdp_conversion_mutex);
+ return retval;
+}
+
+/* find a cached conversion program
+ if one is found it will be moved to the back of the queue (MRU) */
+t_pdp_conversion_program *
+_pdp_type_find_cached_conversion_program(t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern)
+{
+ t_pdp_conversion *c, *c_tmp;
+ t_pdp_atom *a;
+ t_pdp_conversion_program *retval = 0;
+
+ /* lock cached list */
+ pthread_mutex_lock(&pdp_cache_mutex);
+
+ for (a = cached_conversion_list->first; a; a=a->next){
+ c = a->w.w_pointer;
+ /* must be exact match */
+ if ((src_pattern == c->src_pattern) &&
+ (dst_pattern == c->dst_pattern)) {
+
+ /* found a program */
+ D pdp_post("DEBUG: _pdp_type_find_cached_conversion_program: found: %s -> %s",
+ c->src_pattern->s_name, c->dst_pattern->s_name);
+ retval = c->program;
+
+ /* make MRU (move to back) */
+ c_tmp = cached_conversion_list->last->w.w_pointer;
+ cached_conversion_list->last->w.w_pointer = c;
+ a->w.w_pointer = c_tmp;
+ goto gotit;
+ }
+ }
+
+ retval = 0;
+
+ gotit:
+
+
+ /* un lock cached list */
+ pthread_mutex_unlock(&pdp_cache_mutex);
+
+ /* no conversion program was found */
+ return retval;
+}
+
+
+/* conversion program manipulations */
+void pdp_conversion_program_free(t_pdp_conversion_program *program)
+{
+ pdp_list_free(program);
+}
+
+/* debug print */
+void _pdp_conversion_program_print(t_pdp_conversion_program *program)
+{
+ D pdp_post("_pdp_conversion_program_print %x", program);
+ pdp_list_print(program);
+}
+
+t_pdp_conversion_program *pdp_conversion_program_new(t_pdp_conversion_method method, ...)
+{
+ t_pdp_conversion_program *p = pdp_list_new(0);
+ t_pdp_conversion_method m = method;
+ va_list ap;
+
+ D pdp_post("DEBUG: pdp_conversion_program_new:BEGIN");
+
+ pdp_list_add_back_pointer(p, m);
+ va_start(ap, method);
+ while (m = va_arg(ap, t_pdp_conversion_method)) pdp_list_add_back_pointer(p, m);
+ va_end(ap);
+
+ D pdp_post("DEBUG: pdp_conversion_program_new:END");
+
+ return p;
+}
+
+t_pdp_conversion_program *pdp_conversion_program_copy(t_pdp_conversion_program *program)
+{
+ if (program) return pdp_list_copy(program);
+ else return 0;
+}
+
+void pdp_conversion_program_add(t_pdp_conversion_program *program,
+ t_pdp_conversion_program *tail)
+{
+ return pdp_list_cat(program, tail);
+}
+
+/* conversion registration */
+void pdp_type_register_conversion (t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern,
+ t_pdp_conversion_program *program)
+{
+ t_pdp_conversion *c = (t_pdp_conversion *)pdp_alloc(sizeof(*c));
+ c->src_pattern = src_pattern;
+ c->dst_pattern = dst_pattern;
+ c->program = program;
+
+ /* lock conversion list */
+ pthread_mutex_lock(&pdp_conversion_mutex);
+
+ pdp_list_add_back_pointer(conversion_list, c);
+
+ /* unlock conversion list */
+ pthread_mutex_unlock(&pdp_conversion_mutex);
+
+}
+
+/* register a cached conversion */
+void pdp_type_register_cached_conversion (t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern, t_pdp_conversion_program *program)
+{
+
+ /* create the new conversion */
+ t_pdp_conversion *c = (t_pdp_conversion *)pdp_alloc(sizeof(*c));
+ c->src_pattern = src_pattern;
+ c->dst_pattern = dst_pattern;
+ c->program = program;
+
+ /* lock cached conversion list */
+ pthread_mutex_lock(&pdp_cache_mutex);
+
+ /* check size, and remove LRU (top) if the cache is full */
+ while (cached_conversion_list->elements >= max_cache_size){
+ t_pdp_conversion *c_old = pdp_list_pop(cached_conversion_list).w_pointer;
+ if (c_old->program) pdp_conversion_program_free(c_old->program);
+ pdp_dealloc(c_old);
+ }
+
+ /* add and make MRU (back) */
+ pdp_list_add_back_pointer(cached_conversion_list, c);
+
+ /* unlock cached conversion list */
+ pthread_mutex_unlock(&pdp_cache_mutex);
+}
+
+/* convert a given packet to a certain type (template) */
+int _pdp_packet_convert(int packet, t_pdp_symbol *dest_template)
+{
+ t_pdp_symbol *type = pdp_packet_get_description(packet);
+ t_pdp_symbol *tmp_type = 0;
+ int tmp_packet = -1;
+
+ t_pdp_conversion_program *program = 0;
+ t_pdp_conversion_program *program_last = 0;
+ t_pdp_conversion_program *program_tail = 0;
+
+ /* check if there is a program in the cached list, if so run it */
+ if (program = _pdp_type_find_cached_conversion_program(type, dest_template))
+ return _pdp_type_run_conversion_program(program, packet, dest_template);
+
+ /* if it is not cached, iteratively convert
+ and save program on top of cache list if a conversion path (program) was found */
+
+ // run first conversion that matches
+ program = pdp_conversion_program_copy
+ (_pdp_type_find_conversion_program(type, dest_template));
+ program_last = program;
+ if (!program){
+ D pdp_post("DEBUG: pdp_type_convert: (1) can't convert %s to %s",
+ type->s_name, dest_template->s_name);
+ return -1;
+ }
+ tmp_packet = _pdp_type_run_conversion_program(program, packet, dest_template);
+ tmp_type = pdp_packet_get_description(tmp_packet);
+
+ // run more conversions if necessary, deleting intermediate packets
+ while (!pdp_type_description_match(tmp_type, dest_template)){
+ int new_packet;
+ program_tail = _pdp_type_find_conversion_program(tmp_type, dest_template);
+ if (!program_tail){
+ D pdp_post("DEBUG: pdp_type_convert: (2) can't convert %s to %s",
+ tmp_type->s_name, dest_template->s_name);
+ pdp_packet_mark_unused(tmp_packet);
+ pdp_conversion_program_free(program);
+ return -1;
+ }
+ if (program_last == program_tail){
+ pdp_post("ERROR: pdp_packet_convert: conversion loop detected");
+ }
+ program_last = program_tail;
+
+ pdp_conversion_program_add(program, program_tail);
+ new_packet = _pdp_type_run_conversion_program(program_tail, tmp_packet, dest_template);
+ pdp_packet_mark_unused(tmp_packet);
+ tmp_packet = new_packet;
+ tmp_type = pdp_packet_get_description(tmp_packet);
+ }
+
+ // save the conversion program in the cache
+ pdp_type_register_cached_conversion(type, dest_template, program);
+
+ // return resulting packet
+ return tmp_packet;
+
+}
+
+/* convert or copy ro */
+int pdp_packet_convert_ro(int packet, t_pdp_symbol *dest_template)
+{
+ t_pdp_symbol *type = pdp_packet_get_description(packet);
+
+ /* if it is compatible, return a ro copy */
+ if (pdp_type_description_match(type, dest_template)) return pdp_packet_copy_ro(packet);
+
+ /* if not, convert to a new type */
+ else return _pdp_packet_convert(packet, dest_template);
+}
+
+/* convert or copy rw */
+int pdp_packet_convert_rw(int packet, t_pdp_symbol *dest_template)
+{
+ t_pdp_symbol *type = pdp_packet_get_description(packet);
+
+ /* if it is compatible, just return a rw copy */
+ if (pdp_type_description_match(type, dest_template)) return pdp_packet_copy_rw(packet);
+
+ /* if not, convert to a new type */
+ else return _pdp_packet_convert(packet, dest_template);
+}
+
+
+/* this is a hack. type cache data: (a type description parsed into
+ a pdp_list for fast comparison) should be setup when
+ a packet symbol is created, but since that can be everywhere,
+ we set it up on first use. type cache is permanent. */
+
+
+static void _setup_type_cache(t_pdp_symbol *s)
+{
+ t_pdp_list *l = pdp_type_to_list(s);
+ if (!pdp_symbol_set_typelist(s, l))
+ pdp_list_free(l); // list was already present -> free cached list
+
+}
+
+/* check if a type description fits a template
+ this function is symmetric */
+int pdp_type_description_match(t_pdp_symbol *description, t_pdp_symbol *pattern)
+{
+ int match = 0; // no match until all tests are passed
+
+ t_pdp_atom *ad, *ap;
+ t_pdp_symbol *wildcard = PDP_SYM_WILDCARD;
+
+ PDP_ASSERT(pattern);
+ PDP_ASSERT(description);
+
+ /* same type symbol -> match */
+ if (description == pattern) {match = 1; goto done;}
+
+ /* check the description list */
+ if (!(description->s_type)) _setup_type_cache(description);
+ if (!(pattern->s_type)) _setup_type_cache(pattern);
+
+ /* compare symbols of description list */
+ for(ad=description->s_type->first, ap=pattern->s_type->first;
+ ad && ap;
+ ad=ad->next, ap=ap->next)
+ {
+
+ if (ad->w.w_symbol == wildcard) continue;
+ if (ap->w.w_symbol == wildcard) continue;
+ if (ad->w.w_symbol != ap->w.w_symbol) {goto done;} /* difference and not a wildcard */
+ }
+
+ /* ok if sizes are equal */
+ if (! (ad || ap)) {match = 1; goto done;}
+
+ /* one of the two is shorter, so the shortest list needs
+ to end with a wildcard to have a match */
+
+ if (ap && description->s_type->last->w.w_symbol != wildcard) goto done;
+ if (ad && pattern->s_type->last->w.w_symbol != wildcard) goto done;
+
+ /* all tests passed: type templates match */
+ match = 1;
+
+ done:
+ D pdp_post("DEBUG: testing match between %s and %s: %s",
+ description->s_name, pattern->s_name, match ? "match" : "no match");
+ return match;
+
+}
+
+
+
+
+
+/* setup method */
+void pdp_type_setup(void)
+{
+ int i;
+
+ // create mutexes
+ pthread_mutex_init(&pdp_conversion_mutex, NULL);
+ pthread_mutex_init(&pdp_cache_mutex, NULL);
+
+ // create conversion lists
+ cached_conversion_list = pdp_list_new(0);
+ conversion_list = pdp_list_new(0);
+ max_cache_size = INIT_MAX_CACHE_SIZE;
+
+
+
+
+}