diff options
Diffstat (limited to 'system/kernel')
-rw-r--r-- | system/kernel/Makefile | 16 | ||||
-rw-r--r-- | system/kernel/pdp.c | 198 | ||||
-rw-r--r-- | system/kernel/pdp_comm.c | 198 | ||||
-rw-r--r-- | system/kernel/pdp_compat.c | 56 | ||||
-rw-r--r-- | system/kernel/pdp_control.c | 186 | ||||
-rw-r--r-- | system/kernel/pdp_dpd_command.c | 87 | ||||
-rw-r--r-- | system/kernel/pdp_forth.c | 541 | ||||
-rw-r--r-- | system/kernel/pdp_list.c | 663 | ||||
-rw-r--r-- | system/kernel/pdp_packet.c | 957 | ||||
-rw-r--r-- | system/kernel/pdp_queue.c | 347 | ||||
-rw-r--r-- | system/kernel/pdp_type.c | 501 | ||||
-rw-r--r-- | system/kernel/pdp_type.c_old | 542 | ||||
-rw-r--r-- | system/kernel/pdp_ut.c | 262 |
13 files changed, 4554 insertions, 0 deletions
diff --git a/system/kernel/Makefile b/system/kernel/Makefile new file mode 100644 index 0000000..f1d82a9 --- /dev/null +++ b/system/kernel/Makefile @@ -0,0 +1,16 @@ + +OBJECTS = pdp.o pdp_ut.o pdp_packet.o pdp_queue.o pdp_comm.o \ + pdp_control.o pdp_compat.o pdp_type.o pdp_dpd_command.o \ + pdp_list.o pdp_forth.o + + +include ../../Makefile.config + +all: pdp_main_clean $(OBJECTS) + +pdp_main_clean: + rm -f pdp.o + +clean: + rm -f *~ + rm -f *.o diff --git a/system/kernel/pdp.c b/system/kernel/pdp.c new file mode 100644 index 0000000..d71e655 --- /dev/null +++ b/system/kernel/pdp.c @@ -0,0 +1,198 @@ +/* + * Pure Data Packet system implementation: setup code + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "pdp_config.h" +#include "pdp.h" +#include <stdio.h> + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* module setup declarations (all C-style) */ + +/* pdp system / internal stuff */ +void pdp_list_setup(void); +void pdp_sym_setup(void); +void pdp_forth_setup(void); +void pdp_type_setup(void); +void pdp_packet_setup(void); +void pdp_ut_setup(void); +void pdp_queue_setup(void); +void pdp_control_setup(void); +void pdp_image_setup(void); +void pdp_bitmap_setup(void); +void pdp_matrix_setup(void); + + +/* pdp modules */ +void pdp_xv_setup(void); +void pdp_add_setup(void); +void pdp_mul_setup(void); +void pdp_mix_setup(void); +void pdp_randmix_setup(void); +void pdp_qt_setup(void); +void pdp_v4l_setup(void); +void pdp_reg_setup(void); +void pdp_conv_setup(void); +void pdp_bq_setup(void); +void pdp_del_setup(void); +void pdp_snap_setup(void); +void pdp_trigger_setup(void); +void pdp_route_setup(void); +void pdp_noise_setup(void); +void pdp_gain_setup(void); +void pdp_chrot_setup(void); +void pdp_scope_setup(void); +void pdp_scale_setup(void); +void pdp_zoom_setup(void); +void pdp_scan_setup(void); +void pdp_scanxy_setup(void); +void pdp_sdl_setup(void); +void pdp_cheby_setup(void); +void pdp_grey2mask_setup(void); +void pdp_constant_setup(void); +void pdp_logic_setup(void); +void pdp_glx_setup(void); +void pdp_loop_setup(void); +void pdp_description_setup(void); +void pdp_convert_setup(void); +void pdp_stateless_setup(void); +void pdp_mat_mul_setup(void); +void pdp_mat_lu_setup(void); +void pdp_mat_vec_setup(void); +void pdp_plasma_setup(void); +void pdp_cog_setup(void); +void pdp_histo_setup(void); +void pdp_array_setup(void); + +/* hacks */ +void pdp_inspect_setup(void); +void pdp_slice_cut_setup(void); +void pdp_slice_glue_setup(void); + +/* testing */ +void pdp_dpd_test_setup(void); +void pdp_forthproc_setup(void); + + + +/* library setup routine */ +void pdp_setup(void){ + + /* babble */ + post ("PDP: pure data packet"); + +#ifdef PDP_VERSION + fprintf(stderr, "PDP: version " PDP_VERSION "\n"); +#endif + + + /* setup pdp system */ + pdp_list_setup(); + pdp_type_setup(); + pdp_sym_setup(); + pdp_packet_setup(); + pdp_forth_setup(); + pdp_control_setup(); + + pdp_image_setup(); + pdp_bitmap_setup(); + pdp_matrix_setup(); + + pdp_queue_setup(); + + /* setup utility toolkit */ + pdp_ut_setup(); + + /* setup pdp modules*/ + pdp_add_setup(); + pdp_mul_setup(); + pdp_mix_setup(); + pdp_randmix_setup(); + pdp_reg_setup(); + pdp_conv_setup(); + pdp_bq_setup(); + pdp_del_setup(); + pdp_snap_setup(); + pdp_trigger_setup(); + pdp_route_setup(); + pdp_noise_setup(); + pdp_plasma_setup(); + pdp_gain_setup(); + pdp_chrot_setup(); + pdp_scope_setup(); + pdp_scale_setup(); + pdp_zoom_setup(); + pdp_scan_setup(); + pdp_scanxy_setup(); + pdp_cheby_setup(); + pdp_grey2mask_setup(); + pdp_constant_setup(); + pdp_logic_setup(); + pdp_loop_setup(); + pdp_description_setup(); + pdp_convert_setup(); + pdp_stateless_setup(); + pdp_mat_mul_setup(); + pdp_mat_lu_setup(); + pdp_mat_vec_setup(); + pdp_cog_setup(); + pdp_histo_setup(); + pdp_array_setup(); + + /* experimental stuff */ + pdp_slice_cut_setup(); + pdp_slice_glue_setup(); + pdp_inspect_setup(); + + /* testing */ + //pdp_dpd_test_setup(); + pdp_forthproc_setup(); + + /* optional modules */ + +#ifdef HAVE_PDP_QT + pdp_qt_setup(); +#endif + +#ifdef HAVE_PDP_XV + pdp_xv_setup(); +#endif + +#ifdef HAVE_PDP_SDL + pdp_sdl_setup(); +#endif + +#ifdef HAVE_PDP_V4L + pdp_v4l_setup(); +#endif + +#ifdef HAVE_PDP_GLX + pdp_glx_setup(); +#endif + +} + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_comm.c b/system/kernel/pdp_comm.c new file mode 100644 index 0000000..bcc0e3a --- /dev/null +++ b/system/kernel/pdp_comm.c @@ -0,0 +1,198 @@ +/* + * Pure Data Packet system implementation. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* this file contains misc communication (packet) methods for pd */ + + +#include "pdp.h" +#include "pdp_internals.h" +#include <stdio.h> + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + +t_symbol *_pdp_sym_pdp; +t_symbol *_pdp_sym_rro; +t_symbol *_pdp_sym_rrw; +t_symbol *_pdp_sym_prc; +t_symbol *_pdp_sym_dpd; +t_symbol *_pdp_sym_ins; +t_symbol *_pdp_sym_acc; + +t_symbol *pdp_sym_pdp() {return _pdp_sym_pdp;} +t_symbol *pdp_sym_rro() {return _pdp_sym_rro;} +t_symbol *pdp_sym_rrw() {return _pdp_sym_rrw;} +t_symbol *pdp_sym_prc() {return _pdp_sym_prc;} +t_symbol *pdp_sym_dpd() {return _pdp_sym_dpd;} +t_symbol *pdp_sym_ins() {return _pdp_sym_ins;} +t_symbol *pdp_sym_acc() {return _pdp_sym_acc;} + +/************** packet management and communication convenience functions ************/ + +/* send a packet to an outlet */ +void outlet_pdp(t_outlet *out, int packetid) +{ + t_atom atom[2]; + + SETFLOAT(atom+1, (float)packetid); + + SETSYMBOL(atom+0, pdp_sym_rro()); + outlet_anything(out, pdp_sym_pdp(), 2, atom); + + SETSYMBOL(atom+0, pdp_sym_rrw()); + outlet_anything(out, pdp_sym_pdp(), 2, atom); + + SETSYMBOL(atom+0, pdp_sym_prc()); + outlet_anything(out, pdp_sym_pdp(), 2, atom); + +} + +/* send an accumulation packet to an outlet */ +void outlet_dpd(t_outlet *out, int packetid) +{ + t_atom atom[2]; + + SETFLOAT(atom+1, (float)packetid); + + SETSYMBOL(atom+0, pdp_sym_ins()); + outlet_anything(out, pdp_sym_dpd(), 2, atom); + + SETSYMBOL(atom+0, pdp_sym_acc()); + outlet_anything(out, pdp_sym_dpd(), 2, atom); + +} + +/* unregister a packet and send it to an outlet */ +void + +pdp_packet_pass_if_valid(t_outlet *outlet, int *packet_ptr) +{ + t_pdp *header = pdp_packet_header(*packet_ptr); + //if (-1 != *packet){ + if (header){ + /* if packet is exclusively owned, mark as passing */ + if (1 == header->users) pdp_packet_mark_passing(packet_ptr); + + /* send */ + outlet_pdp(outlet, *packet_ptr); + + /* if the packet is still here (was passing and not registered, + or it was a shared copy): unregister it */ + if (-1 != *packet_ptr){ + pdp_packet_unmark_passing(*packet_ptr); + pdp_packet_mark_unused(*packet_ptr); + *packet_ptr = -1; + } + } +} + +void +pdp_packet_replace_if_valid(int *dpacket, int *spacket) +{ + if (-1 != *spacket){ + pdp_packet_mark_unused(*dpacket); + *dpacket = *spacket; + *spacket = -1; + } + +} + + +int +pdp_packet_copy_ro_or_drop(int *dpacket, int spacket) +{ + int drop = 0; + if (*dpacket == -1) *dpacket = pdp_packet_copy_ro(spacket); + else { + /* send a notification there is a dropped packet */ + pdp_control_notify_drop(spacket); + drop = 1; + } + return drop; +} + + +int +pdp_packet_copy_rw_or_drop(int *dpacket, int spacket) +{ + int drop = 0; + if (*dpacket == -1) *dpacket = pdp_packet_copy_rw(spacket); + else { + /* send a notification there is a dropped packet */ + pdp_control_notify_drop(spacket); + drop = 1; + } + return drop; +} + +int +pdp_packet_convert_ro_or_drop(int *dpacket, int spacket, t_pdp_symbol *template) +{ + int drop = 0; + + if (!template) return pdp_packet_copy_ro_or_drop(dpacket, spacket); + + if (*dpacket == -1) *dpacket = pdp_packet_convert_ro(spacket, template); + else { + /* send a notification there is a dropped packet */ + pdp_control_notify_drop(spacket); + drop = 1; + } + return drop; +} + + +int +pdp_packet_convert_rw_or_drop(int *dpacket, int spacket, t_pdp_symbol *template) +{ + int drop = 0; + + if (!template) return pdp_packet_copy_rw_or_drop(dpacket, spacket); + + if (*dpacket == -1) *dpacket = pdp_packet_convert_rw(spacket, template); + else { + /* send a notification there is a dropped packet */ + pdp_control_notify_drop(spacket); + drop = 1; + } + return drop; +} + + +void +pdp_sym_setup(void) +{ + _pdp_sym_pdp = gensym("pdp"); + _pdp_sym_rro = gensym("register_ro"); + _pdp_sym_rrw = gensym("register_rw"); + _pdp_sym_prc = gensym("process"); + _pdp_sym_dpd = gensym("dpd"); + _pdp_sym_ins = gensym("inspect"); + _pdp_sym_acc = gensym("accumulate"); +} + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_compat.c b/system/kernel/pdp_compat.c new file mode 100644 index 0000000..4bcf00d --- /dev/null +++ b/system/kernel/pdp_compat.c @@ -0,0 +1,56 @@ +/* + * Pure Data Packet system implementation. Compatibility routines. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* this file contains misc communication methods */ + + +#include "pdp.h" +#include "pdp_internals.h" +#include <stdio.h> + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + + + +void +pdp_pass_if_valid(t_outlet *outlet, int *packet) +{ + pdp_packet_pass_if_valid(outlet, packet); +} + +void +pdp_replace_if_valid(int *dpacket, int *spacket) +{ + pdp_packet_replace_if_valid(dpacket, spacket); + +} + + + + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_control.c b/system/kernel/pdp_control.c new file mode 100644 index 0000000..2cba7b7 --- /dev/null +++ b/system/kernel/pdp_control.c @@ -0,0 +1,186 @@ +/* + * Pure Data Packet system implementation: control object + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* this is an actual pd class that is used for communication with the + pdp framework */ + +#include "pdp.h" +#include "pdp_internals.h" +#include "pdp_control.h" +#include <stdio.h> + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + + + +static long dropped_packets; + +static t_class* pdp_control_class; + + +/* pdp control instance data */ + +struct _pdp_control; +typedef struct _pdp_control +{ + t_object x_obj; + t_outlet *x_outlet0; + struct _pdp_control *x_next; + +} t_pdp_control; + + + +static t_pdp_control *pdp_control_list; + +static void pdp_control_info(t_pdp_control *x) +{ +} + +static void pdp_control_collectgarbage(t_pdp_control *x) +{ + int nb_packets_freed = pdp_pool_collect_garbage(); + post("pdp_control: freed %d packets", nb_packets_freed); + +} + +static void pdp_control_set_mem_limit(t_pdp_control *x, t_floatarg f) +{ + int limit = (int)f; + if (limit < 0) limit = 0; + pdp_pool_set_max_mem_usage(limit); + if (limit) post("pdp_control: set memory limit to %d bytes", limit); + else post("pdp_control: disabled memory limit"); + +} + +static void pdp_control_thread(t_pdp_control *x, t_floatarg f) +{ + int t = (int)f; + + if (t){ + post("pdp_control: pdp is now using its own processing thread"); + pdp_queue_use_thread(1); + } + else { + post("pdp_control: pdp is now using the main pd thread"); + pdp_queue_use_thread(0); + } +} + + +static void pdp_control_send_drop_message(t_pdp_control *x) +{ + t_atom atom[1]; + t_symbol *s = gensym("pdp_drop"); + + SETFLOAT(atom+0, (float)dropped_packets); + outlet_anything(x->x_outlet0, s, 1, atom); +} + + +static void pdp_control_free(t_pdp_control *x) +{ + /* remove from linked list */ + t_pdp_control *curr = pdp_control_list; + if (pdp_control_list == x) pdp_control_list = x->x_next; + else while (curr){ + if (curr->x_next == x) { + curr->x_next = x->x_next; + break; + } + else { + curr = curr->x_next; + } + + } +} + + +static void *pdp_control_new(void) +{ + t_pdp_control *x = (t_pdp_control *)pd_new(pdp_control_class); + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + /* add to list */ + x->x_next = pdp_control_list; + pdp_control_list = x; + return x; +} + +/************************* class methods ***************************************/ + + +void pdp_control_addmethod(t_method m, t_symbol *s) +{ + class_addmethod(pdp_control_class, m, s, A_GIMME, A_NULL); +} + +void pdp_control_setup(void) +{ + + pdp_control_list = 0; + dropped_packets = 0; + + /* setup pd class data */ + pdp_control_class = class_new(gensym("pdp_control"), (t_newmethod)pdp_control_new, + (t_method)pdp_control_free, sizeof(t_pdp_control), 0, A_NULL); + + + class_addmethod(pdp_control_class, (t_method)pdp_control_info, gensym("info"), A_NULL); + class_addmethod(pdp_control_class, (t_method)pdp_control_thread, gensym("thread"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_control_class, (t_method)pdp_control_collectgarbage, gensym("collectgarbage"), A_NULL); + class_addmethod(pdp_control_class, (t_method)pdp_control_set_mem_limit, gensym("memlimit"), A_FLOAT, A_NULL); +} + + + +void pdp_control_notify_broadcast(t_pdp_control_method_notify *notify) +{ + t_pdp_control *curr = pdp_control_list; + while (curr){ + (*notify)(curr); + curr = curr->x_next; + } +} + + + +/************************* notify class methods *************************/ + +void pdp_control_notify_drop(int packet) +{ + dropped_packets++; + + /* send drop notify to controller class instances */ + pdp_control_notify_broadcast(pdp_control_send_drop_message); + //post("dropped packet"); +} + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_dpd_command.c b/system/kernel/pdp_dpd_command.c new file mode 100644 index 0000000..0d3ecf1 --- /dev/null +++ b/system/kernel/pdp_dpd_command.c @@ -0,0 +1,87 @@ + +/* + * Pure Data Packet header file. DPD command class + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* this object implements a dpd command queue and command object */ + +#include "pdp.h" +#include "pdp_dpd_command.h" + +void pdp_dpd_commandfactory_init(t_pdp_dpd_commandfactory *x, u32 size) +{ + x->nb_commands = 0; + x->command_size = size; + x->command = 0; +} + +void _pdp_dpd_commandfactory_free(t_pdp_dpd_command *x) +{ + if (x) _pdp_dpd_commandfactory_free(x->next); + pdp_dealloc(x); +} + +void pdp_dpd_commandfactory_free(t_pdp_dpd_commandfactory *x) +{ + _pdp_dpd_commandfactory_free(x->command); + x->command = 0; + x->nb_commands = 0; +} + + +/* factory method */ +t_pdp_dpd_command *pdp_dpd_commandfactory_get_new_command(t_pdp_dpd_commandfactory *x) +{ + + t_pdp_dpd_command *c = x->command; + t_pdp_dpd_command *oldhead = c; + + /* check if we can reuse */ + while (c){ + if (!c->used){ + c->used = 1; + //post("reusing command %x", c, c->used); + return c; + } + //post("command %x is used %d", c, c->used); + c = c->next; + } + + /* create a new command */ + x->command = (t_pdp_dpd_command *)pdp_alloc(x->command_size); + x->command->next = oldhead; + x->command->used = 1; + x->nb_commands++; + //post("created command %x, nbcommands: %d", x->command, x->nb_commands); + return x->command; + +} + + +/* (self)destructor */ +void pdp_dpd_command_suicide(void *x) +{ + t_pdp_dpd_command *c = (t_pdp_dpd_command *)x; + c->used = 0; + //post("command %x committed suicide %d", c, c->used); +} + + + + diff --git a/system/kernel/pdp_forth.c b/system/kernel/pdp_forth.c new file mode 100644 index 0000000..1764004 --- /dev/null +++ b/system/kernel/pdp_forth.c @@ -0,0 +1,541 @@ +/* + * Pure Data Packet header file. Packet processor system + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdlib.h> +#include <math.h> +#include "pdp.h" +#include "pdp_forth.h" + +#define D if (0) + + + +t_pdp_stack *pdp_stack_new(void) {return pdp_list_new(0);} + +void pdp_stack_free(t_pdp_stack *s) { + pdp_tree_strip_packets(s); + pdp_list_free(s); +} + + +/* some stack manips */ +t_pdp_word_error pdp_stack_dup(t_pdp_stack *s) +{ + if (!s->first) return e_underflow; + pdp_list_add(s, s->first->t, s->first->w); + + /* copy it properly if its a packet */ + if (s->first->t == a_packet){ + s->first->w.w_packet = pdp_packet_copy_ro(s->first->w.w_packet); + } + return e_ok; +} + +t_pdp_word_error pdp_stack_drop(t_pdp_stack *s) +{ + if (!s->first) return e_underflow; + + /* delete it properly if its a packet */ + if (s->first->t == a_packet){ + pdp_packet_mark_unused(s->first->w.w_packet); + } + pdp_list_pop(s); + + return e_ok; +} + +t_pdp_word_error pdp_stack_over(t_pdp_stack *s) +{ + if (s->elements < 2) return e_underflow; + pdp_list_add(s, s->first->next->t, s->first->next->w); + + /* copy it properly if its a packet */ + if (s->first->t == a_packet){ + s->first->w.w_packet = pdp_packet_copy_ro(s->first->w.w_packet); + } + + return e_ok; +} + +t_pdp_word_error pdp_stack_swap(t_pdp_stack *s) +{ + t_pdp_word w; + t_pdp_word_type t; + if (s->elements < 2) return e_underflow; + w = s->first->w; + t = s->first->t; + s->first->w = s->first->next->w; + s->first->t = s->first->next->t; + s->first->next->w = w; + s->first->next->t = t; + return e_ok; + +} + +/* pushing and popping the stack */ + +t_pdp_word_error pdp_stack_push_float(t_pdp_stack *s, float f) {pdp_list_add(s, a_float, (t_pdp_word)f); return e_ok;} +t_pdp_word_error pdp_stack_push_int(t_pdp_stack *s, int i) {pdp_list_add(s, a_int, (t_pdp_word)i); return e_ok;} +t_pdp_word_error pdp_stack_push_pointer(t_pdp_stack *s, void *x) {pdp_list_add(s, a_pointer, (t_pdp_word)x); return e_ok;} +t_pdp_word_error pdp_stack_push_symbol(t_pdp_stack *s, t_pdp_symbol *x) {pdp_list_add(s, a_symbol, (t_pdp_word)x); return e_ok;} + +/* note: packets pushed stack are owned by the stack. if a caller wants to keep a packet that + will be deleted by the word, it should make a copy before transferring it to the stack. + if a stack processor wants to write to a packet, it should replace it with a writable copy first */ + +t_pdp_word_error pdp_stack_push_packet(t_pdp_stack *s, int p) {pdp_list_add(s, a_packet, (t_pdp_word)p); return e_ok;} + + + + + +t_pdp_word_error pdp_stack_pop_float(t_pdp_stack *s, float *f) +{ + if (!s->first) return e_underflow; + + if (s->first->t == a_float) *f = s->first->w.w_float; + else if (s->first->t == a_int) *f = (float)s->first->w.w_int; + else *f = 0.0f; + pdp_stack_drop(s); + return e_ok; +} + +t_pdp_word_error pdp_stack_pop_int(t_pdp_stack *s, int *i) +{ + if (!s->first) return e_underflow; + if (s->first->t == a_int) *i = s->first->w.w_int; + else if (s->first->t == a_float) *i = (int)s->first->w.w_float; + else *i = 0; + pdp_stack_drop(s); + return e_ok; +} + +t_pdp_word_error pdp_stack_pop_pointer(t_pdp_stack *s, void **x) +{ + if (!s->first) return e_underflow; + *x = (s->first->t == a_pointer) ? s->first->w.w_pointer : 0; + pdp_stack_drop(s); + return e_ok; +} + +t_pdp_word_error pdp_stack_pop_symbol(t_pdp_stack *s, t_pdp_symbol **x) +{ + if (!s->first) return e_underflow; + *x = (s->first->t == a_symbol) ? s->first->w.w_symbol : pdp_gensym("invalid"); + pdp_stack_drop(s); + return e_ok; +} + +/* packets popped from the stack are owned by the caller */ + +t_pdp_word_error pdp_stack_pop_packet(t_pdp_stack *s, int *p) +{ + if (!s->first) return e_underflow; + *p = (s->first->t == a_packet) ? s->first->w.w_packet : -1; + pdp_list_pop(s); //ownership is transferred to receiver, drop kills the packet + return e_ok; +} + + +t_pdp_word_error pdp_stack_mov(t_pdp_stack *s) +{ + int position; + t_pdp_atom *a, *a_before; + if (s->elements < 2) return e_underflow; + if (s->first->t != a_int) return e_type; + + pdp_stack_pop_int(s, &position); // get insert point + if (position < 1) return e_ok; // < 0 : invalid; do nothing, 0 : nop (= insert at start, but already at start) + if ((s->elements-1) < position) return e_underflow; + + a = s->first; // get first atom + s->first = a->next; + + if (s->elements-1 == position){ //insert at end + s->last->next = a; + a->next = 0; + s->last = a; + } + else { //insert somewhere in the middle + a_before = s->first; + while (--position) a_before = a_before->next; + a->next = a_before->next; + a_before->next = a; + } + return e_ok; +} + +/* rotate stack down (tos -> bottom) */ +t_pdp_word_error pdp_stack_rdown(t_pdp_stack *s) +{ + t_pdp_word_type t = s->first->t; + t_pdp_word w = s->first->w; + pdp_list_pop(s); + pdp_list_add_back(s, t, w); + return e_ok; +} + + +/* convert to int */ +t_pdp_word_error pdp_stack_int(t_pdp_stack *s) +{ + int i; + pdp_stack_pop_int(s, &i); + pdp_stack_push_int(s, i); + return e_ok; +} + +/* convert to float */ +t_pdp_word_error pdp_stack_float(t_pdp_stack *s) +{ + float f; + pdp_stack_pop_float(s, &f); + pdp_stack_push_float(s, f); + return e_ok; +} + + +#define OP1(name, type, op) \ +t_pdp_word_error pdp_stack_##name##_##type (t_pdp_stack *s) \ +{ \ + type x0; \ + pdp_stack_pop_##type (s, &(x0)); \ + pdp_stack_push_##type (s, op (x0)); \ + return e_ok; \ +} + +#define OP2(name, type, op) \ +t_pdp_word_error pdp_stack_##name##_##type (t_pdp_stack *s) \ +{ \ + type x0, x1; \ + pdp_stack_pop_##type (s, &(x0)); \ + pdp_stack_pop_##type (s, &(x1)); \ + pdp_stack_push_##type (s, x1 op x0); \ + return e_ok; \ +} + +/* some floating point and integer stuff */ + +OP2(add, float, +); +OP2(sub, float, -); +OP2(mul, float, *); +OP2(div, float, /); + +OP1(sin, float, sin); +OP1(cos, float, cos); + +OP2(add, int, +); +OP2(sub, int, -); +OP2(mul, int, *); +OP2(div, int, /); +OP2(mod, int, %); + +OP2(and, int, &); +OP2(or, int, |); +OP2(xor, int, ^); + + +/* some integer stuff */ + +t_pdp_word_error pdp_stack_push_invalid_packet(t_pdp_stack *s) +{ + pdp_stack_push_packet(s, -1); + return e_ok; +} + +/* dictionary manipulation */ + +void pdp_forth_word_print_debug(t_pdp_symbol *s) +{ + t_pdp_atom *a; + if (!s->s_word_spec){ + post("%s is not a forth word", s->s_name); + } + else{ + post(""); + post("forth word %s", s->s_name); + post("\tinput: %d", s->s_word_spec->input_size); + post("\toutput: %d", s->s_word_spec->output_size); + post("\ttype index: %d", s->s_word_spec->type_index); + + post("\nimplementations:"); + for(a=s->s_word_spec->implementations->first; a; a=a->next){ + t_pdp_forthword_imp *i = a->w.w_pointer; + startpost("\t%s\t", i->type ? i->type->s_name : "anything"); + pdp_list_print(i->def); + + } + post(""); + } +} + +/* add a definition (list of high level words (symbols) or primitive routines) */ +void pdp_forthdict_add_word(t_pdp_symbol *name, t_pdp_list *def, int input_size, int output_size, + int type_index, t_pdp_symbol *type) +{ + t_pdp_forthword_spec *spec = 0; + t_pdp_forthword_imp *imp = 0; + t_pdp_forthword_imp *old_imp = 0; + t_pdp_atom *a; + /* check if the word complies to a previously defined word spec with the same name */ + if (spec = name->s_word_spec){ + if ((spec->input_size != input_size) + ||(spec->output_size != output_size) + ||(spec->type_index != type_index)){ + post("ERROR: pdp_forthdict_add_word: new implementation of [%s] does not comply to old spec", + name->s_name); + return; + } + + } + /* no previous word spec with this name, so create a new spec */ + else{ + spec = name->s_word_spec = (t_pdp_forthword_spec *)pdp_alloc(sizeof(t_pdp_forthword_spec)); + spec->name = name; + spec->input_size = input_size; + spec->output_size = output_size; + spec->type_index = type_index; + spec->implementations = pdp_list_new(0); + } + + /* create the new implementation and add it */ + imp = (t_pdp_forthword_imp *)pdp_alloc(sizeof(t_pdp_forthword_imp)); + imp->name = name; + imp->def = def; + imp->type = type; + + /* can't delete old implemetations because of thread safety */ + pdp_list_add_pointer(spec->implementations, imp); + +} + +/* add a primitive */ +void pdp_forthdict_add_primitive(t_pdp_symbol *name, t_pdp_forthword w, int input_size, int output_size, + int type_index, t_pdp_symbol *type) +{ + t_pdp_list *def = pdp_list_new(1); + def->first->t = a_pointer; + def->first->w.w_pointer = w; + pdp_forthdict_add_word(name, def, input_size, output_size, type_index, type); +} + +/* parse a new definition from a null terminated string */ +t_pdp_list *pdp_forth_compile_def(char *chardef) +{ + t_pdp_list *l; + char *c; + + if (!(l = pdp_list_from_cstring(chardef, &c))){ + post ("ERROR: pdp_forth_compile_def: parse error parsing: %s", chardef); + if (*c) post ("ERROR: remaining input: %s", c); + } + if (*c){ + post ("WARNING: pdp_forth_compile_def: parsing: %s", chardef); + if (*c) post ("garbage at end of string: %s", c); + } + + return l; + +} + +void pdp_forthdict_compile_word(t_pdp_symbol *name, char *chardef, int input_size, int output_size, + int type_index, t_pdp_symbol *type) +{ + /* add the definition list to the dictionary */ + t_pdp_list *def; + + if (def = pdp_forth_compile_def (chardef)) + pdp_forthdict_add_word(name, def, input_size, output_size, type_index, type); + + +} + + + +/* execute a definition list + a def list is a list of primitives, immediates or symbolic words */ +t_pdp_word_error pdp_forth_execute_def(t_pdp_stack *stack, t_pdp_list *def) +{ + t_pdp_atom *a; + t_pdp_word_error e; + t_pdp_forthword w; + float f; + int i,p; + + D post("pdp_forth_execute_def %x %x", stack, def); + D pdp_list_print(def); + + for (a = def->first; a; a=a->next){ + switch(a->t){ + case a_float: // an immidiate float + f = a->w.w_float; + D post("pushing %f onto the stack", f); + pdp_stack_push_float(stack, f); + break; + case a_int: // an immidiate int + i = a->w.w_int; + D post("pushing %d onto the stack", i); + pdp_stack_push_int(stack, i); + break; + case a_packet: // an immidiate int + p = a->w.w_packet; + D post("pushing packet %d onto the stack", p); + pdp_stack_push_packet(stack, pdp_packet_copy_ro(p)); + break; + case a_symbol: // a high level word or an immediate symbol + D post("interpeting symbol %s", a->w.w_symbol->s_name); + if (e = pdp_forth_execute_word(stack, a->w.w_symbol)) return e; + break; + case a_pointer: // a primitive + w = a->w.w_pointer; + D post("exec primitive %x", w); + if (e = (w(stack))) return e; + break; + default: + return e_internal; + + } + } + return e_ok; +} + +/* execute a symbol (a high level word or an immediate) + this routine does the type based word multiplexing and stack checking */ +t_pdp_word_error pdp_forth_execute_word(t_pdp_stack *stack, t_pdp_symbol *word) +{ + t_pdp_symbol *type = 0; + t_pdp_atom *a; + t_pdp_forthword_spec *spec; + t_pdp_forthword_imp *imp = 0; + int i; + + D post("pdp_forth_execute_word %x %x", stack, word); + + /* first check if the word is defined. if not, the symbol will be loaded + onto the stack as an immidiate symbol */ + + if (!(spec = word->s_word_spec)){ + D post ("pushing symbol %s on the stack", word->s_name); + pdp_stack_push_symbol(stack, word); + return e_ok; + } + + D post("exec high level word [%s]", word->s_name); + + /* it is a word. check the stack size */ + if (stack->elements < spec->input_size){ + D post ("error executing [%s]: stack underflow", word->s_name); + return e_underflow; + } + + /* if the word is type oblivious, symply execute the first (only) + implementation in the list */ + if (spec->type_index < 0){ + D post("exec type oblivious word [%s]", word->s_name); + imp = spec->implementations->first->w.w_pointer; + return pdp_forth_execute_def(stack , imp->def); + } + + /* if it is not type oblivious, find the type template + to determine the correct implementation */ + + for(i=spec->type_index,a=stack->first; i--; a=a->next); + switch (a->t){ + /* get type description from first item on*/ + case a_packet: + type = pdp_packet_get_description(a->w.w_packet); break; + case a_symbol: + type = a->w.w_symbol; break; + case a_float: + type = pdp_gensym("float"); break; + case a_int: + type = pdp_gensym("int"); break; + case a_pointer: + type = pdp_gensym("pointer"); break; + default: + /* no type description found on top of stack. */ + type = pdp_gensym("unknown"); + break; + } + + /* scan the implementation list until a definition with matching type is found + if the type spec for a word is NULL, it counts as a match (for generic words) */ + for (a = spec->implementations->first; a; a = a->next){ + imp = a->w.w_pointer; + if ((!imp->type) || pdp_type_description_match(type, imp->type)){ + return pdp_forth_execute_def(stack , imp->def); + } + } + D post("ERROR: pdp_forth_execute_word: type error executing [%s] (2). stack:",word->s_name); + D pdp_list_print(stack); + + return e_type; // type error + +} + + +static void _add_2op(char *name, t_pdp_forthword w, char *type){ + pdp_forthdict_add_primitive(pdp_gensym(name), w, 2, 1, 0, pdp_gensym(type)); +} + +static void _add_1op(char *name, t_pdp_forthword w, char *type){ + pdp_forthdict_add_primitive(pdp_gensym(name), w, 1, 1, 0, pdp_gensym(type)); +} + + +void pdp_forth_setup(void) +{ + + /* add type oblivious (type_index = -1, type = NULL) stack manip primitives */ + pdp_forthdict_add_primitive(pdp_gensym("dup"), (t_pdp_forthword)pdp_stack_dup, 1, 2, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("swap"), (t_pdp_forthword)pdp_stack_swap, 2, 2, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("drop"), (t_pdp_forthword)pdp_stack_drop, 1, 0, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("over"), (t_pdp_forthword)pdp_stack_over, 2, 3, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("mov"), (t_pdp_forthword)pdp_stack_mov, 2, 1, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("down"), (t_pdp_forthword)pdp_stack_rdown, 1, 1, -1, 0); + + /* type converters (casts) */ + pdp_forthdict_add_primitive(pdp_gensym("int"), (t_pdp_forthword)pdp_stack_int, 1, 1, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("float"), (t_pdp_forthword)pdp_stack_float, 1, 1, -1, 0); + + /* add floating point ops */ + _add_2op("add", (t_pdp_forthword)pdp_stack_add_float, "float"); + _add_2op("sub", (t_pdp_forthword)pdp_stack_sub_float, "float"); + _add_2op("mul", (t_pdp_forthword)pdp_stack_mul_float, "float"); + _add_2op("div", (t_pdp_forthword)pdp_stack_div_float, "float"); + + _add_1op("sin", (t_pdp_forthword)pdp_stack_sin_float, "float"); + _add_1op("cos", (t_pdp_forthword)pdp_stack_cos_float, "float"); + + /* add integer ops */ + _add_2op("add", (t_pdp_forthword)pdp_stack_add_int, "int"); + _add_2op("sub", (t_pdp_forthword)pdp_stack_sub_int, "int"); + _add_2op("mul", (t_pdp_forthword)pdp_stack_mul_int, "int"); + _add_2op("div", (t_pdp_forthword)pdp_stack_div_int, "int"); + _add_2op("mod", (t_pdp_forthword)pdp_stack_mod_int, "int"); + + _add_2op("and", (t_pdp_forthword)pdp_stack_and_int, "int"); + _add_2op("or", (t_pdp_forthword)pdp_stack_or_int, "int"); + _add_2op("xor", (t_pdp_forthword)pdp_stack_xor_int, "int"); + + /* some immidiates */ + pdp_forthdict_add_primitive(pdp_gensym("ip"), (t_pdp_forthword)pdp_stack_push_invalid_packet, 0, 1, -1, 0); + +} diff --git a/system/kernel/pdp_list.c b/system/kernel/pdp_list.c new file mode 100644 index 0000000..ed2ad76 --- /dev/null +++ b/system/kernel/pdp_list.c @@ -0,0 +1,663 @@ + +/* + * Pure Data Packet header file. List class + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* who can live without a list, hmm? */ + +/* rethink the list of list thing: + should free and copy really copy the lists, or should + references to lists be allowed too? + + maybe references to lists as generic pointers? */ + +#include "pdp_list.h" +#include "pdp_types.h" +#include "pdp.h" +#include <pthread.h> +#include <stdlib.h> + +#define D if (0) + +#define PDP_LIST_BLOCKSIZE 4096 + +#define PDP_ATOM_ALLOC _pdp_atom_reuse +#define PDP_ATOM_DEALLOC _pdp_atom_save +//#define PDP_ATOM_ALLOC _pdp_atom_alloc +//#define PDP_ATOM_DEALLOC _pdp_atom_dealloc + +static t_pdp_atom *freelist; +static t_pdp_list *blocklist; +static pthread_mutex_t mut; + +/* memory allocation (for ease of wrapping) */ +static inline t_pdp_list* _pdp_list_alloc(void){return (t_pdp_list *)pdp_alloc(sizeof(t_pdp_list));} +static inline void _pdp_list_dealloc(t_pdp_list *list){pdp_dealloc(list);} +static inline t_pdp_atom* _pdp_atom_alloc(void){return (t_pdp_atom *)pdp_alloc(sizeof(t_pdp_atom));} +static inline void _pdp_atom_dealloc(t_pdp_atom *a){pdp_dealloc(a);} + + + +/* some private helper methods */ + +/* list pool setup */ +void pdp_list_setup(void) +{ + freelist = 0; + blocklist = _pdp_list_alloc(); + blocklist->elements = 0; + blocklist->first = 0; + pthread_mutex_init(&mut, NULL); + +#if 0 + { + char *c = "(test 1 2 (23 4)) (een (zes (ze)ven ()))) (())"; + while (*c){ + t_pdp_list *l = pdp_list_from_char(c, &c); + if (l) pdp_list_print(l); + else{ + post("parse error: remaining input: %s", c); break; + } + } + } +#endif + +} + +/* allocate a block */ +static t_pdp_atom* _pdp_atom_block_alloc(int size) +{ + int i = size; + t_pdp_atom *atom_block, *atom; + atom_block = (t_pdp_atom *)pdp_alloc(sizeof(t_pdp_atom) * size); + atom = atom_block; + while(i--){ + atom->next = atom + 1; + atom->t = a_undef; + atom->w.w_int = 0; + atom++; + } + atom_block[size-1].next = 0; + return atom_block; +} + +static void _pdp_atomlist_fprint(FILE* f, t_pdp_atom *a); + +static void _pdp_atom_refill_freelist(void) +{ + t_pdp_atom *atom; + + if (freelist != 0) post("ERROR:_pdp_atom_do_reuse: freelist != 0"); + + /* get a new block */ + freelist = _pdp_atom_block_alloc(PDP_LIST_BLOCKSIZE); + //post("new block"); + //_pdp_atomlist_fprint(stderr, freelist); + + /* take the first element from the block to serve as a blocklist element */ + atom = freelist; + atom->t = a_pointer; + atom->w.w_pointer = freelist; + freelist = freelist->next; + atom->next = blocklist->first; + blocklist->first = atom; + blocklist->elements++; + +} + + +/* reuse an old element from the freelist, or allocate a new block */ +static inline t_pdp_atom* _pdp_atom_reuse(void) +{ + t_pdp_atom *atom; + + pthread_mutex_lock(&mut); + + while (!(atom = freelist)){ + _pdp_atom_refill_freelist(); + } + /* delete the element from the freelist */ + freelist = freelist->next; + atom->next = 0; + + pthread_mutex_unlock(&mut); + + return atom; +} + +static inline void _pdp_atom_save(t_pdp_atom *atom) +{ + pthread_mutex_lock(&mut); + + atom->next = freelist; + freelist = atom; + + pthread_mutex_unlock(&mut); +} + + + + + + +/* 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; +} + + +static inline int _is_whitespace(char c){return (c == ' ' || c == '\n' || c == '\t');} +static inline void _skip_whitespace(char **c){while (_is_whitespace(**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_separator(char c) {return (_is_left_separator(c) || _is_right_separator(c) || _is_whitespace(c));} + + +static inline void _parse_atom(t_pdp_word_type *t, t_pdp_word *w, char *c, int n) +{ + char tmp[n+1]; + //post("_parse_atom(%d, %x, %s, %d)", *t, w, c, n); + strncpy(tmp, c, n); + tmp[n] = 0; + + + /* check if it's a number */ + if (tmp[0] >= '0' && tmp[0] <= '9'){ + int is_float = 0; + char *t2; + for(t2 = tmp; *t2; t2++){ + if (*t2 == '.') { + is_float = 1; + break; + } + } + /* it's a float */ + if (is_float){ + float f = strtod(tmp, 0); + D post("adding float %f", f); + *t = a_float; + *w = (t_pdp_word)f; + } + + /* it's an int */ + else { + int i = strtol(tmp, 0, 0); + D post("adding int %d", i); + *t = a_int; + *w = (t_pdp_word)i; + } + + } + /* it's a symbol */ + else { + D post("adding symbol %s", tmp); + *t = a_symbol; + *w = (t_pdp_word)pdp_gensym(tmp); + } + + D post("done parsing atom"); + +} + + +/* create a list from a character string */ +t_pdp_list *pdp_list_from_cstring(char *chardef, char **nextchar) +{ + t_pdp_list *l = pdp_list_new(0); + char *c = chardef; + char *lastname = c; + int n = 0; + + D post ("creating list from char: %s", chardef); + + /* find opening parenthesis and skip it*/ + _skip_whitespace(&c); + if (!_is_left_separator(*c)) goto error; else c++; + + /* start counting at the first non-whitespace + character after opening parenthesis */ + _skip_whitespace(&c); + lastname = c; + n = 0; + + while(*c){ + if (!_is_separator(*c)){ + /* item not terminated: keep counting */ + c++; + n++; + } + else { + /* separator encountered whitespace or parenthesis */ + + if (n){ + /* if there was something between this and the previous + separator, we've found and atom and parse it */ + pdp_list_add_back(l, a_undef, (t_pdp_word)0); + _parse_atom(&l->last->t, &l->last->w, lastname, n); + + /* skip the whitespace after the parsed word, if any */ + _skip_whitespace(&c); + } + + /* if we're at a right separator, we're done */ + if (_is_right_separator(*c)) {c++; goto done;} + + /* if it's a left separater, we have a sublist */ + if (_is_left_separator(*c)){ + t_pdp_list *sublist = pdp_list_from_cstring(c, &c); + if (!sublist) goto error; //sublist had parse error + pdp_list_add_back(l, a_list, (t_pdp_word)sublist); + } + + /* prepare for next atom */ + lastname = c; + n = 0; + } + + } + + error: + /* end of string encountered: parse error */ + D post("parse error: %s", c); + if (nextchar) *nextchar = c; + pdp_tree_free(l); //this will free all sublists too + return 0; + + + done: + _skip_whitespace(&c); + if (nextchar) *nextchar = c; + return l; + + + +} + + +/* 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->t = a_undef; + a->w.w_int = 0; + } +} + +void pdp_tree_strip_packets (t_pdp_list *l) +{ + pdp_tree_apply(l, _atom_packet_mark_unused); +} + + +/* debug */ +static void _pdp_atomlist_fprint(FILE* f, t_pdp_atom *a) +{ + fprintf(f, "("); + while (a){ + switch(a->t){ + 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>",(unsigned int)a->w.w_pointer); break; + case a_list: _pdp_atomlist_fprint(f, a->w.w_list->first); break; + case a_undef: fprintf(f, "undef"); break; + + default: fprintf(f, "unknown"); break; + } + 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); +} + + +/* public list operations */ + + +/* add a word to the start of the list */ +void pdp_list_add(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w) +{ + t_pdp_atom *new_a = PDP_ATOM_ALLOC(); + l->elements++; + new_a->next = l->first; + new_a->w = w; + new_a->t = t; + l->first = new_a; + if (!l->last) l->last = new_a; +} + + +/* add a word to the end of the list */ +void pdp_list_add_back(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w) +{ + t_pdp_atom *new_a = PDP_ATOM_ALLOC(); + l->elements++; + new_a->next = 0; + new_a->w = w; + new_a->t = t; + if (l->last){ + l->last->next = new_a; + } + else{ + l->first = new_a; + } + l->last = new_a; +} + +/* get list size */ +int pdp_list_size(t_pdp_list *l) +{ + return l->elements; +} + + + + +/* pop: return first item and remove */ +t_pdp_word pdp_list_pop(t_pdp_list *l) +{ + t_pdp_atom *a = l->first; + t_pdp_word w; + + w = a->w; + l->first = a->next; + PDP_ATOM_DEALLOC(a); + l->elements--; + if (!l->first) l->last = 0; + + return w; +} + + +/* 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_cat (t_pdp_list *l, t_pdp_list *tail) +{ + t_pdp_list *tmp = pdp_list_copy(tail); + l->elements += tmp->elements; + l->last->next = tmp->first; + l->last = tmp->last; + _pdp_list_dealloc(tmp); //delete the list stub + +} + + +/* check if a list contains an element */ +int pdp_list_contains(t_pdp_list *list, t_pdp_word_type t, t_pdp_word w) +{ + t_pdp_atom *a; + for(a = list->first; a; a=a->next){ + if (a->w.w_int == w.w_int && a->t == t) return 1; + } + return 0; +} + +/* add a thing to the start of the list if it's not in there already */ +void pdp_list_add_to_set(t_pdp_list *list, t_pdp_word_type t, t_pdp_word w) +{ + if (!pdp_list_contains(list, t, w)) + pdp_list_add(list, t, w); +} + + + diff --git a/system/kernel/pdp_packet.c b/system/kernel/pdp_packet.c new file mode 100644 index 0000000..a811381 --- /dev/null +++ b/system/kernel/pdp_packet.c @@ -0,0 +1,957 @@ +/* + * Pure Data Packet system implementation: Packet Manager + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "pdp.h" +#include <stdio.h> +#include <pthread.h> +#include <unistd.h> +#include <string.h> + + +/* TODO: + implement an indexing system to the packet pool (array) to speed up searches. + -> a list of lists of recycled packes, arranged by packet type. + -> a list of unused slots +*/ + + + +#define D if (0) + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + + +/* pdp specific constants */ +#define PDP_ALIGN 8 + +/* this needs to be able to grow dynamically, think about it later */ +/* for ordinary work, this is enough and can help finding memory leaks */ + +#define PDP_INITIAL_POOL_SIZE 1 +#define PDP_PACKET_MAX_MEM_USAGE -1 + +/* the pool */ +static unsigned int pdp_packet_mem_usage; +static unsigned int pdp_packet_max_mem_usage; +static int pdp_pool_size; +static t_pdp** pdp_pool; + +/* this is for detecting memory leaks */ +static int pdp_packet_count; + +/* some global vars */ +static t_symbol* pdp_sym_register_rw; +static t_symbol* pdp_sym_register_ro; +static t_symbol* pdp_sym_process; + +/* mutex */ +static pthread_mutex_t 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); + post("debug info for packet %d", packet); + if (!h){ + post("invalid packet"); + } + else{ + post ("\ttype: %d", h->type); + post ("\tdesc: %s", h->desc ? h->desc->s_name : "unknown"); + post ("\tsize: %d", h->size); + post ("\tflags: %x", h->flags); + post ("\tusers: %d", h->users); + post ("\trefloc: %x", h->refloc); + post ("\tclass: %x", h->theclass); + } +} + + + +/* setup methods */ + +void +pdp_packet_setup(void) +{ + + pdp_pool_size = PDP_INITIAL_POOL_SIZE; + pdp_packet_count = 0; + pdp_packet_mem_usage = 0; + pdp_packet_max_mem_usage = PDP_PACKET_MAX_MEM_USAGE; + pdp_pool = (t_pdp **)malloc(PDP_INITIAL_POOL_SIZE * sizeof(t_pdp *)); + bzero(pdp_pool, pdp_pool_size * sizeof(t_pdp *)); + pdp_sym_register_rw = gensym("register_rw"); + pdp_sym_register_ro = gensym("register_ro"); + pdp_sym_process = gensym("process"); + pdp_packet_count = 0; + 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 + //c->attributes = pdp_list_new(0); // create an empty attribute list + pdp_list_add(class_list, a_pointer, (t_pdp_word)((void *)c)); + //post("added class %s %x", c->type->s_name, c->create); + + return c; +} + +#if 0 +void pdp_class_addmethod(t_pdp_class *c, t_pdp_symbol *name, t_pdp_attribute_method method, + t_pdp_list *in_spec, t_pdp_list *out_spec) +{ + t_pdp_attribute *attr = (t_pdp_attribute *)pdp_alloc(sizeof(t_pdp_attribute)); + attr->name = name; + attr->method = method; + attr->in_spec = in_spec; + attr->out_spec = out_spec; + pdp_list_add_pointer(c->attributes, attr); + +} +#endif + +/* the packet factory */ +int pdp_factory_newpacket(t_pdp_symbol *type) +{ + t_pdp_class *c; + t_pdp_atom *a = class_list->first; + while(a){ + c = (t_pdp_class *)(a->w.w_pointer); + if (c->type && pdp_type_description_match(type, c->type)){ + //post("method %x, type %s", c->create, type->s_name); + return (c->create) ? (*c->create)(type) : -1; + } + a = a->next; + } + return -1; +} + +#if 0 +/* generic methods. actually a forth word operation on a stack. + first item on stack must be a packet and it's type will + determine the place to look for the operator */ +int pdp_packet_op(t_pdp_symbol *operation, struct _pdp_list *stack) +{ + int packet = stack->first->w.w_packet; + t_pdp *h = pdp_packet_header(packet); + t_pdp_atom *i; + t_pdp_attribute *attr; + + if (!(h && h->theclass)) goto exit; + + PDP_POINTER_IN(h->theclass->attributes, i, attr){ + //post("attribute:%s", attr->name->s_name); + if (attr->name == operation){ + + /* FOUND -> exec (check arguments first ???) */ + return attr->method (stack); + } + + } + exit: + // method not found + post ("WARNING: pdp_packet_op: packet %d from class %s has no operation %s", + packet, (h && h->theclass) ? h->theclass->type->s_name : "UNKNOWN", operation->s_name); + return 0; +} + +#endif + +static void +_pdp_pool_expand(void){ + int i; + + /* double the size */ + int new_pool_size = pdp_pool_size << 1; + t_pdp **new_pool = (t_pdp **)malloc(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 *)); + free(pdp_pool); + pdp_pool = new_pool; + pdp_pool_size = new_pool_size; + + D post("DEBUG: _pdp_pool_expand: resized pool to contain %d packets", pdp_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(t_pdp *p) +{ + unsigned int size = p->size; + if (p->theclass && p->theclass->cleanup) + (*p->theclass->cleanup)(p); + + free (p); + pdp_packet_mem_usage -= size; + pdp_packet_count--; + D post("DEBUG: _pdp_packet_new_dealloc: freed packet. pool contains %d packets", pdp_packet_count); +} + +static t_pdp* +_pdp_packet_alloc(unsigned int datatype, unsigned int datasize) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + unsigned int align; + t_pdp *p = 0; + + /* check if there is a memory usage limit */ + if (pdp_packet_max_mem_usage){ + /* if it would exceed the limit, fail */ + if (pdp_packet_mem_usage + totalsize > pdp_packet_max_mem_usage){ + D post("DEBUG: _pdp_packet_new_alloc: memory usage limit exceeded"); + return 0; + } + } + p = (t_pdp *)malloc(totalsize); + if (p){ + align = ((unsigned int)p) & (PDP_ALIGN - 1); + if (align) post("WARNING: _pdp_packet_alloc: data misaligned by %x", align); + memset(p, 0, PDP_HEADER_SIZE); //initialize header to 0 + p->type = datatype; + p->size = totalsize; + p->users = 1; + pdp_packet_mem_usage += totalsize; + pdp_packet_count++; + D post("DEBUG: _pdp_packet_new_alloc: allocated new packet. pool contains %d packets, using %d bytes", + pdp_packet_count, pdp_packet_mem_usage); + } + + return p; +} + + +void +pdp_packet_destroy(void) +{ + int i = 0; + /* dealloc all the data in object stack */ + post("DEBUG: pdp_packet_destroy: clearing object pool."); + while ((i < pdp_pool_size) && (pdp_pool[i])) _pdp_packet_dealloc(pdp_pool[i++]); +} + + + +/* try to find a packet based on main type and datasize */ +static int +_pdp_packet_reuse_type_size(unsigned int datatype, unsigned int datasize) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + int i = 0; + int return_packet = -1; + t_pdp* p; + + for (i=0; i < pdp_pool_size; i++){ + p = pdp_pool[i]; + /* check if we can reuse this one if it is already allocated */ + if (p) { + /* search for unused packets of equal size & type */ + if ((p->users == 0) && (p->size == totalsize) && (p->type == datatype)){ + D post("DEBUG: _pdp_packet_reuse_type_size: can reuse %d", i); + + /* if possible, a packet will be reused and reinitialized + i haven't found a use for this, so it's only used for discriminating + between pure and not-so-pure packets */ + if (p->theclass && p->theclass->reinit){ + (*p->theclass->reinit)(p); + } + /* if no re-init method is found, the header will be reset to all 0 + this ensures the header is in a predictable state */ + else { + memset(p, 0, PDP_HEADER_SIZE); + p->type = datatype; + p->size = totalsize; + } + + p->users = 1; + return_packet = i; + goto exit; + } + else{ + D post("DEBUG _pdp_packet_reuse_type_size: can't reuse %d, (%d users)", i, p->users); + } + } + } + + D post("DEBUG: _pdp_packet_reuse_type_size: no reusable packet found"); + + exit: + return return_packet; +} + + +/* create a new packet in an empty slot. + if this fails, the garbage collector needs to be called */ +static int +_pdp_packet_create_in_empty_slot(unsigned int datatype, unsigned int datasize /*without header*/) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + int i = 0; + int return_packet = -1; + int out_of_mem = 0; + t_pdp* p; + + + /* no reusable packets found, try to find an empty slot */ + for (i=0; i < pdp_pool_size; i++){ + p = pdp_pool[i]; + if (!p) { + p = _pdp_packet_alloc(datatype, datasize); + + if (!p) { + D post("DEBUG: _pdp_packet_create_in_empty_slot: out of memory (malloc returned NULL)"); + return_packet = -1; + goto exit; + } + + pdp_pool[i] = p; + return_packet = i; + goto exit; + } + } + + /* if we got here the pool is full: resize the pool and try again */ + _pdp_pool_expand(); + return_packet = _pdp_packet_create_in_empty_slot(datatype, datasize); + + exit: + return return_packet; + +} + +/* find an unused packet, free it and create a new packet. + if this fails, something is seriously wrong */ +static int +_pdp_packet_create_in_unused_slot(unsigned int datatype, unsigned int datasize /*without header*/) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + int i = 0; + int return_packet = -1; + int out_of_mem = 0; + t_pdp* p; + + D post("DEBUG: _pdp_packet_create_in_unused_slot: collecting garbage"); + + /* search backwards */ + for (i=pdp_pool_size-1; i >= 0; i--){ + p = pdp_pool[i]; + if (p){ + if (p->users == 0){ + _pdp_packet_dealloc(p); + p = _pdp_packet_alloc(datatype, datasize); + pdp_pool[i] = p; + + /* alloc succeeded, return */ + if (p) { + post("DEBUG _pdp_packet_create_in_unused_slot: garbage collect succesful"); + return_packet = i; + goto exit; + } + + /* alloc failed, continue collecting garbage */ + D post("DEBUG _pdp_packet_create_in_unused_slot: freed one packet, still out of memory (malloc returned NULL)"); + out_of_mem = 1; + + } + } + } + + /* if we got here, we ran out of memory */ + D post("DEBUG: _pdp_packet_create_in_unused_slot: out of memory after collecting garbage"); + return_packet = -1; + +exit: + return return_packet; +} + +/* warning: for "not so pure" packets, this method will only return an initialized + packet if it can reuse a privious one. that is, if it finds a reinit method + in the packet. use the pdp_packet_new_<type> constructor if possible */ + +static int +_pdp_packet_brandnew(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int return_packet = -1; + + /* try to create a new packet in an empty slot */ + return_packet = _pdp_packet_create_in_empty_slot(datatype, datasize); + if (return_packet != -1) goto exit; + + /* if we still don't have a packet, we need to call the garbage collector until we can allocate */ + return_packet = _pdp_packet_create_in_unused_slot(datatype, datasize); + + exit: + return return_packet; +} + + +static int +_pdp_packet_new(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int return_packet = -1; + + /* try to reuse a packet based on main type and datasize */ + return_packet = _pdp_packet_reuse_type_size(datatype, datasize); + if (return_packet != -1) goto exit; + + /* create a brandnew packet */ + return_packet = _pdp_packet_brandnew(datatype, datasize); + if (return_packet != -1) goto exit; + + post("WARNING: maximum packet memory usage limit (%d bytes) reached after garbage collect.", pdp_packet_max_mem_usage); + post("WARNING: increase memory limit or decrease packet usage (i.e. pdp_loop, pdp_delay)."); + post("WARNING: pool contains %d packets, using %d bytes.", pdp_packet_count, pdp_packet_mem_usage); + + exit: + return return_packet; + +} + + + +/* public pool operations: have to be thread safe so each entry point + locks the mutex */ + +/* reuse an old packet based on high level description */ +int +pdp_packet_reuse(t_pdp_symbol *description) +{ + int i; + int return_packet = -1; + t_pdp *p; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + for (i=0; i < pdp_pool_size; i++){ + p = pdp_pool[i]; + /* check if we can reuse this one if it is already allocated */ + if ((p) && (p->users == 0) && (p->desc == description)){ + /* mark it as used */ + p->users = 1; + return_packet = i; + goto gotit; + } + } + + gotit: + /* LOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + + return return_packet; + + +} + +/* create a new packet, or reuse an old one based on main type and size */ +int +pdp_packet_new(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int packet; + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + packet = _pdp_packet_new(datatype, datasize); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + return packet; +} + + +/* create a brand new packet, don't reuse an old one */ +int +pdp_packet_brandnew(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int packet; + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + packet = _pdp_packet_brandnew(datatype, datasize); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + return packet; +} + + +/* this returns a copy of a packet for read only access. */ +int +pdp_packet_copy_ro(int handle) +{ + int out_handle = -1; + t_pdp* p; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + if ((handle >= 0) + && (handle < pdp_pool_size) + && (p = pdp_pool[handle])){ + + /* it is an error to copy a packet without an owner */ + if (!p->users){ + post("pdp_packet_copy_ro: ERROR: request to copy packet %d which has 0 users", handle); + out_handle = -1; + } + + /* if it's a passing packet, reset the reference location + and turn it into a normal packet */ + else if (p->refloc){ + *p->refloc = -1; + p->refloc = 0; + out_handle = handle; + } + /* if it's a normal packet, increase the number of users */ + else { + p->users++; + out_handle = handle; + } + } + else out_handle = -1; + + //post("pdp_copy_ro: outhandle:%d", out_handle); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + + return out_handle; +} + +/* copy a packet. if the packet is marked passing, it will be aquired. + otherwize a new packet will be created with a copy of the contents. */ + +int +pdp_packet_copy_rw(int handle) +{ + int out_handle = -1; + t_pdp* p; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + if ((handle >= 0) + && (handle < pdp_pool_size) + && (p = pdp_pool[handle])){ + /* if there are other users, copy the object otherwize return the same handle */ + + /* it is an error to copy a packet without an owner */ + if (!p->users){ + post("pdp_packet_copy_rw: ERROR: request to copy packet %d which has 0 users", handle); + out_handle = -1; + } + + /* if it is a passing packet, remove the owner's reference to it */ + else if (p->refloc){ + *p->refloc = -1; + p->refloc = 0; + out_handle = handle; + } + + /* check if packet supports copy (for fanout) */ + else if(p->flags & PDP_FLAG_DONOTCOPY) out_handle = -1; + + + /* copy the packet, since it already has 1 or more users */ + else{ + int new_handle = _pdp_packet_new(p->type, p->size - PDP_HEADER_SIZE); + t_pdp* new_p = pdp_packet_header(new_handle); + + /* check if valid */ + if (!new_p) out_handle = -1; + + else { + /* if a copy constructor is found, it will be called */ + if (p->theclass && p->theclass->copy){ + (*p->theclass->copy)(new_p, p); + } + /* if not, the entire packet will be copied, assuming a pure data packet */ + else { + memcpy(new_p, p, p->size); + } + new_p->users = 1; + out_handle = new_handle; + } + } + + //post("pdp_copy_rw: inhandle:%d outhandle:%d", handle, out_handle); + + } + else out_handle = -1; + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + return out_handle; +} + +/* create a new packet, copying the header data of another + packet, without copying the data */ +int +pdp_packet_clone_rw(int handle) +{ + int out_handle; + t_pdp* p; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + if ((handle >= 0) + && (handle < pdp_pool_size) + && (p = pdp_pool[handle])){ + + /* clone the packet header, don't copy the data */ + int new_handle = _pdp_packet_new(p->type, p->size - PDP_HEADER_SIZE); + t_pdp* new_p = pdp_packet_header(new_handle); + + /* if a clone initializer is found, it will be called */ + if (p->theclass && p->theclass->clone){ + (*p->theclass->clone)(new_p, p); + } + /* if not, just the header will be copied, assuming a pure data packet */ + else { + memcpy(new_p, p, PDP_HEADER_SIZE); + } + new_p->users = 1; /* the new packet has 1 user */ + new_p->refloc = 0; /* it is not a passing packet, even if the template was */ + out_handle = new_handle; + } + + else out_handle = -1; + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + + return out_handle; +} + +void +_pdp_packet_mark_unused_nolock(int handle) +{ + t_pdp* p; + + if ((handle >= 0) && (handle < pdp_pool_size)){ + if (p = pdp_pool[handle]) { + /* mark_unused on a passing packet has no effect + this is to support automatic conversion for passing packets + so in order to delete a passing packet, it should be marked normal first */ + if (p->refloc){ + post("DEBUG: pdp_mark_unused called on a passing packet. ignored."); + return; + } + + /* decrease the refcount */ + if (p->users) { + p->users--; + //post("pdp_mark_unused: handle %d, users left %d", handle, p->users); + } + else { + post("pdp_mark_unused: ERROR: handle %d has zero users (duplicate pdp_mark_unused ?)", handle); + } + } + else { + post("pdp_mark_unused: ERROR: invalid handle %d: no associated object", handle); + } + } + + else { + /* -1 is the only invalid handle that doesn't trigger a warning */ + if (handle != -1) post("pdp_mark_unused: WARNING: invalid handle %d: out of bound", handle); + } + +} + +/* mark a packet as unused, decreasing the reference count. + if the reference count reaches zero, the packet is ready to be recycled + by a new packet allocation. it is illegal to reference a packet with + reference count == zero. if the reference count is not == 1, only readonly + access is permitted. */ + +void +pdp_packet_mark_unused(int handle) +{ + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + _pdp_packet_mark_unused_nolock(handle); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); +} + +void +pdp_packet_mark_unused_atomic(int *handle) +{ + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + _pdp_packet_mark_unused_nolock(*handle); + *handle = -1; + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); +} + +/* delete a packet. this is more than mark_unused: + it actually removes any reference. can be used for + some packet types that have "expensive" resources. + usually this is up to the garbage collector to call. */ + +void +pdp_packet_delete(int handle) +{ + t_pdp *header = pdp_packet_header(handle); + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + + /* check if it's a valid handle */ + if ((handle >= 0) && (handle < pdp_pool_size) && pdp_pool[handle]){ + + /* mark it unused */ + _pdp_packet_mark_unused_nolock(handle); + + /* if no users, dealloc */ + if (!header->users){ + _pdp_packet_dealloc(header); + pdp_pool[handle] = NULL; + } + + /* print a warning if failed */ + else{ + post("WARNING: pdp_packet_delete: packet %d was not deleted. %d users remaining", + handle, header->users); + } + } + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + +} + + +/* this turns any packet into a normal (non-passing) packet */ + +void +pdp_packet_unmark_passing(int packet) +{ + t_pdp* header = pdp_packet_header(packet); + if (!header) return; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + header->refloc = 0; + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + +} + + +/* this marks a packet as passing. this means it changes + owner on first copy_ro or copy_rw. the previous owner is + notified by setting the handler to -1. */ + +void +pdp_packet_mark_passing(int *phandle) +{ + t_pdp* header = pdp_packet_header(*phandle); + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + if (header){ + if (header->refloc){ + post("pdp_packet_mark_passing: ERROR: duplicate mark_passing on packet %d", *phandle); + } + else if (1 != header->users){ + post("pdp_packet_mark_passing: ERROR: packet %d is not exclusively owned by caller, it has %d users", *phandle, header->users); + } + else { + header->refloc = phandle; + } + } + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + +} + + +/* 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; +} + + +/* OBSOLETE: use packet description */ +/* check if two packets are allocated and of the same type */ +bool pdp_packet_compat(int packet0, int packet1) +{ + + t_pdp *header0 = pdp_packet_header(packet0); + t_pdp *header1 = pdp_packet_header(packet1); + + if (!(header1)){ + //post("pdp_type_compat: invalid header packet1"); + return 0; + } + if (!(header0)){ + //post("pdp_type_compat: invalid header packet 0"); + return 0; + } + if (header0->type != header1->type){ + //post("pdp_type_compat: types do not match"); + return 0; + } + + return 1; +} + +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) +{ + t_pdp *p; + int i; + int nbpackets = pdp_packet_count; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + for (i=0; i < pdp_pool_size; i++){ + p = pdp_pool[i]; + if(p && !p->users) { + _pdp_packet_dealloc(p); + pdp_pool[i] = 0; + } + } + nbpackets -= pdp_packet_count; + //post("pdp_pool_collect_garbage: deleted %d unused packets", nbpackets); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + + return nbpackets; +} + +void +pdp_pool_set_max_mem_usage(int max) +{ + if (max < 0) max = 0; + pdp_packet_max_mem_usage = max; + +} + + + + + + +/* malloc wrapper that calls garbage collector */ +void *pdp_alloc(int size) +{ + void *ptr = malloc(size); + + //post ("malloc called for %d bytes", size); + + if (ptr) return ptr; + + post ("malloc failed in a pdp module: running garbage collector."); + + pdp_pool_collect_garbage(); + return malloc(size); +} + + +void pdp_dealloc(void *stuff) +{ + free (stuff); +} + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_queue.c b/system/kernel/pdp_queue.c new file mode 100644 index 0000000..665a236 --- /dev/null +++ b/system/kernel/pdp_queue.c @@ -0,0 +1,347 @@ +/* + * Pure Data Packet - processor queue module. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +/* + this is a the processor queue pdp system module + it receives tasks from objects that are schedules to + be computed in another thread. the object is signalled back + when the task is completed. + + this is not a standard pd class. it is a sigleton class + using a standard pd clock to poll for compleded methods on + every scheduler run. this is a hack to do thread synchronization + in a thread unsafe pd. + + the queue object can be reused. the pdp system however only + has one instance (one pdp queue. pdp remains a serial program.) + + */ + +#include "pdp.h" +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> + + +#define D if (0) + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define PDP_QUEUE_LOGSIZE 10 +#define PDP_QUEUE_DELTIME 10.0f + + + + +/********************* general purpose pd process queue class *********************/ + +void pdp_procqueue_wait(t_pdp_procqueue *q) +{ + D post("pdp_procqueue_wait(%x): waiting for pdp_queue_thread to finish processing", q); + pthread_mutex_lock(&q->mut); + while(((q->curr - q->head) & q->mask) != 0){ + + pthread_cond_wait(&q->cond_processingdone, &q->mut); + } + pthread_mutex_unlock(&q->mut); + D post("pdp_procqueue_wait(%x): pdp_procqueue_thread has finished processing", q); + +} +void pdp_procqueue_finish(t_pdp_procqueue *q, int index) +{ + + if (-1 == index) { + //post("pdp_pq_remove: index == -1"); + return; + } + /* wait for processing thread to finish*/ + pdp_procqueue_wait(q); + + /* invalidate callback at index */ + q->q[index & q->mask].x_callback = 0; + q->q[index & q->mask].x_queue_id = 0; + +} + +static void pdp_procqueue_signal_processor(t_pdp_procqueue *q) +{ + + //NOTE: uncommenting these post statements causes a libc crash + //in mutex lock in putc + //D post("pdp_procqueue_signal_processor(%x): signalling process thread", q); + pthread_mutex_lock(&q->mut); + pthread_cond_signal(&q->cond_dataready); + pthread_mutex_unlock(&q->mut); + //D post("pdp_procqueue_signal_processor(%x): signalling done", q); + + +} + +static void pdp_procqueue_wait_for_feeder(t_pdp_procqueue *q) +{ + + + /* only use locking when there is no data */ + if(((q->curr - q->head) & q->mask) == 0){ + + /* signal processing done */ + D post("pdp_procqueue_wait_for_feeder(%x): signalling processing is done", q); + pthread_mutex_lock(&q->mut); + pthread_cond_signal(&q->cond_processingdone); + + /* wait until there is an item in the queue */ + while(((q->curr - q->head) & q->mask) == 0){ + pthread_cond_wait(&q->cond_dataready, &q->mut); + } + + pthread_mutex_unlock(&q->mut); + D post("pdp_procqueue_wait_for_feeder(%x): waiting done", q); + + } +} + +void pdp_procqueue_add(t_pdp_procqueue *q, void *owner, void *process, void *callback, int *queue_id) +{ + int i; + + /* if processing is in not in thread, just call the funcs */ + if (!q->use_thread){ + D post("pdp_procqueue_add(%q): calling processing routine directly", q); + if (queue_id) *queue_id = -1; + if (process) ((t_pdpmethod) process)(owner); + if (callback) ((t_pdpmethod) callback)(owner); + return; + } + + + /* if queue is full, call directly */ + if (1 == ((q->tail - q->head) & q->mask)) { + //post("pdp_procqueue_add: WARNING: processing queue (%x) is full.\n", q); + //post("pdp_procqueue_add: WARNING: tail %x, head %x, mask %x.\n", q->tail, q->head, q->mask); + //post("pdp_procqueue_add: WARNING: skipping process method, calling callback directly.\n"); + if (queue_id) *queue_id = -1; + if (callback) ((t_pdpmethod) callback)(owner); + return; + //exit(1); + } + + /* schedule method in thread queue */ + i = q->head & q->mask; + q->q[i].x_owner = owner; + q->q[i].x_process = process; + q->q[i].x_callback = callback; + q->q[i].x_queue_id = queue_id; + if (queue_id) *queue_id = i; + //post("pdp_queue_add: added method to queue, index %d", i); + + + // increase the packet count + q->packets++; + + // move head forward + q->head++; + + pdp_procqueue_signal_processor(q); + +} + + +/* processing thread */ +static void *pdp_procqueue_thread(void *vq) +{ + t_pdp_procqueue *q = (t_pdp_procqueue *)vq; + + D post("pdp_procqueue_thread(%x): thread started", q); + + while(1){ + t_process_queue_item *p; + + + D post("pdp_procqueue_thread(%x): waiting for feeder", q); + + /* wait until there is data available */ + pdp_procqueue_wait_for_feeder(q); + + + D post("pdp_procqueue_thread(%x): processing %d", q, q->curr & q->mask); + + + /* call the process routine */ + p = &q->q[q->curr & q->mask]; + if (p->x_process) + (p->x_process)(p->x_owner); + + /* advance */ + q->curr++; + + + } +} + + +/* call back all the callbacks */ +static void pdp_procqueue_callback (t_pdp_procqueue *q) +{ + + /* call callbacks for finished packets */ + while(0 != ((q->curr - q->tail) & q->mask)) + { + int i = q->tail & q->mask; + /* invalidate queue id */ + if(q->q[i].x_queue_id) *q->q[i].x_queue_id = -1; + /* call callback */ + if(q->q[i].x_callback) (q->q[i].x_callback)(q->q[i].x_owner); + //else post("pdp_pq_tick: callback %d is disabled",i ); + q->tail++; + } + +} + +/* the clock method */ +static void pdp_procqueue_tick (t_pdp_procqueue *q) +{ + /* do work */ + //if (!(ticks % 1000)) post("pdp tick %d", ticks); + + if (!q->use_thread) return; + + /* call callbacks */ + pdp_procqueue_callback(q); + + /* increase counter */ + q->ticks++; + + /* set clock for next update */ + clock_delay(q->pdp_clock, q->deltime); +} + + + +void pdp_procqueue_use_thread(t_pdp_procqueue* q, int t) +{ + /* if thread usage is being disabled, + wait for thread to finish processing first */ + if (t == 0) { + pdp_procqueue_wait(q); + q->use_thread = 0; + pdp_procqueue_callback(q); + clock_unset(q->pdp_clock); + } + else { + clock_unset(q->pdp_clock); + clock_delay(q->pdp_clock, q->deltime); + q->use_thread = 1; + } + +} + +void pdp_procqueue_init(t_pdp_procqueue *q, double milliseconds, int logsize) +{ + pthread_attr_t attr; + int size = 1 << logsize; + + /* setup pdp queue processor object */ + q->ticks = 0; + q->deltime = milliseconds; + + /* setup queue data */ + q->mask = size - 1; + q->head = 0; + q->tail = 0; + q->curr = 0; + q->q = pdp_alloc(size * sizeof(t_process_queue_item)); + memset(q->q, 0, size * sizeof(t_process_queue_item)); + + /* enable threads */ + q->use_thread = 1; + + /* setup synchro stuff */ + pthread_mutex_init(&q->mut, NULL); + pthread_cond_init(&q->cond_dataready, NULL); + pthread_cond_init(&q->cond_processingdone, NULL); + + + /* allocate the clock */ + q->pdp_clock = clock_new(q, (t_method)pdp_procqueue_tick); + + /* set the clock */ + clock_delay(q->pdp_clock, 0); + + /* start processing thread */ + + /* glibc doc says SCHED_OTHER is default, + but it seems not to be when initiated from a RT thread + so we explicitly set it here */ + pthread_attr_init (&attr); + //pthread_attr_setschedpolicy(&attr, SCHED_FIFO); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + + D post("pdp_procqueue_init(%x): starting thread", q); + pthread_create(&q->thread_id, &attr, pdp_procqueue_thread, (void *)q); + D post("pdp_procqueue_init(%x): back in pd thread", q); + + /* wait for processing thread to finish */ + //pdp_procqueue_wait(q); + + /* set default disable/enable thread here */ + //post("pdp_queue: THREAD PROCESSING ON BY DEFAULT!!"); + pdp_procqueue_use_thread(q,0); + +} + + + + +/* the (static) pdp queue object */ +static t_pdp_procqueue pdp_queue; + + +/* get the default queue */ +t_pdp_procqueue *pdp_queue_get_queue(void){return &pdp_queue;} + + +#if 1 +/* default pdp queue shortcut methods */ +void pdp_queue_wait() {pdp_procqueue_wait(&pdp_queue);} +void pdp_queue_finish(int index) { pdp_procqueue_finish(&pdp_queue, index);} +void pdp_queue_add(void *owner, void *process, void *callback, int *queue_id) { + pdp_procqueue_add(&pdp_queue, owner, process, callback, queue_id); +} +void pdp_queue_use_thread(int t) {pdp_procqueue_use_thread(&pdp_queue, t);} +void pdp_queue_setup(void){ + pdp_procqueue_init(&pdp_queue, PDP_QUEUE_DELTIME, PDP_QUEUE_LOGSIZE); + pdp_procqueue_use_thread(&pdp_queue,0); +} +#endif + + + + + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_type.c b/system/kernel/pdp_type.c new file mode 100644 index 0000000..c220269 --- /dev/null +++ b/system/kernel/pdp_type.c @@ -0,0 +1,501 @@ +/* + * Pure Data Packet system implementation. : code for handling different packet types + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* this file contains type handling routines */ + +#include <stdarg.h> +#include <string.h> +#include <pthread.h> +#include "pdp.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_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){ + + 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 post("DEBUG: _pdp_type_run_conversion_program: method = %x", m); + p = m(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 (a=a->next; a; a=a->next){ + m = a->w.w_pointer; + D 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 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 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) +{ + 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 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 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 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) +{ + int retval = 0; + + t_pdp_atom *ad, *ap; + t_pdp_symbol *wildcard = pdp_gensym("*"); + + + if (!(pattern)) post("PANICA"); + if (!(description)) post("PANICB"); + + + /* same type -> match */ + if (description == pattern) {retval = 1; goto done;} + + /* 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); + + if (!(pattern->s_type)) post("PANIC1"); + if (!(description->s_type)) post("PANIC2"); + + /* check size */ + if (description->s_type->elements != pattern->s_type->elements){retval = 0; goto done;} + + /* 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) {retval = 0; goto done;} /* difference and not a wildcard */ + } + + /* type templates match */ + retval = 1; + + done: + D post("DEBUG: testing match between %s and %s: %s", + description->s_name, pattern->s_name, retval ? "match" : "no match"); + return retval; + +} + + + + + +/* 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 symbol hash + memset(pdp_symhash, 0, HASHSIZE * sizeof(t_pdp_symbol *)); + + // create conversion lists + cached_conversion_list = pdp_list_new(0); + conversion_list = pdp_list_new(0); + max_cache_size = INIT_MAX_CACHE_SIZE; + + + + +} diff --git a/system/kernel/pdp_type.c_old b/system/kernel/pdp_type.c_old new file mode 100644 index 0000000..4e42d53 --- /dev/null +++ b/system/kernel/pdp_type.c_old @@ -0,0 +1,542 @@ +/* + * Pure Data Packet system implementation. : code for handling different packet types + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* this file contains type handling routines + note: pd symbols are used for type identification + for porting to other hosts, i suggest reimplementing t_pdp_symbol */ + +// debug + +#include <fnmatch.h> +#include <stdarg.h> +#include <string.h> +#include <pthread.h> +#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; i<CACHE_SIZE; i++){ + t_pdp_conversion *c = cached_conversion_array + i; + c->src_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; + + + +} diff --git a/system/kernel/pdp_ut.c b/system/kernel/pdp_ut.c new file mode 100644 index 0000000..dadc493 --- /dev/null +++ b/system/kernel/pdp_ut.c @@ -0,0 +1,262 @@ +/* + * Pure Data Packet - Utility toolkit objects. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* This file contains some small utility pd objects that make working with + pdp objects a lot easier. Mainly as glue to be used in the abstractions + in the distro. */ + + +#include "pdp.h" +#include <math.h> + +/* this object does an add, scale, clip operation */ + +t_class *pdp_ut_addscaleclip_class; + +typedef struct pdp_ut_addscaleclip_struct +{ + t_object x_obj; + t_outlet *x_outlet0; + t_float x_min; + t_float x_max; + t_float x_offset; + t_float x_scale; +} t_pdp_ut_addscaleclip; + + +static void pdp_ut_addscaleclip_float(t_pdp_ut_addscaleclip *x, t_floatarg f) +{ + f += x->x_offset; + f *= x->x_scale; + f = (f < x->x_min) ? x->x_min : f; + f = (f > x->x_max) ? x->x_max : f; + outlet_float(x->x_outlet0, f); +} + +static void pdp_ut_addscaleclip_free(t_pdp_ut_addscaleclip *x){} + +void *pdp_ut_addscaleclip_new(t_floatarg offset, t_floatarg scale, t_floatarg min, t_floatarg max) +{ + t_pdp_ut_addscaleclip *x = (t_pdp_ut_addscaleclip *)pd_new(pdp_ut_addscaleclip_class); + x->x_outlet0 = outlet_new(&x->x_obj, &s_float); + x->x_offset = offset; + x->x_scale = scale; + x->x_min = min; + x->x_max = max; + return (void *)x; +} + +void pdp_ut_addscaleclip_setup(void) +{ + pdp_ut_addscaleclip_class = class_new(gensym("pdp_ut_addscaleclip"), (t_newmethod)pdp_ut_addscaleclip_new, + (t_method)pdp_ut_addscaleclip_free, sizeof(t_pdp_ut_addscaleclip), 0, + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addfloat(pdp_ut_addscaleclip_class, pdp_ut_addscaleclip_float); +} + + +/* pdp_ut_logmap does a logarithmic parameter mapping [0->1] x -> min(max/min)^x max an add, scale, clip operation */ +/* pdp_ut_logmap_comp does x -> min(max/min)^(1-x) */ +/* pdp_ut_linmap dos x -> min + (max - min * x */ + +t_class *pdp_ut_linmap_class; +t_class *pdp_ut_logmap_class; +t_class *pdp_ut_logmap_comp_class; + +typedef struct pdp_ut_map_struct +{ + t_object x_obj; + t_outlet *x_outlet0; + t_float x_min; + t_float x_max; +} t_pdp_ut_map; + + +static void pdp_ut_logmap_float(t_pdp_ut_map *x, t_floatarg f) +{ + f = (f < 0.0f) ? 0.0f : f; + f = (f > 1.0f) ? 1.0f : f; + + f = x->x_min * pow((x->x_max / x->x_min), f); + + outlet_float(x->x_outlet0, f); +} + +static void pdp_ut_linmap_float(t_pdp_ut_map *x, t_floatarg f) +{ + f = (f < 0.0f) ? 0.0f : f; + f = (f > 1.0f) ? 1.0f : f; + + f = x->x_min + ((x->x_max - x->x_min) * f); + + outlet_float(x->x_outlet0, f); +} + +static void pdp_ut_logmap_comp_float(t_pdp_ut_map *x, t_floatarg f) +{ + f = (f < 0.0f) ? 0.0f : f; + f = (f > 1.0f) ? 1.0f : f; + + f = x->x_min * pow((x->x_max / x->x_min), (1.0f - f)); + + outlet_float(x->x_outlet0, f); +} + +static void pdp_ut_map_free(t_pdp_ut_map *x){} + + +void pdp_ut_map_init(t_pdp_ut_map *x, t_floatarg min, t_floatarg max) +{ + x->x_outlet0 = outlet_new(&x->x_obj, &s_float); + x->x_min = min; + x->x_max = max; +} + +void *pdp_ut_logmap_new(t_floatarg min, t_floatarg max) +{ + t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_logmap_class); + pdp_ut_map_init(x, min, max); + return (void *)x; +} + +void *pdp_ut_linmap_new(t_floatarg min, t_floatarg max) +{ + t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_linmap_class); + pdp_ut_map_init(x, min, max); + return (void *)x; +} + +void *pdp_ut_logmap_comp_new(t_floatarg min, t_floatarg max) +{ + t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_logmap_comp_class); + pdp_ut_map_init(x, min, max); + return (void *)x; +} + +void pdp_ut_logmap_setup(void) +{ + pdp_ut_logmap_class = class_new(gensym("pdp_ut_logmap"), (t_newmethod)pdp_ut_logmap_new, + (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0, + A_FLOAT, A_FLOAT, A_NULL); + class_addfloat(pdp_ut_logmap_class, pdp_ut_logmap_float); +} + +void pdp_ut_logmap_comp_setup(void) +{ + pdp_ut_logmap_comp_class = class_new(gensym("pdp_ut_logmap_comp"), (t_newmethod)pdp_ut_logmap_comp_new, + (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0, + A_FLOAT, A_FLOAT, A_NULL); + class_addfloat(pdp_ut_logmap_comp_class, pdp_ut_logmap_comp_float); +} + +void pdp_ut_linmap_setup(void) +{ + pdp_ut_linmap_class = class_new(gensym("pdp_ut_linmap"), (t_newmethod)pdp_ut_linmap_new, + (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0, + A_FLOAT, A_FLOAT, A_NULL); + class_addfloat(pdp_ut_linmap_class, pdp_ut_linmap_float); +} + + + +t_class *pdp_ut_rgb2ycrcb_class; + +typedef struct pdp_ut_rgb2ycrcb +{ + t_object x_obj; + t_outlet *x_outlet_luma; + t_outlet *x_outlet_chroma_red; + t_outlet *x_outlet_chroma_blue; + + t_float x_red, x_green, x_blue; + +} t_pdp_ut_rgb2ycrcb; + + +static void pdp_ut_rgb2ycrcb_bang (t_pdp_ut_rgb2ycrcb* x) +{ + + float luma = 0.299f * x->x_red + 0.587f * x->x_green + 0.114f * x->x_blue; + float chroma_red = (x->x_red - luma) * 0.713f; + float chroma_blue = (x->x_blue - luma) * 0.565f; + + outlet_float(x->x_outlet_chroma_blue, chroma_blue); + outlet_float(x->x_outlet_chroma_red, chroma_red); + outlet_float(x->x_outlet_luma, luma); + +} + + +static void pdp_ut_rgb2ycrcb_red (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_red = f; pdp_ut_rgb2ycrcb_bang(x);} +static void pdp_ut_rgb2ycrcb_green (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_green = f; pdp_ut_rgb2ycrcb_bang(x);} +static void pdp_ut_rgb2ycrcb_blue (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_blue = f; pdp_ut_rgb2ycrcb_bang(x);} + + + +static void pdp_ut_rgb2ycrcb_free (t_pdp_ut_rgb2ycrcb* x) {} +static void* pdp_ut_rgb2ycrcb_new(void) +{ + t_pdp_ut_rgb2ycrcb *x = (t_pdp_ut_rgb2ycrcb *)pd_new(pdp_ut_rgb2ycrcb_class); + + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("green")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("blue")); + + x->x_outlet_luma = outlet_new(&x->x_obj, &s_float); + x->x_outlet_chroma_red = outlet_new(&x->x_obj, &s_float); + x->x_outlet_chroma_blue = outlet_new(&x->x_obj, &s_float); + + x->x_red = 0.0f; + x->x_green = 0.0f; + x->x_blue = 0.0f; + + + return (void *)x; +} + +void pdp_ut_rgb2ycrcb_setup(void) +{ + pdp_ut_rgb2ycrcb_class = class_new(gensym("pdp_ut_rgb2ycrcb"), (t_newmethod)pdp_ut_rgb2ycrcb_new, + (t_method)pdp_ut_rgb2ycrcb_free, sizeof(t_pdp_ut_rgb2ycrcb), 0, A_NULL); + class_addfloat(pdp_ut_rgb2ycrcb_class, pdp_ut_rgb2ycrcb_red); + class_addmethod(pdp_ut_rgb2ycrcb_class, (t_method)pdp_ut_rgb2ycrcb_green, gensym("green"), A_FLOAT, A_NULL); + class_addmethod(pdp_ut_rgb2ycrcb_class, (t_method)pdp_ut_rgb2ycrcb_blue, gensym("blue"), A_FLOAT, A_NULL); +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + +void pdp_ut_setup(void) +{ + pdp_ut_addscaleclip_setup(); + pdp_ut_logmap_setup(); + pdp_ut_logmap_comp_setup(); + pdp_ut_linmap_setup(); + pdp_ut_rgb2ycrcb_setup(); +} + + +#ifdef __cplusplus +} +#endif |