From 8357e669c79e8db95865bc4d802ab3bcc8f6a1cc Mon Sep 17 00:00:00 2001 From: Thomas Mayer Date: Thu, 28 Jul 2011 22:23:47 +0200 Subject: [PATCH 1/4] Makefile "works for me" under Debian Wheezy i386 --- Makefile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 455df80..72d77fd 100644 --- a/Makefile +++ b/Makefile @@ -2,19 +2,21 @@ # User configuration goes here # Uncomment the particular line according to your platform -#PLATFORM = linux -PLATFORM = macosx +PLATFORM = linux +#PLATFORM = macosx #PLATFORM = mingw # If Pd is in an unusual place, put -I/path/to m_pd.h here: -PDINCLUDE = -I/Applications/Pd-extended.app/Contents/Resources/include +#PDINCLUDE = -I/Applications/Pd-extended.app/Contents/Resources/include #PDINCLUDE = -I./ #PDINCLUDE = -I$(HOME)/include #PDINCLUDE = -I/opt/puredata/include #PDINCLUDE = -I/Applications/Pd-0.41-4.app/Contents/Resources/src # build architecture: comment 2 lines to build in default 64bit on 64 bit platforms -ARCH = -arch i386 +#ARCH = -arch i386 +#ARCH = --target=i386 +ARCH = HIREDISARCH = 32bit # End of user configuration @@ -46,12 +48,13 @@ LIBCSVURL = http://downloads.sourceforge.net/project/libcsv/libcsv/libcsv-3.0.1/ # Hiredis setup HIREDIS = hiredis HIREDISD = antirez-hiredis-3cc6a7f -HIREDISTGZ = antirez-hiredis-v0.10.1-0-g3cc6a7f.tar.gz +HIREDISTGZ = v0.10.1 HIREDISURL = https://github.com/antirez/hiredis/tarball/v0.10.1 HIREDISI = -I$(HIREDISD) HIREDISL = $(HIREDISD)/libhiredis.a -CFLAGS = -ansi -Wall -O2 -fPIC -bundle -undefined suppress -flat_namespace $(ARCH) $(HIREDISI) $(LIBCSVI) $(PDINCLUDE) +#CFLAGS = -ansi -Wall -O2 -fPIC -bundle -undefined suppress -flat_namespace $(ARCH) $(HIREDISI) $(LIBCSVI) $(PDINCLUDE) +CFLAGS = -ansi -Wall -O2 -fPIC $(ARCH) $(HIREDISI) $(LIBCSVI) $(PDINCLUDE) # build default: $(puredis_$(PLATFORM)) From 48b0df8f4523cb7ffbab9afb688fb82ff1f44aba Mon Sep 17 00:00:00 2001 From: Thomas Mayer Date: Thu, 28 Jul 2011 23:29:26 +0200 Subject: [PATCH 2/4] Fix for https://github.com/lp/puredis/issues/1: replaced references to .a files to .o and .so for Linux. --- Makefile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 72d77fd..18a8604 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ PLATFORM = linux #PDINCLUDE = -I$(HOME)/include #PDINCLUDE = -I/opt/puredata/include #PDINCLUDE = -I/Applications/Pd-0.41-4.app/Contents/Resources/src +PDINCLUDE = -I$(HOME)/dev/diverse/Pd-0.42.5-extended/pd/src # build architecture: comment 2 lines to build in default 64bit on 64 bit platforms #ARCH = -arch i386 @@ -41,7 +42,9 @@ LIBS_mingw = pd.dll LIBCSVD = libcsv-3.0.1 LIBCSVI = -I$(LIBCSVD) -LIBCSVL = $(LIBCSVD)/libcsv.a +LIBCSVL_macosx = $(LIBCSVD)/libcsv.a +LIBCSVL_linux = $(LIBCSVD)/libcsv.o +LIBCSVL_mingw = LIBCSVTGZ = libcsv-3.0.1.tar.gz LIBCSVURL = http://downloads.sourceforge.net/project/libcsv/libcsv/libcsv-3.0.1/libcsv-3.0.1.tar.gz @@ -51,7 +54,9 @@ HIREDISD = antirez-hiredis-3cc6a7f HIREDISTGZ = v0.10.1 HIREDISURL = https://github.com/antirez/hiredis/tarball/v0.10.1 HIREDISI = -I$(HIREDISD) -HIREDISL = $(HIREDISD)/libhiredis.a +HIREDISL_macosx = $(HIREDISD)/libhiredis.a +HIREDISL_linux = $(HIREDISD)/libhiredis.so +HIREDISL_mingw = #CFLAGS = -ansi -Wall -O2 -fPIC -bundle -undefined suppress -flat_namespace $(ARCH) $(HIREDISI) $(LIBCSVI) $(PDINCLUDE) CFLAGS = -ansi -Wall -O2 -fPIC $(ARCH) $(HIREDISI) $(LIBCSVI) $(PDINCLUDE) @@ -69,7 +74,7 @@ install: # compile $(puredis_$(PLATFORM)): $(puredis_src) $(HIREDISD)/build.stamp $(LIBCSVD)/build.stamp - gcc $(CFLAGS) $(CFLAGS_$(PLATFORM)) $(HIREDISL) $(LIBCSVL) -o $(puredis_$(PLATFORM)) $(puredis_src) + gcc $(CFLAGS) $(CFLAGS_$(PLATFORM)) $(HIREDISL_$(PLATFORM)) $(LIBCSVL_$PLATFORM()) -o $(puredis_$(PLATFORM)) $(puredis_src) # get hiredis: download, unpack, compile, install locally $(HIREDISD)/build.stamp: $(HIREDISD)/unpack.stamp From d46a8503aa7cf211facdbd4594b3953b7c35cee4 Mon Sep 17 00:00:00 2001 From: Thomas Mayer Date: Tue, 17 Apr 2012 22:03:08 +0200 Subject: [PATCH 3/4] Library template 1.0.12 --- .gitignore | 9 + LICENCE => LICENCE.txt | 0 Makefile | 221 +++++++---- README.txt | 114 ++++++ apuredis.c | 21 ++ libpuredis.c | 818 +++++++++++++++++++++++++++++++++++++++++ spuredis.c | 21 ++ 7 files changed, 1137 insertions(+), 67 deletions(-) create mode 100644 .gitignore rename LICENCE => LICENCE.txt (100%) create mode 100644 README.txt create mode 100644 apuredis.c create mode 100644 libpuredis.c create mode 100644 spuredis.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5cf5770 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.o +*.pd_* +*.deb +*.dll +libpuredis.so +libpuredis.dylib +libpuredis.dll +libcsv* +antirez-hiredis* diff --git a/LICENCE b/LICENCE.txt similarity index 100% rename from LICENCE rename to LICENCE.txt diff --git a/Makefile b/Makefile index 3b6ab4f..82b142e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -## Pd library template version 1.0.10 +## Pd library template version 1.0.12 # For instructions on how to use this template, see: # http://puredata.info/docs/developer/MakefileTemplate LIBRARY_NAME = puredis @@ -6,24 +6,28 @@ LIBRARY_NAME = puredis # add your .c source files, one object per file, to the SOURCES # variable, help files will be included automatically, and for GUI # objects, the matching .tcl file too -SOURCES = puredis.c +SOURCES = puredis.c apuredis.c spuredis.c # list all pd objects (i.e. myobject.pd) files here, and their helpfiles will # be included automatically -# PDOBJECTS = mypdobject.pd +PDOBJECTS = # example patches and related files, in the 'examples' subfolder -# EXAMPLES = bothtogether.pd +EXAMPLES = # manuals and related files, in the 'manual' subfolder -# MANUAL = manual.txt +MANUAL = # if you want to include any other files in the source and binary tarballs, # list them here. This can be anything from header files, test patches, # documentation, etc. README.txt and LICENSE.txt are required and therefore # automatically included -EXTRA_DIST = +EXTRA_DIST = CHANGES +# unit tests and related files here, in the 'unittests' subfolder +UNITTESTS = + +HELPPATCHES = puredis-help.pd apuredis-help.pd spuredis-help.pd #------------------------------------------------------------------------------# @@ -32,10 +36,10 @@ EXTRA_DIST = # #------------------------------------------------------------------------------# -# -I"$(PD_INCLUDE)/pd" supports the header location for 0.43 -CFLAGS = -I"$(PD_INCLUDE)/pd" -Wall -W -g $(HIREDISI) $(LIBCSVI) -LDFLAGS = -LIBS = $(LIBCSVL) $(HIREDISL) +ALL_CFLAGS = -I"$(PD_INCLUDE)" $(HIREDISI) $(LIBCSVI) +ALL_LDFLAGS = +SHARED_LDFLAGS = +ALL_LIBS = $(LIBCSVL) $(HIREDISL) # libcsv @@ -59,12 +63,19 @@ HIREDISL = $(HIREDISD)/libhiredis.a # #------------------------------------------------------------------------------# +# these can be set from outside without (usually) breaking the build +CFLAGS = -Wall -W -g +LDFLAGS = +LIBS = +CROSS = +CROSS_PATH = + # get library version from meta file LIBRARY_VERSION = $(shell sed -n 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' $(LIBRARY_NAME)-meta.pd) -CFLAGS += -DPD -DVERSION='"$(LIBRARY_VERSION)"' +ALL_CFLAGS += -DPD -DVERSION='"$(LIBRARY_VERSION)"' -PD_INCLUDE = $(PD_PATH)/include +PD_INCLUDE = $(PD_PATH)/include/pd # where to install the library, overridden below depending on platform prefix = /usr/local libdir = $(prefix)/lib @@ -80,7 +91,7 @@ ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows) DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) -ORIGDIR=pd-$(LIBRARY_NAME:~=)_$(LIBRARY_VERSION) +ORIGDIR=pd-`echo $(LIBRARY_NAME:~=)|tr '_' '-'`_$(LIBRARY_VERSION) UNAME := $(shell uname -s) ifeq ($(UNAME),Darwin) @@ -88,6 +99,7 @@ ifeq ($(UNAME),Darwin) ifeq ($(CPU),arm) # iPhone/iPod Touch SOURCES += $(SOURCES_iphoneos) EXTENSION = pd_darwin + SHARED_EXTENSION = dylib OS = iphoneos PD_PATH = /Applications/Pd-extended.app/Contents/Resources IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin @@ -97,33 +109,33 @@ ifeq ($(UNAME),Darwin) ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6 OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer - CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) - LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT) - LIBS += -lc + ALL_CFLAGS := $(IPHONE_CFLAGS) $(ALL_CFLAGS) + ALL_LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT) + SHARED_LDFLAGS += -arch armv6 -dynamiclib -undefined dynamic_lookup $(ISYSROOT) + ALL_LIBS += -lc $(LIBS_iphoneos) STRIP = strip -x DISTBINDIR=$(DISTDIR)-$(OS) else # Mac OS X SOURCES += $(SOURCES_macosx) EXTENSION = pd_darwin + SHARED_EXTENSION = dylib OS = macosx PD_PATH = /Applications/Pd-extended.app/Contents/Resources - OPT_CFLAGS = -fast - CC=gcc + OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast # build universal 32-bit on 10.4 and 32/64 on newer ifeq ($(shell uname -r | sed 's|\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*|\1|'), 8) - FAT_FLAGS = -arch i386 -mmacosx-version-min=10.4 + #FAT_FLAGS = -arch ppc -arch i386 -mmacosx-version-min=10.4 else - FAT_FLAGS = -arch i386 -arch x86_64 -mmacosx-version-min=10.4 - # FAT_FLAGS = -arch i386 -mmacosx-version-min=10.4 + #FAT_FLAGS = -arch ppc -arch i386 -arch x86_64 -mmacosx-version-min=10.4 SOURCES += $(SOURCES_iphoneos) endif - IFINK := $(shell perl -e 'if (-d "/sw/include") { print "-I/sw/include" } else { print "" }') - LFINK := $(shell perl -e 'if (-d "/sw/lib") { print "-L/sw/lib" } else { print "" }') - CFLAGS += $(FAT_FLAGS) -fPIC $(IFINK) - LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup $(LFINK) + ALL_CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include # if the 'pd' binary exists, check the linking against it to aid with stripping - LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd) - LIBS += -lc + BUNDLE_LOADER = $(shell test ! -e $(PD_PATH)/bin/pd || echo -bundle_loader $(PD_PATH)/bin/pd) + ALL_LDFLAGS += $(FAT_FLAGS) -bundle $(BUNDLE_LOADER) -undefined dynamic_lookup -L/sw/lib + SHARED_LDFLAGS += $(FAT_FLAGS) -dynamiclib -undefined dynamic_lookup \ + -install_name @loader_path/$(SHARED_LIB) -compatibility_version 1 -current_version 1.0 + ALL_LIBS += -lc $(LIBS_macosx) STRIP = strip -x DISTBINDIR=$(DISTDIR)-$(OS) # install into ~/Library/Pd on Mac OS X since /usr/local isn't used much @@ -136,6 +148,7 @@ ifeq ($(UNAME),ANDROID) CPU := arm SOURCES += $(SOURCES_android) EXTENSION = pd_linux + SHARED_EXTENSION = so OS = android PD_PATH = /usr NDK_BASE := /usr/local/android-ndk @@ -146,8 +159,9 @@ ifeq ($(UNAME),ANDROID) CC := $(NDK_TOOLCHAIN_BASE)/bin/arm-linux-androideabi-gcc --sysroot=$(NDK_SYSROOT) OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer CFLAGS += - LDFLAGS += -Wl,--export-dynamic -shared - LIBS += -lc + LDFLAGS += -rdynamic -shared + SHARED_LDFLAGS += -Wl,-soname,$(SHARED_LIB) -shared + LIBS += -lc $(LIBS_android) STRIP := $(NDK_TOOLCHAIN_BASE)/bin/arm-linux-androideabi-strip \ --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) @@ -156,12 +170,14 @@ ifeq ($(UNAME),Linux) CPU := $(shell uname -m) SOURCES += $(SOURCES_linux) EXTENSION = pd_linux + SHARED_EXTENSION = so OS = linux PD_PATH = /usr OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer - CFLAGS += -fPIC - LDFLAGS += -Wl,--export-dynamic -shared -fPIC - LIBS += -lc + ALL_CFLAGS += -fPIC + ALL_LDFLAGS += -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags + SHARED_LDFLAGS += -Wl,-soname,$(SHARED_LIB) -shared + ALL_LIBS += -lc $(LIBS_linux) STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) endif @@ -170,12 +186,14 @@ ifeq ($(UNAME),GNU) CPU := $(shell uname -m) SOURCES += $(SOURCES_linux) EXTENSION = pd_linux + SHARED_EXTENSION = so OS = linux PD_PATH = /usr OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer - CFLAGS += -fPIC - LDFLAGS += -Wl,--export-dynamic -shared -fPIC - LIBS += -lc + ALL_CFLAGS += -fPIC + ALL_LDFLAGS += -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags + SHARED_LDFLAGS += -shared -Wl,-soname,$(SHARED_LIB) + ALL_LIBS += -lc $(LIBS_linux) STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) endif @@ -184,12 +202,14 @@ ifeq ($(UNAME),GNU/kFreeBSD) CPU := $(shell uname -m) SOURCES += $(SOURCES_linux) EXTENSION = pd_linux + SHARED_EXTENSION = so OS = linux PD_PATH = /usr OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer - CFLAGS += -fPIC - LDFLAGS += -Wl,--export-dynamic -shared -fPIC - LIBS += -lc + ALL_CFLAGS += -fPIC + ALL_LDFLAGS += -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags + SHARED_LDFLAGS += -shared -Wl,-soname,$(SHARED_LIB) + ALL_LIBS += -lc $(LIBS_linux) STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) endif @@ -197,12 +217,14 @@ ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME))) CPU := $(shell uname -m) SOURCES += $(SOURCES_cygwin) EXTENSION = dll + SHARED_EXTENSION = dll OS = cygwin PD_PATH = $(shell cygpath $$PROGRAMFILES)/pd OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer - CFLAGS += - LDFLAGS += -Wl,--export-dynamic -shared -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" - LIBS += -lc -lpd + ALL_CFLAGS += + ALL_LDFLAGS += -rdynamic -shared -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" + SHARED_LDFLAGS += -shared -Wl,-soname,$(SHARED_LIB) + ALL_LIBS += -lc -lpd $(LIBS_cygwin) STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS) endif @@ -210,14 +232,25 @@ ifeq (MINGW,$(findstring MINGW,$(UNAME))) CPU := $(shell uname -m) SOURCES += $(SOURCES_windows) EXTENSION = dll + SHARED_EXTENSION = dll OS = windows PD_PATH = $(shell cd "$$PROGRAMFILES/pd" && pwd) # MinGW doesn't seem to include cc so force gcc CC=gcc + ifneq ($(strip $(CROSS)),) + CC = $(CROSS)-gcc + LD = $(CROSS)-ld + AR = $(CROSS)-ar + PKG_CONFIG = $(CROSS)-pkg-config + CFLAGS += -I$(CROSS_PATH)/$(CROSS)/include -I../../pd/src + LIBS += -L$(CROSS_PATH)/$(CROSS)/bin -L$(CROSS_PATH)/$(CROSS)/lib -L/../../pd/bin + PATH := $(CROSS_PATH)/bin:$(PATH) + endif OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer - CFLAGS += -mms-bitfields - LDFLAGS += -s -shared -Wl,--enable-auto-import - LIBS += -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" -L"$(PD_PATH)/obj" -lpd -lwsock32 -lkernel32 -luser32 -lgdi32 + ALL_CFLAGS += -mms-bitfields $(CFLAGS_windows) + ALL_LDFLAGS += -s -shared -Wl,--enable-auto-import -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" -L"$(PD_PATH)/obj" + SHARED_LDFLAGS += -shared -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" -L"$(PD_PATH)/obj" + ALL_LIBS += -lpd -lwsock32 -lkernel32 -luser32 -lgdi32 $(LIBS_windows) STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS) endif @@ -225,47 +258,61 @@ endif # in case somebody manually set the HELPPATCHES above HELPPATCHES ?= $(SOURCES:.c=-help.pd) $(PDOBJECTS:.pd=-help.pd) -CFLAGS += $(OPT_CFLAGS) +ALL_CFLAGS := $(ALL_CFLAGS) $(CFLAGS) $(OPT_CFLAGS) +ALL_LDFLAGS := $(LDFLAGS) $(ALL_LDFLAGS) +ALL_LIBS := $(LIBS) $(ALL_LIBS) +SHARED_SOURCE ?= $(wildcard lib$(LIBRARY_NAME).c) +SHARED_HEADER ?= $(shell test ! -e $(LIBRARY_NAME).h || echo $(LIBRARY_NAME).h) +SHARED_LIB = $(SHARED_SOURCE:.c=.$(SHARED_EXTENSION)) +SHARED_TCL_LIB = $(wildcard lib$(LIBRARY_NAME).tcl) -.PHONY = install libdir_install single_install install-doc install-exec install-examples install-manual clean dist etags $(LIBRARY_NAME) +.PHONY = install libdir_install single_install install-doc install-examples install-manual install-unittests clean distclean dist etags $(LIBRARY_NAME) all: $(HIREDISD)/build.stamp $(LIBCSVD)/build.stamp $(SOURCES:.c=.$(EXTENSION)) -# gcc -ansi -Wall -O2 -fPIC -Ilua-5.1.4/include/ -shared -o pdtest.pd_linux pdtest.c lua-5.1.4/lib/liblua.a - %.o: %.c - $(CC) $(CFLAGS) -o "$*.o" -c "$*.c" + $(CC) $(ALL_CFLAGS) -o "$*.o" -c "$*.c" -%.$(EXTENSION): %.o - $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS) +%.$(EXTENSION): %.o $(SHARED_LIB) + $(CC) $(ALL_LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(ALL_LIBS) $(SHARED_LIB) chmod a-x "$*.$(EXTENSION)" # this links everything into a single binary file -$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o - $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS) +$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o lib$(LIBRARY_NAME).o + $(CC) $(ALL_LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) \ + $(LIBRARY_NAME).o lib$(LIBRARY_NAME).o $(ALL_LIBS) chmod a-x $(LIBRARY_NAME).$(EXTENSION) +$(SHARED_LIB): $(SHARED_SOURCE:.c=.o) + $(CC) $(SHARED_LDFLAGS) -o $(SHARED_LIB) $(SHARED_SOURCE:.c=.o) $(ALL_LIBS) + install: libdir_install # The meta and help files are explicitly installed to make sure they are # actually there. Those files are not optional, then need to be there. -libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples install-manual +libdir_install: $(SOURCES:.c=.$(EXTENSION)) $(SHARED_LIB) install-doc install-examples install-manual install-unittests $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) test -z "$(strip $(SOURCES))" || (\ $(INSTALL_PROGRAM) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \ $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION)))) - test -z "$(strip $(shell ls $(SOURCES:.c=.tcl)))" || \ - $(INSTALL_DATA) $(shell ls $(SOURCES:.c=.tcl)) \ + test -z "$(strip $(SHARED_LIB))" || \ + $(INSTALL_DATA) $(SHARED_LIB) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(wildcard $(SOURCES:.c=.tcl)))" || \ + $(INSTALL_DATA) $(wildcard $(SOURCES:.c=.tcl)) \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) test -z "$(strip $(PDOBJECTS))" || \ $(INSTALL_DATA) $(PDOBJECTS) \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(SHARED_TCL_LIB))" || \ + $(INSTALL_DATA) $(SHARED_TCL_LIB) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) # install library linked as single binary -single_install: $(LIBRARY_NAME) install-doc install-exec +single_install: $(LIBRARY_NAME) install-doc install-examples install-manual install-unittests $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) $(INSTALL_PROGRAM) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION) @@ -292,11 +339,19 @@ install-manual: $(INSTALL_DATA) manual/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual; \ done +install-unittests: + test -z "$(strip $(UNITTESTS))" || \ + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/unittests && \ + for file in $(UNITTESTS); do \ + $(INSTALL_DATA) unittests/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/unittests; \ + done + clean: - rm -f -- $(SOURCES:.c=.o) $(SOURCES_LIB:.c=.o) - rm -f -- $(SOURCES:.c=.$(EXTENSION)) - rm -f -- $(LIBRARY_NAME).o - rm -f -- $(LIBRARY_NAME).$(EXTENSION) + -rm -f -- $(SOURCES:.c=.o) $(SOURCES_LIB:.c=.o) $(SHARED_SOURCE:.c=.o) + -rm -f -- $(SOURCES:.c=.$(EXTENSION)) + -rm -f -- $(LIBRARY_NAME).o + -rm -f -- $(LIBRARY_NAME).$(EXTENSION) + -rm -f -- $(SHARED_LIB) rm -f -R $(HIREDISD) rm -f $(HIREDISTGZ) rm -f -R $(LIBCSVD) @@ -309,6 +364,10 @@ distclean: clean -rm -rf -- $(DISTDIR) -rm -f -- $(ORIGDIR).tar.gz -rm -rf -- $(ORIGDIR) + rm -f -R $(HIREDISD) + rm -f $(HIREDISTGZ) + rm -f -R $(LIBCSVD) + rm -f $(LIBCSVTGZ) $(DISTBINDIR): @@ -316,7 +375,7 @@ $(DISTBINDIR): libdir: all $(DISTBINDIR) $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR) - $(INSTALL_DATA) $(SOURCES) $(DISTBINDIR) + $(INSTALL_DATA) $(SOURCES) $(SHARED_SOURCE) $(SHARED_HEADER) $(DISTBINDIR) $(INSTALL_DATA) $(HELPPATCHES) $(DISTBINDIR) test -z "$(strip $(EXTRA_DIST))" || \ $(INSTALL_DATA) $(EXTRA_DIST) $(DISTBINDIR) @@ -335,8 +394,16 @@ dist: $(DISTDIR) $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd $(DISTDIR) test -z "$(strip $(ALLSOURCES))" || \ $(INSTALL_DATA) $(ALLSOURCES) $(DISTDIR) - test -z "$(strip $(shell ls $(ALLSOURCES:.c=.tcl)))" || \ - $(INSTALL_DATA) $(shell ls $(ALLSOURCES:.c=.tcl)) $(DISTDIR) + test -z "$(strip $(wildcard $(ALLSOURCES:.c=.tcl)))" || \ + $(INSTALL_DATA) $(wildcard $(ALLSOURCES:.c=.tcl)) $(DISTDIR) + test -z "$(strip $(wildcard $(LIBRARY_NAME).c))" || \ + $(INSTALL_DATA) $(LIBRARY_NAME).c $(DISTDIR) + test -z "$(strip $(SHARED_HEADER))" || \ + $(INSTALL_DATA) $(SHARED_HEADER) $(DISTDIR) + test -z "$(strip $(SHARED_SOURCE))" || \ + $(INSTALL_DATA) $(SHARED_SOURCE) $(DISTDIR) + test -z "$(strip $(SHARED_TCL_LIB))" || \ + $(INSTALL_DATA) $(SHARED_TCL_LIB) $(DISTDIR) test -z "$(strip $(PDOBJECTS))" || \ $(INSTALL_DATA) $(PDOBJECTS) $(DISTDIR) test -z "$(strip $(HELPPATCHES))" || \ @@ -353,6 +420,11 @@ dist: $(DISTDIR) for file in $(MANUAL); do \ $(INSTALL_DATA) manual/$$file $(DISTDIR)/manual; \ done + test -z "$(strip $(UNITTESTS))" || \ + $(INSTALL_DIR) $(DISTDIR)/unittests && \ + for file in $(UNITTESTS); do \ + $(INSTALL_DATA) unittests/$$file $(DISTDIR)/unittests; \ + done tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR) # make a Debian source package @@ -365,28 +437,43 @@ dpkg-source: rm -rf -- $(DISTDIR) $(ORIGDIR) cd .. && dpkg-source -b $(LIBRARY_NAME) -etags: - etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h +dpkg-deb: dpkg-source + dpkg-buildpackage -b -us + +etags: TAGS + +TAGS: $(wildcard $(PD_INCLUDE)/*.h) $(SOURCES) $(SHARED_SOURCE) $(SHARED_HEADER) + etags $(wildcard $(PD_INCLUDE)/*.h) + etags -a *.h $(SOURCES) $(SHARED_SOURCE) $(SHARED_HEADER) + etags -a --language=none --regex="/proc[ \t]+\([^ \t]+\)/\1/" *.tcl showsetup: @echo "CC: $(CC)" @echo "CFLAGS: $(CFLAGS)" @echo "LDFLAGS: $(LDFLAGS)" @echo "LIBS: $(LIBS)" + @echo "ALL_CFLAGS: $(ALL_CFLAGS)" + @echo "ALL_LDFLAGS: $(ALL_LDFLAGS)" + @echo "ALL_LIBS: $(ALL_LIBS)" @echo "PD_INCLUDE: $(PD_INCLUDE)" @echo "PD_PATH: $(PD_PATH)" @echo "objectsdir: $(objectsdir)" @echo "LIBRARY_NAME: $(LIBRARY_NAME)" @echo "LIBRARY_VERSION: $(LIBRARY_VERSION)" @echo "SOURCES: $(SOURCES)" + @echo "SHARED_HEADER: $(SHARED_HEADER)" + @echo "SHARED_SOURCE: $(SHARED_SOURCE)" + @echo "SHARED_LIB: $(SHARED_LIB)" + @echo "SHARED_TCL_LIB: $(SHARED_TCL_LIB)" @echo "PDOBJECTS: $(PDOBJECTS)" @echo "ALLSOURCES: $(ALLSOURCES)" + @echo "ALLSOURCES TCL: $(wildcard $(ALLSOURCES:.c=.tcl))" @echo "UNAME: $(UNAME)" @echo "CPU: $(CPU)" @echo "pkglibdir: $(pkglibdir)" @echo "DISTDIR: $(DISTDIR)" @echo "ORIGDIR: $(ORIGDIR)" - + # get hiredis: download, unpack, compile, install locally $(HIREDISD)/build.stamp: $(HIREDISD)/unpack.stamp make -C $(HIREDISD) CFLAGS="$(FAT_FLAGS)" LDFLAGS="$(FAT_FLAGS)" diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..e58549e --- /dev/null +++ b/README.txt @@ -0,0 +1,114 @@ +h1. Puredis + +h3. "Redis":http://redis.io/ external for "Pure Data":http://puredata.info/. + +h2. Build + +bc. wget https://github.com/lp/puredis/tarball/master +PUREDIS_SRC=`find . -name lp-puredis-* -print` +tar xzf $PUREDIS_SRC +cd ${PUREDIS_SRC:0:20} +# *** edit the Makefile top portion with proper system settings *** +make + +h2. Dependencies + +p. The build process will take care of fetching, compiling and linking to "Hiredis":https://github.com/antirez/hiredis and "libcsv":http://sourceforge.net/projects/libcsv/. The core and purpose of this external, "Redis":http://redis.io/, is not bundled herein and must be installed separately. The only mandatory Redis configuration (redis.conf) option for Puredis is "timeout 0" (meaning no timeout), unless your use of Puredis is limited to short time spans. + +h2. Usage: + +!https://github.com/lp/puredis/raw/master/img/puredis-help.png! + +bc. SET: symbol OK +GET: symbol BAR +LPUSH: 1 +LPUSH: 2 +LPUSH: 3 +LPOP: symbol VALUE3 +LRANGE: list VALUE2 VALUE1 +HMSET: symbol OK +HGET: symbol value2 +HMGET: list value1 value3 +HVALS: list value1 value2 value3 +SADD: 1 +SADD: 1 +SADD: 1 +SADD: 1 +SADD: 1 +SINTER: symbol C +SUNION: list C A B +ZADD: 1 +ZADD: 1 +ZADD: 1 +ZADD: 1 +ZADD: 1 +ZINCRBY: symbol 4 +ZRANGEBYSCORE: list D 10 E 100 +ZRANGE: list A 1 B 2 C 4 + +h2. Async Puredis: apuredis + +!https://github.com/lp/puredis/raw/master/img/apuredis-help.png! + +bc. right: 1 +bang: bang +right: 1 +bang: bang +... +left: symbol BAR +right: 0 + +h2. Pub/Sub Puredis: spuredis + +!https://github.com/lp/puredis/raw/master/img/spuredis-help.png! + +bc. subscriber: list subscribe X 1 +subscriber: list subscribe NORTH 2 +subscriber: list subscribe CHANNEL_Z 3 +subscriber: list unsubscribe X 2 +publisher: 1 +subscriber: list message CHANNEL_Z BEEEZZZZ +publisher: 1 +subscriber: list message NORTH HOHOHO +publisher: 0 + +h2. Loading datasets from .csv files + +p. It is also possible to load datasets from csv files in a puredis object. The puredis object won't output anything while loading except for Redis error messages to the pd console. A status message is sent when loading completes. + +!https://github.com/lp/puredis/raw/master/img/puredis-csv-help.png! + +p. For csv loading, your files will need to be formatted in a way puredis can understand. (Lines preceded by # are ignored). + +h4. CSV Strings: + +bc. # KEY, VALUE +KEY1, VALUE1 +KEY2, VALUE2 +KEY3, VALUE3 + +h4. CSV Lists: + +bc. # LIST,ITEM1,ITEM2,... +MYLIST,A,B,C,D,E +MYLIST2,F,G,H,I,J + +h4. CSV Hashes: + +bc. PERSON,AGE,CITY +ANNA,26,Vancouver +PIETER,32,Dublin +SERGEI,41,Moscow + +h4. CSV Sets: + +bc. # SET,ITEM1,ITEM2,... +MYSET1,A,B,C,D,E +MYSET2,F,G,H,I,J + +h4. CSV Sorted Sets: + +bc. # ZSET, SCORE1, VALUE1, SCORE2, VALUE2, ... +MYZSET1,1,A,2,B,3,C,4,D,5,E +MYZSET2,1,F,2,G,3,H,4,I,5,J + diff --git a/apuredis.c b/apuredis.c new file mode 100644 index 0000000..d880cab --- /dev/null +++ b/apuredis.c @@ -0,0 +1,21 @@ +/* +Copyright (c) 2011 Louis-Philippe Perron + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "libpuredis.c" + diff --git a/libpuredis.c b/libpuredis.c new file mode 100644 index 0000000..ffd06e7 --- /dev/null +++ b/libpuredis.c @@ -0,0 +1,818 @@ +/* +Copyright (c) 2011 Louis-Philippe Perron + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "m_pd.h" +#include +#include +#include +#include +#include + +#define PUREDIS_MAJOR 0 +#define PUREDIS_MINOR 5 +#define PUREDIS_PATCH 3 + +#define MAX_ARRAY_SIZE 512 + +/************************************ + * Puredis * + * Puredata Redis External * + * * + ************************************/ +static t_class *puredis_class; + +/************************************ + * Apuredis * + * Async Redis External for PD * + * * + ************************************/ +static t_class *apuredis_class; + +/************************************ + * Spuredis * + * Redis Subscriber external for PD* + * * + ************************************/ +static t_class *spuredis_class; + +typedef struct _redis { + t_object x_obj; + redisContext * redis; + + char * r_host; + int r_port; + int out_count; + t_atom out[MAX_ARRAY_SIZE]; + + /* async vars */ + int async; + t_outlet *q_out; + int async_num; + int async_prev_num; + int async_run; + t_clock *async_clock; + + /* loader vars */ + t_symbol * ltype; + int lnumload; + int lnumerror; + char * lcmd; + char * lkey; + int lnew; + int lcount; + /* hash loader vars */ + int hargc; + char ** hheaders; + int hbufsize; + int hcount; + /* zset loader vars */ + int zcount; + char * zscore; +} t_redis; + +/* declarations */ +void puredis_setup(void); + +/* memory */ +static void freeVectorAndLengths(int argc, char ** vector, size_t * lengths); +void redis_free(t_redis *x); + +/* general */ +void *redis_new(t_symbol *s, int argc, t_atom *argv); +void redis_command(t_redis *x, t_symbol *s, int argc, t_atom *argv); +static void redis_postCommandAsync(t_redis * x, int argc, char ** vector, size_t * lengths); +static void redis_prepareOutList(t_redis *x, redisReply * reply); +static void redis_parseReply(t_redis *x, redisReply * reply); + +/* puredis */ +static void setup_puredis(void); +static void puredis_postCommandSync(t_redis * x, int argc, char ** vector, size_t * lengths); +static void puredis_csv_postCommand(t_redis * x, int argc, char ** vector, size_t * lengths); +static void puredis_csv_parse(t_redis *x, int argc, void *s, size_t i); +static void puredis_csv_cb1 (void *s, size_t i, void *userdata); +static void puredis_csv_cb2 (int c, void *userdata); +static void puredis_csv_init(t_redis *x); +static void puredis_csv_free(t_redis *x); +void puredis_csv(t_redis *x, t_symbol *s, int argc, t_atom *argv); + +/* async redis */ +static void setup_apuredis(void); +static void apuredis_yield(t_redis * x); +static void apuredis_q_out(t_redis * x); +static void apuredis_run(t_redis *x); +static void apuredis_schedule(t_redis *x); +void apuredis_bang(t_redis *x); +void apuredis_start(t_redis *x, t_symbol *s); +void apuredis_stop(t_redis *x, t_symbol *s); + +/* subscriber redis */ +static void setup_spuredis(void); +static void spuredis_run(t_redis *x); +static void spuredis_schedule(t_redis *x); +static void spuredis_manage(t_redis *x, t_symbol *s, int argc); +void spuredis_bang(t_redis *x); +void spuredis_start(t_redis *x, t_symbol *s); +void spuredis_stop(t_redis *x, t_symbol *s); +void spuredis_subscribe(t_redis *x, t_symbol *s, int argc, t_atom *argv); + + +/* implementation */ + +/* mandatory PD setup method */ +void puredis_setup(void) +{ + setup_puredis(); + setup_apuredis(); + setup_spuredis(); + post("Puredis %i.%i.%i (MIT) 2011 Louis-Philippe Perron ", PUREDIS_MAJOR, PUREDIS_MINOR, PUREDIS_PATCH); + post("Puredis: compiled for pd-%d.%d on %s %s", PD_MAJOR_VERSION, PD_MINOR_VERSION, __DATE__, __TIME__); +} + +/* memory management methods */ + +static void freeVectorAndLengths(int argc, char ** vector, size_t * lengths) +{ + int i; + for (i = 0; i < argc; i++) free(vector[i]); + free(vector); + free(lengths); +} + +void redis_free(t_redis *x) +{ + redisFree(x->redis); +} + +/* common methods */ + +/* constructor used by Puredis, Apuredis and Spuredis */ +void *redis_new(t_symbol *s, int argc, t_atom *argv) +{ + t_redis *x = NULL; + + int port = 6379; /* default port */ + char host[16] = "127.0.0.1"; /* default IP address */ + switch(argc){ /* parsing init arguments */ + default: + break; + case 2: + port = (int)atom_getint(argv+1); + case 1: + atom_string(argv, host, 16); + break; + case 0: + break; + } + + /* class initialisation */ + if (s == gensym("apuredis")) { + x = (t_redis*)pd_new(apuredis_class); + x->redis = redisConnectNonBlock((char*)host,port); + x->async = 1; x->async_num = 0; + x->async_prev_num = 0; x->async_run = 0; + x->async_clock = clock_new(x, (t_method)apuredis_run); + } else if (s == gensym("spuredis")) { + x = (t_redis*)pd_new(spuredis_class); + x->redis = redisConnectNonBlock((char*)host,port); + x->async_clock = clock_new(x, (t_method)spuredis_run); + x->async_num = 0; x->async_run = 0; + } else { + x = (t_redis*)pd_new(puredis_class); + x->redis = redisConnect((char*)host,port); + x->async = 0; + } + x->r_host = (char*)host; + x->r_port = port; + outlet_new(&x->x_obj, NULL); + if (x->async) { + x->q_out = outlet_new(&x->x_obj, &s_float); + } + + if (x->redis->err) { + post("could not connect to redis..."); + return NULL; + } + post("Puredis %i.%i.%i connected to redis host: %s port: %u", PUREDIS_MAJOR, PUREDIS_MINOR, PUREDIS_PATCH, x->r_host, x->r_port); + + return (void*)x; +} + +/* redis command parsing -- Puredis/Apuredis */ +void redis_command(t_redis *x, t_symbol *s, int argc, t_atom *argv) +{ + (void)s; + if (argc < 1) { + post("puredis: wrong command"); return; + } + + int i; + char ** vector = NULL; + size_t * lengths = NULL; + + if (((vector = malloc(argc*sizeof(char*))) == NULL) || ((lengths = malloc(argc*sizeof(size_t))) == NULL)) { + post("puredis: can not proceed!! Memory Error!"); return; + } + + for (i = 0; i < argc; i++) { + char cmdpart[256]; + atom_string(argv+i, cmdpart, 256); + + if ((vector[i] = malloc(strlen(cmdpart)+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + + strcpy(vector[i], (char*)cmdpart); + lengths[i] = strlen(vector[i]); + } + if (x->async) { + redis_postCommandAsync(x, argc, vector, lengths); + x->async_num++; + apuredis_q_out(x); + apuredis_schedule(x); + } else { + puredis_postCommandSync(x, argc, vector, lengths); + } +} + +/* sends command async to Redis */ +static void redis_postCommandAsync(t_redis * x, int argc, char ** vector, size_t * lengths) +{ + redisAppendCommandArgv(x->redis, argc, (const char**)vector, (const size_t *)lengths); + freeVectorAndLengths(argc, vector, lengths); +} + +/* recursive redis reply parsing as pd list */ +static void redis_prepareOutList(t_redis *x, redisReply * reply) +{ + if (reply->type == REDIS_REPLY_ERROR) { + SETSYMBOL(&x->out[x->out_count],gensym(reply->str)); + x->out_count++; + } else if (reply->type == REDIS_REPLY_STATUS) { + SETSYMBOL(&x->out[x->out_count],gensym(reply->str)); + x->out_count++; + } else if (reply->type == REDIS_REPLY_STRING) { + SETSYMBOL(&x->out[x->out_count],gensym(reply->str)); + x->out_count++; + } else if (reply->type == REDIS_REPLY_ARRAY) { + int i; + for (i = 0; i < (int)reply->elements; i++) { + redis_prepareOutList(x,reply->element[i]); + } + } else if (reply->type == REDIS_REPLY_INTEGER) { + SETFLOAT(&x->out[x->out_count],reply->integer); + x->out_count++; + } else if (reply->type == REDIS_REPLY_NIL) { + SETSYMBOL(&x->out[x->out_count],gensym("nil")); + x->out_count++; + } +} + +/* sends redis reply to outlet */ +static void redis_parseReply(t_redis *x, redisReply * reply) +{ + if (reply->type == REDIS_REPLY_ERROR) { + outlet_symbol(x->x_obj.ob_outlet, gensym(reply->str)); + } else if (reply->type == REDIS_REPLY_STATUS) { + outlet_symbol(x->x_obj.ob_outlet, gensym(reply->str)); + } else if (reply->type == REDIS_REPLY_STRING) { + outlet_symbol(x->x_obj.ob_outlet, gensym(reply->str)); + } else if (reply->type == REDIS_REPLY_ARRAY) { + x->out_count = 0; + redis_prepareOutList(x,reply); + outlet_list(x->x_obj.ob_outlet, &s_list, x->out_count, &x->out[0]); + } else if (reply->type == REDIS_REPLY_INTEGER) { + t_atom value; + SETFLOAT(&value, reply->integer); + outlet_float(x->x_obj.ob_outlet, atom_getfloat(&value)); + } else if (reply->type == REDIS_REPLY_NIL) { + outlet_symbol(x->x_obj.ob_outlet, gensym("nil")); + } + freeReplyObject(reply); +} + +/* puredis */ + +/* puredis setup method */ +static void setup_puredis(void) +{ + puredis_class = class_new(gensym("puredis"), + (t_newmethod)redis_new, + (t_method)redis_free, + sizeof(t_redis), + CLASS_DEFAULT, + A_GIMME, 0); + + class_addmethod(puredis_class, + (t_method)redis_command, gensym("command"), + A_GIMME, 0); + class_addmethod(puredis_class, + (t_method)puredis_csv, gensym("csv"), + A_GIMME, 0); + class_sethelpsymbol(puredis_class, gensym("puredis-help")); +} + +/* sends command sync to Redis */ +static void puredis_postCommandSync(t_redis * x, int argc, char ** vector, size_t * lengths) +{ + redisReply * reply = redisCommandArgv(x->redis, argc, (const char**)vector, (const size_t *)lengths); + freeVectorAndLengths(argc, vector, lengths); + redis_parseReply(x,reply); +} + +/* sends command sync to redis for csv data loading */ +static void puredis_csv_postCommand(t_redis * x, int argc, char ** vector, size_t * lengths) +{ + redisReply * reply = redisCommandArgv(x->redis, argc, (const char**)vector, (const size_t *)lengths); + freeVectorAndLengths(argc, vector, lengths); + x->lnumload++; + if (reply->type == REDIS_REPLY_ERROR) { + x->lnumerror++; + x->lnumload--; + post("Puredis csv load Redis error: %s", reply->str); + } + freeReplyObject(reply); +} + +/* parse csv data and loads it in redis */ +static void puredis_csv_parse(t_redis *x, int argc, void *s, size_t i) +{ + char ** vector = NULL; + size_t * lengths = NULL; + char * item = (char*)s; + + if (((vector = malloc(argc*sizeof(char*))) == NULL) || ((lengths = malloc(argc*sizeof(size_t))) == NULL)) { + post("puredis: can not proceed!! Memory Error!"); return; + } + + if ((vector[0] = malloc(strlen(x->lcmd)+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(vector[0], x->lcmd); + lengths[0] = strlen(vector[0]); + + if ((vector[1] = malloc(strlen(x->lkey)+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(vector[1], x->lkey); + lengths[1] = strlen(vector[1]); + + if (x->ltype == gensym("hash")) { + if ((vector[2] = malloc(strlen(x->hheaders[x->hcount])+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(vector[2], x->hheaders[x->hcount]); + lengths[2] = strlen(vector[2]); + } else if (x->ltype == gensym("zset")) { + if ((vector[2] = malloc(strlen(x->zscore)+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(vector[2], x->zscore); + lengths[2] = strlen(vector[2]); + } + + if ((vector[argc-1] = malloc(i+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(vector[argc-1], item); + lengths[argc-1] = i; + + puredis_csv_postCommand(x, argc, vector, lengths); +} + +/* libcsv callback for each values in csv data */ +static void puredis_csv_cb1 (void *s, size_t i, void *userdata) +{ + t_redis *x = (t_redis *)userdata; + + if (x->lnew && x->lkey[0] == '#') return; + + if (x->ltype == gensym("hash")) { + if (x->lcount > 0) { + if (x->lnew) { + x->hcount++; + puredis_csv_parse(x, 4, s,i); + } else { + x->lnew = 1; + x->hcount = 0; + free(x->lkey); + if ((x->lkey = malloc(i+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(x->lkey, (char*)s); + } + } else { + if ((x->hheaders[x->hcount] = malloc(i+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(x->hheaders[x->hcount], (char*)s); + x->hcount++; + x->hargc++; + if (x->hbufsize == x->hargc) { + x->hbufsize += x->hbufsize / 2; + x->hheaders = realloc(x->hheaders, x->hbufsize * sizeof(char*)); + } + } + } else if (x->ltype == gensym("zset")) { + if (x->lnew) { + if (x->zcount) { + puredis_csv_parse(x, 4, s,i); + x->zcount = 0; + } else { + x->zcount = 1; + free(x->zscore); + if ((x->zscore = malloc(i+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(x->zscore, (char*)s); + } + } else { + x->lnew = 1; + x->zcount = 0; + free(x->lkey); + if ((x->lkey = malloc(i+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(x->lkey, (char*)s); + } + } else { + if (x->lnew) { + puredis_csv_parse(x, 3, s,i); + } else { + x->lnew = 1; + + free(x->lkey); + if ((x->lkey = malloc(i+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(x->lkey, (char*)s); + } + } +} + +/* libcsv callback for each line change in csv data */ +static void puredis_csv_cb2 (int c, void *userdata) +{ + (void)c; + t_redis *x = (t_redis *)userdata; + x->lnew = 0; + x->lcount++; +} + +/* libcsv csv data parsing initialization */ +static void puredis_csv_init(t_redis *x) +{ + x->lcount = 0; x->lnew = 0; x->lnumload = 0; x->lnumerror = 0; + if ((x->lkey = malloc(8)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + + char * cmd = NULL; + if (x->ltype == gensym("string")) { + cmd = "SET"; + } else if (x->ltype == gensym("list")) { + cmd = "RPUSH"; + } else if (x->ltype == gensym("set")) { + cmd = "SADD"; + } else if (x->ltype == gensym("zset")) { + if ((x->zscore = malloc(8)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + cmd = "ZADD"; + } else if (x->ltype == gensym("hash")) { + x->hargc = 0; + x->hcount = 0; + x->hbufsize = 2; + x->hheaders = NULL; + if ((x->hheaders = calloc(x->hbufsize, sizeof(char*))) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + cmd = "HSET"; + } + + if ((x->lcmd = malloc(strlen(cmd)+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(x->lcmd, cmd); +} + +/* libcsv cleanup */ +static void puredis_csv_free(t_redis *x) +{ + free(x->lkey); + free(x->lcmd); + if (x->ltype == gensym("hash")) { + int i; + for (i = 0; i < x->hargc; i++) free(x->hheaders[i]); + free(x->hheaders); + } else if (x->ltype == gensym("zset")) { + free(x->zscore); + } +} + +/* puredis csv data load method */ +void puredis_csv(t_redis *x, t_symbol *s, int argc, t_atom *argv) +{ + (void)s; (void)argc; + char buf[1024]; size_t i; char filename[256]; + atom_string(argv, filename, 256); + x->ltype = atom_getsymbol(argv+1); + + struct csv_parser p; + FILE *csvfile = NULL; + csv_init(&p, 0); + csv_set_opts(&p, CSV_APPEND_NULL); + puredis_csv_init(x); + + csvfile = fopen((const char*)filename, "rb"); + if (csvfile == NULL) { + post("Puredis failed to open csv file: %s", csvfile); + puredis_csv_free(x); + return; + } + + while ((i=fread(buf, 1, 1024, csvfile)) > 0) { + if (csv_parse(&p, buf, i, puredis_csv_cb1, puredis_csv_cb2, x) != i) { + post("Puredis error parsing csv file: %s", csv_strerror(csv_error(&p))); + fclose(csvfile); + puredis_csv_free(x); + return; + } + } + + csv_fini(&p, puredis_csv_cb1, puredis_csv_cb2, x); + csv_free(&p); + puredis_csv_free(x); + + t_atom stats[7]; + SETSYMBOL(&stats[0], gensym("csv-load-status")); + SETSYMBOL(&stats[1], gensym("lines")); + SETFLOAT(&stats[2], x->lcount); + SETSYMBOL(&stats[3], gensym("entries")); + SETFLOAT(&stats[4], x->lnumload); + SETSYMBOL(&stats[5], gensym("error")); + SETFLOAT(&stats[6], x->lnumerror); + outlet_list(x->x_obj.ob_outlet, &s_list, 7, &stats[0]); +} + +/* apuredis */ + +/* apuredis setup method */ +static void setup_apuredis(void) +{ + apuredis_class = class_new(gensym("apuredis"), + (t_newmethod)redis_new, + (t_method)redis_free, + sizeof(t_redis), + CLASS_DEFAULT, + A_GIMME, 0); + + class_addbang(apuredis_class,apuredis_bang); + class_addmethod(apuredis_class, + (t_method)apuredis_stop, gensym("stop"),0); + class_addmethod(apuredis_class, + (t_method)apuredis_start, gensym("start"),0); + class_addmethod(apuredis_class, + (t_method)redis_command, gensym("command"), + A_GIMME, 0); + class_sethelpsymbol(apuredis_class, gensym("apuredis-help")); +} + +/* apuredis data yielding callback */ +static void apuredis_yield(t_redis * x) +{ + if (x->async_num > 0) { + void * tmpreply = NULL; + if ( redisGetReply(x->redis, &tmpreply) == REDIS_ERR) return; + if (tmpreply == NULL) { + int wdone = 0; + if (redisBufferWrite(x->redis,&wdone) == REDIS_ERR) return; + + if (redisBufferRead(x->redis) == REDIS_ERR) + return; + if (redisGetReplyFromReader(x->redis,&tmpreply) == REDIS_ERR) + return; + + if (tmpreply != NULL) { + x->async_num--; + redisReply * reply = (redisReply*)tmpreply; + redis_parseReply(x, reply); + } + } else { + x->async_num--; + redisReply * reply = (redisReply*)tmpreply; + redis_parseReply(x, reply); + } + } + + if (x->async_prev_num != x->async_num) { + x->async_prev_num = x->async_num; + apuredis_q_out(x); + } +} + +/* apuredis outputs queue lenght on second outlet */ +static void apuredis_q_out(t_redis * x) +{ + t_atom value; + SETFLOAT(&value, x->async_num); + outlet_float(x->q_out, atom_getfloat(&value)); +} + +/* apuredis scheduled callback */ +static void apuredis_run(t_redis *x) +{ + apuredis_yield(x); + apuredis_schedule(x); +} + +/* apuredis (re-)scheduling method */ +static void apuredis_schedule(t_redis *x) +{ + if ((!x->async_run) || x->async_num < 1) { + clock_unset(x->async_clock); + } else if (x->async_run && x->async_num > 0) { + clock_delay(x->async_clock, 1); + } +} + +/* apuredis manual yielding method w/bang */ +void apuredis_bang(t_redis *x) +{ + apuredis_yield(x); +} + +/* apuredis start message method */ +void apuredis_start(t_redis *x, t_symbol *s) +{ + (void)s; + x->async_run = 1; + apuredis_schedule(x); +} + +/* apuredis stop message method */ +void apuredis_stop(t_redis *x, t_symbol *s) +{ + (void)s; + x->async_run = 0; + apuredis_schedule(x); +} + +/* spuredis */ + +/* spuredis setup method */ +static void setup_spuredis(void) +{ + spuredis_class = class_new(gensym("spuredis"), + (t_newmethod)redis_new, + (t_method)redis_free, + sizeof(t_redis), + CLASS_DEFAULT, + A_GIMME, 0); + + class_addbang(spuredis_class,spuredis_bang); + class_addmethod(spuredis_class, + (t_method)spuredis_stop, gensym("stop"),0); + class_addmethod(spuredis_class, + (t_method)spuredis_start, gensym("start"),0); + class_addmethod(spuredis_class, + (t_method)spuredis_subscribe, gensym("subscribe"), + A_GIMME, 0); + class_addmethod(spuredis_class, + (t_method)spuredis_subscribe, gensym("unsubscribe"), + A_GIMME, 0); + class_sethelpsymbol(spuredis_class, gensym("spuredis-help")); +} + +/* spuredis scheduled callback */ +static void spuredis_run(t_redis *x) +{ + if (x->async_run) { + void * tmpreply = NULL; + if ( redisGetReply(x->redis, &tmpreply) == REDIS_ERR) return; + if (tmpreply == NULL) { + int wdone = 0; + if (redisBufferWrite(x->redis,&wdone) == REDIS_ERR) return; + + if (redisBufferRead(x->redis) == REDIS_ERR) + return; + if (redisGetReplyFromReader(x->redis,&tmpreply) == REDIS_ERR) + return; + + if (tmpreply != NULL) { + redisReply * reply = (redisReply*)tmpreply; + redis_parseReply(x, reply); + } + } else { + redisReply * reply = (redisReply*)tmpreply; + redis_parseReply(x, reply); + } + + clock_delay(x->async_clock, 100); + } +} + +/* spuredis (re-)scheduling method */ +static void spuredis_schedule(t_redis *x) +{ + if (x->async_run && x->async_num < 1) { + x->async_run = 0; + clock_unset(x->async_clock); + } else if ((!x->async_run) && x->async_num > 0) { + x->async_run = 1; + clock_delay(x->async_clock, 0); + } +} + +/* spuredis subscriptions management */ +static void spuredis_manage(t_redis *x, t_symbol *s, int argc) +{ + if (s == gensym("subscribe")) { + x->async_num = x->async_num + argc; + } else { + x->async_num = x->async_num - argc; + } + + spuredis_schedule(x); +} + +/* apuredis alternate start method with bang */ +void spuredis_bang(t_redis *x) +{ + spuredis_schedule(x); +} + +/* apuredis start message method */ +void spuredis_start(t_redis *x, t_symbol *s) +{ + (void)s; + spuredis_schedule(x); +} + +/* apuredis stop message method */ +void spuredis_stop(t_redis *x, t_symbol *s) +{ + (void)s; + x->async_run = 0; +} + +/* apuredis subscribe message method */ +void spuredis_subscribe(t_redis *x, t_symbol *s, int argc, t_atom *argv) +{ + if (argc < 1) { + post("spuredis: subscribe need at least one channel"); return; + } + + int i; + char ** vector = NULL; + size_t * lengths = NULL; + + if (((vector = malloc((argc+1)*sizeof(char*))) == NULL) || ((lengths = malloc((argc+1)*sizeof(size_t))) == NULL)) { + post("puredis: can not proceed!! Memory Error!"); return; + } + + /* setting subscribe or unscribe as redis first command part */ + if (s == gensym("subscribe")) { + if ((vector[0] = malloc(strlen("subscribe")+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(vector[0], "subscribe"); + lengths[0] = strlen(vector[0]); + } else { + if ((vector[0] = malloc(strlen("unsubscribe")+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + strcpy(vector[0], "unsubscribe"); + lengths[0] = strlen(vector[0]); + } + + for (i = 0; i < argc; i++) { + char cmdpart[256]; + atom_string(argv+i, cmdpart, 256); + + if ((vector[i+1] = malloc(strlen(cmdpart)+1)) == NULL) { + post("puredis: can not proceed!! Memory Error!"); return; + } + + strcpy(vector[i+1], (char*)cmdpart); + lengths[i+1] = strlen(vector[i+1]); + } + redis_postCommandAsync(x, argc+1, vector, lengths); + spuredis_manage(x, s, argc); +} + diff --git a/spuredis.c b/spuredis.c new file mode 100644 index 0000000..d880cab --- /dev/null +++ b/spuredis.c @@ -0,0 +1,21 @@ +/* +Copyright (c) 2011 Louis-Philippe Perron + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "libpuredis.c" + From 5ac2ecde6d957ff435e161c85de8c75955b7aa77 Mon Sep 17 00:00:00 2001 From: Thomas Mayer Date: Tue, 17 Apr 2012 23:51:41 +0200 Subject: [PATCH 4/4] Using shared library for puredis.c as well. --- puredis.c | 801 +----------------------------------------------------- 1 file changed, 1 insertion(+), 800 deletions(-) diff --git a/puredis.c b/puredis.c index 13f05a1..d880cab 100644 --- a/puredis.c +++ b/puredis.c @@ -17,804 +17,5 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "m_pd.h" -#include -#include -#include -#include -#include - -#define PUREDIS_MAJOR 0 -#define PUREDIS_MINOR 5 -#define PUREDIS_PATCH 3 -#define PD_MAJOR_VERSION 0 -#define PD_MINOR_VERSION 42 - -#define MAX_ARRAY_SIZE 512 - -/************************************ - * Puredis * - * Puredata Redis External * - * * - ************************************/ -static t_class *puredis_class; - -/************************************ - * Apuredis * - * Async Redis External for PD * - * * - ************************************/ -static t_class *apuredis_class; - -/************************************ - * Spuredis * - * Redis Subscriber external for PD* - * * - ************************************/ -static t_class *spuredis_class; - -typedef struct _redis { - t_object x_obj; - redisContext * redis; - - char * r_host; - int r_port; - int out_count; - t_atom out[MAX_ARRAY_SIZE]; - - /* async vars */ - int async; - t_outlet *q_out; - int async_num; - int async_prev_num; - int async_run; - t_clock *async_clock; - - /* loader vars */ - t_symbol * ltype; - int lnumload; - int lnumerror; - char * lcmd; - char * lkey; - int lnew; - int lcount; - /* hash loader vars */ - int hargc; - char ** hheaders; - int hbufsize; - int hcount; - /* zset loader vars */ - int zcount; - char * zscore; -} t_redis; - -/* declarations */ -void puredis_setup(void); - -/* memory */ -static void freeVectorAndLengths(int argc, char ** vector, size_t * lengths); -void redis_free(t_redis *x); - -/* general */ -void *redis_new(t_symbol *s, int argc, t_atom *argv); -void redis_command(t_redis *x, t_symbol *s, int argc, t_atom *argv); -static void redis_postCommandAsync(t_redis * x, int argc, char ** vector, size_t * lengths); -static void redis_prepareOutList(t_redis *x, redisReply * reply); -static void redis_parseReply(t_redis *x, redisReply * reply); - -/* puredis */ -static void setup_puredis(void); -static void puredis_postCommandSync(t_redis * x, int argc, char ** vector, size_t * lengths); -static void puredis_csv_postCommand(t_redis * x, int argc, char ** vector, size_t * lengths); -static void puredis_csv_parse(t_redis *x, int argc, void *s, size_t i); -static void puredis_csv_cb1 (void *s, size_t i, void *userdata); -static void puredis_csv_cb2 (int c, void *userdata); -static void puredis_csv_init(t_redis *x); -static void puredis_csv_free(t_redis *x); -void puredis_csv(t_redis *x, t_symbol *s, int argc, t_atom *argv); - -/* async redis */ -static void setup_apuredis(void); -static void apuredis_yield(t_redis * x); -static void apuredis_q_out(t_redis * x); -static void apuredis_run(t_redis *x); -static void apuredis_schedule(t_redis *x); -void apuredis_bang(t_redis *x); -void apuredis_start(t_redis *x, t_symbol *s); -void apuredis_stop(t_redis *x, t_symbol *s); - -/* subscriber redis */ -static void setup_spuredis(void); -static void spuredis_run(t_redis *x); -static void spuredis_schedule(t_redis *x); -static void spuredis_manage(t_redis *x, t_symbol *s, int argc); -void spuredis_bang(t_redis *x); -void spuredis_start(t_redis *x, t_symbol *s); -void spuredis_stop(t_redis *x, t_symbol *s); -void spuredis_subscribe(t_redis *x, t_symbol *s, int argc, t_atom *argv); - - -/* implementation */ - -/* mandatory PD setup method */ -void puredis_setup(void) -{ - setup_puredis(); - setup_apuredis(); - setup_spuredis(); - post("Puredis %i.%i.%i (MIT) 2011 Louis-Philippe Perron ", PUREDIS_MAJOR, PUREDIS_MINOR, PUREDIS_PATCH); - post("Puredis: compiled for pd-%d.%d on %s %s", PD_MAJOR_VERSION, PD_MINOR_VERSION, __DATE__, __TIME__); -} - -/* memory management methods */ - -static void freeVectorAndLengths(int argc, char ** vector, size_t * lengths) -{ - int i; - for (i = 0; i < argc; i++) free(vector[i]); - free(vector); - free(lengths); -} - -void redis_free(t_redis *x) -{ - redisFree(x->redis); -} - -/* common methods */ - -/* constructor used by Puredis, Apuredis and Spuredis */ -void *redis_new(t_symbol *s, int argc, t_atom *argv) -{ - t_redis *x = NULL; - - int port = 6379; /* default port */ - char host[16] = "127.0.0.1"; /* default IP address */ - switch(argc){ /* parsing init arguments */ - default: - break; - case 2: - port = (int)atom_getint(argv+1); - case 1: - atom_string(argv, host, 16); - break; - case 0: - break; - } - - /* class initialisation */ - if (s == gensym("apuredis")) { - x = (t_redis*)pd_new(apuredis_class); - x->redis = redisConnectNonBlock((char*)host,port); - x->async = 1; x->async_num = 0; - x->async_prev_num = 0; x->async_run = 0; - x->async_clock = clock_new(x, (t_method)apuredis_run); - } else if (s == gensym("spuredis")) { - x = (t_redis*)pd_new(spuredis_class); - x->redis = redisConnectNonBlock((char*)host,port); - x->async_clock = clock_new(x, (t_method)spuredis_run); - x->async_num = 0; x->async_run = 0; - } else { - x = (t_redis*)pd_new(puredis_class); - x->redis = redisConnect((char*)host,port); - x->async = 0; - } - x->r_host = (char*)host; - x->r_port = port; - outlet_new(&x->x_obj, NULL); - if (x->async) { - x->q_out = outlet_new(&x->x_obj, &s_float); - } - - if (x->redis->err) { - post("could not connect to redis..."); - return NULL; - } - post("Puredis %i.%i.%i connected to redis host: %s port: %u", PUREDIS_MAJOR, PUREDIS_MINOR, PUREDIS_PATCH, x->r_host, x->r_port); - - return (void*)x; -} - -/* redis command parsing -- Puredis/Apuredis */ -void redis_command(t_redis *x, t_symbol *s, int argc, t_atom *argv) -{ - (void)s; - if (argc < 1) { - post("puredis: wrong command"); return; - } - - int i; - char ** vector = NULL; - size_t * lengths = NULL; - - if (((vector = malloc(argc*sizeof(char*))) == NULL) || ((lengths = malloc(argc*sizeof(size_t))) == NULL)) { - post("puredis: can not proceed!! Memory Error!"); return; - } - - for (i = 0; i < argc; i++) { - char cmdpart[256]; - atom_string(argv+i, cmdpart, 256); - - if ((vector[i] = malloc(strlen(cmdpart)+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - - strcpy(vector[i], (char*)cmdpart); - lengths[i] = strlen(vector[i]); - } - if (x->async) { - redis_postCommandAsync(x, argc, vector, lengths); - x->async_num++; - apuredis_q_out(x); - apuredis_schedule(x); - } else { - puredis_postCommandSync(x, argc, vector, lengths); - } -} - -/* sends command async to Redis */ -static void redis_postCommandAsync(t_redis * x, int argc, char ** vector, size_t * lengths) -{ - redisAppendCommandArgv(x->redis, argc, (const char**)vector, (const size_t *)lengths); - freeVectorAndLengths(argc, vector, lengths); -} - -/* recursive redis reply parsing as pd list */ -static void redis_prepareOutList(t_redis *x, redisReply * reply) -{ - if (reply->type == REDIS_REPLY_ERROR) { - SETSYMBOL(&x->out[x->out_count],gensym(reply->str)); - x->out_count++; - } else if (reply->type == REDIS_REPLY_STATUS) { - SETSYMBOL(&x->out[x->out_count],gensym(reply->str)); - x->out_count++; - } else if (reply->type == REDIS_REPLY_STRING) { - SETSYMBOL(&x->out[x->out_count],gensym(reply->str)); - x->out_count++; - } else if (reply->type == REDIS_REPLY_ARRAY) { - int i; - for (i = 0; i < (int)reply->elements; i++) { - redis_prepareOutList(x,reply->element[i]); - } - } else if (reply->type == REDIS_REPLY_INTEGER) { - SETFLOAT(&x->out[x->out_count],reply->integer); - x->out_count++; - } else if (reply->type == REDIS_REPLY_NIL) { - SETSYMBOL(&x->out[x->out_count],gensym("nil")); - x->out_count++; - } -} - -/* sends redis reply to outlet */ -static void redis_parseReply(t_redis *x, redisReply * reply) -{ - if (reply->type == REDIS_REPLY_ERROR) { - outlet_symbol(x->x_obj.ob_outlet, gensym(reply->str)); - } else if (reply->type == REDIS_REPLY_STATUS) { - outlet_symbol(x->x_obj.ob_outlet, gensym(reply->str)); - } else if (reply->type == REDIS_REPLY_STRING) { - outlet_symbol(x->x_obj.ob_outlet, gensym(reply->str)); - } else if (reply->type == REDIS_REPLY_ARRAY) { - x->out_count = 0; - redis_prepareOutList(x,reply); - outlet_list(x->x_obj.ob_outlet, &s_list, x->out_count, &x->out[0]); - } else if (reply->type == REDIS_REPLY_INTEGER) { - t_atom value; - SETFLOAT(&value, reply->integer); - outlet_float(x->x_obj.ob_outlet, atom_getfloat(&value)); - } else if (reply->type == REDIS_REPLY_NIL) { - outlet_symbol(x->x_obj.ob_outlet, gensym("nil")); - } - freeReplyObject(reply); -} - -/* puredis */ - -/* puredis setup method */ -static void setup_puredis(void) -{ - puredis_class = class_new(gensym("puredis"), - (t_newmethod)redis_new, - (t_method)redis_free, - sizeof(t_redis), - CLASS_DEFAULT, - A_GIMME, 0); - - class_addmethod(puredis_class, - (t_method)redis_command, gensym("command"), - A_GIMME, 0); - class_addmethod(puredis_class, - (t_method)puredis_csv, gensym("csv"), - A_GIMME, 0); - class_sethelpsymbol(puredis_class, gensym("puredis-help")); -} - -/* sends command sync to Redis */ -static void puredis_postCommandSync(t_redis * x, int argc, char ** vector, size_t * lengths) -{ - redisReply * reply = redisCommandArgv(x->redis, argc, (const char**)vector, (const size_t *)lengths); - freeVectorAndLengths(argc, vector, lengths); - redis_parseReply(x,reply); -} - -/* sends command sync to redis for csv data loading */ -static void puredis_csv_postCommand(t_redis * x, int argc, char ** vector, size_t * lengths) -{ - redisReply * reply = redisCommandArgv(x->redis, argc, (const char**)vector, (const size_t *)lengths); - freeVectorAndLengths(argc, vector, lengths); - x->lnumload++; - if (reply->type == REDIS_REPLY_ERROR) { - x->lnumerror++; - x->lnumload--; - post("Puredis csv load Redis error: %s", reply->str); - } - freeReplyObject(reply); -} - -/* parse csv data and loads it in redis */ -static void puredis_csv_parse(t_redis *x, int argc, void *s, size_t i) -{ - char ** vector = NULL; - size_t * lengths = NULL; - char * item = (char*)s; - - if (((vector = malloc(argc*sizeof(char*))) == NULL) || ((lengths = malloc(argc*sizeof(size_t))) == NULL)) { - post("puredis: can not proceed!! Memory Error!"); return; - } - - if ((vector[0] = malloc(strlen(x->lcmd)+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(vector[0], x->lcmd); - lengths[0] = strlen(vector[0]); - - if ((vector[1] = malloc(strlen(x->lkey)+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(vector[1], x->lkey); - lengths[1] = strlen(vector[1]); - - if (x->ltype == gensym("hash")) { - if ((vector[2] = malloc(strlen(x->hheaders[x->hcount])+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(vector[2], x->hheaders[x->hcount]); - lengths[2] = strlen(vector[2]); - } else if (x->ltype == gensym("zset")) { - if ((vector[2] = malloc(strlen(x->zscore)+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(vector[2], x->zscore); - lengths[2] = strlen(vector[2]); - } - - if ((vector[argc-1] = malloc(i+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(vector[argc-1], item); - lengths[argc-1] = i; - - puredis_csv_postCommand(x, argc, vector, lengths); -} - -/* libcsv callback for each values in csv data */ -static void puredis_csv_cb1 (void *s, size_t i, void *userdata) -{ - t_redis *x = (t_redis *)userdata; - - if (x->lnew && x->lkey[0] == '#') return; - - if (x->ltype == gensym("hash")) { - if (x->lcount > 0) { - if (x->lnew) { - x->hcount++; - puredis_csv_parse(x, 4, s,i); - } else { - x->lnew = 1; - x->hcount = 0; - free(x->lkey); - if ((x->lkey = malloc(i+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(x->lkey, (char*)s); - } - } else { - if ((x->hheaders[x->hcount] = malloc(i+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(x->hheaders[x->hcount], (char*)s); - x->hcount++; - x->hargc++; - if (x->hbufsize == x->hargc) { - x->hbufsize += x->hbufsize / 2; - x->hheaders = realloc(x->hheaders, x->hbufsize * sizeof(char*)); - } - } - } else if (x->ltype == gensym("zset")) { - if (x->lnew) { - if (x->zcount) { - puredis_csv_parse(x, 4, s,i); - x->zcount = 0; - } else { - x->zcount = 1; - free(x->zscore); - if ((x->zscore = malloc(i+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(x->zscore, (char*)s); - } - } else { - x->lnew = 1; - x->zcount = 0; - free(x->lkey); - if ((x->lkey = malloc(i+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(x->lkey, (char*)s); - } - } else { - if (x->lnew) { - puredis_csv_parse(x, 3, s,i); - } else { - x->lnew = 1; - - free(x->lkey); - if ((x->lkey = malloc(i+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(x->lkey, (char*)s); - } - } -} - -/* libcsv callback for each line change in csv data */ -static void puredis_csv_cb2 (int c, void *userdata) -{ - (void)c; - t_redis *x = (t_redis *)userdata; - x->lnew = 0; - x->lcount++; -} - -/* libcsv csv data parsing initialization */ -static void puredis_csv_init(t_redis *x) -{ - x->lcount = 0; x->lnew = 0; x->lnumload = 0; x->lnumerror = 0; - if ((x->lkey = malloc(8)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - - char * cmd = NULL; - if (x->ltype == gensym("string")) { - cmd = "SET"; - } else if (x->ltype == gensym("list")) { - cmd = "RPUSH"; - } else if (x->ltype == gensym("set")) { - cmd = "SADD"; - } else if (x->ltype == gensym("zset")) { - if ((x->zscore = malloc(8)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - cmd = "ZADD"; - } else if (x->ltype == gensym("hash")) { - x->hargc = 0; - x->hcount = 0; - x->hbufsize = 2; - x->hheaders = NULL; - if ((x->hheaders = calloc(x->hbufsize, sizeof(char*))) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - cmd = "HSET"; - } - - if ((x->lcmd = malloc(strlen(cmd)+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(x->lcmd, cmd); -} - -/* libcsv cleanup */ -static void puredis_csv_free(t_redis *x) -{ - free(x->lkey); - free(x->lcmd); - if (x->ltype == gensym("hash")) { - int i; - for (i = 0; i < x->hargc; i++) free(x->hheaders[i]); - free(x->hheaders); - } else if (x->ltype == gensym("zset")) { - free(x->zscore); - } -} - -/* puredis csv data load method */ -void puredis_csv(t_redis *x, t_symbol *s, int argc, t_atom *argv) -{ - (void)s; (void)argc; - char buf[1024]; size_t i; char filename[256]; - atom_string(argv, filename, 256); - x->ltype = atom_getsymbol(argv+1); - - struct csv_parser p; - FILE *csvfile = NULL; - csv_init(&p, 0); - csv_set_opts(&p, CSV_APPEND_NULL); - puredis_csv_init(x); - - csvfile = fopen((const char*)filename, "rb"); - if (csvfile == NULL) { - post("Puredis failed to open csv file: %s", csvfile); - puredis_csv_free(x); - return; - } - - while ((i=fread(buf, 1, 1024, csvfile)) > 0) { - if (csv_parse(&p, buf, i, puredis_csv_cb1, puredis_csv_cb2, x) != i) { - post("Puredis error parsing csv file: %s", csv_strerror(csv_error(&p))); - fclose(csvfile); - puredis_csv_free(x); - return; - } - } - - csv_fini(&p, puredis_csv_cb1, puredis_csv_cb2, x); - csv_free(&p); - puredis_csv_free(x); - - t_atom stats[7]; - SETSYMBOL(&stats[0], gensym("csv-load-status")); - SETSYMBOL(&stats[1], gensym("lines")); - SETFLOAT(&stats[2], x->lcount); - SETSYMBOL(&stats[3], gensym("entries")); - SETFLOAT(&stats[4], x->lnumload); - SETSYMBOL(&stats[5], gensym("error")); - SETFLOAT(&stats[6], x->lnumerror); - outlet_list(x->x_obj.ob_outlet, &s_list, 7, &stats[0]); -} - -/* apuredis */ - -/* apuredis setup method */ -static void setup_apuredis(void) -{ - apuredis_class = class_new(gensym("apuredis"), - (t_newmethod)redis_new, - (t_method)redis_free, - sizeof(t_redis), - CLASS_DEFAULT, - A_GIMME, 0); - - class_addbang(apuredis_class,apuredis_bang); - class_addmethod(apuredis_class, - (t_method)apuredis_stop, gensym("stop"),0); - class_addmethod(apuredis_class, - (t_method)apuredis_start, gensym("start"),0); - class_addmethod(apuredis_class, - (t_method)redis_command, gensym("command"), - A_GIMME, 0); - class_sethelpsymbol(apuredis_class, gensym("apuredis-help")); -} - -/* apuredis data yielding callback */ -static void apuredis_yield(t_redis * x) -{ - if (x->async_num > 0) { - void * tmpreply = NULL; - if ( redisGetReply(x->redis, &tmpreply) == REDIS_ERR) return; - if (tmpreply == NULL) { - int wdone = 0; - if (redisBufferWrite(x->redis,&wdone) == REDIS_ERR) return; - - if (redisBufferRead(x->redis) == REDIS_ERR) - return; - if (redisGetReplyFromReader(x->redis,&tmpreply) == REDIS_ERR) - return; - - if (tmpreply != NULL) { - x->async_num--; - redisReply * reply = (redisReply*)tmpreply; - redis_parseReply(x, reply); - } - } else { - x->async_num--; - redisReply * reply = (redisReply*)tmpreply; - redis_parseReply(x, reply); - } - } - - if (x->async_prev_num != x->async_num) { - x->async_prev_num = x->async_num; - apuredis_q_out(x); - } -} - -/* apuredis outputs queue lenght on second outlet */ -static void apuredis_q_out(t_redis * x) -{ - t_atom value; - SETFLOAT(&value, x->async_num); - outlet_float(x->q_out, atom_getfloat(&value)); -} - -/* apuredis scheduled callback */ -static void apuredis_run(t_redis *x) -{ - apuredis_yield(x); - apuredis_schedule(x); -} - -/* apuredis (re-)scheduling method */ -static void apuredis_schedule(t_redis *x) -{ - if ((!x->async_run) || x->async_num < 1) { - clock_unset(x->async_clock); - } else if (x->async_run && x->async_num > 0) { - clock_delay(x->async_clock, 1); - } -} - -/* apuredis manual yielding method w/bang */ -void apuredis_bang(t_redis *x) -{ - apuredis_yield(x); -} - -/* apuredis start message method */ -void apuredis_start(t_redis *x, t_symbol *s) -{ - (void)s; - x->async_run = 1; - apuredis_schedule(x); -} - -/* apuredis stop message method */ -void apuredis_stop(t_redis *x, t_symbol *s) -{ - (void)s; - x->async_run = 0; - apuredis_schedule(x); -} - -/* spuredis */ - -/* spuredis setup method */ -static void setup_spuredis(void) -{ - spuredis_class = class_new(gensym("spuredis"), - (t_newmethod)redis_new, - (t_method)redis_free, - sizeof(t_redis), - CLASS_DEFAULT, - A_GIMME, 0); - - class_addbang(spuredis_class,spuredis_bang); - class_addmethod(spuredis_class, - (t_method)spuredis_stop, gensym("stop"),0); - class_addmethod(spuredis_class, - (t_method)spuredis_start, gensym("start"),0); - class_addmethod(spuredis_class, - (t_method)spuredis_subscribe, gensym("subscribe"), - A_GIMME, 0); - class_addmethod(spuredis_class, - (t_method)spuredis_subscribe, gensym("unsubscribe"), - A_GIMME, 0); - class_sethelpsymbol(spuredis_class, gensym("spuredis-help")); -} - -/* spuredis scheduled callback */ -static void spuredis_run(t_redis *x) -{ - if (x->async_run) { - void * tmpreply = NULL; - if ( redisGetReply(x->redis, &tmpreply) == REDIS_ERR) return; - if (tmpreply == NULL) { - int wdone = 0; - if (redisBufferWrite(x->redis,&wdone) == REDIS_ERR) return; - - if (redisBufferRead(x->redis) == REDIS_ERR) - return; - if (redisGetReplyFromReader(x->redis,&tmpreply) == REDIS_ERR) - return; - - if (tmpreply != NULL) { - redisReply * reply = (redisReply*)tmpreply; - redis_parseReply(x, reply); - } - } else { - redisReply * reply = (redisReply*)tmpreply; - redis_parseReply(x, reply); - } - - clock_delay(x->async_clock, 100); - } -} - -/* spuredis (re-)scheduling method */ -static void spuredis_schedule(t_redis *x) -{ - if (x->async_run && x->async_num < 1) { - x->async_run = 0; - clock_unset(x->async_clock); - } else if ((!x->async_run) && x->async_num > 0) { - x->async_run = 1; - clock_delay(x->async_clock, 0); - } -} - -/* spuredis subscriptions management */ -static void spuredis_manage(t_redis *x, t_symbol *s, int argc) -{ - if (s == gensym("subscribe")) { - x->async_num = x->async_num + argc; - } else { - x->async_num = x->async_num - argc; - } - - spuredis_schedule(x); -} - -/* apuredis alternate start method with bang */ -void spuredis_bang(t_redis *x) -{ - spuredis_schedule(x); -} - -/* apuredis start message method */ -void spuredis_start(t_redis *x, t_symbol *s) -{ - (void)s; - spuredis_schedule(x); -} - -/* apuredis stop message method */ -void spuredis_stop(t_redis *x, t_symbol *s) -{ - (void)s; - x->async_run = 0; -} - -/* apuredis subscribe message method */ -void spuredis_subscribe(t_redis *x, t_symbol *s, int argc, t_atom *argv) -{ - if (argc < 1) { - post("spuredis: subscribe need at least one channel"); return; - } - - int i; - char ** vector = NULL; - size_t * lengths = NULL; - - if (((vector = malloc((argc+1)*sizeof(char*))) == NULL) || ((lengths = malloc((argc+1)*sizeof(size_t))) == NULL)) { - post("puredis: can not proceed!! Memory Error!"); return; - } - - /* setting subscribe or unscribe as redis first command part */ - if (s == gensym("subscribe")) { - if ((vector[0] = malloc(strlen("subscribe")+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(vector[0], "subscribe"); - lengths[0] = strlen(vector[0]); - } else { - if ((vector[0] = malloc(strlen("unsubscribe")+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - strcpy(vector[0], "unsubscribe"); - lengths[0] = strlen(vector[0]); - } - - for (i = 0; i < argc; i++) { - char cmdpart[256]; - atom_string(argv+i, cmdpart, 256); - - if ((vector[i+1] = malloc(strlen(cmdpart)+1)) == NULL) { - post("puredis: can not proceed!! Memory Error!"); return; - } - - strcpy(vector[i+1], (char*)cmdpart); - lengths[i+1] = strlen(vector[i+1]); - } - redis_postCommandAsync(x, argc+1, vector, lengths); - spuredis_manage(x, s, argc); -} +#include "libpuredis.c"