/* * Pure Data Packet system implementation. : code for handling different packet types * Copyright (c) by Tom Schouten * * 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 note: pd symbols are used for type identification for porting to other hosts, i suggest reimplementing t_pdp_symbol */ // debug #include #include #include #include #include "pdp.h" /* */ #define D if (0) static t_pdp_conversion *conversion_list; #define CACHE_SIZE 32 static t_pdp_conversion *cached_conversion_list_start; static t_pdp_conversion cached_conversion_array[CACHE_SIZE]; /* mutex */ static pthread_mutex_t pdp_hash_mutex; static pthread_mutex_t pdp_conversion_mutex; static pthread_mutex_t pdp_cache_mutex; #define HASHSIZE 1024 static t_pdp_symbol *pdp_symhash[HASHSIZE]; /* 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 */ pthread_mutex_lock(&pdp_hash_mutex); 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)); sym2->s_name = pdp_alloc(length+1); _pdp_symbol_clear_namespaces(sym2); sym2->s_next = 0; strcpy(sym2->s_name, s); } *sym1 = sym2; gotit: /* unlock hash */ pthread_mutex_unlock(&pdp_hash_mutex); return (sym2); } t_pdp_symbol *pdp_gensym(char *s) { return(_pdp_dogensym(s, 0)); } /* convert a type to a list */ t_pdp_list *pdp_type_to_list(t_pdp_symbol *type) { char *c = type->s_name; char *lastname = c; char tmp[100]; int n = 0; 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_list_add_back(l, a_symbol, (t_pdp_word)pdp_gensym(lastname)); return l; } /* get the description symbol. this includes some compat transition stuff with a warning */ 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){ /* if description is not defined, try to reconstruct it (for backwards compat) */ if (header->type == PDP_IMAGE){ post("FIXME: pdp_type_get_description: packet %d has no description. using workaround.", packet); return pdp_packet_image_get_description(packet); } else 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; D post("DEBUG: _pdp_type_run_conversion_program: program = %x", program); // run the first line of the program D post("DEBUG: _pdp_type_run_conversion_program: method = %x", *program); p = (*program->method)(packet, dest_template); D post("DEBUG: _pdp_type_run_conversion_program: packet returned = %d, type = %s", p, pdp_packet_get_description(p)->s_name); // run the remaining lines + cleanup intermediates for (program = program->next; program ; program = program->next){ D post("DEBUG: _pdp_type_run_conversion_program: next method ptr = %x", *program->method); tmp = (*program->method)(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_conversion_program *retval = 0; /* lock conversion list */ pthread_mutex_lock(&pdp_conversion_mutex); for (c = conversion_list; c; c=c->next){ /* 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 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 */ t_pdp_conversion_program * _pdp_type_find_cached_conversion_program(t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern) { t_pdp_conversion *c; t_pdp_conversion_program *retval = 0; /* lock cached list */ pthread_mutex_lock(&pdp_cache_mutex); for (c = cached_conversion_list_start; c; c=c->next){ /* must be exact match */ if ((src_pattern == c->src_pattern) && (dst_pattern == c->dst_pattern)) { /* found a program */ D post("DEBUG: _pdp_type_find_cached_conversion_program: found: %s -> %s", c->src_pattern->s_name, c->dst_pattern->s_name); retval = c->program; 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) { t_pdp_conversion_program *p = 0; while (program) { p = program; program = program->next; pdp_dealloc (p); } } /* debug print */ void _pdp_conversion_program_print(t_pdp_conversion_program *program) { D post("DEBUG: conversion program: %x", program); while(program){ D post("DEBUG: method: %x", program->method); program = program->next; } } t_pdp_conversion_program *pdp_conversion_program_new(t_pdp_conversion_method method, ...) { t_pdp_conversion_program head; t_pdp_conversion_program *p = &head; t_pdp_conversion_method m = method; va_list ap; D post("DEBUG: pdp_conversion_program_new:BEGIN"); p = p->next = (t_pdp_conversion_program *)pdp_alloc(sizeof(*p)); p->method = m; va_start(ap, method); while (m = va_arg(ap, t_pdp_conversion_method)){ p = p->next = (t_pdp_conversion_program *)pdp_alloc(sizeof(*p)); p->method = m; } p->next = 0; // terminate list va_end(ap); D post("DEBUG: pdp_conversion_program_new:END"); D _pdp_conversion_program_print(head.next); return head.next; } t_pdp_conversion_program *pdp_conversion_program_copy(t_pdp_conversion_program *program) { t_pdp_conversion_program head; t_pdp_conversion_program *p = &head; for(;program; program=program->next){ p = p->next = (t_pdp_conversion_program *)pdp_alloc(sizeof(*p)); p->method = program->method; } p->next = 0; return head.next; } void pdp_conversion_program_add(t_pdp_conversion_program *program, t_pdp_conversion_program *tail) { t_pdp_conversion_program *p = program; //D post("DEBUG: pdp_conversion_program_add:BEGIN"); while (p->next) p = p->next; p->next = pdp_conversion_program_copy(tail); //D post("DEBUG: pdp_conversion_program_add:END"); } /* 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 head; t_pdp_conversion *c = &head; /* lock conversion list */ pthread_mutex_lock(&pdp_conversion_mutex); head.next = conversion_list; while (c->next) c=c->next; // add a new one to the end c = c->next = (t_pdp_conversion *)pdp_alloc(sizeof(*c)); c->src_pattern = src_pattern; c->dst_pattern = dst_pattern; c->program = program; c->next = 0; conversion_list = head.next; /* 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) { t_pdp_conversion *save, *c, *c2; /* unlock cahed conversion list */ pthread_mutex_lock(&pdp_cache_mutex); c2 = save = cached_conversion_list_start; while (c2->next->next) c2 = c2->next; c = c2->next; c2->next = 0; if (c->program) pdp_conversion_program_free(c->program); c->src_pattern = src_pattern; c->dst_pattern = dst_pattern; c->program = program; c->next = save; cached_conversion_list_start = 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 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 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){ 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 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); } static void _setup_type_cache(t_pdp_symbol *s) { t_pdp_list *l = pdp_type_to_list(s); pthread_mutex_lock(&pdp_hash_mutex); //lock to prevent re-entry if (!s->s_type){ s->s_type = l; l = 0; } pthread_mutex_unlock(&pdp_hash_mutex); if (l) pdp_list_free(l); } /* 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) { #if 1 t_pdp_atom *ad, *ap; t_pdp_symbol *wildcard = pdp_gensym("*"); /* same type -> match */ if (description == pattern) return 1; /* use the description list stored in the symbol for comparison */ if (!(description->s_type)) _setup_type_cache(description); if (!(pattern->s_type)) _setup_type_cache(pattern); /* check size */ if (description->s_type->elements != pattern->s_type->elements) return 0; /* compare symbols of type list */ for(ad=description->s_type->first, ap=pattern->s_type->first; ad; 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) return 0; /* difference and not a wildcard */ } /* type templates match */ return 1; #else int retval = 0; if (description == pattern) retval = 1; //else retval = (!fnmatch(pattern->s_name, description->s_name, FNM_FILE_NAME)); //hack: make a symmetric match symmetric by calling fnmatch a second time with reversed attributes else retval = ((!fnmatch(pattern->s_name, description->s_name, FNM_FILE_NAME)) ||(!fnmatch(description->s_name, pattern->s_name, FNM_FILE_NAME))); D post("DEBUG: testing match between %s and %s: %s", description->s_name, pattern->s_name, retval ? "match" : "no match"); return retval; #endif } /* setup method */ void pdp_type_setup(void) { int i; // create mutexes pthread_mutex_init(&pdp_hash_mutex, NULL); pthread_mutex_init(&pdp_conversion_mutex, NULL); pthread_mutex_init(&pdp_cache_mutex, NULL); // init hash memset(pdp_symhash, 0, HASHSIZE * sizeof(t_pdp_symbol *)); // init conversion lists conversion_list = 0; for(i=0; isrc_pattern = 0; c->dst_pattern = 0; c->program = 0; c->next = c + 1; } cached_conversion_list_start = cached_conversion_array; cached_conversion_array[CACHE_SIZE-1].next = 0; }