[PD-cvs] externals/pdp/system/kernel CONTENTS, 1.2, 1.3 Makefile, 1.2, 1.3 pdp_debug.c, 1.2, 1.3 pdp_dpd_command.c, 1.2, 1.3 pdp_list.c, 1.2, 1.3 pdp_mem.c, 1.2, 1.3 pdp_packet.c, 1.2, 1.3 pdp_packet2.c, 1.2, 1.3 pdp_post.c, 1.2, 1.3 pdp_symbol.c, 1.2, 1.3 pdp_type.c, 1.2, 1.3

Hans-Christoph Steiner eighthave at users.sourceforge.net
Fri Dec 16 02:05:42 CET 2005


Update of /cvsroot/pure-data/externals/pdp/system/kernel
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6756/system/kernel

Added Files:
	CONTENTS Makefile pdp_debug.c pdp_dpd_command.c pdp_list.c 
	pdp_mem.c pdp_packet.c pdp_packet2.c pdp_post.c pdp_symbol.c 
	pdp_type.c 
Log Message:
checking in pdp 0.12.4 from http://zwizwa.fartit.com/pd/pdp/pdp-0.12.4.tar.gz

--- NEW FILE: pdp_list.c ---

/*
 *   Pure Data Packet header file. List class
 *   Copyright (c) by Tom Schouten <pdp at 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);
}





--- NEW FILE: pdp_dpd_command.c ---

/*
 *   Pure Data Packet header file. DPD command class
 *   Copyright (c) by Tom Schouten <pdp at 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);
}





--- NEW FILE: pdp_packet.c ---
/*
 *   Pure Data Packet system implementation: Packet Manager
 *   Copyright (c) by Tom Schouten <pdp at 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

--- NEW FILE: Makefile ---

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

--- NEW FILE: pdp_debug.c ---
#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;
}

--- NEW FILE: pdp_type.c ---
/*
 *   Pure Data Packet system implementation. : code for handling different packet types
 *   Copyright (c) by Tom Schouten <pdp at 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;




}

--- NEW FILE: CONTENTS ---
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, ...

--- NEW FILE: pdp_symbol.c ---
/*
 *   Pure Data Packet system implementation. : code implementing pdp's namespace (symbols)
 *   Copyright (c) by Tom Schouten <pdp at 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);

}



--- NEW FILE: pdp_mem.c ---
/*
 *   Pure Data Packet system file: memory allocation
 *   Copyright (c) by Tom Schouten <pdp at 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;
}


--- NEW FILE: pdp_packet2.c ---
/*
 *   Pure Data Packet system implementation: Packet Manager
 *   Copyright (c) by Tom Schouten <pdp at 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

--- NEW FILE: pdp_post.c ---

/*
 *   Pure Data Packet system file. pdp logging.
 *   Copyright (c) by Tom Schouten <pdp at 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);
}







More information about the Pd-cvs mailing list