removing things

This commit is contained in:
perroned 2014-07-18 07:19:49 -07:00
parent 5a96d7c370
commit 0de71c6039
121 changed files with 0 additions and 15120 deletions

27
labs/node_modules/hiredis/COPYING generated vendored
View File

@ -1,27 +0,0 @@
Copyright (c) 2010-2012, Pieter Noordhuis
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Redis nor the names of its contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

70
labs/node_modules/hiredis/README.md generated vendored
View File

@ -1,70 +0,0 @@
[![Build Status](https://travis-ci.org/redis/hiredis-node.png?branch=master)](https://travis-ci.org/redis/hiredis-node)
# hiredis-node
Node extension that wraps [hiredis][hiredis].
Because Node is already good at doing I/O, hiredis-node only provides
bindings to the protocol parser.
The hiredis protocol parser is faster than JavaScript protocol parsers,
but the speedup only becomes noticable for large replies.
If you use Redis for simple SET/GET operations, there won't be a big
benefit to using hiredis.
If you use Redis for big SUNION/SINTER/LRANGE/ZRANGE operations, the
benefit to using hiredis-node can be significant.
[hiredis]: http://github.com/redis/hiredis
## Install
Install with [NPM][npm]:
```
npm install hiredis
```
[npm]: https://npmjs.org/
## Usage
hiredis-node works out of the box with Matt Ranney's [node_redis][node_redis].
The latter has an optional dependency on hiredis-node, so maybe you're
already using it without knowing.
Alternatively, you can use it directly:
```javascript
var hiredis = require("hiredis"),
reader = new hiredis.Reader();
// Data comes in
reader.feed("$5\r\nhello\r\n");
// Reply comes out
reader.get() // => "hello"
```
Instead of returning strings for bulk payloads, it can also return
buffers:
```javascript
var hiredis = require("hiredis"),
reader = new hiredis.Reader({ return_buffers: true });
// Data comes in
reader.feed("$5\r\nhello\r\n");
// Reply comes out
reader.get() // => <Buffer 68 65 6c 6c 6f>
```
[node_redis]: http://github.com/mranney/node_redis
## Windows
Dmitry Gorbunov (@fuwaneko) made a [fork of hiredis-node][windows_fork] with Windows support.
[windows_fork]: https://github.com/fuwaneko/hiredis-node
## License
This code is released under the BSD license, after the license of hiredis.

121
labs/node_modules/hiredis/bench.js generated vendored
View File

@ -1,121 +0,0 @@
var hiredis = require("./hiredis"),
num_clients = 10,
active_clients = 0,
pipeline = 0,
num_requests = parseInt(process.argv[2]) || 20000,
issued_requests = 0,
test_start;
var tests = [];
tests.push({
descr: "PING",
command: ["PING"]
});
tests.push({
descr: "SET",
command: ["SET", "foo", "bar"]
});
tests.push({
descr: "GET",
command: ["GET", "foo"]
});
tests.push({
descr: "LPUSH 8 bytes",
command: ["LPUSH", "mylist-8", new Buffer(Array(8).join("-"))]
});
tests.push({
descr: "LPUSH 64 bytes",
command: ["LPUSH", "mylist-64", new Buffer(Array(64).join("-"))]
});
tests.push({
descr: "LPUSH 512 bytes",
command: ["LPUSH", "mylist-512", new Buffer(Array(512).join("-"))]
});
tests.push({
descr: "LRANGE 10 elements, 8 bytes",
command: ["LRANGE", "mylist-8", "0", "9"]
});
tests.push({
descr: "LRANGE 100 elements, 8 bytes",
command: ["LRANGE", "mylist-8", "0", "99"]
});
tests.push({
descr: "LRANGE 100 elements, 64 bytes",
command: ["LRANGE", "mylist-64", "0", "99"]
});
tests.push({
descr: "LRANGE 100 elements, 512 bytes",
command: ["LRANGE", "mylist-512", "0", "99"]
});
function call(client, test) {
client.on("reply", function() {
if (issued_requests < num_requests) {
request();
} else {
client.end();
if (--active_clients == 0)
done(test);
}
});
function request() {
issued_requests++;
client.write.apply(client,test.command);
};
request();
}
function done(test) {
var time = (new Date - test_start);
var op_rate = (num_requests/(time/1000.0)).toFixed(2);
console.log(test.descr + ": " + op_rate + " ops/sec");
next();
}
function concurrent_test(test) {
var i = num_clients;
var client;
issued_requests = 0;
test_start = new Date;
while(i-- && issued_requests < num_requests) {
active_clients++;
client = hiredis.createConnection();
call(client, test);
}
}
function pipelined_test(test) {
var client = hiredis.createConnection();
var received_replies = 0;
issued_requests = 0;
while (issued_requests < num_requests) {
issued_requests++;
client.write.apply(client,test.command);
}
test_start = new Date;
client.on("reply", function() {
if (++received_replies == num_requests) {
client.end();
done(test);
}
});
}
function next() {
var test = tests.shift();
if (test) {
if (pipeline) {
pipelined_test(test);
} else {
concurrent_test(test);
}
}
}
next();

View File

@ -1,22 +0,0 @@
{
'targets': [
{
'target_name': 'hiredis',
'sources': [
'src/hiredis.cc'
, 'src/reader.cc'
],
'include_dirs': ["<!(node -e \"require('nan')\")"],
'dependencies': [
'deps/hiredis.gyp:hiredis'
],
'defines': [
'_GNU_SOURCE'
],
'cflags': [
'-Wall',
'-O3'
]
}
]
}

View File

@ -1,337 +0,0 @@
# We borrow heavily from the kernel build setup, though we are simpler since
# we don't have Kconfig tweaking settings on us.
# The implicit make rules have it looking for RCS files, among other things.
# We instead explicitly write all the rules we care about.
# It's even quicker (saves ~200ms) to pass -r on the command line.
MAKEFLAGS=-r
# The source directory tree.
srcdir := ..
abs_srcdir := $(abspath $(srcdir))
# The name of the builddir.
builddir_name ?= .
# The V=1 flag on command line makes us verbosely print command lines.
ifdef V
quiet=
else
quiet=quiet_
endif
# Specify BUILDTYPE=Release on the command line for a release build.
BUILDTYPE ?= Release
# Directory all our build output goes into.
# Note that this must be two directories beneath src/ for unit tests to pass,
# as they reach into the src/ directory for data with relative paths.
builddir ?= $(builddir_name)/$(BUILDTYPE)
abs_builddir := $(abspath $(builddir))
depsdir := $(builddir)/.deps
# Object output directory.
obj := $(builddir)/obj
abs_obj := $(abspath $(obj))
# We build up a list of every single one of the targets so we can slurp in the
# generated dependency rule Makefiles in one pass.
all_deps :=
CC.target ?= $(CC)
CFLAGS.target ?= $(CFLAGS)
CXX.target ?= $(CXX)
CXXFLAGS.target ?= $(CXXFLAGS) $(CPPFLAGS)
LINK.target ?= $(LINK)
LDFLAGS.target ?= $(LDFLAGS)
AR.target ?= $(AR)
# C++ apps need to be linked with g++.
#
# Note: flock is used to seralize linking. Linking is a memory-intensive
# process so running parallel links can often lead to thrashing. To disable
# the serialization, override LINK via an envrionment variable as follows:
#
# export LINK=g++
#
# This will allow make to invoke N linker processes as specified in -jN.
LINK ?= flock $(builddir)/linker.lock $(CXX.target)
# TODO(evan): move all cross-compilation logic to gyp-time so we don't need
# to replicate this environment fallback in make as well.
CC.host ?= gcc
CFLAGS.host ?=
CXX.host ?= g++
CXXFLAGS.host ?=
LINK.host ?= $(CXX.host)
LDFLAGS.host ?=
AR.host ?= ar
# Define a dir function that can handle spaces.
# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions
# "leading spaces cannot appear in the text of the first argument as written.
# These characters can be put into the argument value by variable substitution."
empty :=
space := $(empty) $(empty)
# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces
replace_spaces = $(subst $(space),?,$1)
unreplace_spaces = $(subst ?,$(space),$1)
dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1)))
# Flags to make gcc output dependency info. Note that you need to be
# careful here to use the flags that ccache and distcc can understand.
# We write to a dep file on the side first and then rename at the end
# so we can't end up with a broken dep file.
depfile = $(depsdir)/$(call replace_spaces,$@).d
DEPFLAGS = -MMD -MF $(depfile).raw
# We have to fixup the deps output in a few ways.
# (1) the file output should mention the proper .o file.
# ccache or distcc lose the path to the target, so we convert a rule of
# the form:
# foobar.o: DEP1 DEP2
# into
# path/to/foobar.o: DEP1 DEP2
# (2) we want missing files not to cause us to fail to build.
# We want to rewrite
# foobar.o: DEP1 DEP2 \
# DEP3
# to
# DEP1:
# DEP2:
# DEP3:
# so if the files are missing, they're just considered phony rules.
# We have to do some pretty insane escaping to get those backslashes
# and dollar signs past make, the shell, and sed at the same time.
# Doesn't work with spaces, but that's fine: .d files have spaces in
# their names replaced with other characters.
define fixup_dep
# The depfile may not exist if the input file didn't have any #includes.
touch $(depfile).raw
# Fixup path as in (1).
sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile)
# Add extra rules as in (2).
# We remove slashes and replace spaces with new lines;
# remove blank lines;
# delete the first line and append a colon to the remaining lines.
sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\
grep -v '^$$' |\
sed -e 1d -e 's|$$|:|' \
>> $(depfile)
rm $(depfile).raw
endef
# Command definitions:
# - cmd_foo is the actual command to run;
# - quiet_cmd_foo is the brief-output summary of the command.
quiet_cmd_cc = CC($(TOOLSET)) $@
cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_cxx = CXX($(TOOLSET)) $@
cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_touch = TOUCH $@
cmd_touch = touch $@
quiet_cmd_copy = COPY $@
# send stderr to /dev/null to ignore messages when linking directories.
cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@")
quiet_cmd_alink = AR($(TOOLSET)) $@
cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^)
quiet_cmd_alink_thin = AR($(TOOLSET)) $@
cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^)
# Due to circular dependencies between libraries :(, we wrap the
# special "figure out circular dependencies" flags around the entire
# input list during linking.
quiet_cmd_link = LINK($(TOOLSET)) $@
cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS)
# We support two kinds of shared objects (.so):
# 1) shared_library, which is just bundling together many dependent libraries
# into a link line.
# 2) loadable_module, which is generating a module intended for dlopen().
#
# They differ only slightly:
# In the former case, we want to package all dependent code into the .so.
# In the latter case, we want to package just the API exposed by the
# outermost module.
# This means shared_library uses --whole-archive, while loadable_module doesn't.
# (Note that --whole-archive is incompatible with the --start-group used in
# normal linking.)
# Other shared-object link notes:
# - Set SONAME to the library filename so our binaries don't reference
# the local, absolute paths used on the link command-line.
quiet_cmd_solink = SOLINK($(TOOLSET)) $@
cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS)
quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS)
# Define an escape_quotes function to escape single quotes.
# This allows us to handle quotes properly as long as we always use
# use single quotes and escape_quotes.
escape_quotes = $(subst ','\'',$(1))
# This comment is here just to include a ' to unconfuse syntax highlighting.
# Define an escape_vars function to escape '$' variable syntax.
# This allows us to read/write command lines with shell variables (e.g.
# $LD_LIBRARY_PATH), without triggering make substitution.
escape_vars = $(subst $$,$$$$,$(1))
# Helper that expands to a shell command to echo a string exactly as it is in
# make. This uses printf instead of echo because printf's behaviour with respect
# to escape sequences is more portable than echo's across different shells
# (e.g., dash, bash).
exact_echo = printf '%s\n' '$(call escape_quotes,$(1))'
# Helper to compare the command we're about to run against the command
# we logged the last time we ran the command. Produces an empty
# string (false) when the commands match.
# Tricky point: Make has no string-equality test function.
# The kernel uses the following, but it seems like it would have false
# positives, where one string reordered its arguments.
# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \
# $(filter-out $(cmd_$@), $(cmd_$(1))))
# We instead substitute each for the empty string into the other, and
# say they're equal if both substitutions produce the empty string.
# .d files contain ? instead of spaces, take that into account.
command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\
$(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1))))
# Helper that is non-empty when a prerequisite changes.
# Normally make does this implicitly, but we force rules to always run
# so we can check their command lines.
# $? -- new prerequisites
# $| -- order-only dependencies
prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?))
# Helper that executes all postbuilds until one fails.
define do_postbuilds
@E=0;\
for p in $(POSTBUILDS); do\
eval $$p;\
E=$$?;\
if [ $$E -ne 0 ]; then\
break;\
fi;\
done;\
if [ $$E -ne 0 ]; then\
rm -rf "$@";\
exit $$E;\
fi
endef
# do_cmd: run a command via the above cmd_foo names, if necessary.
# Should always run for a given target to handle command-line changes.
# Second argument, if non-zero, makes it do asm/C/C++ dependency munging.
# Third argument, if non-zero, makes it do POSTBUILDS processing.
# Note: We intentionally do NOT call dirx for depfile, since it contains ? for
# spaces already and dirx strips the ? characters.
define do_cmd
$(if $(or $(command_changed),$(prereq_changed)),
@$(call exact_echo, $($(quiet)cmd_$(1)))
@mkdir -p "$(call dirx,$@)" "$(dir $(depfile))"
$(if $(findstring flock,$(word 1,$(cmd_$1))),
@$(cmd_$(1))
@echo " $(quiet_cmd_$(1)): Finished",
@$(cmd_$(1))
)
@$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile)
@$(if $(2),$(fixup_dep))
$(if $(and $(3), $(POSTBUILDS)),
$(call do_postbuilds)
)
)
endef
# Declare the "all" target first so it is the default,
# even though we don't have the deps yet.
.PHONY: all
all:
# make looks for ways to re-generate included makefiles, but in our case, we
# don't have a direct way. Explicitly telling make that it has nothing to do
# for them makes it go faster.
%.d: ;
# Use FORCE_DO_CMD to force a target to run. Should be coupled with
# do_cmd.
.PHONY: FORCE_DO_CMD
FORCE_DO_CMD:
TOOLSET := target
# Suffix rules, putting all outputs into $(obj).
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD
@$(call do_cmd,cc,1)
# Try building from generated source, too.
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD
@$(call do_cmd,cc,1)
ifeq ($(strip $(foreach prefix,$(NO_LOAD),\
$(findstring $(join ^,$(prefix)),\
$(join ^,deps/hiredis.target.mk)))),)
include deps/hiredis.target.mk
endif
ifeq ($(strip $(foreach prefix,$(NO_LOAD),\
$(findstring $(join ^,$(prefix)),\
$(join ^,hiredis.target.mk)))),)
include hiredis.target.mk
endif
quiet_cmd_regen_makefile = ACTION Regenerating $@
cmd_regen_makefile = cd $(srcdir); /usr/share/node-gyp/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." -I/home/firstuser/dev/bigbluebutton/labs/node_modules/hiredis/build/config.gypi -I/usr/share/node-gyp/addon.gypi -I/usr/include/nodejs/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/usr/include/nodejs" "-Dmodule_root_dir=/home/firstuser/dev/bigbluebutton/labs/node_modules/hiredis" binding.gyp
Makefile: $(srcdir)/../../../../../../../usr/share/node-gyp/addon.gypi $(srcdir)/../../../../../../../usr/include/nodejs/common.gypi $(srcdir)/deps/hiredis.gyp $(srcdir)/build/config.gypi $(srcdir)/binding.gyp
$(call do_cmd,regen_makefile)
# "all" is a concatenation of the "all" targets from all the included
# sub-makefiles. This is just here to clarify.
all:
# Add in dependency-tracking rules. $(all_deps) is the list of every single
# target in our tree. Only consider the ones with .d (dependency) info:
d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d))
ifneq ($(d_files),)
include $(d_files)
endif

View File

@ -1 +0,0 @@
cmd_Release/hiredis.a := ln -f "Release/obj.target/deps/hiredis.a" "Release/hiredis.a" 2>/dev/null || (rm -rf "Release/hiredis.a" && cp -af "Release/obj.target/deps/hiredis.a" "Release/hiredis.a")

View File

@ -1 +0,0 @@
cmd_Release/hiredis.node := ln -f "Release/obj.target/hiredis.node" "Release/hiredis.node" 2>/dev/null || (rm -rf "Release/hiredis.node" && cp -af "Release/obj.target/hiredis.node" "Release/hiredis.node")

View File

@ -1 +0,0 @@
cmd_Release/obj.target/deps/hiredis.a := rm -f Release/obj.target/deps/hiredis.a && ar crs Release/obj.target/deps/hiredis.a Release/obj.target/hiredis/deps/hiredis/hiredis.o Release/obj.target/hiredis/deps/hiredis/net.o Release/obj.target/hiredis/deps/hiredis/sds.o Release/obj.target/hiredis/deps/hiredis/async.o

View File

@ -1 +0,0 @@
cmd_Release/obj.target/hiredis.node := flock ./Release/linker.lock g++ -shared -pthread -rdynamic -m64 -Wl,-soname=hiredis.node -o Release/obj.target/hiredis.node -Wl,--start-group Release/obj.target/hiredis/src/hiredis.o Release/obj.target/hiredis/src/reader.o Release/obj.target/deps/hiredis.a -Wl,--end-group

View File

@ -1,13 +0,0 @@
cmd_Release/obj.target/hiredis/deps/hiredis/async.o := cc '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' -I/usr/include/nodejs/src -I/usr/include/nodejs/deps/uv/include -I/usr/include/nodejs/deps/v8/include -fPIC -Wall -Wextra -Wno-unused-parameter -pthread -m64 -O2 -fno-strict-aliasing -fno-tree-vrp -fno-omit-frame-pointer -MMD -MF ./Release/.deps/Release/obj.target/hiredis/deps/hiredis/async.o.d.raw -c -o Release/obj.target/hiredis/deps/hiredis/async.o ../deps/hiredis/async.c
Release/obj.target/hiredis/deps/hiredis/async.o: ../deps/hiredis/async.c \
../deps/hiredis/fmacros.h ../deps/hiredis/async.h \
../deps/hiredis/hiredis.h ../deps/hiredis/net.h ../deps/hiredis/dict.c \
../deps/hiredis/dict.h ../deps/hiredis/sds.h
../deps/hiredis/async.c:
../deps/hiredis/fmacros.h:
../deps/hiredis/async.h:
../deps/hiredis/hiredis.h:
../deps/hiredis/net.h:
../deps/hiredis/dict.c:
../deps/hiredis/dict.h:
../deps/hiredis/sds.h:

View File

@ -1,9 +0,0 @@
cmd_Release/obj.target/hiredis/deps/hiredis/hiredis.o := cc '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' -I/usr/include/nodejs/src -I/usr/include/nodejs/deps/uv/include -I/usr/include/nodejs/deps/v8/include -fPIC -Wall -Wextra -Wno-unused-parameter -pthread -m64 -O2 -fno-strict-aliasing -fno-tree-vrp -fno-omit-frame-pointer -MMD -MF ./Release/.deps/Release/obj.target/hiredis/deps/hiredis/hiredis.o.d.raw -c -o Release/obj.target/hiredis/deps/hiredis/hiredis.o ../deps/hiredis/hiredis.c
Release/obj.target/hiredis/deps/hiredis/hiredis.o: \
../deps/hiredis/hiredis.c ../deps/hiredis/fmacros.h \
../deps/hiredis/hiredis.h ../deps/hiredis/net.h ../deps/hiredis/sds.h
../deps/hiredis/hiredis.c:
../deps/hiredis/fmacros.h:
../deps/hiredis/hiredis.h:
../deps/hiredis/net.h:
../deps/hiredis/sds.h:

View File

@ -1,9 +0,0 @@
cmd_Release/obj.target/hiredis/deps/hiredis/net.o := cc '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' -I/usr/include/nodejs/src -I/usr/include/nodejs/deps/uv/include -I/usr/include/nodejs/deps/v8/include -fPIC -Wall -Wextra -Wno-unused-parameter -pthread -m64 -O2 -fno-strict-aliasing -fno-tree-vrp -fno-omit-frame-pointer -MMD -MF ./Release/.deps/Release/obj.target/hiredis/deps/hiredis/net.o.d.raw -c -o Release/obj.target/hiredis/deps/hiredis/net.o ../deps/hiredis/net.c
Release/obj.target/hiredis/deps/hiredis/net.o: ../deps/hiredis/net.c \
../deps/hiredis/fmacros.h ../deps/hiredis/net.h \
../deps/hiredis/hiredis.h ../deps/hiredis/sds.h
../deps/hiredis/net.c:
../deps/hiredis/fmacros.h:
../deps/hiredis/net.h:
../deps/hiredis/hiredis.h:
../deps/hiredis/sds.h:

View File

@ -1,5 +0,0 @@
cmd_Release/obj.target/hiredis/deps/hiredis/sds.o := cc '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' -I/usr/include/nodejs/src -I/usr/include/nodejs/deps/uv/include -I/usr/include/nodejs/deps/v8/include -fPIC -Wall -Wextra -Wno-unused-parameter -pthread -m64 -O2 -fno-strict-aliasing -fno-tree-vrp -fno-omit-frame-pointer -MMD -MF ./Release/.deps/Release/obj.target/hiredis/deps/hiredis/sds.o.d.raw -c -o Release/obj.target/hiredis/deps/hiredis/sds.o ../deps/hiredis/sds.c
Release/obj.target/hiredis/deps/hiredis/sds.o: ../deps/hiredis/sds.c \
../deps/hiredis/sds.h
../deps/hiredis/sds.c:
../deps/hiredis/sds.h:

View File

@ -1,29 +0,0 @@
cmd_Release/obj.target/hiredis/src/hiredis.o := g++ '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_GNU_SOURCE' '-DBUILDING_NODE_EXTENSION' -I/usr/include/nodejs/src -I/usr/include/nodejs/deps/uv/include -I/usr/include/nodejs/deps/v8/include -I../node_modules/nan -I../deps -fPIC -Wall -Wextra -Wno-unused-parameter -pthread -m64 -Wall -O2 -fno-strict-aliasing -fno-tree-vrp -fno-omit-frame-pointer -fno-rtti -fno-exceptions -MMD -MF ./Release/.deps/Release/obj.target/hiredis/src/hiredis.o.d.raw -c -o Release/obj.target/hiredis/src/hiredis.o ../src/hiredis.cc
Release/obj.target/hiredis/src/hiredis.o: ../src/hiredis.cc \
/usr/include/nodejs/deps/v8/include/v8.h \
/usr/include/nodejs/deps/v8/include/v8stdint.h \
/usr/include/nodejs/src/node.h /usr/include/nodejs/deps/uv/include/uv.h \
/usr/include/nodejs/deps/uv/include/uv-private/uv-unix.h \
/usr/include/nodejs/deps/uv/include/uv-private/ngx-queue.h \
/usr/include/nodejs/deps/uv/include/uv-private/uv-linux.h \
/usr/include/nodejs/src/node_object_wrap.h \
/usr/include/nodejs/src/node.h ../src/reader.h ../deps/hiredis/hiredis.h \
../node_modules/nan/nan.h /usr/include/nodejs/src/node_buffer.h \
/usr/include/nodejs/src/node_version.h \
/usr/include/nodejs/src/node_object_wrap.h
../src/hiredis.cc:
/usr/include/nodejs/deps/v8/include/v8.h:
/usr/include/nodejs/deps/v8/include/v8stdint.h:
/usr/include/nodejs/src/node.h:
/usr/include/nodejs/deps/uv/include/uv.h:
/usr/include/nodejs/deps/uv/include/uv-private/uv-unix.h:
/usr/include/nodejs/deps/uv/include/uv-private/ngx-queue.h:
/usr/include/nodejs/deps/uv/include/uv-private/uv-linux.h:
/usr/include/nodejs/src/node_object_wrap.h:
/usr/include/nodejs/src/node.h:
../src/reader.h:
../deps/hiredis/hiredis.h:
../node_modules/nan/nan.h:
/usr/include/nodejs/src/node_buffer.h:
/usr/include/nodejs/src/node_version.h:
/usr/include/nodejs/src/node_object_wrap.h:

View File

@ -1,29 +0,0 @@
cmd_Release/obj.target/hiredis/src/reader.o := g++ '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_GNU_SOURCE' '-DBUILDING_NODE_EXTENSION' -I/usr/include/nodejs/src -I/usr/include/nodejs/deps/uv/include -I/usr/include/nodejs/deps/v8/include -I../node_modules/nan -I../deps -fPIC -Wall -Wextra -Wno-unused-parameter -pthread -m64 -Wall -O2 -fno-strict-aliasing -fno-tree-vrp -fno-omit-frame-pointer -fno-rtti -fno-exceptions -MMD -MF ./Release/.deps/Release/obj.target/hiredis/src/reader.o.d.raw -c -o Release/obj.target/hiredis/src/reader.o ../src/reader.cc
Release/obj.target/hiredis/src/reader.o: ../src/reader.cc \
/usr/include/nodejs/deps/v8/include/v8.h \
/usr/include/nodejs/deps/v8/include/v8stdint.h \
/usr/include/nodejs/src/node.h /usr/include/nodejs/deps/uv/include/uv.h \
/usr/include/nodejs/deps/uv/include/uv-private/uv-unix.h \
/usr/include/nodejs/deps/uv/include/uv-private/ngx-queue.h \
/usr/include/nodejs/deps/uv/include/uv-private/uv-linux.h \
/usr/include/nodejs/src/node_object_wrap.h \
/usr/include/nodejs/src/node.h ../src/reader.h ../deps/hiredis/hiredis.h \
../node_modules/nan/nan.h /usr/include/nodejs/src/node_buffer.h \
/usr/include/nodejs/src/node_version.h \
/usr/include/nodejs/src/node_object_wrap.h
../src/reader.cc:
/usr/include/nodejs/deps/v8/include/v8.h:
/usr/include/nodejs/deps/v8/include/v8stdint.h:
/usr/include/nodejs/src/node.h:
/usr/include/nodejs/deps/uv/include/uv.h:
/usr/include/nodejs/deps/uv/include/uv-private/uv-unix.h:
/usr/include/nodejs/deps/uv/include/uv-private/ngx-queue.h:
/usr/include/nodejs/deps/uv/include/uv-private/uv-linux.h:
/usr/include/nodejs/src/node_object_wrap.h:
/usr/include/nodejs/src/node.h:
../src/reader.h:
../deps/hiredis/hiredis.h:
../node_modules/nan/nan.h:
/usr/include/nodejs/src/node_buffer.h:
/usr/include/nodejs/src/node_version.h:
/usr/include/nodejs/src/node_object_wrap.h:

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

View File

@ -1,6 +0,0 @@
# This file is generated by gyp; do not edit.
export builddir_name ?= build/./.
.PHONY: all
all:
$(MAKE) hiredis

View File

@ -1,116 +0,0 @@
# Do not edit. File was generated by node-gyp's "configure" step
{
"target_defaults": {
"cflags": [],
"default_configuration": "Release",
"defines": [],
"include_dirs": [],
"libraries": []
},
"variables": {
"clang": 0,
"gcc_version": 48,
"host_arch": "x64",
"node_byteorder": "little",
"node_install_npm": "false",
"node_prefix": "/usr",
"node_shared_cares": "true",
"node_shared_http_parser": "false",
"node_shared_libuv": "false",
"node_shared_openssl": "true",
"node_shared_v8": "true",
"node_shared_zlib": "true",
"node_tag": "",
"node_unsafe_optimizations": 0,
"node_use_dtrace": "false",
"node_use_etw": "false",
"node_use_openssl": "true",
"node_use_perfctr": "false",
"node_use_systemtap": "false",
"python": "/usr/bin/python",
"target_arch": "x64",
"v8_enable_gdbjit": 0,
"v8_no_strict_aliasing": 1,
"v8_use_snapshot": "false",
"nodedir": "/usr/include/nodejs",
"copy_dev_lib": "true",
"standalone_static_library": 1,
"cache_lock_stale": "60000",
"pre": "",
"sign_git_tag": "",
"always_auth": "",
"user_agent": "node/v0.10.25 linux x64",
"bin_links": "true",
"description": "true",
"fetch_retries": "2",
"init_version": "0.0.0",
"user": "",
"force": "",
"ignore": "",
"cache_min": "10",
"editor": "vi",
"rollback": "true",
"cache_max": "null",
"userconfig": "/home/firstuser/.npmrc",
"coverage": "",
"engine_strict": "",
"init_author_name": "",
"init_author_url": "",
"tmp": "/home/firstuser/tmp",
"userignorefile": "/home/firstuser/.npmignore",
"yes": "",
"depth": "null",
"save_dev": "",
"usage": "",
"https_proxy": "",
"onload_script": "",
"rebuild_bundle": "true",
"save_bundle": "",
"shell": "/bin/bash",
"prefix": "/usr/local",
"registry": "https://registry.npmjs.org/",
"__DO_NOT_MODIFY_THIS_FILE___use__etc_npmrc_instead_": "true",
"browser": "",
"cache_lock_wait": "10000",
"save_optional": "",
"searchopts": "",
"versions": "",
"cache": "/home/firstuser/.npm",
"npaturl": "http://npat.npmjs.org/",
"searchsort": "name",
"version": "",
"viewer": "man",
"color": "true",
"fetch_retry_mintimeout": "10000",
"umask": "18",
"fetch_retry_maxtimeout": "60000",
"message": "%s",
"global": "",
"link": "",
"save": "",
"unicode": "true",
"long": "",
"production": "",
"unsafe_perm": "true",
"node_version": "v0.10.25",
"tag": "latest",
"shrinkwrap": "true",
"fetch_retry_factor": "10",
"npat": "",
"proprietary_attribs": "true",
"strict_ssl": "true",
"username": "",
"globalconfig": "/etc/npmrc",
"dev": "",
"init_module": "/home/firstuser/.npm-init.js",
"parseable": "",
"globalignorefile": "/etc/npmignore",
"cache_lock_retries": "10",
"group": "1000",
"init_author_email": "",
"searchexclude": "",
"git": "git",
"optional": "true",
"json": ""
}
}

View File

@ -1,6 +0,0 @@
# This file is generated by gyp; do not edit.
export builddir_name ?= build/./deps/.
.PHONY: all
all:
$(MAKE) -C .. hiredis

View File

@ -1,139 +0,0 @@
# This file is generated by gyp; do not edit.
TOOLSET := target
TARGET := hiredis
DEFS_Debug := \
'-D_LARGEFILE_SOURCE' \
'-D_FILE_OFFSET_BITS=64' \
'-DDEBUG' \
'-D_DEBUG'
# Flags passed to all source files.
CFLAGS_Debug := \
-fPIC \
-Wall \
-Wextra \
-Wno-unused-parameter \
-pthread \
-m64 \
-g \
-O0
# Flags passed to only C files.
CFLAGS_C_Debug :=
# Flags passed to only C++ files.
CFLAGS_CC_Debug := \
-fno-rtti \
-fno-exceptions
INCS_Debug := \
-I/usr/include/nodejs/src \
-I/usr/include/nodejs/deps/uv/include \
-I/usr/include/nodejs/deps/v8/include
DEFS_Release := \
'-D_LARGEFILE_SOURCE' \
'-D_FILE_OFFSET_BITS=64'
# Flags passed to all source files.
CFLAGS_Release := \
-fPIC \
-Wall \
-Wextra \
-Wno-unused-parameter \
-pthread \
-m64 \
-O2 \
-fno-strict-aliasing \
-fno-tree-vrp \
-fno-omit-frame-pointer
# Flags passed to only C files.
CFLAGS_C_Release :=
# Flags passed to only C++ files.
CFLAGS_CC_Release := \
-fno-rtti \
-fno-exceptions
INCS_Release := \
-I/usr/include/nodejs/src \
-I/usr/include/nodejs/deps/uv/include \
-I/usr/include/nodejs/deps/v8/include
OBJS := \
$(obj).target/$(TARGET)/deps/hiredis/hiredis.o \
$(obj).target/$(TARGET)/deps/hiredis/net.o \
$(obj).target/$(TARGET)/deps/hiredis/sds.o \
$(obj).target/$(TARGET)/deps/hiredis/async.o
# Add to the list of files we specially track dependencies for.
all_deps += $(OBJS)
# CFLAGS et al overrides must be target-local.
# See "Target-specific Variable Values" in the GNU Make manual.
$(OBJS): TOOLSET := $(TOOLSET)
$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
# Suffix rules, putting all outputs into $(obj).
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
# Try building from generated source, too.
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
# End of this set of suffix rules
### Rules for final target.
LDFLAGS_Debug := \
-pthread \
-rdynamic \
-m64
LDFLAGS_Release := \
-pthread \
-rdynamic \
-m64
LIBS :=
$(obj).target/deps/hiredis.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
$(obj).target/deps/hiredis.a: LIBS := $(LIBS)
$(obj).target/deps/hiredis.a: TOOLSET := $(TOOLSET)
$(obj).target/deps/hiredis.a: $(OBJS) FORCE_DO_CMD
$(call do_cmd,alink)
all_deps += $(obj).target/deps/hiredis.a
# Add target alias
.PHONY: hiredis
hiredis: $(obj).target/deps/hiredis.a
# Add target alias to "all" target.
.PHONY: all
all: hiredis
# Add target alias
.PHONY: hiredis
hiredis: $(builddir)/hiredis.a
# Copy this to the static library output path.
$(builddir)/hiredis.a: TOOLSET := $(TOOLSET)
$(builddir)/hiredis.a: $(obj).target/deps/hiredis.a FORCE_DO_CMD
$(call do_cmd,copy)
all_deps += $(builddir)/hiredis.a
# Short alias for building this static library.
.PHONY: hiredis.a
hiredis.a: $(obj).target/deps/hiredis.a $(builddir)/hiredis.a
# Add static library to "all" target.
.PHONY: all
all: $(builddir)/hiredis.a

View File

@ -1,143 +0,0 @@
# This file is generated by gyp; do not edit.
TOOLSET := target
TARGET := hiredis
DEFS_Debug := \
'-D_LARGEFILE_SOURCE' \
'-D_FILE_OFFSET_BITS=64' \
'-D_GNU_SOURCE' \
'-DBUILDING_NODE_EXTENSION' \
'-DDEBUG' \
'-D_DEBUG'
# Flags passed to all source files.
CFLAGS_Debug := \
-fPIC \
-Wall \
-Wextra \
-Wno-unused-parameter \
-pthread \
-m64 \
-Wall \
-O3 \
-g \
-O0
# Flags passed to only C files.
CFLAGS_C_Debug :=
# Flags passed to only C++ files.
CFLAGS_CC_Debug := \
-fno-rtti \
-fno-exceptions
INCS_Debug := \
-I/usr/include/nodejs/src \
-I/usr/include/nodejs/deps/uv/include \
-I/usr/include/nodejs/deps/v8/include \
-I$(srcdir)/node_modules/nan \
-I$(srcdir)/deps
DEFS_Release := \
'-D_LARGEFILE_SOURCE' \
'-D_FILE_OFFSET_BITS=64' \
'-D_GNU_SOURCE' \
'-DBUILDING_NODE_EXTENSION'
# Flags passed to all source files.
CFLAGS_Release := \
-fPIC \
-Wall \
-Wextra \
-Wno-unused-parameter \
-pthread \
-m64 \
-Wall \
-O2 \
-fno-strict-aliasing \
-fno-tree-vrp \
-fno-omit-frame-pointer
# Flags passed to only C files.
CFLAGS_C_Release :=
# Flags passed to only C++ files.
CFLAGS_CC_Release := \
-fno-rtti \
-fno-exceptions
INCS_Release := \
-I/usr/include/nodejs/src \
-I/usr/include/nodejs/deps/uv/include \
-I/usr/include/nodejs/deps/v8/include \
-I$(srcdir)/node_modules/nan \
-I$(srcdir)/deps
OBJS := \
$(obj).target/$(TARGET)/src/hiredis.o \
$(obj).target/$(TARGET)/src/reader.o
# Add to the list of files we specially track dependencies for.
all_deps += $(OBJS)
# Make sure our dependencies are built before any of us.
$(OBJS): | $(builddir)/hiredis.a $(obj).target/deps/hiredis.a
# CFLAGS et al overrides must be target-local.
# See "Target-specific Variable Values" in the GNU Make manual.
$(OBJS): TOOLSET := $(TOOLSET)
$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
# Suffix rules, putting all outputs into $(obj).
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
# Try building from generated source, too.
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
# End of this set of suffix rules
### Rules for final target.
LDFLAGS_Debug := \
-pthread \
-rdynamic \
-m64
LDFLAGS_Release := \
-pthread \
-rdynamic \
-m64
LIBS :=
$(obj).target/hiredis.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
$(obj).target/hiredis.node: LIBS := $(LIBS)
$(obj).target/hiredis.node: TOOLSET := $(TOOLSET)
$(obj).target/hiredis.node: $(OBJS) $(obj).target/deps/hiredis.a FORCE_DO_CMD
$(call do_cmd,solink_module)
all_deps += $(obj).target/hiredis.node
# Add target alias
.PHONY: hiredis
hiredis: $(builddir)/hiredis.node
# Copy this to the executable output path.
$(builddir)/hiredis.node: TOOLSET := $(TOOLSET)
$(builddir)/hiredis.node: $(obj).target/hiredis.node FORCE_DO_CMD
$(call do_cmd,copy)
all_deps += $(builddir)/hiredis.node
# Short alias for building this executable.
.PHONY: hiredis.node
hiredis.node: $(obj).target/hiredis.node $(builddir)/hiredis.node
# Add executable to "all" target.
.PHONY: all
all: $(builddir)/hiredis.node

View File

@ -1,27 +0,0 @@
{
'targets': [
{
'target_name': 'hiredis',
'type': 'static_library',
'direct_dependent_settings': {
'include_dirs': [ '.' ],
},
'sources': [
'./hiredis/hiredis.c',
'./hiredis/net.c',
'./hiredis/sds.c',
'./hiredis/async.c',
],
'conditions': [
['OS=="mac"', {
'xcode_settings': {
'GCC_C_LANGUAGE_STANDARD': 'c99'
}
}],
['OS=="solaris"', {
'cflags+': [ '-std=c99' ]
}]
]
}
]
}

View File

@ -1,24 +0,0 @@
### 0.11.0
* Increase the maximum multi-bulk reply depth to 8.
* Increase the read buffer size from 2k to 16k.
* Use poll(2) instead of select(2) to support large fds (>= 1024).
### 0.10.1
* Makefile overhaul. Important to check out if you override one or more
variables using environment variables or via arguments to the "make" tool.
* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements
being created by the default reply object functions.
* Issue #43: Don't crash in an asynchronous context when Redis returns an error
reply after the connection has been made (this happens when the maximum
number of connections is reached).
### 0.10.0
* See commit log.

View File

@ -1,29 +0,0 @@
Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Redis nor the names of its contributors may be used
to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,148 +0,0 @@
# Hiredis Makefile
# Copyright (C) 2010-2011 Salvatore Sanfilippo <antirez at gmail dot com>
# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
# This file is released under the BSD license, see the COPYING file
OBJ=net.o hiredis.o sds.o async.o
BINS=hiredis-example hiredis-test
LIBNAME=libhiredis
HIREDIS_MAJOR=0
HIREDIS_MINOR=10
# Fallback to gcc when $CC is not in $PATH.
CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
OPTIMIZATION?=-O3
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
DEBUG?= -g -ggdb
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG) $(ARCH)
REAL_LDFLAGS=$(LDFLAGS) $(ARCH)
DYLIBSUFFIX=so
STLIBSUFFIX=a
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR).$(HIREDIS_MINOR)
DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD=ar rcs $(STLIBNAME)
# Platform-specific overrides
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
ifeq ($(uname_S),SunOS)
REAL_LDFLAGS+= -ldl -lnsl -lsocket
DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
INSTALL= cp -r
endif
ifeq ($(uname_S),Darwin)
DYLIBSUFFIX=dylib
DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(DYLIBSUFFIX)
DYLIB_MAJOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
endif
all: $(DYLIBNAME) $(BINS)
# Deps (use make dep to generate this)
net.o: net.c fmacros.h net.h hiredis.h
async.o: async.c async.h hiredis.h sds.h dict.c dict.h
example.o: example.c hiredis.h
hiredis.o: hiredis.c fmacros.h hiredis.h net.h sds.h
sds.o: sds.c sds.h
test.o: test.c hiredis.h
$(DYLIBNAME): $(OBJ)
$(DYLIB_MAKE_CMD) $(OBJ)
$(STLIBNAME): $(OBJ)
$(STLIB_MAKE_CMD) $(OBJ)
dynamic: $(DYLIBNAME)
static: $(STLIBNAME)
# Binaries:
hiredis-example-libevent: example-libevent.c adapters/libevent.h $(STLIBNAME)
$(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -levent example-libevent.c $(STLIBNAME)
hiredis-example-libev: example-libev.c adapters/libev.h $(STLIBNAME)
$(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -lev example-libev.c $(STLIBNAME)
ifndef AE_DIR
hiredis-example-ae:
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
@false
else
hiredis-example-ae: example-ae.c adapters/ae.h $(STLIBNAME)
$(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I$(AE_DIR) $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o example-ae.c $(STLIBNAME)
endif
hiredis-%: %.o $(STLIBNAME)
$(CC) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)
test: hiredis-test
./hiredis-test
check: hiredis-test
echo \
"daemonize yes\n" \
"pidfile /tmp/hiredis-test-redis.pid\n" \
"port 56379\n" \
"bind 127.0.0.1\n" \
"unixsocket /tmp/hiredis-test-redis.sock" \
| redis-server -
./hiredis-test -h 127.0.0.1 -p 56379 -s /tmp/hiredis-test-redis.sock || \
( kill `cat /tmp/hiredis-test-redis.pid` && false )
kill `cat /tmp/hiredis-test-redis.pid`
.c.o:
$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
clean:
rm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
dep:
$(CC) -MM *.c
# Installation related variables and target
PREFIX?=/usr/local
INCLUDE_PATH?=include/hiredis
LIBRARY_PATH?=lib
INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
ifeq ($(uname_S),SunOS)
INSTALL?= cp -r
endif
INSTALL?= cp -a
install: $(DYLIBNAME) $(STLIBNAME)
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
$(INSTALL) hiredis.h async.h adapters $(INSTALL_INCLUDE_PATH)
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MAJOR_NAME) $(DYLIBNAME)
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
32bit:
@echo ""
@echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
@echo ""
$(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
gprof:
$(MAKE) CFLAGS="-pg" LDFLAGS="-pg"
gcov:
$(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs"
coverage: gcov
make check
mkdir -p tmp/lcov
lcov -d . -c -o tmp/lcov/hiredis.info
genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info
noopt:
$(MAKE) OPTIMIZATION=""
.PHONY: all test check clean dep install 32bit gprof gcov noopt

View File

@ -1,379 +0,0 @@
# HIREDIS
Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database.
It is minimalistic because it just adds minimal support for the protocol, but
at the same time it uses an high level printf-alike API in order to make it
much higher level than otherwise suggested by its minimal code base and the
lack of explicit bindings for every Redis command.
Apart from supporting sending commands and receiving replies, it comes with
a reply parser that is decoupled from the I/O layer. It
is a stream parser designed for easy reusability, which can for instance be used
in higher level language bindings for efficient reply parsing.
Hiredis only supports the binary-safe Redis protocol, so you can use it with any
Redis version >= 1.2.0.
The library comes with multiple APIs. There is the
*synchronous API*, the *asynchronous API* and the *reply parsing API*.
## UPGRADING
Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
code using hiredis should not be a big pain. The key thing to keep in mind when
upgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to
the stateless 0.0.1 that only has a file descriptor to work with.
## Synchronous API
To consume the synchronous API, there are only a few function calls that need to be introduced:
redisContext *redisConnect(const char *ip, int port);
void *redisCommand(redisContext *c, const char *format, ...);
void freeReplyObject(void *reply);
### Connecting
The function `redisConnect` is used to create a so-called `redisContext`. The
context is where Hiredis holds state for a connection. The `redisContext`
struct has an integer `err` field that is non-zero when an the connection is in
an error state. The field `errstr` will contain a string with a description of
the error. More information on errors can be found in the **Errors** section.
After trying to connect to Redis using `redisConnect` you should
check the `err` field to see if establishing the connection was successful:
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c->err) {
printf("Error: %s\n", c->errstr);
// handle error
}
### Sending commands
There are several ways to issue commands to Redis. The first that will be introduced is
`redisCommand`. This function takes a format similar to printf. In the simplest form,
it is used like this:
reply = redisCommand(context, "SET foo bar");
The specifier `%s` interpolates a string in the command, and uses `strlen` to
determine the length of the string:
reply = redisCommand(context, "SET foo %s", value);
When you need to pass binary safe strings in a command, the `%b` specifier can be
used. Together with a pointer to the string, it requires a `size_t` length argument
of the string:
reply = redisCommand(context, "SET foo %b", value, valuelen);
Internally, Hiredis splits the command in different arguments and will
convert it to the protocol used to communicate with Redis.
One or more spaces separates arguments, so you can use the specifiers
anywhere in an argument:
reply = redisCommand(context, "SET key:%s %s", myid, value);
### Using replies
The return value of `redisCommand` holds a reply when the command was
successfully executed. When an error occurs, the return value is `NULL` and
the `err` field in the context will be set (see section on **Errors**).
Once an error is returned the context cannot be reused and you should set up
a new connection.
The standard replies that `redisCommand` are of the type `redisReply`. The
`type` field in the `redisReply` should be used to test what kind of reply
was received:
* **`REDIS_REPLY_STATUS`**:
* The command replied with a status reply. The status string can be accessed using `reply->str`.
The length of this string can be accessed using `reply->len`.
* **`REDIS_REPLY_ERROR`**:
* The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`.
* **`REDIS_REPLY_INTEGER`**:
* The command replied with an integer. The integer value can be accessed using the
`reply->integer` field of type `long long`.
* **`REDIS_REPLY_NIL`**:
* The command replied with a **nil** object. There is no data to access.
* **`REDIS_REPLY_STRING`**:
* A bulk (string) reply. The value of the reply can be accessed using `reply->str`.
The length of this string can be accessed using `reply->len`.
* **`REDIS_REPLY_ARRAY`**:
* A multi bulk reply. The number of elements in the multi bulk reply is stored in
`reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well
and can be accessed via `reply->element[..index..]`.
Redis may reply with nested arrays but this is fully supported.
Replies should be freed using the `freeReplyObject()` function.
Note that this function will take care of freeing sub-replies objects
contained in arrays and nested arrays, so there is no need for the user to
free the sub replies (it is actually harmful and will corrupt the memory).
**Important:** the current version of hiredis (0.10.0) free's replies when the
asynchronous API is used. This means you should not call `freeReplyObject` when
you use this API. The reply is cleaned up by hiredis _after_ the callback
returns. This behavior will probably change in future releases, so make sure to
keep an eye on the changelog when upgrading (see issue #39).
### Cleaning up
To disconnect and free the context the following function can be used:
void redisFree(redisContext *c);
This function immediately closes the socket and then free's the allocations done in
creating the context.
### Sending commands (cont'd)
Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands.
It has the following prototype:
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the
arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will
use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments
need to be binary safe, the entire array of lengths `argvlen` should be provided.
The return value has the same semantic as `redisCommand`.
### Pipelining
To explain how Hiredis supports pipelining in a blocking connection, there needs to be
understanding of the internal execution flow.
When any of the functions in the `redisCommand` family is called, Hiredis first formats the
command according to the Redis protocol. The formatted command is then put in the output buffer
of the context. This output buffer is dynamic, so it can hold any number of commands.
After the command is put in the output buffer, `redisGetReply` is called. This function has the
following two execution paths:
1. The input buffer is non-empty:
* Try to parse a single reply from the input buffer and return it
* If no reply could be parsed, continue at *2*
2. The input buffer is empty:
* Write the **entire** output buffer to the socket
* Read from the socket until a single reply could be parsed
The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply
is expected on the socket. To pipeline commands, the only things that needs to be done is
filling up the output buffer. For this cause, two commands can be used that are identical
to the `redisCommand` family, apart from not returning a reply:
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
After calling either function one or more times, `redisGetReply` can be used to receive the
subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where
the latter means an error occurred while reading a reply. Just as with the other commands,
the `err` field in the context can be used to find out what the cause of this error is.
The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and
a single call to `read(2)`):
redisReply *reply;
redisAppendCommand(context,"SET foo bar");
redisAppendCommand(context,"GET foo");
redisGetReply(context,&reply); // reply for SET
freeReplyObject(reply);
redisGetReply(context,&reply); // reply for GET
freeReplyObject(reply);
This API can also be used to implement a blocking subscriber:
reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply);
while(redisGetReply(context,&reply) == REDIS_OK) {
// consume message
freeReplyObject(reply);
}
### Errors
When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is
returned. The `err` field inside the context will be non-zero and set to one of the
following constants:
* **`REDIS_ERR_IO`**:
There was an I/O error while creating the connection, trying to write
to the socket or read from the socket. If you included `errno.h` in your
application, you can use the global `errno` variable to find out what is
wrong.
* **`REDIS_ERR_EOF`**:
The server closed the connection which resulted in an empty read.
* **`REDIS_ERR_PROTOCOL`**:
There was an error while parsing the protocol.
* **`REDIS_ERR_OTHER`**:
Any other error. Currently, it is only used when a specified hostname to connect
to cannot be resolved.
In every case, the `errstr` field in the context will be set to hold a string representation
of the error.
## Asynchronous API
Hiredis comes with an asynchronous API that works easily with any event library.
Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html)
and [libevent](http://monkey.org/~provos/libevent/).
### Connecting
The function `redisAsyncConnect` can be used to establish a non-blocking connection to
Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field
should be checked after creation to see if there were errors creating the connection.
Because the connection that will be created is non-blocking, the kernel is not able to
instantly return if the specified host and port is able to accept a connection.
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
printf("Error: %s\n", c->errstr);
// handle error
}
The asynchronous context can hold a disconnect callback function that is called when the
connection is disconnected (either because of an error or per user request). This function should
have the following prototype:
void(const redisAsyncContext *c, int status);
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`
field in the context can be accessed to find out the cause of the error.
The context object is always free'd after the disconnect callback fired. When a reconnect is needed,
the disconnect callback is a good point to do so.
Setting the disconnect callback can only be done once per context. For subsequent calls it will
return `REDIS_ERR`. The function to set the disconnect callback has the following prototype:
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
### Sending commands and their callbacks
In an asynchronous context, commands are automatically pipelined due to the nature of an event loop.
Therefore, unlike the synchronous API, there is only a single way to send commands.
Because commands are sent to Redis asynchronously, issuing a command requires a callback function
that is called when the reply is received. Reply callbacks should have the following prototype:
void(redisAsyncContext *c, void *reply, void *privdata);
The `privdata` argument can be used to curry arbitrary data to the callback from the point where
the command is initially queued for execution.
The functions that can be used to issue commands in an asynchronous context are:
int redisAsyncCommand(
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
const char *format, ...);
int redisAsyncCommandArgv(
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
int argc, const char **argv, const size_t *argvlen);
Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command
was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection
is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is
returned on calls to the `redisAsyncCommand` family.
If the reply for a command with a `NULL` callback is read, it is immediately free'd. When the callback
for a command is non-`NULL`, the memory is free'd immediately following the callback: the reply is only
valid for the duration of the callback.
All pending callbacks are called with a `NULL` reply when the context encountered an error.
### Disconnecting
An asynchronous connection can be terminated using:
void redisAsyncDisconnect(redisAsyncContext *ac);
When this function is called, the connection is **not** immediately terminated. Instead, new
commands are no longer accepted and the connection is only terminated when all pending commands
have been written to the socket, their respective replies have been read and their respective
callbacks have been executed. After this, the disconnection callback is executed with the
`REDIS_OK` status and the context object is free'd.
### Hooking it up to event library *X*
There are a few hooks that need to be set on the context object after it is created.
See the `adapters/` directory for bindings to *libev* and *libevent*.
## Reply parsing API
Hiredis comes with a reply parsing API that makes it easy for writing higher
level language bindings.
The reply parsing API consists of the following functions:
redisReader *redisReaderCreate(void);
void redisReaderFree(redisReader *reader);
int redisReaderFeed(redisReader *reader, const char *buf, size_t len);
int redisReaderGetReply(redisReader *reader, void **reply);
The same set of functions are used internally by hiredis when creating a
normal Redis context, the above API just exposes it to the user for a direct
usage.
### Usage
The function `redisReaderCreate` creates a `redisReader` structure that holds a
buffer with unparsed data and state for the protocol parser.
Incoming data -- most likely from a socket -- can be placed in the internal
buffer of the `redisReader` using `redisReaderFeed`. This function will make a
copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed
when `redisReaderGetReply` is called. This function returns an integer status
and a reply object (as described above) via `void **reply`. The returned status
can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went
wrong (either a protocol error, or an out of memory error).
### Customizing replies
The function `redisReaderGetReply` creates `redisReply` and makes the function
argument `reply` point to the created `redisReply` variable. For instance, if
the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply`
will hold the status as a vanilla C string. However, the functions that are
responsible for creating instances of the `redisReply` can be customized by
setting the `fn` field on the `redisReader` struct. This should be done
immediately after creating the `redisReader`.
For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c)
uses customized reply object functions to create Ruby objects.
### Reader max buffer
Both when using the Reader API directly or when using it indirectly via a
normal Redis context, the redisReader structure uses a buffer in order to
accumulate data from the server.
Usually this buffer is destroyed when it is empty and is larger than 16
kb in order to avoid wasting memory in unused buffers
However when working with very big payloads destroying the buffer may slow
down performances considerably, so it is possible to modify the max size of
an idle buffer changing the value of the `maxbuf` field of the reader structure
to the desired value. The special value of 0 means that there is no maximum
value for an idle buffer, so the buffer will never get freed.
For instance if you have a normal Redis context you can set the maximum idle
buffer to zero (unlimited) just with:
context->reader->maxbuf = 0;
This should be done only in order to maximize performances when working with
large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again
as soon as possible in order to prevent allocation of useless memory.
## AUTHORS
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.

View File

@ -1,97 +0,0 @@
#ifndef __HIREDIS_AE_H__
#define __HIREDIS_AE_H__
#include <sys/types.h>
#include <ae.h>
#include "../hiredis.h"
#include "../async.h"
typedef struct redisAeEvents {
redisAsyncContext *context;
aeEventLoop *loop;
int fd;
int reading, writing;
} redisAeEvents;
static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleRead(e->context);
}
static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleWrite(e->context);
}
static void redisAeAddRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->reading) {
e->reading = 1;
aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);
}
}
static void redisAeDelRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->reading) {
e->reading = 0;
aeDeleteFileEvent(loop,e->fd,AE_READABLE);
}
}
static void redisAeAddWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->writing) {
e->writing = 1;
aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);
}
}
static void redisAeDelWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->writing) {
e->writing = 0;
aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);
}
}
static void redisAeCleanup(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
redisAeDelRead(privdata);
redisAeDelWrite(privdata);
free(e);
}
static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisAeEvents *e;
/* Nothing should be attached when something is already attached */
if (ac->ev.data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisAeEvents*)malloc(sizeof(*e));
e->context = ac;
e->loop = loop;
e->fd = c->fd;
e->reading = e->writing = 0;
/* Register functions to start/stop listening for events */
ac->ev.addRead = redisAeAddRead;
ac->ev.delRead = redisAeDelRead;
ac->ev.addWrite = redisAeAddWrite;
ac->ev.delWrite = redisAeDelWrite;
ac->ev.cleanup = redisAeCleanup;
ac->ev.data = e;
return REDIS_OK;
}
#endif

View File

@ -1,117 +0,0 @@
#ifndef __HIREDIS_LIBEV_H__
#define __HIREDIS_LIBEV_H__
#include <stdlib.h>
#include <sys/types.h>
#include <ev.h>
#include "../hiredis.h"
#include "../async.h"
typedef struct redisLibevEvents {
redisAsyncContext *context;
struct ev_loop *loop;
int reading, writing;
ev_io rev, wev;
} redisLibevEvents;
static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY
((void)loop);
#endif
((void)revents);
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
redisAsyncHandleRead(e->context);
}
static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY
((void)loop);
#endif
((void)revents);
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
redisAsyncHandleWrite(e->context);
}
static void redisLibevAddRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (!e->reading) {
e->reading = 1;
ev_io_start(EV_A_ &e->rev);
}
}
static void redisLibevDelRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (e->reading) {
e->reading = 0;
ev_io_stop(EV_A_ &e->rev);
}
}
static void redisLibevAddWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (!e->writing) {
e->writing = 1;
ev_io_start(EV_A_ &e->wev);
}
}
static void redisLibevDelWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (e->writing) {
e->writing = 0;
ev_io_stop(EV_A_ &e->wev);
}
}
static void redisLibevCleanup(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
redisLibevDelRead(privdata);
redisLibevDelWrite(privdata);
free(e);
}
static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisLibevEvents *e;
/* Nothing should be attached when something is already attached */
if (ac->ev.data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisLibevEvents*)malloc(sizeof(*e));
e->context = ac;
#if EV_MULTIPLICITY
e->loop = loop;
#else
e->loop = NULL;
#endif
e->reading = e->writing = 0;
e->rev.data = e;
e->wev.data = e;
/* Register functions to start/stop listening for events */
ac->ev.addRead = redisLibevAddRead;
ac->ev.delRead = redisLibevDelRead;
ac->ev.addWrite = redisLibevAddWrite;
ac->ev.delWrite = redisLibevDelWrite;
ac->ev.cleanup = redisLibevCleanup;
ac->ev.data = e;
/* Initialize read/write events */
ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
return REDIS_OK;
}
#endif

View File

@ -1,78 +0,0 @@
#ifndef __HIREDIS_LIBEVENT_H__
#define __HIREDIS_LIBEVENT_H__
#include <event.h>
#include "../hiredis.h"
#include "../async.h"
typedef struct redisLibeventEvents {
redisAsyncContext *context;
struct event rev, wev;
} redisLibeventEvents;
static void redisLibeventReadEvent(int fd, short event, void *arg) {
((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleRead(e->context);
}
static void redisLibeventWriteEvent(int fd, short event, void *arg) {
((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleWrite(e->context);
}
static void redisLibeventAddRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->rev,NULL);
}
static void redisLibeventDelRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev);
}
static void redisLibeventAddWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->wev,NULL);
}
static void redisLibeventDelWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->wev);
}
static void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev);
event_del(&e->wev);
free(e);
}
static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
redisContext *c = &(ac->c);
redisLibeventEvents *e;
/* Nothing should be attached when something is already attached */
if (ac->ev.data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisLibeventEvents*)malloc(sizeof(*e));
e->context = ac;
/* Register functions to start/stop listening for events */
ac->ev.addRead = redisLibeventAddRead;
ac->ev.delRead = redisLibeventDelRead;
ac->ev.addWrite = redisLibeventAddWrite;
ac->ev.delWrite = redisLibeventDelWrite;
ac->ev.cleanup = redisLibeventCleanup;
ac->ev.data = e;
/* Initialize and install read/write events */
event_set(&e->rev,c->fd,EV_READ,redisLibeventReadEvent,e);
event_set(&e->wev,c->fd,EV_WRITE,redisLibeventWriteEvent,e);
event_base_set(base,&e->rev);
event_base_set(base,&e->wev);
return REDIS_OK;
}
#endif

View File

@ -1,622 +0,0 @@
/*
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmacros.h"
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include "async.h"
#include "net.h"
#include "dict.c"
#include "sds.h"
#define _EL_ADD_READ(ctx) do { \
if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
} while(0)
#define _EL_DEL_READ(ctx) do { \
if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
} while(0)
#define _EL_ADD_WRITE(ctx) do { \
if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
} while(0)
#define _EL_DEL_WRITE(ctx) do { \
if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
} while(0)
#define _EL_CLEANUP(ctx) do { \
if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
} while(0);
/* Forward declaration of function in hiredis.c */
void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
/* Functions managing dictionary of callbacks for pub/sub. */
static unsigned int callbackHash(const void *key) {
return dictGenHashFunction((unsigned char*)key,sdslen((char*)key));
}
static void *callbackValDup(void *privdata, const void *src) {
((void) privdata);
redisCallback *dup = malloc(sizeof(*dup));
memcpy(dup,src,sizeof(*dup));
return dup;
}
static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) {
int l1, l2;
((void) privdata);
l1 = sdslen((sds)key1);
l2 = sdslen((sds)key2);
if (l1 != l2) return 0;
return memcmp(key1,key2,l1) == 0;
}
static void callbackKeyDestructor(void *privdata, void *key) {
((void) privdata);
sdsfree((sds)key);
}
static void callbackValDestructor(void *privdata, void *val) {
((void) privdata);
free(val);
}
static dictType callbackDict = {
callbackHash,
NULL,
callbackValDup,
callbackKeyCompare,
callbackKeyDestructor,
callbackValDestructor
};
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
c = &(ac->c);
/* The regular connect functions will always set the flag REDIS_CONNECTED.
* For the async API, we want to wait until the first write event is
* received up before setting this flag, so reset it here. */
c->flags &= ~REDIS_CONNECTED;
ac->err = 0;
ac->errstr = NULL;
ac->data = NULL;
ac->ev.data = NULL;
ac->ev.addRead = NULL;
ac->ev.delRead = NULL;
ac->ev.addWrite = NULL;
ac->ev.delWrite = NULL;
ac->ev.cleanup = NULL;
ac->onConnect = NULL;
ac->onDisconnect = NULL;
ac->replies.head = NULL;
ac->replies.tail = NULL;
ac->sub.invalid.head = NULL;
ac->sub.invalid.tail = NULL;
ac->sub.channels = dictCreate(&callbackDict,NULL);
ac->sub.patterns = dictCreate(&callbackDict,NULL);
return ac;
}
/* We want the error field to be accessible directly instead of requiring
* an indirection to the redisContext struct. */
static void __redisAsyncCopyError(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
ac->err = c->err;
ac->errstr = c->errstr;
}
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
redisContext *c = redisConnectNonBlock(ip,port);
redisAsyncContext *ac = redisAsyncInitialize(c);
__redisAsyncCopyError(ac);
return ac;
}
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
redisContext *c = redisConnectUnixNonBlock(path);
redisAsyncContext *ac = redisAsyncInitialize(c);
__redisAsyncCopyError(ac);
return ac;
}
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
if (ac->onConnect == NULL) {
ac->onConnect = fn;
/* The common way to detect an established connection is to wait for
* the first write event to be fired. This assumes the related event
* library functions are already set. */
_EL_ADD_WRITE(ac);
return REDIS_OK;
}
return REDIS_ERR;
}
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
if (ac->onDisconnect == NULL) {
ac->onDisconnect = fn;
return REDIS_OK;
}
return REDIS_ERR;
}
/* Helper functions to push/shift callbacks */
static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
redisCallback *cb;
/* Copy callback from stack to heap */
cb = malloc(sizeof(*cb));
if (source != NULL) {
memcpy(cb,source,sizeof(*cb));
cb->next = NULL;
}
/* Store callback in list */
if (list->head == NULL)
list->head = cb;
if (list->tail != NULL)
list->tail->next = cb;
list->tail = cb;
return REDIS_OK;
}
static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) {
redisCallback *cb = list->head;
if (cb != NULL) {
list->head = cb->next;
if (cb == list->tail)
list->tail = NULL;
/* Copy callback from heap to stack */
if (target != NULL)
memcpy(target,cb,sizeof(*cb));
free(cb);
return REDIS_OK;
}
return REDIS_ERR;
}
static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) {
redisContext *c = &(ac->c);
if (cb->fn != NULL) {
c->flags |= REDIS_IN_CALLBACK;
cb->fn(ac,reply,cb->privdata);
c->flags &= ~REDIS_IN_CALLBACK;
}
}
/* Helper function to free the context. */
static void __redisAsyncFree(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisCallback cb;
dictIterator *it;
dictEntry *de;
/* Execute pending callbacks with NULL reply. */
while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)
__redisRunCallback(ac,&cb,NULL);
/* Execute callbacks for invalid commands */
while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
__redisRunCallback(ac,&cb,NULL);
/* Run subscription callbacks callbacks with NULL reply */
it = dictGetIterator(ac->sub.channels);
while ((de = dictNext(it)) != NULL)
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
dictReleaseIterator(it);
dictRelease(ac->sub.channels);
it = dictGetIterator(ac->sub.patterns);
while ((de = dictNext(it)) != NULL)
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
dictReleaseIterator(it);
dictRelease(ac->sub.patterns);
/* Signal event lib to clean up */
_EL_CLEANUP(ac);
/* Execute disconnect callback. When redisAsyncFree() initiated destroying
* this context, the status will always be REDIS_OK. */
if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {
if (c->flags & REDIS_FREEING) {
ac->onDisconnect(ac,REDIS_OK);
} else {
ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);
}
}
/* Cleanup self */
redisFree(c);
}
/* Free the async context. When this function is called from a callback,
* control needs to be returned to redisProcessCallbacks() before actual
* free'ing. To do so, a flag is set on the context which is picked up by
* redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
void redisAsyncFree(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
c->flags |= REDIS_FREEING;
if (!(c->flags & REDIS_IN_CALLBACK))
__redisAsyncFree(ac);
}
/* Helper function to make the disconnect happen and clean up. */
static void __redisAsyncDisconnect(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
/* Make sure error is accessible if there is any */
__redisAsyncCopyError(ac);
if (ac->err == 0) {
/* For clean disconnects, there should be no pending callbacks. */
assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR);
} else {
/* Disconnection is caused by an error, make sure that pending
* callbacks cannot call new commands. */
c->flags |= REDIS_DISCONNECTING;
}
/* For non-clean disconnects, __redisAsyncFree() will execute pending
* callbacks with a NULL-reply. */
__redisAsyncFree(ac);
}
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
* from being issued, but tries to flush the output buffer and execute
* callbacks for all remaining replies. When this function is called from a
* callback, there might be more replies and we can safely defer disconnecting
* to redisProcessCallbacks(). Otherwise, we can only disconnect immediately
* when there are no pending callbacks. */
void redisAsyncDisconnect(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
c->flags |= REDIS_DISCONNECTING;
if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)
__redisAsyncDisconnect(ac);
}
static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
redisContext *c = &(ac->c);
dict *callbacks;
dictEntry *de;
int pvariant;
char *stype;
sds sname;
/* Custom reply functions are not supported for pub/sub. This will fail
* very hard when they are used... */
if (reply->type == REDIS_REPLY_ARRAY) {
assert(reply->elements >= 2);
assert(reply->element[0]->type == REDIS_REPLY_STRING);
stype = reply->element[0]->str;
pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;
if (pvariant)
callbacks = ac->sub.patterns;
else
callbacks = ac->sub.channels;
/* Locate the right callback */
assert(reply->element[1]->type == REDIS_REPLY_STRING);
sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
de = dictFind(callbacks,sname);
if (de != NULL) {
memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb));
/* If this is an unsubscribe message, remove it. */
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
dictDelete(callbacks,sname);
/* If this was the last unsubscribe message, revert to
* non-subscribe mode. */
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
if (reply->element[2]->integer == 0)
c->flags &= ~REDIS_SUBSCRIBED;
}
}
sdsfree(sname);
} else {
/* Shift callback for invalid commands. */
__redisShiftCallback(&ac->sub.invalid,dstcb);
}
return REDIS_OK;
}
void redisProcessCallbacks(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisCallback cb;
void *reply = NULL;
int status;
while((status = redisGetReply(c,&reply)) == REDIS_OK) {
if (reply == NULL) {
/* When the connection is being disconnected and there are
* no more replies, this is the cue to really disconnect. */
if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) {
__redisAsyncDisconnect(ac);
return;
}
/* If monitor mode, repush callback */
if(c->flags & REDIS_MONITORING) {
__redisPushCallback(&ac->replies,&cb);
}
/* When the connection is not being disconnected, simply stop
* trying to get replies and wait for the next loop tick. */
break;
}
/* Even if the context is subscribed, pending regular callbacks will
* get a reply before pub/sub messages arrive. */
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
/*
* A spontaneous reply in a not-subscribed context can be the error
* reply that is sent when a new connection exceeds the maximum
* number of allowed connections on the server side.
*
* This is seen as an error instead of a regular reply because the
* server closes the connection after sending it.
*
* To prevent the error from being overwritten by an EOF error the
* connection is closed here. See issue #43.
*
* Another possibility is that the server is loading its dataset.
* In this case we also want to close the connection, and have the
* user wait until the server is ready to take our request.
*/
if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
c->err = REDIS_ERR_OTHER;
snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
__redisAsyncDisconnect(ac);
return;
}
/* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */
assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING));
if(c->flags & REDIS_SUBSCRIBED)
__redisGetSubscribeCallback(ac,reply,&cb);
}
if (cb.fn != NULL) {
__redisRunCallback(ac,&cb,reply);
c->reader->fn->freeObject(reply);
/* Proceed with free'ing when redisAsyncFree() was called. */
if (c->flags & REDIS_FREEING) {
__redisAsyncFree(ac);
return;
}
} else {
/* No callback for this reply. This can either be a NULL callback,
* or there were no callbacks to begin with. Either way, don't
* abort with an error, but simply ignore it because the client
* doesn't know what the server will spit out over the wire. */
c->reader->fn->freeObject(reply);
}
}
/* Disconnect when there was an error reading the reply */
if (status != REDIS_OK)
__redisAsyncDisconnect(ac);
}
/* Internal helper function to detect socket status the first time a read or
* write event fires. When connecting was not succesful, the connect callback
* is called with a REDIS_ERR status and the context is free'd. */
static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
if (redisCheckSocketError(c,c->fd) == REDIS_ERR) {
/* Try again later when connect(2) is still in progress. */
if (errno == EINPROGRESS)
return REDIS_OK;
if (ac->onConnect) ac->onConnect(ac,REDIS_ERR);
__redisAsyncDisconnect(ac);
return REDIS_ERR;
}
/* Mark context as connected. */
c->flags |= REDIS_CONNECTED;
if (ac->onConnect) ac->onConnect(ac,REDIS_OK);
return REDIS_OK;
}
/* This function should be called when the socket is readable.
* It processes all replies that can be read and executes their callbacks.
*/
void redisAsyncHandleRead(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
if (!(c->flags & REDIS_CONNECTED)) {
/* Abort connect was not successful. */
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
return;
/* Try again later when the context is still not connected. */
if (!(c->flags & REDIS_CONNECTED))
return;
}
if (redisBufferRead(c) == REDIS_ERR) {
__redisAsyncDisconnect(ac);
} else {
/* Always re-schedule reads */
_EL_ADD_READ(ac);
redisProcessCallbacks(ac);
}
}
void redisAsyncHandleWrite(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
int done = 0;
if (!(c->flags & REDIS_CONNECTED)) {
/* Abort connect was not successful. */
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
return;
/* Try again later when the context is still not connected. */
if (!(c->flags & REDIS_CONNECTED))
return;
}
if (redisBufferWrite(c,&done) == REDIS_ERR) {
__redisAsyncDisconnect(ac);
} else {
/* Continue writing when not done, stop writing otherwise */
if (!done)
_EL_ADD_WRITE(ac);
else
_EL_DEL_WRITE(ac);
/* Always schedule reads after writes */
_EL_ADD_READ(ac);
}
}
/* Sets a pointer to the first argument and its length starting at p. Returns
* the number of bytes to skip to get to the following argument. */
static char *nextArgument(char *start, char **str, size_t *len) {
char *p = start;
if (p[0] != '$') {
p = strchr(p,'$');
if (p == NULL) return NULL;
}
*len = (int)strtol(p+1,NULL,10);
p = strchr(p,'\r');
assert(p);
*str = p+2;
return p+2+(*len)+2;
}
/* Helper function for the redisAsyncCommand* family of functions. Writes a
* formatted command to the output buffer and registers the provided callback
* function with the context. */
static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, char *cmd, size_t len) {
redisContext *c = &(ac->c);
redisCallback cb;
int pvariant, hasnext;
char *cstr, *astr;
size_t clen, alen;
char *p;
sds sname;
/* Don't accept new commands when the connection is about to be closed. */
if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
/* Setup callback */
cb.fn = fn;
cb.privdata = privdata;
/* Find out which command will be appended. */
p = nextArgument(cmd,&cstr,&clen);
assert(p != NULL);
hasnext = (p[0] == '$');
pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0;
cstr += pvariant;
clen -= pvariant;
if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) {
c->flags |= REDIS_SUBSCRIBED;
/* Add every channel/pattern to the list of subscription callbacks. */
while ((p = nextArgument(p,&astr,&alen)) != NULL) {
sname = sdsnewlen(astr,alen);
if (pvariant)
dictReplace(ac->sub.patterns,sname,&cb);
else
dictReplace(ac->sub.channels,sname,&cb);
}
} else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
/* It is only useful to call (P)UNSUBSCRIBE when the context is
* subscribed to one or more channels or patterns. */
if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;
/* (P)UNSUBSCRIBE does not have its own response: every channel or
* pattern that is unsubscribed will receive a message. This means we
* should not append a callback function for this command. */
} else if(strncasecmp(cstr,"monitor\r\n",9) == 0) {
/* Set monitor flag and push callback */
c->flags |= REDIS_MONITORING;
__redisPushCallback(&ac->replies,&cb);
} else {
if (c->flags & REDIS_SUBSCRIBED)
/* This will likely result in an error reply, but it needs to be
* received and passed to the callback. */
__redisPushCallback(&ac->sub.invalid,&cb);
else
__redisPushCallback(&ac->replies,&cb);
}
__redisAppendCommand(c,cmd,len);
/* Always schedule a write when the write buffer is non-empty */
_EL_ADD_WRITE(ac);
return REDIS_OK;
}
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
char *cmd;
int len;
int status;
len = redisvFormatCommand(&cmd,format,ap);
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
free(cmd);
return status;
}
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) {
va_list ap;
int status;
va_start(ap,format);
status = redisvAsyncCommand(ac,fn,privdata,format,ap);
va_end(ap);
return status;
}
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
char *cmd;
int len;
int status;
len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
free(cmd);
return status;
}

View File

@ -1,125 +0,0 @@
/*
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __HIREDIS_ASYNC_H
#define __HIREDIS_ASYNC_H
#include "hiredis.h"
#ifdef __cplusplus
extern "C" {
#endif
struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
struct dict; /* dictionary header is included in async.c */
/* Reply callback prototype and container */
typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
typedef struct redisCallback {
struct redisCallback *next; /* simple singly linked list */
redisCallbackFn *fn;
void *privdata;
} redisCallback;
/* List of callbacks for either regular replies or pub/sub */
typedef struct redisCallbackList {
redisCallback *head, *tail;
} redisCallbackList;
/* Connection callback prototypes */
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
/* Context for an async connection to Redis */
typedef struct redisAsyncContext {
/* Hold the regular context, so it can be realloc'ed. */
redisContext c;
/* Setup error flags so they can be used directly. */
int err;
char *errstr;
/* Not used by hiredis */
void *data;
/* Event library data and hooks */
struct {
void *data;
/* Hooks that are called when the library expects to start
* reading/writing. These functions should be idempotent. */
void (*addRead)(void *privdata);
void (*delRead)(void *privdata);
void (*addWrite)(void *privdata);
void (*delWrite)(void *privdata);
void (*cleanup)(void *privdata);
} ev;
/* Called when either the connection is terminated due to an error or per
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
redisDisconnectCallback *onDisconnect;
/* Called when the first write event was received. */
redisConnectCallback *onConnect;
/* Regular command callbacks */
redisCallbackList replies;
/* Subscription callbacks */
struct {
redisCallbackList invalid;
struct dict *channels;
struct dict *patterns;
} sub;
} redisAsyncContext;
/* Functions that proxy to hiredis */
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
redisAsyncContext *redisAsyncConnectUnix(const char *path);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
void redisAsyncDisconnect(redisAsyncContext *ac);
void redisAsyncFree(redisAsyncContext *ac);
/* Handle read/write events */
void redisAsyncHandleRead(redisAsyncContext *ac);
void redisAsyncHandleWrite(redisAsyncContext *ac);
/* Command functions for an async context. Write the command to the
* output buffer and register the provided callback. */
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,338 +0,0 @@
/* Hash table implementation.
*
* This file implements in memory hash tables with insert/del/replace/find/
* get-random-element operations. Hash tables will auto resize if needed
* tables of power of two in size are used, collisions are handled by
* chaining. See the source code for more information... :)
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmacros.h"
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include "dict.h"
/* -------------------------- private prototypes ---------------------------- */
static int _dictExpandIfNeeded(dict *ht);
static unsigned long _dictNextPower(unsigned long size);
static int _dictKeyIndex(dict *ht, const void *key);
static int _dictInit(dict *ht, dictType *type, void *privDataPtr);
/* -------------------------- hash functions -------------------------------- */
/* Generic hash function (a popular one from Bernstein).
* I tested a few and this was the best. */
static unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
unsigned int hash = 5381;
while (len--)
hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
return hash;
}
/* ----------------------------- API implementation ------------------------- */
/* Reset an hashtable already initialized with ht_init().
* NOTE: This function should only called by ht_destroy(). */
static void _dictReset(dict *ht) {
ht->table = NULL;
ht->size = 0;
ht->sizemask = 0;
ht->used = 0;
}
/* Create a new hash table */
static dict *dictCreate(dictType *type, void *privDataPtr) {
dict *ht = malloc(sizeof(*ht));
_dictInit(ht,type,privDataPtr);
return ht;
}
/* Initialize the hash table */
static int _dictInit(dict *ht, dictType *type, void *privDataPtr) {
_dictReset(ht);
ht->type = type;
ht->privdata = privDataPtr;
return DICT_OK;
}
/* Expand or create the hashtable */
static int dictExpand(dict *ht, unsigned long size) {
dict n; /* the new hashtable */
unsigned long realsize = _dictNextPower(size), i;
/* the size is invalid if it is smaller than the number of
* elements already inside the hashtable */
if (ht->used > size)
return DICT_ERR;
_dictInit(&n, ht->type, ht->privdata);
n.size = realsize;
n.sizemask = realsize-1;
n.table = calloc(realsize,sizeof(dictEntry*));
/* Copy all the elements from the old to the new table:
* note that if the old hash table is empty ht->size is zero,
* so dictExpand just creates an hash table. */
n.used = ht->used;
for (i = 0; i < ht->size && ht->used > 0; i++) {
dictEntry *he, *nextHe;
if (ht->table[i] == NULL) continue;
/* For each hash entry on this slot... */
he = ht->table[i];
while(he) {
unsigned int h;
nextHe = he->next;
/* Get the new element index */
h = dictHashKey(ht, he->key) & n.sizemask;
he->next = n.table[h];
n.table[h] = he;
ht->used--;
/* Pass to the next element */
he = nextHe;
}
}
assert(ht->used == 0);
free(ht->table);
/* Remap the new hashtable in the old */
*ht = n;
return DICT_OK;
}
/* Add an element to the target hash table */
static int dictAdd(dict *ht, void *key, void *val) {
int index;
dictEntry *entry;
/* Get the index of the new element, or -1 if
* the element already exists. */
if ((index = _dictKeyIndex(ht, key)) == -1)
return DICT_ERR;
/* Allocates the memory and stores key */
entry = malloc(sizeof(*entry));
entry->next = ht->table[index];
ht->table[index] = entry;
/* Set the hash entry fields. */
dictSetHashKey(ht, entry, key);
dictSetHashVal(ht, entry, val);
ht->used++;
return DICT_OK;
}
/* Add an element, discarding the old if the key already exists.
* Return 1 if the key was added from scratch, 0 if there was already an
* element with such key and dictReplace() just performed a value update
* operation. */
static int dictReplace(dict *ht, void *key, void *val) {
dictEntry *entry, auxentry;
/* Try to add the element. If the key
* does not exists dictAdd will suceed. */
if (dictAdd(ht, key, val) == DICT_OK)
return 1;
/* It already exists, get the entry */
entry = dictFind(ht, key);
/* Free the old value and set the new one */
/* Set the new value and free the old one. Note that it is important
* to do that in this order, as the value may just be exactly the same
* as the previous one. In this context, think to reference counting,
* you want to increment (set), and then decrement (free), and not the
* reverse. */
auxentry = *entry;
dictSetHashVal(ht, entry, val);
dictFreeEntryVal(ht, &auxentry);
return 0;
}
/* Search and remove an element */
static int dictDelete(dict *ht, const void *key) {
unsigned int h;
dictEntry *de, *prevde;
if (ht->size == 0)
return DICT_ERR;
h = dictHashKey(ht, key) & ht->sizemask;
de = ht->table[h];
prevde = NULL;
while(de) {
if (dictCompareHashKeys(ht,key,de->key)) {
/* Unlink the element from the list */
if (prevde)
prevde->next = de->next;
else
ht->table[h] = de->next;
dictFreeEntryKey(ht,de);
dictFreeEntryVal(ht,de);
free(de);
ht->used--;
return DICT_OK;
}
prevde = de;
de = de->next;
}
return DICT_ERR; /* not found */
}
/* Destroy an entire hash table */
static int _dictClear(dict *ht) {
unsigned long i;
/* Free all the elements */
for (i = 0; i < ht->size && ht->used > 0; i++) {
dictEntry *he, *nextHe;
if ((he = ht->table[i]) == NULL) continue;
while(he) {
nextHe = he->next;
dictFreeEntryKey(ht, he);
dictFreeEntryVal(ht, he);
free(he);
ht->used--;
he = nextHe;
}
}
/* Free the table and the allocated cache structure */
free(ht->table);
/* Re-initialize the table */
_dictReset(ht);
return DICT_OK; /* never fails */
}
/* Clear & Release the hash table */
static void dictRelease(dict *ht) {
_dictClear(ht);
free(ht);
}
static dictEntry *dictFind(dict *ht, const void *key) {
dictEntry *he;
unsigned int h;
if (ht->size == 0) return NULL;
h = dictHashKey(ht, key) & ht->sizemask;
he = ht->table[h];
while(he) {
if (dictCompareHashKeys(ht, key, he->key))
return he;
he = he->next;
}
return NULL;
}
static dictIterator *dictGetIterator(dict *ht) {
dictIterator *iter = malloc(sizeof(*iter));
iter->ht = ht;
iter->index = -1;
iter->entry = NULL;
iter->nextEntry = NULL;
return iter;
}
static dictEntry *dictNext(dictIterator *iter) {
while (1) {
if (iter->entry == NULL) {
iter->index++;
if (iter->index >=
(signed)iter->ht->size) break;
iter->entry = iter->ht->table[iter->index];
} else {
iter->entry = iter->nextEntry;
}
if (iter->entry) {
/* We need to save the 'next' here, the iterator user
* may delete the entry we are returning. */
iter->nextEntry = iter->entry->next;
return iter->entry;
}
}
return NULL;
}
static void dictReleaseIterator(dictIterator *iter) {
free(iter);
}
/* ------------------------- private functions ------------------------------ */
/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *ht) {
/* If the hash table is empty expand it to the intial size,
* if the table is "full" dobule its size. */
if (ht->size == 0)
return dictExpand(ht, DICT_HT_INITIAL_SIZE);
if (ht->used == ht->size)
return dictExpand(ht, ht->size*2);
return DICT_OK;
}
/* Our hash table capability is a power of two */
static unsigned long _dictNextPower(unsigned long size) {
unsigned long i = DICT_HT_INITIAL_SIZE;
if (size >= LONG_MAX) return LONG_MAX;
while(1) {
if (i >= size)
return i;
i *= 2;
}
}
/* Returns the index of a free slot that can be populated with
* an hash entry for the given 'key'.
* If the key already exists, -1 is returned. */
static int _dictKeyIndex(dict *ht, const void *key) {
unsigned int h;
dictEntry *he;
/* Expand the hashtable if needed */
if (_dictExpandIfNeeded(ht) == DICT_ERR)
return -1;
/* Compute the key hash value */
h = dictHashKey(ht, key) & ht->sizemask;
/* Search if this slot does not already contain the given key */
he = ht->table[h];
while(he) {
if (dictCompareHashKeys(ht, key, he->key))
return -1;
he = he->next;
}
return h;
}

View File

@ -1,126 +0,0 @@
/* Hash table implementation.
*
* This file implements in memory hash tables with insert/del/replace/find/
* get-random-element operations. Hash tables will auto resize if needed
* tables of power of two in size are used, collisions are handled by
* chaining. See the source code for more information... :)
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __DICT_H
#define __DICT_H
#define DICT_OK 0
#define DICT_ERR 1
/* Unused arguments generate annoying warnings... */
#define DICT_NOTUSED(V) ((void) V)
typedef struct dictEntry {
void *key;
void *val;
struct dictEntry *next;
} dictEntry;
typedef struct dictType {
unsigned int (*hashFunction)(const void *key);
void *(*keyDup)(void *privdata, const void *key);
void *(*valDup)(void *privdata, const void *obj);
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
void (*keyDestructor)(void *privdata, void *key);
void (*valDestructor)(void *privdata, void *obj);
} dictType;
typedef struct dict {
dictEntry **table;
dictType *type;
unsigned long size;
unsigned long sizemask;
unsigned long used;
void *privdata;
} dict;
typedef struct dictIterator {
dict *ht;
int index;
dictEntry *entry, *nextEntry;
} dictIterator;
/* This is the initial size of every hash table */
#define DICT_HT_INITIAL_SIZE 4
/* ------------------------------- Macros ------------------------------------*/
#define dictFreeEntryVal(ht, entry) \
if ((ht)->type->valDestructor) \
(ht)->type->valDestructor((ht)->privdata, (entry)->val)
#define dictSetHashVal(ht, entry, _val_) do { \
if ((ht)->type->valDup) \
entry->val = (ht)->type->valDup((ht)->privdata, _val_); \
else \
entry->val = (_val_); \
} while(0)
#define dictFreeEntryKey(ht, entry) \
if ((ht)->type->keyDestructor) \
(ht)->type->keyDestructor((ht)->privdata, (entry)->key)
#define dictSetHashKey(ht, entry, _key_) do { \
if ((ht)->type->keyDup) \
entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \
else \
entry->key = (_key_); \
} while(0)
#define dictCompareHashKeys(ht, key1, key2) \
(((ht)->type->keyCompare) ? \
(ht)->type->keyCompare((ht)->privdata, key1, key2) : \
(key1) == (key2))
#define dictHashKey(ht, key) (ht)->type->hashFunction(key)
#define dictGetEntryKey(he) ((he)->key)
#define dictGetEntryVal(he) ((he)->val)
#define dictSlots(ht) ((ht)->size)
#define dictSize(ht) ((ht)->used)
/* API */
static unsigned int dictGenHashFunction(const unsigned char *buf, int len);
static dict *dictCreate(dictType *type, void *privDataPtr);
static int dictExpand(dict *ht, unsigned long size);
static int dictAdd(dict *ht, void *key, void *val);
static int dictReplace(dict *ht, void *key, void *val);
static int dictDelete(dict *ht, const void *key);
static void dictRelease(dict *ht);
static dictEntry * dictFind(dict *ht, const void *key);
static dictIterator *dictGetIterator(dict *ht);
static dictEntry *dictNext(dictIterator *iter);
static void dictReleaseIterator(dictIterator *iter);
#endif /* __DICT_H */

View File

@ -1,56 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis.h"
#include "async.h"
#include "adapters/ae.h"
/* Put event loop in the global scope, so it can be explicitly stopped */
static aeEventLoop *loop;
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Disconnected...\n");
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
loop = aeCreateEventLoop();
redisAeAttach(loop, c);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
aeMain(loop);
return 0;
}

View File

@ -1,51 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis.h"
#include "async.h"
#include "adapters/libev.h"
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Disconnected...\n");
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
redisLibevAttach(EV_DEFAULT_ c);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
ev_loop(EV_DEFAULT_ 0);
return 0;
}

View File

@ -1,52 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis.h"
#include "async.h"
#include "adapters/libevent.h"
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Disconnected...\n");
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
struct event_base *base = event_base_new();
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
redisLibeventAttach(c,base);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
event_base_dispatch(base);
return 0;
}

View File

@ -1,68 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hiredis.h"
int main(void) {
unsigned int j;
redisContext *c;
redisReply *reply;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
c = redisConnectWithTimeout((char*)"127.0.0.2", 6379, timeout);
if (c->err) {
printf("Connection error: %s\n", c->errstr);
exit(1);
}
/* PING server */
reply = redisCommand(c,"PING");
printf("PING: %s\n", reply->str);
freeReplyObject(reply);
/* Set a key */
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
printf("SET: %s\n", reply->str);
freeReplyObject(reply);
/* Set a key using binary safe API */
reply = redisCommand(c,"SET %b %b", "bar", 3, "hello", 5);
printf("SET (binary API): %s\n", reply->str);
freeReplyObject(reply);
/* Try a GET and two INCR */
reply = redisCommand(c,"GET foo");
printf("GET foo: %s\n", reply->str);
freeReplyObject(reply);
reply = redisCommand(c,"INCR counter");
printf("INCR counter: %lld\n", reply->integer);
freeReplyObject(reply);
/* again ... */
reply = redisCommand(c,"INCR counter");
printf("INCR counter: %lld\n", reply->integer);
freeReplyObject(reply);
/* Create a list of numbers, from 0 to 9 */
reply = redisCommand(c,"DEL mylist");
freeReplyObject(reply);
for (j = 0; j < 10; j++) {
char buf[64];
snprintf(buf,64,"%d",j);
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
freeReplyObject(reply);
}
/* Let's check what we have inside the list */
reply = redisCommand(c,"LRANGE mylist 0 -1");
if (reply->type == REDIS_REPLY_ARRAY) {
for (j = 0; j < reply->elements; j++) {
printf("%u) %s\n", j, reply->element[j]->str);
}
}
freeReplyObject(reply);
return 0;
}

View File

@ -1,16 +0,0 @@
#ifndef __HIREDIS_FMACRO_H
#define __HIREDIS_FMACRO_H
#if !defined(_BSD_SOURCE)
#define _BSD_SOURCE
#endif
#if defined(__sun__)
#define _POSIX_C_SOURCE 200112L
#elif defined(__linux__)
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,210 +0,0 @@
/*
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __HIREDIS_H
#define __HIREDIS_H
#include <stdio.h> /* for size_t */
#include <stdarg.h> /* for va_list */
#include <sys/time.h> /* for struct timeval */
#define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 11
#define HIREDIS_PATCH 0
#define REDIS_ERR -1
#define REDIS_OK 0
/* When an error occurs, the err flag in a context is set to hold the type of
* error that occured. REDIS_ERR_IO means there was an I/O error and you
* should use the "errno" variable to find out what is wrong.
* For other values, the "errstr" field will hold a description. */
#define REDIS_ERR_IO 1 /* Error in read or write */
#define REDIS_ERR_EOF 3 /* End of file */
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
#define REDIS_ERR_OOM 5 /* Out of memory */
#define REDIS_ERR_OTHER 2 /* Everything else... */
/* Connection type can be blocking or non-blocking and is set in the
* least significant bit of the flags field in redisContext. */
#define REDIS_BLOCK 0x1
/* Connection may be disconnected before being free'd. The second bit
* in the flags field is set when the context is connected. */
#define REDIS_CONNECTED 0x2
/* The async API might try to disconnect cleanly and flush the output
* buffer and read all subsequent replies before disconnecting.
* This flag means no new commands can come in and the connection
* should be terminated once all replies have been read. */
#define REDIS_DISCONNECTING 0x4
/* Flag specific to the async API which means that the context should be clean
* up as soon as possible. */
#define REDIS_FREEING 0x8
/* Flag that is set when an async callback is executed. */
#define REDIS_IN_CALLBACK 0x10
/* Flag that is set when the async context has one or more subscriptions. */
#define REDIS_SUBSCRIBED 0x20
/* Flag that is set when monitor mode is active */
#define REDIS_MONITORING 0x40
#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
#ifdef __cplusplus
extern "C" {
#endif
/* This is the reply object returned by redisCommand() */
typedef struct redisReply {
int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
int len; /* Length of string */
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;
typedef struct redisReadTask {
int type;
int elements; /* number of elements in multibulk container */
int idx; /* index in parent (array) object */
void *obj; /* holds user-generated value for a read task */
struct redisReadTask *parent; /* parent task */
void *privdata; /* user-settable arbitrary field */
} redisReadTask;
typedef struct redisReplyObjectFunctions {
void *(*createString)(const redisReadTask*, char*, size_t);
void *(*createArray)(const redisReadTask*, int);
void *(*createInteger)(const redisReadTask*, long long);
void *(*createNil)(const redisReadTask*);
void (*freeObject)(void*);
} redisReplyObjectFunctions;
/* State for the protocol parser */
typedef struct redisReader {
int err; /* Error flags, 0 when there is no error */
char errstr[128]; /* String representation of error when applicable */
char *buf; /* Read buffer */
size_t pos; /* Buffer cursor */
size_t len; /* Buffer length */
size_t maxbuf; /* Max length of unused buffer */
redisReadTask rstack[9];
int ridx; /* Index of current read task */
void *reply; /* Temporary reply pointer */
redisReplyObjectFunctions *fn;
void *privdata;
} redisReader;
/* Public API for the protocol parser. */
redisReader *redisReaderCreate(void);
void redisReaderFree(redisReader *r);
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
int redisReaderGetReply(redisReader *r, void **reply);
/* Backwards compatibility, can be removed on big version bump. */
#define redisReplyReaderCreate redisReaderCreate
#define redisReplyReaderFree redisReaderFree
#define redisReplyReaderFeed redisReaderFeed
#define redisReplyReaderGetReply redisReaderGetReply
#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
/* Function to free the reply objects hiredis returns by default. */
void freeReplyObject(void *reply);
/* Functions to format a command according to the protocol. */
int redisvFormatCommand(char **target, const char *format, va_list ap);
int redisFormatCommand(char **target, const char *format, ...);
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
/* Context for a connection to Redis */
typedef struct redisContext {
int err; /* Error flags, 0 when there is no error */
char errstr[128]; /* String representation of error when applicable */
int fd;
int flags;
char *obuf; /* Write buffer */
redisReader *reader; /* Protocol reader */
} redisContext;
redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
redisContext *redisConnectNonBlock(const char *ip, int port);
redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path);
int redisSetTimeout(redisContext *c, struct timeval tv);
void redisFree(redisContext *c);
int redisBufferRead(redisContext *c);
int redisBufferWrite(redisContext *c, int *done);
/* In a blocking context, this function first checks if there are unconsumed
* replies to return and returns one if so. Otherwise, it flushes the output
* buffer to the socket and reads until it has a reply. In a non-blocking
* context, it will return unconsumed replies until there are no more. */
int redisGetReply(redisContext *c, void **reply);
int redisGetReplyFromReader(redisContext *c, void **reply);
/* Write a command to the output buffer. Use these functions in blocking mode
* to get a pipeline of commands. */
int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
int redisAppendCommand(redisContext *c, const char *format, ...);
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
/* Issue a command to Redis. In a blocking context, it is identical to calling
* redisAppendCommand, followed by redisGetReply. The function will return
* NULL if there was an error in performing the request, otherwise it will
* return the reply. In a non-blocking context, it is identical to calling
* only redisAppendCommand and will always return NULL. */
void *redisvCommand(redisContext *c, const char *format, va_list ap);
void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,291 +0,0 @@
/* Extracted from anet.c to work properly with Hiredis error reporting.
*
* Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmacros.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <poll.h>
#include <limits.h>
#include "net.h"
#include "sds.h"
/* Defined in hiredis.c */
void __redisSetError(redisContext *c, int type, const char *str);
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
char buf[128];
size_t len = 0;
if (prefix != NULL)
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
strerror_r(errno,buf+len,sizeof(buf)-len);
__redisSetError(c,type,buf);
}
static int redisSetReuseAddr(redisContext *c, int fd) {
int on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
close(fd);
return REDIS_ERR;
}
return REDIS_OK;
}
static int redisCreateSocket(redisContext *c, int type) {
int s;
if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
return REDIS_ERR;
}
if (type == AF_INET) {
if (redisSetReuseAddr(c,s) == REDIS_ERR) {
return REDIS_ERR;
}
}
return s;
}
static int redisSetBlocking(redisContext *c, int fd, int blocking) {
int flags;
/* Set the socket nonblocking.
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
* interrupted by a signal. */
if ((flags = fcntl(fd, F_GETFL)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
close(fd);
return REDIS_ERR;
}
if (blocking)
flags &= ~O_NONBLOCK;
else
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
close(fd);
return REDIS_ERR;
}
return REDIS_OK;
}
static int redisSetTcpNoDelay(redisContext *c, int fd) {
int yes = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
close(fd);
return REDIS_ERR;
}
return REDIS_OK;
}
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {
struct pollfd wfd[1];
long msec;
msec = -1;
wfd[0].fd = fd;
wfd[0].events = POLLOUT;
/* Only use timeout when not NULL. */
if (timeout != NULL) {
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
close(fd);
return REDIS_ERR;
}
msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
if (msec < 0 || msec > INT_MAX) {
msec = INT_MAX;
}
}
if (errno == EINPROGRESS) {
int res;
if ((res = poll(wfd, 1, msec)) == -1) {
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
close(fd);
return REDIS_ERR;
} else if (res == 0) {
errno = ETIMEDOUT;
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
close(fd);
return REDIS_ERR;
}
if (redisCheckSocketError(c, fd) != REDIS_OK)
return REDIS_ERR;
return REDIS_OK;
}
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
close(fd);
return REDIS_ERR;
}
int redisCheckSocketError(redisContext *c, int fd) {
int err = 0;
socklen_t errlen = sizeof(err);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
close(fd);
return REDIS_ERR;
}
if (err) {
errno = err;
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
close(fd);
return REDIS_ERR;
}
return REDIS_OK;
}
int redisContextSetTimeout(redisContext *c, struct timeval tv) {
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
return REDIS_ERR;
}
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
return REDIS_ERR;
}
return REDIS_OK;
}
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
int s, rv;
char _port[6]; /* strlen("65535"); */
struct addrinfo hints, *servinfo, *p;
int blocking = (c->flags & REDIS_BLOCK);
snprintf(_port, 6, "%d", port);
memset(&hints,0,sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
return REDIS_ERR;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
continue;
if (redisSetBlocking(c,s,0) != REDIS_OK)
goto error;
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
if (errno == EHOSTUNREACH) {
close(s);
continue;
} else if (errno == EINPROGRESS && !blocking) {
/* This is ok. */
} else {
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
goto error;
}
}
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
goto error;
if (redisSetTcpNoDelay(c,s) != REDIS_OK)
goto error;
c->fd = s;
c->flags |= REDIS_CONNECTED;
rv = REDIS_OK;
goto end;
}
if (p == NULL) {
char buf[128];
snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
__redisSetError(c,REDIS_ERR_OTHER,buf);
goto error;
}
error:
rv = REDIS_ERR;
end:
freeaddrinfo(servinfo);
return rv; // Need to return REDIS_OK if alright
}
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {
int s;
int blocking = (c->flags & REDIS_BLOCK);
struct sockaddr_un sa;
if ((s = redisCreateSocket(c,AF_LOCAL)) < 0)
return REDIS_ERR;
if (redisSetBlocking(c,s,0) != REDIS_OK)
return REDIS_ERR;
sa.sun_family = AF_LOCAL;
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
if (errno == EINPROGRESS && !blocking) {
/* This is ok. */
} else {
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
return REDIS_ERR;
}
}
/* Reset socket to be blocking after connect(2). */
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
return REDIS_ERR;
c->fd = s;
c->flags |= REDIS_CONNECTED;
return REDIS_OK;
}

View File

@ -1,47 +0,0 @@
/* Extracted from anet.c to work properly with Hiredis error reporting.
*
* Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NET_H
#define __NET_H
#include "hiredis.h"
#if defined(__sun)
#define AF_LOCAL AF_UNIX
#endif
int redisCheckSocketError(redisContext *c, int fd);
int redisContextSetTimeout(redisContext *c, struct timeval tv);
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);
#endif

View File

@ -1,605 +0,0 @@
/* SDSLib, A C dynamic strings library
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sds.h"
#ifdef SDS_ABORT_ON_OOM
static void sdsOomAbort(void) {
fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
abort();
}
#endif
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
sh = malloc(sizeof(struct sdshdr)+initlen+1);
#ifdef SDS_ABORT_ON_OOM
if (sh == NULL) sdsOomAbort();
#else
if (sh == NULL) return NULL;
#endif
sh->len = initlen;
sh->free = 0;
if (initlen) {
if (init) memcpy(sh->buf, init, initlen);
else memset(sh->buf,0,initlen);
}
sh->buf[initlen] = '\0';
return (char*)sh->buf;
}
sds sdsempty(void) {
return sdsnewlen("",0);
}
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
}
sds sdsdup(const sds s) {
return sdsnewlen(s, sdslen(s));
}
void sdsfree(sds s) {
if (s == NULL) return;
free(s-sizeof(struct sdshdr));
}
void sdsupdatelen(sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
int reallen = strlen(s);
sh->free += (sh->len-reallen);
sh->len = reallen;
}
static sds sdsMakeRoomFor(sds s, size_t addlen) {
struct sdshdr *sh, *newsh;
size_t free = sdsavail(s);
size_t len, newlen;
if (free >= addlen) return s;
len = sdslen(s);
sh = (void*) (s-(sizeof(struct sdshdr)));
newlen = (len+addlen)*2;
newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1);
#ifdef SDS_ABORT_ON_OOM
if (newsh == NULL) sdsOomAbort();
#else
if (newsh == NULL) return NULL;
#endif
newsh->free = newlen - len;
return newsh->buf;
}
/* Grow the sds to have the specified length. Bytes that were not part of
* the original length of the sds will be set to zero. */
sds sdsgrowzero(sds s, size_t len) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
size_t totlen, curlen = sh->len;
if (len <= curlen) return s;
s = sdsMakeRoomFor(s,len-curlen);
if (s == NULL) return NULL;
/* Make sure added region doesn't contain garbage */
sh = (void*)(s-(sizeof(struct sdshdr)));
memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
totlen = sh->len+sh->free;
sh->len = len;
sh->free = totlen-sh->len;
return s;
}
sds sdscatlen(sds s, const void *t, size_t len) {
struct sdshdr *sh;
size_t curlen = sdslen(s);
s = sdsMakeRoomFor(s,len);
if (s == NULL) return NULL;
sh = (void*) (s-(sizeof(struct sdshdr)));
memcpy(s+curlen, t, len);
sh->len = curlen+len;
sh->free = sh->free-len;
s[curlen+len] = '\0';
return s;
}
sds sdscat(sds s, const char *t) {
return sdscatlen(s, t, strlen(t));
}
sds sdscpylen(sds s, char *t, size_t len) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
size_t totlen = sh->free+sh->len;
if (totlen < len) {
s = sdsMakeRoomFor(s,len-sh->len);
if (s == NULL) return NULL;
sh = (void*) (s-(sizeof(struct sdshdr)));
totlen = sh->free+sh->len;
}
memcpy(s, t, len);
s[len] = '\0';
sh->len = len;
sh->free = totlen-len;
return s;
}
sds sdscpy(sds s, char *t) {
return sdscpylen(s, t, strlen(t));
}
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
va_list cpy;
char *buf, *t;
size_t buflen = 16;
while(1) {
buf = malloc(buflen);
#ifdef SDS_ABORT_ON_OOM
if (buf == NULL) sdsOomAbort();
#else
if (buf == NULL) return NULL;
#endif
buf[buflen-2] = '\0';
va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy);
if (buf[buflen-2] != '\0') {
free(buf);
buflen *= 2;
continue;
}
break;
}
t = sdscat(s, buf);
free(buf);
return t;
}
sds sdscatprintf(sds s, const char *fmt, ...) {
va_list ap;
char *t;
va_start(ap, fmt);
t = sdscatvprintf(s,fmt,ap);
va_end(ap);
return t;
}
sds sdstrim(sds s, const char *cset) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
char *start, *end, *sp, *ep;
size_t len;
sp = start = s;
ep = end = s+sdslen(s)-1;
while(sp <= end && strchr(cset, *sp)) sp++;
while(ep > start && strchr(cset, *ep)) ep--;
len = (sp > ep) ? 0 : ((ep-sp)+1);
if (sh->buf != sp) memmove(sh->buf, sp, len);
sh->buf[len] = '\0';
sh->free = sh->free+(sh->len-len);
sh->len = len;
return s;
}
sds sdsrange(sds s, int start, int end) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
size_t newlen, len = sdslen(s);
if (len == 0) return s;
if (start < 0) {
start = len+start;
if (start < 0) start = 0;
}
if (end < 0) {
end = len+end;
if (end < 0) end = 0;
}
newlen = (start > end) ? 0 : (end-start)+1;
if (newlen != 0) {
if (start >= (signed)len) {
newlen = 0;
} else if (end >= (signed)len) {
end = len-1;
newlen = (start > end) ? 0 : (end-start)+1;
}
} else {
start = 0;
}
if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
sh->buf[newlen] = 0;
sh->free = sh->free+(sh->len-newlen);
sh->len = newlen;
return s;
}
void sdstolower(sds s) {
int len = sdslen(s), j;
for (j = 0; j < len; j++) s[j] = tolower(s[j]);
}
void sdstoupper(sds s) {
int len = sdslen(s), j;
for (j = 0; j < len; j++) s[j] = toupper(s[j]);
}
int sdscmp(sds s1, sds s2) {
size_t l1, l2, minlen;
int cmp;
l1 = sdslen(s1);
l2 = sdslen(s2);
minlen = (l1 < l2) ? l1 : l2;
cmp = memcmp(s1,s2,minlen);
if (cmp == 0) return l1-l2;
return cmp;
}
/* Split 's' with separator in 'sep'. An array
* of sds strings is returned. *count will be set
* by reference to the number of tokens returned.
*
* On out of memory, zero length string, zero length
* separator, NULL is returned.
*
* Note that 'sep' is able to split a string using
* a multi-character separator. For example
* sdssplit("foo_-_bar","_-_"); will return two
* elements "foo" and "bar".
*
* This version of the function is binary-safe but
* requires length arguments. sdssplit() is just the
* same function but for zero-terminated strings.
*/
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
int elements = 0, slots = 5, start = 0, j;
sds *tokens = malloc(sizeof(sds)*slots);
#ifdef SDS_ABORT_ON_OOM
if (tokens == NULL) sdsOomAbort();
#endif
if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
if (len == 0) {
*count = 0;
return tokens;
}
for (j = 0; j < (len-(seplen-1)); j++) {
/* make sure there is room for the next element and the final one */
if (slots < elements+2) {
sds *newtokens;
slots *= 2;
newtokens = realloc(tokens,sizeof(sds)*slots);
if (newtokens == NULL) {
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort();
#else
goto cleanup;
#endif
}
tokens = newtokens;
}
/* search the separator */
if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
tokens[elements] = sdsnewlen(s+start,j-start);
if (tokens[elements] == NULL) {
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort();
#else
goto cleanup;
#endif
}
elements++;
start = j+seplen;
j = j+seplen-1; /* skip the separator */
}
}
/* Add the final element. We are sure there is room in the tokens array. */
tokens[elements] = sdsnewlen(s+start,len-start);
if (tokens[elements] == NULL) {
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort();
#else
goto cleanup;
#endif
}
elements++;
*count = elements;
return tokens;
#ifndef SDS_ABORT_ON_OOM
cleanup:
{
int i;
for (i = 0; i < elements; i++) sdsfree(tokens[i]);
free(tokens);
return NULL;
}
#endif
}
void sdsfreesplitres(sds *tokens, int count) {
if (!tokens) return;
while(count--)
sdsfree(tokens[count]);
free(tokens);
}
sds sdsfromlonglong(long long value) {
char buf[32], *p;
unsigned long long v;
v = (value < 0) ? -value : value;
p = buf+31; /* point to the last character */
do {
*p-- = '0'+(v%10);
v /= 10;
} while(v);
if (value < 0) *p-- = '-';
p++;
return sdsnewlen(p,32-(p-buf));
}
sds sdscatrepr(sds s, char *p, size_t len) {
s = sdscatlen(s,"\"",1);
if (s == NULL) return NULL;
while(len--) {
switch(*p) {
case '\\':
case '"':
s = sdscatprintf(s,"\\%c",*p);
break;
case '\n': s = sdscatlen(s,"\\n",2); break;
case '\r': s = sdscatlen(s,"\\r",2); break;
case '\t': s = sdscatlen(s,"\\t",2); break;
case '\a': s = sdscatlen(s,"\\a",2); break;
case '\b': s = sdscatlen(s,"\\b",2); break;
default:
if (isprint(*p))
s = sdscatprintf(s,"%c",*p);
else
s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
break;
}
p++;
if (s == NULL) return NULL;
}
return sdscatlen(s,"\"",1);
}
/* Split a line into arguments, where every argument can be in the
* following programming-language REPL-alike form:
*
* foo bar "newline are supported\n" and "\xff\x00otherstuff"
*
* The number of arguments is stored into *argc, and an array
* of sds is returned. The caller should sdsfree() all the returned
* strings and finally free() the array itself.
*
* Note that sdscatrepr() is able to convert back a string into
* a quoted string in the same format sdssplitargs() is able to parse.
*/
sds *sdssplitargs(char *line, int *argc) {
char *p = line;
char *current = NULL;
char **vector = NULL, **_vector = NULL;
*argc = 0;
while(1) {
/* skip blanks */
while(*p && isspace(*p)) p++;
if (*p) {
/* get a token */
int inq=0; /* set to 1 if we are in "quotes" */
int done=0;
if (current == NULL) {
current = sdsempty();
if (current == NULL) goto err;
}
while(!done) {
if (inq) {
if (*p == '\\' && *(p+1)) {
char c;
p++;
switch(*p) {
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'b': c = '\b'; break;
case 'a': c = '\a'; break;
default: c = *p; break;
}
current = sdscatlen(current,&c,1);
} else if (*p == '"') {
/* closing quote must be followed by a space */
if (*(p+1) && !isspace(*(p+1))) goto err;
done=1;
} else if (!*p) {
/* unterminated quotes */
goto err;
} else {
current = sdscatlen(current,p,1);
}
} else {
switch(*p) {
case ' ':
case '\n':
case '\r':
case '\t':
case '\0':
done=1;
break;
case '"':
inq=1;
break;
default:
current = sdscatlen(current,p,1);
break;
}
}
if (*p) p++;
if (current == NULL) goto err;
}
/* add the token to the vector */
_vector = realloc(vector,((*argc)+1)*sizeof(char*));
if (_vector == NULL) goto err;
vector = _vector;
vector[*argc] = current;
(*argc)++;
current = NULL;
} else {
return vector;
}
}
err:
while((*argc)--)
sdsfree(vector[*argc]);
if (vector != NULL) free(vector);
if (current != NULL) sdsfree(current);
return NULL;
}
#ifdef SDS_TEST_MAIN
#include <stdio.h>
int __failed_tests = 0;
int __test_num = 0;
#define test_cond(descr,_c) do { \
__test_num++; printf("%d - %s: ", __test_num, descr); \
if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
} while(0);
#define test_report() do { \
printf("%d tests, %d passed, %d failed\n", __test_num, \
__test_num-__failed_tests, __failed_tests); \
if (__failed_tests) { \
printf("=== WARNING === We have failed tests here...\n"); \
} \
} while(0);
int main(void) {
{
sds x = sdsnew("foo"), y;
test_cond("Create a string and obtain the length",
sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
sdsfree(x);
x = sdsnewlen("foo",2);
test_cond("Create a string with specified length",
sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
x = sdscat(x,"bar");
test_cond("Strings concatenation",
sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
x = sdscpy(x,"a");
test_cond("sdscpy() against an originally longer string",
sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
test_cond("sdscpy() against an originally shorter string",
sdslen(x) == 33 &&
memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
sdsfree(x);
x = sdscatprintf(sdsempty(),"%d",123);
test_cond("sdscatprintf() seems working in the base case",
sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
sdsfree(x);
x = sdstrim(sdsnew("xxciaoyyy"),"xy");
test_cond("sdstrim() correctly trims characters",
sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
y = sdsrange(sdsdup(x),1,1);
test_cond("sdsrange(...,1,1)",
sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),1,-1);
test_cond("sdsrange(...,1,-1)",
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),-2,-1);
test_cond("sdsrange(...,-2,-1)",
sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),2,1);
test_cond("sdsrange(...,2,1)",
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),1,100);
test_cond("sdsrange(...,1,100)",
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),100,100);
test_cond("sdsrange(...,100,100)",
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
sdsfree(y);
sdsfree(x);
x = sdsnew("foo");
y = sdsnew("foa");
test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
sdsfree(y);
sdsfree(x);
x = sdsnew("bar");
y = sdsnew("bar");
test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
sdsfree(y);
sdsfree(x);
x = sdsnew("aar");
y = sdsnew("bar");
test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
}
test_report()
}
#endif

View File

@ -1,88 +0,0 @@
/* SDSLib, A C dynamic strings library
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SDS_H
#define __SDS_H
#include <sys/types.h>
#include <stdarg.h>
typedef char *sds;
struct sdshdr {
int len;
int free;
char buf[];
};
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->len;
}
static inline size_t sdsavail(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->free;
}
sds sdsnewlen(const void *init, size_t initlen);
sds sdsnew(const char *init);
sds sdsempty(void);
size_t sdslen(const sds s);
sds sdsdup(const sds s);
void sdsfree(sds s);
size_t sdsavail(sds s);
sds sdsgrowzero(sds s, size_t len);
sds sdscatlen(sds s, const void *t, size_t len);
sds sdscat(sds s, const char *t);
sds sdscpylen(sds s, char *t, size_t len);
sds sdscpy(sds s, char *t);
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
#ifdef __GNUC__
sds sdscatprintf(sds s, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
#else
sds sdscatprintf(sds s, const char *fmt, ...);
#endif
sds sdstrim(sds s, const char *cset);
sds sdsrange(sds s, int start, int end);
void sdsupdatelen(sds s);
int sdscmp(sds s1, sds s2);
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count);
void sdsfreesplitres(sds *tokens, int count);
void sdstolower(sds s);
void sdstoupper(sds s);
sds sdsfromlonglong(long long value);
sds sdscatrepr(sds s, char *p, size_t len);
sds *sdssplitargs(char *line, int *argc);
#endif

View File

@ -1,656 +0,0 @@
#include "fmacros.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include "hiredis.h"
enum connection_type {
CONN_TCP,
CONN_UNIX
};
struct config {
enum connection_type type;
struct {
const char *host;
int port;
} tcp;
struct {
const char *path;
} unix;
};
/* The following lines make up our testing "framework" :) */
static int tests = 0, fails = 0;
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;}
static long long usec(void) {
struct timeval tv;
gettimeofday(&tv,NULL);
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
}
static redisContext *select_database(redisContext *c) {
redisReply *reply;
/* Switch to DB 9 for testing, now that we know we can chat. */
reply = redisCommand(c,"SELECT 9");
assert(reply != NULL);
freeReplyObject(reply);
/* Make sure the DB is emtpy */
reply = redisCommand(c,"DBSIZE");
assert(reply != NULL);
if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) {
/* Awesome, DB 9 is empty and we can continue. */
freeReplyObject(reply);
} else {
printf("Database #9 is not empty, test can not continue\n");
exit(1);
}
return c;
}
static void disconnect(redisContext *c) {
redisReply *reply;
/* Make sure we're on DB 9. */
reply = redisCommand(c,"SELECT 9");
assert(reply != NULL);
freeReplyObject(reply);
reply = redisCommand(c,"FLUSHDB");
assert(reply != NULL);
freeReplyObject(reply);
/* Free the context as well. */
redisFree(c);
}
static redisContext *connect(struct config config) {
redisContext *c = NULL;
if (config.type == CONN_TCP) {
c = redisConnect(config.tcp.host, config.tcp.port);
} else if (config.type == CONN_UNIX) {
c = redisConnectUnix(config.unix.path);
} else {
assert(NULL);
}
if (c->err) {
printf("Connection error: %s\n", c->errstr);
exit(1);
}
return select_database(c);
}
static void test_format_commands(void) {
char *cmd;
int len;
test("Format command without interpolation: ");
len = redisFormatCommand(&cmd,"SET foo bar");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
test("Format command with %%s string interpolation: ");
len = redisFormatCommand(&cmd,"SET %s %s","foo","bar");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
test("Format command with %%s and an empty string: ");
len = redisFormatCommand(&cmd,"SET %s %s","foo","");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd);
test("Format command with an empty string in between proper interpolations: ");
len = redisFormatCommand(&cmd,"SET %s %s","","foo");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(0+2)+4+(3+2));
free(cmd);
test("Format command with %%b string interpolation: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
test("Format command with %%b and an empty string: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"",0);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd);
test("Format command with literal %%: ");
len = redisFormatCommand(&cmd,"SET %% %%");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
free(cmd);
/* Vararg width depends on the type. These tests make sure that the
* width is correctly determined using the format and subsequent varargs
* can correctly be interpolated. */
#define INTEGER_WIDTH_TEST(fmt, type) do { \
type value = 123; \
test("Format command with printf-delegation (" #type "): "); \
len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \
free(cmd); \
} while(0)
#define FLOAT_WIDTH_TEST(type) do { \
type value = 123.0; \
test("Format command with printf-delegation (" #type "): "); \
len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \
free(cmd); \
} while(0)
INTEGER_WIDTH_TEST("d", int);
INTEGER_WIDTH_TEST("hhd", char);
INTEGER_WIDTH_TEST("hd", short);
INTEGER_WIDTH_TEST("ld", long);
INTEGER_WIDTH_TEST("lld", long long);
INTEGER_WIDTH_TEST("u", unsigned int);
INTEGER_WIDTH_TEST("hhu", unsigned char);
INTEGER_WIDTH_TEST("hu", unsigned short);
INTEGER_WIDTH_TEST("lu", unsigned long);
INTEGER_WIDTH_TEST("llu", unsigned long long);
FLOAT_WIDTH_TEST(float);
FLOAT_WIDTH_TEST(double);
test("Format command with invalid printf format: ");
len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",3);
test_cond(len == -1);
const char *argv[3];
argv[0] = "SET";
argv[1] = "foo\0xxx";
argv[2] = "bar";
size_t lens[3] = { 3, 7, 3 };
int argc = 3;
test("Format command by passing argc/argv without lengths: ");
len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
test("Format command by passing argc/argv with lengths: ");
len = redisFormatCommandArgv(&cmd,argc,argv,lens);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(7+2)+4+(3+2));
free(cmd);
}
static void test_reply_reader(void) {
redisReader *reader;
void *reply;
int ret;
int i;
test("Error handling in reply parser: ");
reader = redisReaderCreate();
redisReaderFeed(reader,(char*)"@foo\r\n",6);
ret = redisReaderGetReply(reader,NULL);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
redisReaderFree(reader);
/* when the reply already contains multiple items, they must be free'd
* on an error. valgrind will bark when this doesn't happen. */
test("Memory cleanup in reply parser: ");
reader = redisReaderCreate();
redisReaderFeed(reader,(char*)"*2\r\n",4);
redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
redisReaderFeed(reader,(char*)"@foo\r\n",6);
ret = redisReaderGetReply(reader,NULL);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
redisReaderFree(reader);
test("Set error on nested multi bulks with depth > 7: ");
reader = redisReaderCreate();
for (i = 0; i < 9; i++) {
redisReaderFeed(reader,(char*)"*1\r\n",4);
}
ret = redisReaderGetReply(reader,NULL);
test_cond(ret == REDIS_ERR &&
strncasecmp(reader->errstr,"No support for",14) == 0);
redisReaderFree(reader);
test("Works with NULL functions for reply: ");
reader = redisReaderCreate();
reader->fn = NULL;
redisReaderFeed(reader,(char*)"+OK\r\n",5);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
redisReaderFree(reader);
test("Works when a single newline (\\r\\n) covers two calls to feed: ");
reader = redisReaderCreate();
reader->fn = NULL;
redisReaderFeed(reader,(char*)"+OK\r",4);
ret = redisReaderGetReply(reader,&reply);
assert(ret == REDIS_OK && reply == NULL);
redisReaderFeed(reader,(char*)"\n",1);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
redisReaderFree(reader);
test("Don't reset state after protocol error: ");
reader = redisReaderCreate();
reader->fn = NULL;
redisReaderFeed(reader,(char*)"x",1);
ret = redisReaderGetReply(reader,&reply);
assert(ret == REDIS_ERR);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR && reply == NULL);
redisReaderFree(reader);
/* Regression test for issue #45 on GitHub. */
test("Don't do empty allocation for empty multi bulk: ");
reader = redisReaderCreate();
redisReaderFeed(reader,(char*)"*0\r\n",4);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK &&
((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
((redisReply*)reply)->elements == 0);
freeReplyObject(reply);
redisReaderFree(reader);
}
static void test_blocking_connection_errors(void) {
redisContext *c;
test("Returns error when host cannot be resolved: ");
c = redisConnect((char*)"idontexist.local", 6379);
test_cond(c->err == REDIS_ERR_OTHER &&
(strcmp(c->errstr,"Name or service not known") == 0 ||
strcmp(c->errstr,"Can't resolve: idontexist.local") == 0));
redisFree(c);
test("Returns error when the port is not open: ");
c = redisConnect((char*)"localhost", 1);
test_cond(c->err == REDIS_ERR_IO &&
strcmp(c->errstr,"Connection refused") == 0);
redisFree(c);
test("Returns error when the unix socket path doesn't accept connections: ");
c = redisConnectUnix((char*)"/tmp/idontexist.sock");
test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
redisFree(c);
}
static void test_blocking_connection(struct config config) {
redisContext *c;
redisReply *reply;
c = connect(config);
test("Is able to deliver commands: ");
reply = redisCommand(c,"PING");
test_cond(reply->type == REDIS_REPLY_STATUS &&
strcasecmp(reply->str,"pong") == 0)
freeReplyObject(reply);
test("Is a able to send commands verbatim: ");
reply = redisCommand(c,"SET foo bar");
test_cond (reply->type == REDIS_REPLY_STATUS &&
strcasecmp(reply->str,"ok") == 0)
freeReplyObject(reply);
test("%%s String interpolation works: ");
reply = redisCommand(c,"SET %s %s","foo","hello world");
freeReplyObject(reply);
reply = redisCommand(c,"GET foo");
test_cond(reply->type == REDIS_REPLY_STRING &&
strcmp(reply->str,"hello world") == 0);
freeReplyObject(reply);
test("%%b String interpolation works: ");
reply = redisCommand(c,"SET %b %b","foo",3,"hello\x00world",11);
freeReplyObject(reply);
reply = redisCommand(c,"GET foo");
test_cond(reply->type == REDIS_REPLY_STRING &&
memcmp(reply->str,"hello\x00world",11) == 0)
test("Binary reply length is correct: ");
test_cond(reply->len == 11)
freeReplyObject(reply);
test("Can parse nil replies: ");
reply = redisCommand(c,"GET nokey");
test_cond(reply->type == REDIS_REPLY_NIL)
freeReplyObject(reply);
/* test 7 */
test("Can parse integer replies: ");
reply = redisCommand(c,"INCR mycounter");
test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1)
freeReplyObject(reply);
test("Can parse multi bulk replies: ");
freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
freeReplyObject(redisCommand(c,"LPUSH mylist bar"));
reply = redisCommand(c,"LRANGE mylist 0 -1");
test_cond(reply->type == REDIS_REPLY_ARRAY &&
reply->elements == 2 &&
!memcmp(reply->element[0]->str,"bar",3) &&
!memcmp(reply->element[1]->str,"foo",3))
freeReplyObject(reply);
/* m/e with multi bulk reply *before* other reply.
* specifically test ordering of reply items to parse. */
test("Can handle nested multi bulk replies: ");
freeReplyObject(redisCommand(c,"MULTI"));
freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1"));
freeReplyObject(redisCommand(c,"PING"));
reply = (redisCommand(c,"EXEC"));
test_cond(reply->type == REDIS_REPLY_ARRAY &&
reply->elements == 2 &&
reply->element[0]->type == REDIS_REPLY_ARRAY &&
reply->element[0]->elements == 2 &&
!memcmp(reply->element[0]->element[0]->str,"bar",3) &&
!memcmp(reply->element[0]->element[1]->str,"foo",3) &&
reply->element[1]->type == REDIS_REPLY_STATUS &&
strcasecmp(reply->element[1]->str,"pong") == 0);
freeReplyObject(reply);
disconnect(c);
}
static void test_blocking_io_errors(struct config config) {
redisContext *c;
redisReply *reply;
void *_reply;
int major, minor;
/* Connect to target given by config. */
c = connect(config);
{
/* Find out Redis version to determine the path for the next test */
const char *field = "redis_version:";
char *p, *eptr;
reply = redisCommand(c,"INFO");
p = strstr(reply->str,field);
major = strtol(p+strlen(field),&eptr,10);
p = eptr+1; /* char next to the first "." */
minor = strtol(p,&eptr,10);
freeReplyObject(reply);
}
test("Returns I/O error when the connection is lost: ");
reply = redisCommand(c,"QUIT");
if (major >= 2 && minor > 0) {
/* > 2.0 returns OK on QUIT and read() should be issued once more
* to know the descriptor is at EOF. */
test_cond(strcasecmp(reply->str,"OK") == 0 &&
redisGetReply(c,&_reply) == REDIS_ERR);
freeReplyObject(reply);
} else {
test_cond(reply == NULL);
}
/* On 2.0, QUIT will cause the connection to be closed immediately and
* the read(2) for the reply on QUIT will set the error to EOF.
* On >2.0, QUIT will return with OK and another read(2) needed to be
* issued to find out the socket was closed by the server. In both
* conditions, the error will be set to EOF. */
assert(c->err == REDIS_ERR_EOF &&
strcmp(c->errstr,"Server closed the connection") == 0);
redisFree(c);
c = connect(config);
test("Returns I/O error on socket timeout: ");
struct timeval tv = { 0, 1000 };
assert(redisSetTimeout(c,tv) == REDIS_OK);
test_cond(redisGetReply(c,&_reply) == REDIS_ERR &&
c->err == REDIS_ERR_IO && errno == EAGAIN);
redisFree(c);
}
static void test_throughput(struct config config) {
redisContext *c = connect(config);
redisReply **replies;
int i, num;
long long t1, t2;
test("Throughput:\n");
for (i = 0; i < 500; i++)
freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
num = 1000;
replies = malloc(sizeof(redisReply*)*num);
t1 = usec();
for (i = 0; i < num; i++) {
replies[i] = redisCommand(c,"PING");
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num);
t1 = usec();
for (i = 0; i < num; i++) {
replies[i] = redisCommand(c,"LRANGE mylist 0 499");
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
assert(replies[i] != NULL && replies[i]->elements == 500);
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
num = 10000;
replies = malloc(sizeof(redisReply*)*num);
for (i = 0; i < num; i++)
redisAppendCommand(c,"PING");
t1 = usec();
for (i = 0; i < num; i++) {
assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num);
for (i = 0; i < num; i++)
redisAppendCommand(c,"LRANGE mylist 0 499");
t1 = usec();
for (i = 0; i < num; i++) {
assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
assert(replies[i] != NULL && replies[i]->elements == 500);
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
disconnect(c);
}
// static long __test_callback_flags = 0;
// static void __test_callback(redisContext *c, void *privdata) {
// ((void)c);
// /* Shift to detect execution order */
// __test_callback_flags <<= 8;
// __test_callback_flags |= (long)privdata;
// }
//
// static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) {
// ((void)c);
// /* Shift to detect execution order */
// __test_callback_flags <<= 8;
// __test_callback_flags |= (long)privdata;
// if (reply) freeReplyObject(reply);
// }
//
// static redisContext *__connect_nonblock() {
// /* Reset callback flags */
// __test_callback_flags = 0;
// return redisConnectNonBlock("127.0.0.1", port, NULL);
// }
//
// static void test_nonblocking_connection() {
// redisContext *c;
// int wdone = 0;
//
// test("Calls command callback when command is issued: ");
// c = __connect_nonblock();
// redisSetCommandCallback(c,__test_callback,(void*)1);
// redisCommand(c,"PING");
// test_cond(__test_callback_flags == 1);
// redisFree(c);
//
// test("Calls disconnect callback on redisDisconnect: ");
// c = __connect_nonblock();
// redisSetDisconnectCallback(c,__test_callback,(void*)2);
// redisDisconnect(c);
// test_cond(__test_callback_flags == 2);
// redisFree(c);
//
// test("Calls disconnect callback and free callback on redisFree: ");
// c = __connect_nonblock();
// redisSetDisconnectCallback(c,__test_callback,(void*)2);
// redisSetFreeCallback(c,__test_callback,(void*)4);
// redisFree(c);
// test_cond(__test_callback_flags == ((2 << 8) | 4));
//
// test("redisBufferWrite against empty write buffer: ");
// c = __connect_nonblock();
// test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1);
// redisFree(c);
//
// test("redisBufferWrite against not yet connected fd: ");
// c = __connect_nonblock();
// redisCommand(c,"PING");
// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
// strncmp(c->error,"write:",6) == 0);
// redisFree(c);
//
// test("redisBufferWrite against closed fd: ");
// c = __connect_nonblock();
// redisCommand(c,"PING");
// redisDisconnect(c);
// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
// strncmp(c->error,"write:",6) == 0);
// redisFree(c);
//
// test("Process callbacks in the right sequence: ");
// c = __connect_nonblock();
// redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING");
// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
// redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING");
//
// /* Write output buffer */
// wdone = 0;
// while(!wdone) {
// usleep(500);
// redisBufferWrite(c,&wdone);
// }
//
// /* Read until at least one callback is executed (the 3 replies will
// * arrive in a single packet, causing all callbacks to be executed in
// * a single pass). */
// while(__test_callback_flags == 0) {
// assert(redisBufferRead(c) == REDIS_OK);
// redisProcessCallbacks(c);
// }
// test_cond(__test_callback_flags == 0x010203);
// redisFree(c);
//
// test("redisDisconnect executes pending callbacks with NULL reply: ");
// c = __connect_nonblock();
// redisSetDisconnectCallback(c,__test_callback,(void*)1);
// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
// redisDisconnect(c);
// test_cond(__test_callback_flags == 0x0201);
// redisFree(c);
// }
int main(int argc, char **argv) {
struct config cfg = {
.tcp = {
.host = "127.0.0.1",
.port = 6379
},
.unix = {
.path = "/tmp/redis.sock"
}
};
int throughput = 1;
/* Ignore broken pipe signal (for I/O error tests). */
signal(SIGPIPE, SIG_IGN);
/* Parse command line options. */
argv++; argc--;
while (argc) {
if (argc >= 2 && !strcmp(argv[0],"-h")) {
argv++; argc--;
cfg.tcp.host = argv[0];
} else if (argc >= 2 && !strcmp(argv[0],"-p")) {
argv++; argc--;
cfg.tcp.port = atoi(argv[0]);
} else if (argc >= 2 && !strcmp(argv[0],"-s")) {
argv++; argc--;
cfg.unix.path = argv[0];
} else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
throughput = 0;
} else {
fprintf(stderr, "Invalid argument: %s\n", argv[0]);
exit(1);
}
argv++; argc--;
}
test_format_commands();
test_reply_reader();
test_blocking_connection_errors();
printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
cfg.type = CONN_TCP;
test_blocking_connection(cfg);
test_blocking_io_errors(cfg);
if (throughput) test_throughput(cfg);
printf("\nTesting against Unix socket connection (%s):\n", cfg.unix.path);
cfg.type = CONN_UNIX;
test_blocking_connection(cfg);
test_blocking_io_errors(cfg);
if (throughput) test_throughput(cfg);
if (fails) {
printf("*** %d TESTS FAILED ***\n", fails);
return 1;
}
printf("ALL TESTS PASSED\n");
return 0;
}

34
labs/node_modules/hiredis/hiredis.js generated vendored
View File

@ -1,34 +0,0 @@
var net = require("net"),
hiredis = require('bindings')('hiredis.node');
exports.Reader = hiredis.Reader;
exports.createConnection = function(port, host) {
var s = net.createConnection(port || 6379, host);
var r = new hiredis.Reader();
var _write = s.write;
s.write = function() {
var i, args = arguments;
_write.call(s, "*" + args.length + "\r\n");
for (i = 0; i < args.length; i++) {
var arg = args[i];
_write.call(s, "$" + arg.length + "\r\n" + arg + "\r\n");
}
}
s.on("data", function(data) {
var reply;
r.feed(data);
try {
while((reply = r.get()) !== undefined)
s.emit("reply", reply);
} catch(err) {
r = null;
s.emit("error", err);
s.destroy();
}
});
return s;
}

View File

@ -1,97 +0,0 @@
node-bindings
=============
### Helper module for loading your native module's .node file
This is a helper module for authors of Node.js native addon modules.
It is basically the "swiss army knife" of `require()`ing your native module's
`.node` file.
Throughout the course of Node's native addon history, addons have ended up being
compiled in a variety of different places, depending on which build tool and which
version of node was used. To make matters worse, now the _gyp_ build tool can
produce either a _Release_ or _Debug_ build, each being built into different
locations.
This module checks _all_ the possible locations that a native addon would be built
at, and returns the first one that loads successfully.
Installation
------------
Install with `npm`:
``` bash
$ npm install bindings
```
Or add it to the `"dependencies"` section of your _package.json_ file.
Example
-------
`require()`ing the proper bindings file for the current node version, platform
and architecture is as simple as:
``` js
var bindings = require('bindings')('binding.node')
// Use your bindings defined in your C files
bindings.your_c_function()
```
Nice Error Output
-----------------
When the `.node` file could not be loaded, `node-bindings` throws an Error with
a nice error message telling you exactly what was tried. You can also check the
`err.tries` Array property.
```
Error: Could not load the bindings file. Tried:
→ /Users/nrajlich/ref/build/binding.node
→ /Users/nrajlich/ref/build/Debug/binding.node
→ /Users/nrajlich/ref/build/Release/binding.node
→ /Users/nrajlich/ref/out/Debug/binding.node
→ /Users/nrajlich/ref/Debug/binding.node
→ /Users/nrajlich/ref/out/Release/binding.node
→ /Users/nrajlich/ref/Release/binding.node
→ /Users/nrajlich/ref/build/default/binding.node
→ /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node
at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13)
at Object.<anonymous> (/Users/nrajlich/ref/lib/ref.js:5:47)
at Module._compile (module.js:449:26)
at Object.Module._extensions..js (module.js:467:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
...
```
License
-------
(The MIT License)
Copyright (c) 2012 Nathan Rajlich &lt;nathan@tootallnate.net&gt;
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.

View File

@ -1,166 +0,0 @@
/**
* Module dependencies.
*/
var fs = require('fs')
, path = require('path')
, join = path.join
, dirname = path.dirname
, exists = fs.existsSync || path.existsSync
, defaults = {
arrow: process.env.NODE_BINDINGS_ARROW || ' → '
, compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled'
, platform: process.platform
, arch: process.arch
, version: process.versions.node
, bindings: 'bindings.node'
, try: [
// node-gyp's linked version in the "build" dir
[ 'module_root', 'build', 'bindings' ]
// node-waf and gyp_addon (a.k.a node-gyp)
, [ 'module_root', 'build', 'Debug', 'bindings' ]
, [ 'module_root', 'build', 'Release', 'bindings' ]
// Debug files, for development (legacy behavior, remove for node v0.9)
, [ 'module_root', 'out', 'Debug', 'bindings' ]
, [ 'module_root', 'Debug', 'bindings' ]
// Release files, but manually compiled (legacy behavior, remove for node v0.9)
, [ 'module_root', 'out', 'Release', 'bindings' ]
, [ 'module_root', 'Release', 'bindings' ]
// Legacy from node-waf, node <= 0.4.x
, [ 'module_root', 'build', 'default', 'bindings' ]
// Production "Release" buildtype binary (meh...)
, [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ]
]
}
/**
* The main `bindings()` function loads the compiled bindings for a given module.
* It uses V8's Error API to determine the parent filename that this function is
* being invoked from, which is then used to find the root directory.
*/
function bindings (opts) {
// Argument surgery
if (typeof opts == 'string') {
opts = { bindings: opts }
} else if (!opts) {
opts = {}
}
opts.__proto__ = defaults
// Get the module root
if (!opts.module_root) {
opts.module_root = exports.getRoot(exports.getFileName())
}
// Ensure the given bindings name ends with .node
if (path.extname(opts.bindings) != '.node') {
opts.bindings += '.node'
}
var tries = []
, i = 0
, l = opts.try.length
, n
, b
, err
for (; i<l; i++) {
n = join.apply(null, opts.try[i].map(function (p) {
return opts[p] || p
}))
tries.push(n)
try {
b = opts.path ? require.resolve(n) : require(n)
if (!opts.path) {
b.path = n
}
return b
} catch (e) {
if (!/not find/i.test(e.message)) {
throw e
}
}
}
err = new Error('Could not locate the bindings file. Tried:\n'
+ tries.map(function (a) { return opts.arrow + a }).join('\n'))
err.tries = tries
throw err
}
module.exports = exports = bindings
/**
* Gets the filename of the JavaScript file that invokes this function.
* Used to help find the root directory of a module.
* Optionally accepts an filename argument to skip when searching for the invoking filename
*/
exports.getFileName = function getFileName (calling_file) {
var origPST = Error.prepareStackTrace
, origSTL = Error.stackTraceLimit
, dummy = {}
, fileName
Error.stackTraceLimit = 10
Error.prepareStackTrace = function (e, st) {
for (var i=0, l=st.length; i<l; i++) {
fileName = st[i].getFileName()
if (fileName !== __filename) {
if (calling_file) {
if (fileName !== calling_file) {
return
}
} else {
return
}
}
}
}
// run the 'prepareStackTrace' function above
Error.captureStackTrace(dummy)
dummy.stack
// cleanup
Error.prepareStackTrace = origPST
Error.stackTraceLimit = origSTL
return fileName
}
/**
* Gets the root directory of a module, given an arbitrary filename
* somewhere in the module tree. The "root directory" is the directory
* containing the `package.json` file.
*
* In: /home/nate/node-native-module/lib/index.js
* Out: /home/nate/node-native-module
*/
exports.getRoot = function getRoot (file) {
var dir = dirname(file)
, prev
while (true) {
if (dir === '.') {
// Avoids an infinite loop in rare cases, like the REPL
dir = process.cwd()
}
if (exists(join(dir, 'package.json')) || exists(join(dir, 'node_modules'))) {
// Found the 'package.json' file or 'node_modules' dir; we're done
return dir
}
if (prev === dir) {
// Got to the top
throw new Error('Could not find module root given file: "' + file
+ '". Do you have a `package.json` file? ')
}
// Try the parent dir next
prev = dir
dir = join(dir, '..')
}
}

View File

@ -1,33 +0,0 @@
{
"name": "bindings",
"description": "Helper module for loading your native module's .node file",
"keywords": [
"native",
"addon",
"bindings",
"gyp",
"waf",
"c",
"c++"
],
"version": "1.2.1",
"author": {
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net",
"url": "http://tootallnate.net"
},
"repository": {
"type": "git",
"url": "git://github.com/TooTallNate/node-bindings.git"
},
"main": "./bindings.js",
"bugs": {
"url": "https://github.com/TooTallNate/node-bindings/issues"
},
"homepage": "https://github.com/TooTallNate/node-bindings",
"license": "MIT",
"readme": "node-bindings\n=============\n### Helper module for loading your native module's .node file\n\nThis is a helper module for authors of Node.js native addon modules.\nIt is basically the \"swiss army knife\" of `require()`ing your native module's\n`.node` file.\n\nThroughout the course of Node's native addon history, addons have ended up being\ncompiled in a variety of different places, depending on which build tool and which\nversion of node was used. To make matters worse, now the _gyp_ build tool can\nproduce either a _Release_ or _Debug_ build, each being built into different\nlocations.\n\nThis module checks _all_ the possible locations that a native addon would be built\nat, and returns the first one that loads successfully.\n\n\nInstallation\n------------\n\nInstall with `npm`:\n\n``` bash\n$ npm install bindings\n```\n\nOr add it to the `\"dependencies\"` section of your _package.json_ file.\n\n\nExample\n-------\n\n`require()`ing the proper bindings file for the current node version, platform\nand architecture is as simple as:\n\n``` js\nvar bindings = require('bindings')('binding.node')\n\n// Use your bindings defined in your C files\nbindings.your_c_function()\n```\n\n\nNice Error Output\n-----------------\n\nWhen the `.node` file could not be loaded, `node-bindings` throws an Error with\na nice error message telling you exactly what was tried. You can also check the\n`err.tries` Array property.\n\n```\nError: Could not load the bindings file. Tried:\n → /Users/nrajlich/ref/build/binding.node\n → /Users/nrajlich/ref/build/Debug/binding.node\n → /Users/nrajlich/ref/build/Release/binding.node\n → /Users/nrajlich/ref/out/Debug/binding.node\n → /Users/nrajlich/ref/Debug/binding.node\n → /Users/nrajlich/ref/out/Release/binding.node\n → /Users/nrajlich/ref/Release/binding.node\n → /Users/nrajlich/ref/build/default/binding.node\n → /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node\n at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13)\n at Object.<anonymous> (/Users/nrajlich/ref/lib/ref.js:5:47)\n at Module._compile (module.js:449:26)\n at Object.Module._extensions..js (module.js:467:10)\n at Module.load (module.js:356:32)\n at Function.Module._load (module.js:312:12)\n ...\n```\n\n\nLicense\n-------\n\n(The MIT License)\n\nCopyright (c) 2012 Nathan Rajlich &lt;nathan@tootallnate.net&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"readmeFilename": "README.md",
"_id": "bindings@1.2.1",
"_from": "bindings@*"
}

View File

@ -1,37 +0,0 @@
## DNT config file
## see https://github.com/rvagg/dnt
NODE_VERSIONS="\
master \
v0.11.13 \
v0.11.10 \
v0.11.9 \
v0.11.8 \
v0.11.7 \
v0.11.6 \
v0.11.5 \
v0.11.4 \
v0.10.28 \
v0.10.26 \
v0.10.25 \
v0.10.24 \
v0.10.23 \
v0.10.22 \
v0.10.21 \
v0.10.20 \
v0.10.19 \
v0.10.18 \
v0.8.26 \
v0.8.25 \
v0.8.24 \
v0.8.23 \
v0.8.22 \
"
OUTPUT_PREFIX="nan-"
TEST_CMD="\
cd /dnt/test/ && \
npm install && \
node_modules/.bin/node-gyp --nodedir /usr/src/node/ rebuild && \
node_modules/.bin/tap --gc js/*-test.js; \
"

View File

@ -1,46 +0,0 @@
Copyright 2013, NAN contributors:
- Rod Vagg <https://github.com/rvagg>
- Benjamin Byholm <https://github.com/kkoopa>
- Trevor Norris <https://github.com/trevnorris>
- Nathan Rajlich <https://github.com/TooTallNate>
- Brett Lawson <https://github.com/brett19>
- Ben Noordhuis <https://github.com/bnoordhuis>
(the "Original Author")
All rights reserved.
MIT +no-false-attribs License
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.
Distributions of all or part of the Software intended to be used
by the recipients as they would use the unmodified Software,
containing modifications that substantially alter, remove, or
disable functionality of the Software, outside of the documented
configuration mechanisms provided by the Software, shall be
modified such that the Original Author's bug reporting email
addresses and urls are either replaced with the contact information
of the parties responsible for the changes, or removed entirely.
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.
Except where noted, this license applies to any and all software
programs and associated documentation files created by the
Original Author, when distributed with the Software.

View File

@ -1,972 +0,0 @@
Native Abstractions for Node.js
===============================
**A header file filled with macro and utility goodness for making add-on development for Node.js easier across versions 0.8, 0.10 and 0.11, and eventually 0.12.**
***Current version: 1.1.2*** *(See [nan.h](https://github.com/rvagg/nan/blob/master/nan.h) for complete ChangeLog)*
[![NPM](https://nodei.co/npm/nan.png?downloads=true)](https://nodei.co/npm/nan/) [![NPM](https://nodei.co/npm-dl/nan.png?months=6)](https://nodei.co/npm/nan/)
Thanks to the crazy changes in V8 (and some in Node core), keeping native addons compiling happily across versions, particularly 0.10 to 0.11/0.12, is a minor nightmare. The goal of this project is to store all logic necessary to develop native Node.js addons without having to inspect `NODE_MODULE_VERSION` and get yourself into a macro-tangle.
This project also contains some helper utilities that make addon development a bit more pleasant.
* **[News & Updates](#news)**
* **[Usage](#usage)**
* **[Example](#example)**
* **[API](#api)**
<a name="news"></a>
## News & Updates
### May-2014: 1.1.0 release
* We've deprecated `NanSymbol()`, you should just use `NanNew<String>()` now.
* `NanNull()`, `NanUndefined()`, `NanTrue()`, `NanFalse()` all return `Local`s now.
* `nan_isolate` is gone, it was intended to be internal-only but if you were using it then you should switch to `v8::Isolate::GetCurrent()`.
* `NanNew()` has received some additional overload-love so you should be able to give it many kinds of values without specifying the `<Type>`.
* Lots of small fixes and additions to expand the V8 API coverage, *use the source, Luke*.
### May-2014: Major changes for V8 3.25 / Node 0.11.13
Node 0.11.11 and 0.11.12 were both broken releases for native add-ons, you simply can't properly compile against either of them for different reasons. But we now have a 0.11.13 release that jumps a couple of versions of V8 ahead and includes some more, major (traumatic) API changes.
Because we are now nearing Node 0.12 and estimate that the version of V8 we are using in Node 0.11.13 will be close to the API we get for 0.12, we have taken the opportunity to not only *fix* NAN for 0.11.13 but make some major changes to improve the NAN API.
We have **removed support for Node 0.11 versions prior to 0.11.13**. As usual, our tests are run against (and pass) the last 5 versions of Node 0.8 and Node 0.10. We also include Node 0.11.13 obviously.
The major change is something that [Benjamin Byholm](kkoopa) has put many hours in to. We now have a fantastic new `NanNew<T>(args)` interface for creating new `Local`s, this replaces `NanNewLocal()` and much more. If you look in [./nan.h](nan.h) you'll see a large number of overloaded versions of this method. In general you should be able to `NanNew<Type>(arguments)` for any type you want to make a `Local` from. This includes `Persistent` types, so we now have a `Local<T> NanNew(const Persistent<T> arg)` to replace `NanPersistentToLocal()`.
We also now have `NanUndefined()`, `NanNull()`, `NanTrue()` and `NanFalse()`. Mainly because of the new requirement for an `Isolate` argument for each of the native V8 versions of this.
V8 has now introduced an `EscapableHandleScope` from which you `scope.Escape(Local<T> value)` to *return* a value from a one scope to another. This replaces the standard `HandleScope` and `scope.Close(Local<T> value)`, although `HandleScope` still exists for when you don't need to return a handle to the caller. For NAN we are exposing it as `NanEscapableScope()` and `NanEscapeScope()`, while `NanScope()` is still how you create a new scope that doesn't need to return handles. For older versions of Node/V8, it'll still map to the older `HandleScope` functionality.
`NanFromV8String()` was deprecated and has now been removed. You should use `NanCString()` or `NanRawString()` instead.
Because `node::MakeCallback()` now takes an `Isolate`, and because it doesn't exist in older versions of Node, we've introduced `NanMakeCallback()`. You should *always* use this when calling a JavaScript function from C++.
There's lots more, check out the Changelog in nan.h or look through [#86](https://github.com/rvagg/nan/pull/86) for all the gory details.
### Dec-2013: NanCString and NanRawString
Two new functions have been introduced to replace the functionality that's been provided by `NanFromV8String` until now. NanCString has sensible defaults so it's super easy to fetch a null-terminated c-style string out of a `v8::String`. `NanFromV8String` is still around and has defaults that allow you to pass a single handle to fetch a `char*` while `NanRawString` requires a little more attention to arguments.
### Nov-2013: Node 0.11.9+ breaking V8 change
The version of V8 that's shipping with Node 0.11.9+ has changed the signature for new `Local`s to: `v8::Local<T>::New(isolate, value)`, i.e. introducing the `isolate` argument and therefore breaking all new `Local` declarations for previous versions. NAN 0.6+ now includes a `NanNewLocal<T>(value)` that can be used in place to work around this incompatibility and maintain compatibility with 0.8->0.11.9+ (minus a few early 0.11 releases).
For example, if you wanted to return a `null` on a callback you will have to change the argument from `v8::Local<v8::Value>::New(v8::Null())` to `NanNewLocal<v8::Value>(v8::Null())`.
### Nov-2013: Change to binding.gyp `"include_dirs"` for NAN
Inclusion of NAN in a project's binding.gyp is now greatly simplified. You can now just use `"<!(node -e \"require('nan')\")"` in your `"include_dirs"`, see example below (note Windows needs the quoting around `require` to be just right: `"require('nan')"` with appropriate `\` escaping).
<a name="usage"></a>
## Usage
Simply add **NAN** as a dependency in the *package.json* of your Node addon:
``` bash
$ npm install --save nan
```
Pull in the path to **NAN** in your *binding.gyp* so that you can use `#include <nan.h>` in your *.cpp* files:
``` python
"include_dirs" : [
"<!(node -e \"require('nan')\")"
]
```
This works like a `-I<path-to-NAN>` when compiling your addon.
<a name="example"></a>
## Example
See **[LevelDOWN](https://github.com/rvagg/node-leveldown/pull/48)** for a full example of **NAN** in use.
For a simpler example, see the **[async pi estimation example](https://github.com/rvagg/nan/tree/master/examples/async_pi_estimate)** in the examples directory for full code and an explanation of what this Monte Carlo Pi estimation example does. Below are just some parts of the full example that illustrate the use of **NAN**.
Compare to the current 0.10 version of this example, found in the [node-addon-examples](https://github.com/rvagg/node-addon-examples/tree/master/9_async_work) repository and also a 0.11 version of the same found [here](https://github.com/kkoopa/node-addon-examples/tree/5c01f58fc993377a567812597e54a83af69686d7/9_async_work).
Note that there is no embedded version sniffing going on here and also the async work is made much simpler, see below for details on the `NanAsyncWorker` class.
```c++
// addon.cc
#include <node.h>
#include <nan.h>
// ...
using v8::FunctionTemplate;
using v8::Handle;
using v8::Object;
using v8::String;
void InitAll(Handle<Object> exports) {
exports->Set(NanNew<String>("calculateSync"),
NanNew<FunctionTemplate>(CalculateSync)->GetFunction());
exports->Set(NanNew<String>("calculateAsync"),
NanNew<FunctionTemplate>(CalculateAsync)->GetFunction());
}
NODE_MODULE(addon, InitAll)
```
```c++
// sync.h
#include <node.h>
#include <nan.h>
NAN_METHOD(CalculateSync);
```
```c++
// sync.cc
#include <node.h>
#include <nan.h>
#include "./sync.h"
// ...
using v8::Number;
// Simple synchronous access to the `Estimate()` function
NAN_METHOD(CalculateSync) {
NanScope();
// expect a number as the first argument
int points = args[0]->Uint32Value();
double est = Estimate(points);
NanReturnValue(NanNew<Number>(est));
}
```
```c++
// async.cc
#include <node.h>
#include <nan.h>
#include "./async.h"
// ...
using v8::Function;
using v8::Local;
using v8::Null;
using v8::Number;
using v8::Value;
class PiWorker : public NanAsyncWorker {
public:
PiWorker(NanCallback *callback, int points)
: NanAsyncWorker(callback), points(points) {}
~PiWorker() {}
// Executed inside the worker-thread.
// It is not safe to access V8, or V8 data structures
// here, so everything we need for input and output
// should go on `this`.
void Execute () {
estimate = Estimate(points);
}
// Executed when the async work is complete
// this function will be run inside the main event loop
// so it is safe to use V8 again
void HandleOKCallback () {
NanScope();
Local<Value> argv[] = {
NanNull()
, NanNew<Number>(estimate)
};
callback->Call(2, argv);
};
private:
int points;
double estimate;
};
// Asynchronous access to the `Estimate()` function
NAN_METHOD(CalculateAsync) {
NanScope();
int points = args[0]->Uint32Value();
NanCallback *callback = new NanCallback(args[1].As<Function>());
NanAsyncQueueWorker(new PiWorker(callback, points));
NanReturnUndefined();
}
```
<a name="api"></a>
## API
* <a href="#api_nan_method"><b><code>NAN_METHOD</code></b></a>
* <a href="#api_nan_getter"><b><code>NAN_GETTER</code></b></a>
* <a href="#api_nan_setter"><b><code>NAN_SETTER</code></b></a>
* <a href="#api_nan_property_getter"><b><code>NAN_PROPERTY_GETTER</code></b></a>
* <a href="#api_nan_property_setter"><b><code>NAN_PROPERTY_SETTER</code></b></a>
* <a href="#api_nan_property_enumerator"><b><code>NAN_PROPERTY_ENUMERATOR</code></b></a>
* <a href="#api_nan_property_deleter"><b><code>NAN_PROPERTY_DELETER</code></b></a>
* <a href="#api_nan_property_query"><b><code>NAN_PROPERTY_QUERY</code></b></a>
* <a href="#api_nan_index_getter"><b><code>NAN_INDEX_GETTER</code></b></a>
* <a href="#api_nan_index_setter"><b><code>NAN_INDEX_SETTER</code></b></a>
* <a href="#api_nan_index_enumerator"><b><code>NAN_INDEX_ENUMERATOR</code></b></a>
* <a href="#api_nan_index_deleter"><b><code>NAN_INDEX_DELETER</code></b></a>
* <a href="#api_nan_index_query"><b><code>NAN_INDEX_QUERY</code></b></a>
* <a href="#api_nan_weak_callback"><b><code>NAN_WEAK_CALLBACK</code></b></a>
* <a href="#api_nan_deprecated"><b><code>NAN_DEPRECATED</code></b></a>
* <a href="#api_nan_inline"><b><code>NAN_INLINE</code></b></a>
* <a href="#api_nan_new"><b><code>NanNew</code></b></a>
* <a href="#api_nan_undefined"><b><code>NanUndefined</code></b></a>
* <a href="#api_nan_null"><b><code>NanNull</code></b></a>
* <a href="#api_nan_true"><b><code>NanTrue</code></b></a>
* <a href="#api_nan_false"><b><code>NanFalse</code></b></a>
* <a href="#api_nan_return_value"><b><code>NanReturnValue</code></b></a>
* <a href="#api_nan_return_undefined"><b><code>NanReturnUndefined</code></b></a>
* <a href="#api_nan_return_null"><b><code>NanReturnNull</code></b></a>
* <a href="#api_nan_return_empty_string"><b><code>NanReturnEmptyString</code></b></a>
* <a href="#api_nan_scope"><b><code>NanScope</code></b></a>
* <a href="#api_nan_escapable_scope"><b><code>NanEscapableScope</code></b></a>
* <a href="#api_nan_escape_scope"><b><code>NanEscapeScope</code></b></a>
* <a href="#api_nan_locker"><b><code>NanLocker</code></b></a>
* <a href="#api_nan_unlocker"><b><code>NanUnlocker</code></b></a>
* <a href="#api_nan_get_internal_field_pointer"><b><code>NanGetInternalFieldPointer</code></b></a>
* <a href="#api_nan_set_internal_field_pointer"><b><code>NanSetInternalFieldPointer</code></b></a>
* <a href="#api_nan_object_wrap_handle"><b><code>NanObjectWrapHandle</code></b></a>
* <del><a href="#api_nan_symbol"><b><code>NanSymbol</code></b></a></del>
* <a href="#api_nan_get_pointer_safe"><b><code>NanGetPointerSafe</code></b></a>
* <a href="#api_nan_set_pointer_safe"><b><code>NanSetPointerSafe</code></b></a>
* <a href="#api_nan_raw_string"><b><code>NanRawString</code></b></a>
* <a href="#api_nan_c_string"><b><code>NanCString</code></b></a>
* <a href="#api_nan_boolean_option_value"><b><code>NanBooleanOptionValue</code></b></a>
* <a href="#api_nan_uint32_option_value"><b><code>NanUInt32OptionValue</code></b></a>
* <a href="#api_nan_error"><b><code>NanError</code></b>, <b><code>NanTypeError</code></b>, <b><code>NanRangeError</code></b></a>
* <a href="#api_nan_throw_error"><b><code>NanThrowError</code></b>, <b><code>NanThrowTypeError</code></b>, <b><code>NanThrowRangeError</code></b>, <b><code>NanThrowError(Handle<Value>)</code></b>, <b><code>NanThrowError(Handle<Value>, int)</code></b></a>
* <a href="#api_nan_new_buffer_handle"><b><code>NanNewBufferHandle(char *, size_t, FreeCallback, void *)</code></b>, <b><code>NanNewBufferHandle(char *, uint32_t)</code></b>, <b><code>NanNewBufferHandle(uint32_t)</code></b></a>
* <a href="#api_nan_buffer_use"><b><code>NanBufferUse(char *, uint32_t)</code></b></a>
* <a href="#api_nan_new_context_handle"><b><code>NanNewContextHandle</code></b></a>
* <a href="#api_nan_get_current_context"><b><code>NanGetCurrentContext</code></b></a>
* <a href="#api_nan_has_instance"><b><code>NanHasInstance</code></b></a>
* <a href="#api_nan_dispose_persistent"><b><code>NanDisposePersistent</code></b></a>
* <a href="#api_nan_assign_persistent"><b><code>NanAssignPersistent</code></b></a>
* <a href="#api_nan_make_weak_persistent"><b><code>NanMakeWeakPersistent</code></b></a>
* <a href="#api_nan_set_template"><b><code>NanSetTemplate</code></b></a>
* <a href="#api_nan_make_callback"><b><code>NanMakeCallback</code></b></a>
* <a href="#api_nan_compile_script"><b><code>NanCompileScript</code></b></a>
* <a href="#api_nan_run_script"><b><code>NanRunScript</code></b></a>
* <a href="#api_nan_adjust_external_memory"><b><code>NanAdjustExternalMemory</code></b></a>
* <a href="#api_nan_add_gc_epilogue_callback"><b><code>NanAddGCEpilogueCallback</code></b></a>
* <a href="#api_nan_add_gc_prologue_callback"><b><code>NanAddGCPrologueCallback</code></b></a>
* <a href="#api_nan_remove_gc_epilogue_callback"><b><code>NanRemoveGCEpilogueCallback</code></b></a>
* <a href="#api_nan_remove_gc_prologue_callback"><b><code>NanRemoveGCPrologueCallback</code></b></a>
* <a href="#api_nan_get_heap_statistics"><b><code>NanGetHeapStatistics</code></b></a>
* <a href="#api_nan_callback"><b><code>NanCallback</code></b></a>
* <a href="#api_nan_async_worker"><b><code>NanAsyncWorker</code></b></a>
* <a href="#api_nan_async_queue_worker"><b><code>NanAsyncQueueWorker</code></b></a>
<a name="api_nan_method"></a>
### NAN_METHOD(methodname)
Use `NAN_METHOD` to define your V8 accessible methods:
```c++
// .h:
class Foo : public node::ObjectWrap {
...
static NAN_METHOD(Bar);
static NAN_METHOD(Baz);
}
// .cc:
NAN_METHOD(Foo::Bar) {
...
}
NAN_METHOD(Foo::Baz) {
...
}
```
The reason for this macro is because of the method signature change in 0.11:
```c++
// 0.10 and below:
Handle<Value> name(const Arguments& args)
// 0.11 and above
void name(const FunctionCallbackInfo<Value>& args)
```
The introduction of `FunctionCallbackInfo` brings additional complications:
<a name="api_nan_getter"></a>
### NAN_GETTER(methodname)
Use `NAN_GETTER` to declare your V8 accessible getters. You get a `Local<String>` `property` and an appropriately typed `args` object that can act like the `args` argument to a `NAN_METHOD` call.
You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_GETTER`.
<a name="api_nan_setter"></a>
### NAN_SETTER(methodname)
Use `NAN_SETTER` to declare your V8 accessible setters. Same as `NAN_GETTER` but you also get a `Local<Value>` `value` object to work with.
<a name="api_nan_property_getter"></a>
### NAN_PROPERTY_GETTER(cbname)
Use `NAN_PROPERTY_GETTER` to declare your V8 accessible property getters. You get a `Local<String>` `property` and an appropriately typed `args` object that can act similar to the `args` argument to a `NAN_METHOD` call.
You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_GETTER`.
<a name="api_nan_property_setter"></a>
### NAN_PROPERTY_SETTER(cbname)
Use `NAN_PROPERTY_SETTER` to declare your V8 accessible property setters. Same as `NAN_PROPERTY_GETTER` but you also get a `Local<Value>` `value` object to work with.
<a name="api_nan_property_enumerator"></a>
### NAN_PROPERTY_ENUMERATOR(cbname)
Use `NAN_PROPERTY_ENUMERATOR` to declare your V8 accessible property enumerators. You get an appropriately typed `args` object like the `args` argument to a `NAN_PROPERTY_GETTER` call.
You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_ENUMERATOR`.
<a name="api_nan_property_deleter"></a>
### NAN_PROPERTY_DELETER(cbname)
Use `NAN_PROPERTY_DELETER` to declare your V8 accessible property deleters. Same as `NAN_PROPERTY_GETTER`.
You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_DELETER`.
<a name="api_nan_property_query"></a>
### NAN_PROPERTY_QUERY(cbname)
Use `NAN_PROPERTY_QUERY` to declare your V8 accessible property queries. Same as `NAN_PROPERTY_GETTER`.
You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_QUERY`.
<a name="api_nan_index_getter"></a>
### NAN_INDEX_GETTER(cbname)
Use `NAN_INDEX_GETTER` to declare your V8 accessible index getters. You get a `uint32_t` `index` and an appropriately typed `args` object that can act similar to the `args` argument to a `NAN_METHOD` call.
You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_INDEX_GETTER`.
<a name="api_nan_index_setter"></a>
### NAN_INDEX_SETTER(cbname)
Use `NAN_INDEX_SETTER` to declare your V8 accessible index setters. Same as `NAN_INDEX_GETTER` but you also get a `Local<Value>` `value` object to work with.
<a name="api_nan_index_enumerator"></a>
### NAN_INDEX_ENUMERATOR(cbname)
Use `NAN_INDEX_ENUMERATOR` to declare your V8 accessible index enumerators. You get an appropriately typed `args` object like the `args` argument to a `NAN_INDEX_GETTER` call.
You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_INDEX_ENUMERATOR`.
<a name="api_nan_index_deleter"></a>
### NAN_INDEX_DELETER(cbname)
Use `NAN_INDEX_DELETER` to declare your V8 accessible index deleters. Same as `NAN_INDEX_GETTER`.
You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_INDEX_DELETER`.
<a name="api_nan_index_query"></a>
### NAN_INDEX_QUERY(cbname)
Use `NAN_INDEX_QUERY` to declare your V8 accessible index queries. Same as `NAN_INDEX_GETTER`.
You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_INDEX_QUERY`.
<a name="api_nan_weak_callback"></a>
### NAN_WEAK_CALLBACK(cbname)
Use `NAN_WEAK_CALLBACK` to define your V8 WeakReference callbacks. Do not use for declaration. There is an argument object `const _NanWeakCallbackData<T, P> &data` allowing access to the weak object and the supplied parameter through its `GetValue` and `GetParameter` methods. You can even access the weak callback info object through the `GetCallbackInfo()`method, but you probably should not. `Revive()` keeps the weak object alive until the next GC round.
```c++
NAN_WEAK_CALLBACK(weakCallback) {
int *parameter = data.GetParameter();
NanMakeCallback(NanGetCurrentContext()->Global(), data.GetValue(), 0, NULL);
if ((*parameter)++ == 0) {
data.Revive();
} else {
delete parameter;
}
}
```
<a name="api_nan_deprecated"></a>
### NAN_DEPRECATED
Declares a function as deprecated.
```c++
static NAN_DEPRECATED NAN_METHOD(foo) {
...
}
```
<a name="api_nan_inline"></a>
### NAN_INLINE
Inlines a function.
```c++
NAN_INLINE int foo(int bar) {
...
}
```
<a name="api_nan_new"></a>
### Local&lt;T&gt; NanNew&lt;T&gt;( ... )
Use `NanNew` to construct almost all v8 objects and make new local handles.
```c++
Local<String> s = NanNew<String>("value");
...
Persistent<Object> o;
...
Local<Object> lo = NanNew(o);
```
<a name="api_nan_undefined"></a>
### Local&lt;Primitive&gt; NanUndefined()
Use instead of `Undefined()`
<a name="api_nan_null"></a>
### Local&lt;Primitive&gt; NanNull()
Use instead of `Null()`
<a name="api_nan_true"></a>
### Local&lt;Boolean&gt; NanTrue()
Use instead of `True()`
<a name="api_nan_false"></a>
### Local&lt;Boolean&gt; NanFalse()
Use instead of `False()`
<a name="api_nan_return_value"></a>
### NanReturnValue(Handle&lt;Value&gt;)
Use `NanReturnValue` when you want to return a value from your V8 accessible method:
```c++
NAN_METHOD(Foo::Bar) {
...
NanReturnValue(NanNew<String>("FooBar!"));
}
```
No `return` statement required.
<a name="api_nan_return_undefined"></a>
### NanReturnUndefined()
Use `NanReturnUndefined` when you don't want to return anything from your V8 accessible method:
```c++
NAN_METHOD(Foo::Baz) {
...
NanReturnUndefined();
}
```
<a name="api_nan_return_null"></a>
### NanReturnNull()
Use `NanReturnNull` when you want to return `Null` from your V8 accessible method:
```c++
NAN_METHOD(Foo::Baz) {
...
NanReturnNull();
}
```
<a name="api_nan_return_empty_string"></a>
### NanReturnEmptyString()
Use `NanReturnEmptyString` when you want to return an empty `String` from your V8 accessible method:
```c++
NAN_METHOD(Foo::Baz) {
...
NanReturnEmptyString();
}
```
<a name="api_nan_scope"></a>
### NanScope()
The introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanScope()` necessary, use it in place of `HandleScope scope` when you do not wish to return handles (`Handle` or `Local`) to the surrounding scope (or in functions directly exposed to V8, as they do not return values in the normal sense):
```c++
NAN_METHOD(Foo::Bar) {
NanScope();
NanReturnValue(NanNew<String>("FooBar!"));
}
```
This method is not directly exposed to V8, nor does it return a handle, so it uses an unescapable scope:
```c++
bool Foo::Bar() {
NanScope();
Local<Boolean> val = NanFalse();
...
return val->Value();
}
```
<a name="api_nan_escapable_scope"></a>
### NanEscapableScope()
The separation of handle scopes into escapable and inescapable scopes makes `NanEscapableScope()` necessary, use it in place of `HandleScope scope` when you later wish to return a handle (`Handle` or `Local`) from the scope, this is for internal functions not directly exposed to V8:
```c++
Handle<String> Foo::Bar() {
NanEscapableScope();
return NanEscapeScope(NanNew<String>("FooBar!"));
}
```
<a name="api_nan_esacpe_scope"></a>
### Local&lt;T&gt; NanEscapeScope(Handle&lt;T&gt; value);
Use together with `NanEscapableScope` to escape the scope. Corresponds to `HandleScope::Close` or `EscapableHandleScope::Escape`.
<a name="api_nan_locker"></a>
### NanLocker()
The introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanLocker()` necessary, use it in place of `Locker locker`:
```c++
NAN_METHOD(Foo::Bar) {
NanLocker();
...
NanUnlocker();
}
```
<a name="api_nan_unlocker"></a>
### NanUnlocker()
The introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanUnlocker()` necessary, use it in place of `Unlocker unlocker`:
```c++
NAN_METHOD(Foo::Bar) {
NanLocker();
...
NanUnlocker();
}
```
<a name="api_nan_get_internal_field_pointer"></a>
### void * NanGetInternalFieldPointer(Handle&lt;Object&gt;, int)
Gets a pointer to the internal field with at `index` from a V8 `Object` handle.
```c++
Local<Object> obj;
...
NanGetInternalFieldPointer(obj, 0);
```
<a name="api_nan_set_internal_field_pointer"></a>
### void NanSetInternalFieldPointer(Handle&lt;Object&gt;, int, void *)
Sets the value of the internal field at `index` on a V8 `Object` handle.
```c++
static Persistent<Function> dataWrapperCtor;
...
Local<Object> wrapper = NanNew(dataWrapperCtor)->NewInstance();
NanSetInternalFieldPointer(wrapper, 0, this);
```
<a name="api_nan_object_wrap_handle"></a>
### Local&lt;Object&gt; NanObjectWrapHandle(Object)
When you want to fetch the V8 object handle from a native object you've wrapped with Node's `ObjectWrap`, you should use `NanObjectWrapHandle`:
```c++
NanObjectWrapHandle(iterator)->Get(NanNew<String>("end"))
```
<a name="api_nan_symbol"></a>
### <del>Local&lt;String&gt; NanSymbol(const char *)</del>
Deprecated. Use `NanNew<String>` instead.
<del>Use to create string symbol objects (i.e. `v8::String::NewSymbol(x)`), for getting and setting object properties, or names of objects.</del>
```c++
bool foo = false;
if (obj->Has(NanNew<String>("foo")))
foo = optionsObj->Get(NanNew<String>("foo"))->BooleanValue()
```
<a name="api_nan_get_pointer_safe"></a>
### Type NanGetPointerSafe(Type *[, Type])
A helper for getting values from optional pointers. If the pointer is `NULL`, the function returns the optional default value, which defaults to `0`. Otherwise, the function returns the value the pointer points to.
```c++
char *plugh(uint32_t *optional) {
char res[] = "xyzzy";
uint32_t param = NanGetPointerSafe<uint32_t>(optional, 0x1337);
switch (param) {
...
}
NanSetPointerSafe<uint32_t>(optional, 0xDEADBEEF);
}
```
<a name="api_nan_set_pointer_safe"></a>
### bool NanSetPointerSafe(Type *, Type)
A helper for setting optional argument pointers. If the pointer is `NULL`, the function simply returns `false`. Otherwise, the value is assigned to the variable the pointer points to.
```c++
const char *plugh(size_t *outputsize) {
char res[] = "xyzzy";
if !(NanSetPointerSafe<size_t>(outputsize, strlen(res) + 1)) {
...
}
...
}
```
<a name="api_nan_raw_string"></a>
### void* NanRawString(Handle&lt;Value&gt;, enum Nan::Encoding, size_t *, void *, size_t, int)
When you want to convert a V8 `String` to a `char*` buffer, use `NanRawString`. You have to supply an encoding as well as a pointer to a variable that will be assigned the number of bytes in the returned string. It is also possible to supply a buffer and its length to the function in order not to have a new buffer allocated. The final argument allows setting `String::WriteOptions`.
Just remember that you'll end up with an object that you'll need to `delete[]` at some point unless you supply your own buffer:
```c++
size_t count;
void* decoded = NanRawString(args[1], Nan::BASE64, &count, NULL, 0, String::HINT_MANY_WRITES_EXPECTED);
char param_copy[count];
memcpy(param_copy, decoded, count);
delete[] decoded;
```
<a name="api_nan_c_string"></a>
### char* NanCString(Handle&lt;Value&gt;, size_t *[, char *, size_t, int])
When you want to convert a V8 `String` to a null-terminated C `char*` use `NanCString`. The resulting `char*` will be UTF-8-encoded, and you need to supply a pointer to a variable that will be assigned the number of bytes in the returned string. It is also possible to supply a buffer and its length to the function in order not to have a new buffer allocated. The final argument allows optionally setting `String::WriteOptions`, which default to `v8::String::NO_OPTIONS`.
Just remember that you'll end up with an object that you'll need to `delete[]` at some point unless you supply your own buffer:
```c++
size_t count;
char* name = NanCString(args[0], &count);
```
<a name="api_nan_boolean_option_value"></a>
### bool NanBooleanOptionValue(Handle&lt;Value&gt;, Handle&lt;String&gt;[, bool])
When you have an "options" object that you need to fetch properties from, boolean options can be fetched with this pair. They check first if the object exists (`IsEmpty`), then if the object has the given property (`Has`) then they get and convert/coerce the property to a `bool`.
The optional last parameter is the *default* value, which is `false` if left off:
```c++
// `foo` is false unless the user supplies a truthy value for it
bool foo = NanBooleanOptionValue(optionsObj, NanNew<String>("foo"));
// `bar` is true unless the user supplies a falsy value for it
bool bar = NanBooleanOptionValueDefTrue(optionsObj, NanNew<String>("bar"), true);
```
<a name="api_nan_uint32_option_value"></a>
### uint32_t NanUInt32OptionValue(Handle&lt;Value&gt;, Handle&lt;String&gt;, uint32_t)
Similar to `NanBooleanOptionValue`, use `NanUInt32OptionValue` to fetch an integer option from your options object. Can be any kind of JavaScript `Number` and it will be coerced to an unsigned 32-bit integer.
Requires all 3 arguments as a default is not optional:
```c++
uint32_t count = NanUInt32OptionValue(optionsObj, NanNew<String>("count"), 1024);
```
<a name="api_nan_error"></a>
### NanError(message), NanTypeError(message), NanRangeError(message)
For making `Error`, `TypeError` and `RangeError` objects.
```c++
Local<Value> res = NanError("you must supply a callback argument");
```
<a name="api_nan_throw_error"></a>
### NanThrowError(message), NanThrowTypeError(message), NanThrowRangeError(message), NanThrowError(Local&lt;Value&gt;), NanThrowError(Local&lt;Value&gt;, int)
For throwing `Error`, `TypeError` and `RangeError` objects.
```c++
NanThrowError("you must supply a callback argument");
```
Can also handle any custom object you may want to throw. If used with the error code argument, it will add the supplied error code to the error object as a property called `code`.
<a name="api_nan_new_buffer_handle"></a>
### Local&lt;Object&gt; NanNewBufferHandle(char *, uint32_t), Local&lt;Object&gt; NanNewBufferHandle(uint32_t)
The `Buffer` API has changed a little in Node 0.11, this helper provides consistent access to `Buffer` creation:
```c++
NanNewBufferHandle((char*)value.data(), value.size());
```
Can also be used to initialize a `Buffer` with just a `size` argument.
Can also be supplied with a `NanFreeCallback` and a hint for the garbage collector.
<a name="api_nan_buffer_use"></a>
### Local&lt;Object&gt; NanBufferUse(char*, uint32_t)
`Buffer::New(char*, uint32_t)` prior to 0.11 would make a copy of the data.
While it was possible to get around this, it required a shim by passing a
callback. So the new API `Buffer::Use(char*, uint32_t)` was introduced to remove
needing to use this shim.
`NanBufferUse` uses the `char*` passed as the backing data, and will free the
memory automatically when the weak callback is called. Keep this in mind, as
careless use can lead to "double free or corruption" and other cryptic failures.
<a name="api_nan_has_instance"></a>
### bool NanHasInstance(Persistent&lt;FunctionTemplate&gt;&, Handle&lt;Value&gt;)
Can be used to check the type of an object to determine it is of a particular class you have already defined and have a `Persistent<FunctionTemplate>` handle for.
<a href="#api_nan_new_context_handle">
### Local&lt;Context&gt; NanNewContextHandle([ExtensionConfiguration*, Handle&lt;ObjectTemplate&gt;, Handle&lt;Value&gt;])
Creates a new `Local<Context>` handle.
```c++
Local<FunctionTemplate> ftmpl = NanNew<FunctionTemplate>();
Local<ObjectTemplate> otmpl = ftmpl->InstanceTemplate();
Local<Context> ctx = NanNewContextHandle(NULL, otmpl);
```
<a href="#api_nan_get_current_context">
### Local&lt;Context&gt; NanGetCurrentContext()
Gets the current context.
```c++
Local<Context> ctx = NanGetCurrentContext();
```
<a name="api_nan_dispose_persistent"></a>
### void NanDisposePersistent(Persistent&lt;T&gt; &)
Use `NanDisposePersistent` to dispose a `Persistent` handle.
```c++
NanDisposePersistent(persistentHandle);
```
<a name="api_nan_assign_persistent"></a>
### NanAssignPersistent(handle, object)
Use `NanAssignPersistent` to assign a non-`Persistent` handle to a `Persistent` one. You can no longer just declare a `Persistent` handle and assign directly to it later, you have to `Reset` it in Node 0.11, so this makes it easier.
In general it is now better to place anything you want to protect from V8's garbage collector as properties of a generic `Object` and then assign that to a `Persistent`. This works in older versions of Node also if you use `NanAssignPersistent`:
```c++
Persistent<Object> persistentHandle;
...
Local<Object> obj = NanNew<Object>();
obj->Set(NanNew<String>("key"), keyHandle); // where keyHandle might be a Local<String>
NanAssignPersistent(persistentHandle, obj)
```
<a name="api_nan_make_weak_persistent"></a>
### _NanWeakCallbackInfo&lt;T, P&gt;* NanMakeWeakPersistent(Handle&lt;T&gt;, P*, _NanWeakCallbackInfo&lt;T, P&gt;::Callback)
Creates a weak persistent handle with the supplied parameter and `NAN_WEAK_CALLBACK`. The callback has to be fully specialized to work on all versions of Node.
```c++
NAN_WEAK_CALLBACK(weakCallback) {
...
}
Local<Function> func;
...
int *parameter = new int(0);
NanMakeWeakPersistent(func, parameter, &weakCallback<Function, int>);
```
<a name="api_nan_set_template"></a>
### NanSetTemplate(templ, name, value)
Use to add properties on object and function templates.
<a name="api_nan_make_callback"></a>
### NanMakeCallback(target, func, argc, argv)
Use instead of `node::MakeCallback` to call javascript functions. This is the only proper way of calling functions.
<a name="api_nan_compile_script"></a>
### NanCompileScript(Handle<String> s [, const ScriptOrigin&amp; origin])
Use to create new scripts bound to the current context.
<a name="api_nan_run_script"></a>
### NanRunScript(script)
Use to run both bound and unbound scripts.
<a name="api_nan_adjust_external_memory"></a>
### NanAdjustExternalMemory(int change_in_bytes)
Simply does `AdjustAmountOfExternalAllocatedMemory`
<a name="api_nan_add_gc_epilogue_callback"></a>
### NanAddGCEpilogueCallback(GCEpilogueCallback callback, GCType gc_type_filter=kGCTypeAll)
Simply does `AddGCEpilogueCallback`
<a name="api_nan_add_gc_prologue_callback"></a>
### NanAddGCPrologueCallback(GCPrologueCallback callback, GCType gc_type_filter=kGCTypeAll)
Simply does `AddGCPrologueCallback`
<a name="api_nan_remove_gc_epilogue_callback"></a>
### NanRemoveGCEpilogueCallback(GCEpilogueCallback callback)
Simply does `RemoveGCEpilogueCallback`
<a name="api_nan_add_gc_prologue_callback"></a>
### NanRemoveGCPrologueCallback(GCPrologueCallback callback)
Simply does `RemoveGCPrologueCallback`
<a name="api_nan_get_heap_statistics"></a>
### NanGetHeapStatistics(HeapStatistics *heap_statistics)
Simply does `GetHeapStatistics`
<a name="api_nan_callback"></a>
### NanCallback
Because of the difficulties imposed by the changes to `Persistent` handles in V8 in Node 0.11, creating `Persistent` versions of your `Handle<Function>` is annoyingly tricky. `NanCallback` makes it easier by taking your handle, making it persistent until the `NanCallback` is deleted and even providing a handy `Call()` method to fetch and execute the callback `Function`.
```c++
Local<Function> callbackHandle = args[0].As<Function>();
NanCallback *callback = new NanCallback(callbackHandle);
// pass `callback` around and it's safe from GC until you:
delete callback;
```
You can execute the callback like so:
```c++
// no arguments:
callback->Call(0, NULL);
// an error argument:
Handle<Value> argv[] = {
NanError(NanNew<String>("fail!"))
};
callback->Call(1, argv);
// a success argument:
Handle<Value> argv[] = {
NanNull(),
NanNew<String>("w00t!")
};
callback->Call(2, argv);
```
`NanCallback` also has a `Local<Function> GetCallback()` method that you can use
to fetch a local handle to the underlying callback function, as well as a
`void SetFunction(Handle<Function>)` for setting the callback on the
`NanCallback`. You can check if a `NanCallback` is empty with the `bool IsEmpty()` method. Additionally a generic constructor is available for using
`NanCallback` without performing heap allocations.
<a name="api_nan_async_worker"></a>
### NanAsyncWorker
`NanAsyncWorker` is an abstract class that you can subclass to have much of the annoying async queuing and handling taken care of for you. It can even store arbitrary V8 objects for you and have them persist while the async work is in progress.
See a rough outline of the implementation:
```c++
class NanAsyncWorker {
public:
NanAsyncWorker (NanCallback *callback);
// Clean up persistent handles and delete the *callback
virtual ~NanAsyncWorker ();
// Check the `ErrorMessage()` and call HandleOKCallback()
// or HandleErrorCallback depending on whether it has been set or not
virtual void WorkComplete ();
// You must implement this to do some async work. If there is an
// error then use `SetErrorMessage()` to set an error message and the callback will
// be passed that string in an Error object
virtual void Execute ();
// Save a V8 object in a Persistent handle to protect it from GC
void SaveToPersistent(const char *key, Local<Object> &obj);
// Fetch a stored V8 object (don't call from within `Execute()`)
Local<Object> GetFromPersistent(const char *key);
// Get the error message (or NULL)
const char *ErrorMessage();
// Set an error message
void SetErrorMessage(const char *msg);
protected:
// Default implementation calls the callback function with no arguments.
// Override this to return meaningful data
virtual void HandleOKCallback ();
// Default implementation calls the callback function with an Error object
// wrapping the `errmsg` string
virtual void HandleErrorCallback ();
};
```
<a name="api_nan_async_queue_worker"></a>
### NanAsyncQueueWorker(NanAsyncWorker *)
`NanAsyncQueueWorker` will run a `NanAsyncWorker` asynchronously via libuv. Both the *execute* and *after_work* steps are taken care of for you&mdash;most of the logic for this is embedded in `NanAsyncWorker`.
### Contributors
NAN is only possible due to the excellent work of the following contributors:
<table><tbody>
<tr><th align="left">Rod Vagg</th><td><a href="https://github.com/rvagg">GitHub/rvagg</a></td><td><a href="http://twitter.com/rvagg">Twitter/@rvagg</a></td></tr>
<tr><th align="left">Benjamin Byholm</th><td><a href="https://github.com/kkoopa/">GitHub/kkoopa</a></td><td>-</td></tr>
<tr><th align="left">Trevor Norris</th><td><a href="https://github.com/trevnorris">GitHub/trevnorris</a></td><td><a href="http://twitter.com/trevnorris">Twitter/@trevnorris</a></td></tr>
<tr><th align="left">Nathan Rajlich</th><td><a href="https://github.com/TooTallNate">GitHub/TooTallNate</a></td><td><a href="http://twitter.com/TooTallNate">Twitter/@TooTallNate</a></td></tr>
<tr><th align="left">Brett Lawson</th><td><a href="https://github.com/brett19">GitHub/brett19</a></td><td><a href="http://twitter.com/brett19x">Twitter/@brett19x</a></td></tr>
<tr><th align="left">Ben Noordhuis</th><td><a href="https://github.com/bnoordhuis">GitHub/bnoordhuis</a></td><td><a href="http://twitter.com/bnoordhuis">Twitter/@bnoordhuis</a></td></tr>
</tbody></table>
Licence &amp; copyright
-----------------------
Copyright (c) 2014 NAN contributors (listed above).
Native Abstractions for Node.js is licensed under an MIT +no-false-attribs license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details.

View File

@ -1,38 +0,0 @@
# Do not edit. File was generated by node-gyp's "configure" step
{
"target_defaults": {
"cflags": [],
"default_configuration": "Release",
"defines": [],
"include_dirs": [],
"libraries": []
},
"variables": {
"clang": 0,
"gcc_version": 47,
"host_arch": "x64",
"node_install_npm": "true",
"node_prefix": "",
"node_shared_cares": "false",
"node_shared_http_parser": "false",
"node_shared_libuv": "false",
"node_shared_openssl": "false",
"node_shared_v8": "false",
"node_shared_zlib": "false",
"node_tag": "",
"node_unsafe_optimizations": 0,
"node_use_dtrace": "false",
"node_use_etw": "false",
"node_use_openssl": "true",
"node_use_perfctr": "false",
"node_use_systemtap": "false",
"python": "/usr/bin/python",
"target_arch": "x64",
"v8_enable_gdbjit": 0,
"v8_no_strict_aliasing": 1,
"v8_use_snapshot": "true",
"nodedir": "/home/rvagg/.node-gyp/0.10.21",
"copy_dev_lib": "true",
"standalone_static_library": 1
}
}

View File

@ -1 +0,0 @@
console.log(require('path').relative('.', __dirname));

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,44 +0,0 @@
{
"name": "hiredis",
"description": "Wrapper for reply processing code in hiredis",
"version": "0.1.17",
"homepage": "http://github.com/redis/hiredis-node",
"author": {
"name": "Pieter Noordhuis",
"email": "pcnoordhuis@gmail.com"
},
"main": "hiredis",
"scripts": {
"test": "node test/reader.js",
"install": "node-gyp rebuild"
},
"dependencies": {
"bindings": "*",
"nan": "~1.1.0"
},
"engines": {
"node": ">= 0.6.0"
},
"repository": {
"type": "git",
"url": "https://github.com/redis/hiredis-node.git"
},
"bugs": {
"url": "https://github.com/redis/hiredis-node/issues"
},
"licenses": [
{
"type": "BSD",
"url": "https://github.com/redis/hiredis-node#license"
}
],
"gypfile": true,
"readme": "[![Build Status](https://travis-ci.org/redis/hiredis-node.png?branch=master)](https://travis-ci.org/redis/hiredis-node)\n\n# hiredis-node\n\nNode extension that wraps [hiredis][hiredis].\nBecause Node is already good at doing I/O, hiredis-node only provides\nbindings to the protocol parser.\nThe hiredis protocol parser is faster than JavaScript protocol parsers,\nbut the speedup only becomes noticable for large replies.\nIf you use Redis for simple SET/GET operations, there won't be a big\nbenefit to using hiredis.\nIf you use Redis for big SUNION/SINTER/LRANGE/ZRANGE operations, the\nbenefit to using hiredis-node can be significant.\n\n[hiredis]: http://github.com/redis/hiredis\n\n## Install\n\nInstall with [NPM][npm]:\n\n```\nnpm install hiredis\n```\n\n[npm]: https://npmjs.org/\n\n## Usage\n\nhiredis-node works out of the box with Matt Ranney's [node_redis][node_redis].\nThe latter has an optional dependency on hiredis-node, so maybe you're\nalready using it without knowing.\n\nAlternatively, you can use it directly:\n\n```javascript\nvar hiredis = require(\"hiredis\"),\n reader = new hiredis.Reader();\n\n// Data comes in\nreader.feed(\"$5\\r\\nhello\\r\\n\");\n\n// Reply comes out\nreader.get() // => \"hello\"\n```\n\nInstead of returning strings for bulk payloads, it can also return\nbuffers:\n\n```javascript\nvar hiredis = require(\"hiredis\"),\n reader = new hiredis.Reader({ return_buffers: true });\n\n// Data comes in\nreader.feed(\"$5\\r\\nhello\\r\\n\");\n\n// Reply comes out\nreader.get() // => <Buffer 68 65 6c 6c 6f>\n```\n\n[node_redis]: http://github.com/mranney/node_redis\n\n## Windows\n\nDmitry Gorbunov (@fuwaneko) made a [fork of hiredis-node][windows_fork] with Windows support.\n\n[windows_fork]: https://github.com/fuwaneko/hiredis-node\n\n## License\n\nThis code is released under the BSD license, after the license of hiredis.\n",
"readmeFilename": "README.md",
"_id": "hiredis@0.1.17",
"dist": {
"shasum": "d3254750c67d4ba82c4576a46875829bf53babd4"
},
"_from": "hiredis@",
"_resolved": "https://registry.npmjs.org/hiredis/-/hiredis-0.1.17.tgz"
}

View File

@ -1,13 +0,0 @@
#include <v8.h>
#include <node.h>
#include "reader.h"
using namespace v8;
extern "C" {
static void init (Handle<Object> target) {
NanScope();
hiredis::Reader::Initialize(target);
}
NODE_MODULE(hiredis, init)
}

View File

@ -1,233 +0,0 @@
#include <v8.h>
#include <node.h>
#if _USE_CUSTOM_BUFFER_POOL
#include <node_buffer.h>
#endif
#include <string.h>
#include <assert.h>
#include "reader.h"
using namespace hiredis;
static void *tryParentize(const redisReadTask *task, const Local<Value> &v) {
Reader *r = reinterpret_cast<Reader*>(task->privdata);
size_t pidx, vidx;
if (task->parent != NULL) {
pidx = (size_t)task->parent->obj;
assert(pidx > 0 && pidx < 9);
/* When there is a parent, it should be an array. */
Local<Value> lvalue = NanNew(r->handle[pidx]);
assert(lvalue->IsArray());
Local<Array> larray = lvalue.As<Array>();
larray->Set(task->idx,v);
/* Store the handle when this is an inner array. Otherwise, hiredis
* doesn't care about the return value as long as the value is set in
* its parent array. */
vidx = pidx+1;
if (v->IsArray()) {
NanDisposePersistent(r->handle[vidx]);
NanAssignPersistent(r->handle[vidx], v);
return (void*)vidx;
} else {
/* Return value doesn't matter for inner value, as long as it is
* not NULL (which means OOM for hiredis). */
return (void*)0xcafef00d;
}
} else {
/* There is no parent, so this value is the root object. */
NanAssignPersistent(r->handle[1], v);
return (void*)1;
}
}
static void *createArray(const redisReadTask *task, int size) {
Local<Value> v(NanNew<Array>(size));
return tryParentize(task,v);
}
static void *createString(const redisReadTask *task, char *str, size_t len) {
Reader *r = reinterpret_cast<Reader*>(task->privdata);
Local<Value> v(r->createString(str,len));
if (task->type == REDIS_REPLY_ERROR)
v = Exception::Error(v->ToString());
return tryParentize(task,v);
}
static void *createInteger(const redisReadTask *task, long long value) {
Local<Value> v(NanNew<Number>(value));
return tryParentize(task,v);
}
static void *createNil(const redisReadTask *task) {
Local<Value> v(NanNew(NanNull()));
return tryParentize(task,v);
}
static redisReplyObjectFunctions v8ReplyFunctions = {
createString,
createArray,
createInteger,
createNil,
NULL /* No free function: cleanup is done in Reader::Get. */
};
Reader::Reader(bool return_buffers) :
return_buffers(return_buffers)
{
reader = redisReaderCreate();
reader->fn = &v8ReplyFunctions;
reader->privdata = this;
#if _USE_CUSTOM_BUFFER_POOL
if (return_buffers) {
Local<Object> global = Context::GetCurrent()->Global();
Local<Value> bv = global->Get(String::NewSymbol("Buffer"));
assert(bv->IsFunction());
Local<Function> bf = Local<Function>::Cast(bv);
buffer_fn = Persistent<Function>::New(bf);
buffer_pool_length = 8*1024; /* Same as node */
buffer_pool_offset = 0;
Buffer *b = Buffer::New(buffer_pool_length);
buffer_pool = Persistent<Object>::New(b->handle_);
}
#endif
}
Reader::~Reader() {
redisReaderFree(reader);
}
/* Don't declare an extra scope here, so the objects are created within the
* scope inherited from the caller (Reader::Get) and we don't have to the pay
* the overhead. */
inline Local<Value> Reader::createString(char *str, size_t len) {
if (return_buffers) {
#if _USE_CUSTOM_BUFFER_POOL
if (len > buffer_pool_length) {
Buffer *b = Buffer::New(str,len);
return Local<Value>::New(b->handle_);
} else {
return createBufferFromPool(str,len);
}
#else
return NanNewBufferHandle(str,len);
#endif
} else {
return NanNew<String>(str,len);
}
}
#if _USE_CUSTOM_BUFFER_POOL
Local<Value> Reader::createBufferFromPool(char *str, size_t len) {
HandleScope scope;
Local<Value> argv[3];
Local<Object> instance;
assert(len <= buffer_pool_length);
if (buffer_pool_length - buffer_pool_offset < len) {
Buffer *b = Buffer::New(buffer_pool_length);
buffer_pool.Dispose();
buffer_pool = Persistent<Object>::New(b->handle_);
buffer_pool_offset = 0;
}
memcpy(Buffer::Data(buffer_pool)+buffer_pool_offset,str,len);
argv[0] = Local<Value>::New(buffer_pool);
argv[1] = Integer::New(len);
argv[2] = Integer::New(buffer_pool_offset);
instance = buffer_fn->NewInstance(3,argv);
buffer_pool_offset += len;
return scope.Close(instance);
}
#endif
NAN_METHOD(Reader::New) {
NanScope();
bool return_buffers = false;
if (args.Length() > 0 && args[0]->IsObject()) {
Local<Value> bv = args[0].As<Object>()->Get(NanNew<String>("return_buffers"));
if (bv->IsBoolean())
return_buffers = bv->ToBoolean()->Value();
}
Reader *r = new Reader(return_buffers);
r->Wrap(args.This());
NanReturnValue(args.This());
}
void Reader::Initialize(Handle<Object> target) {
NanScope();
Local<FunctionTemplate> t = NanNew<FunctionTemplate>(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(t, "feed", Feed);
NODE_SET_PROTOTYPE_METHOD(t, "get", Get);
target->Set(NanNew<String>("Reader"), t->GetFunction());
}
NAN_METHOD(Reader::Feed) {
NanScope();
Reader *r = ObjectWrap::Unwrap<Reader>(args.This());
if (args.Length() == 0) {
NanThrowTypeError("First argument must be a string or buffer");
} else {
if (Buffer::HasInstance(args[0])) {
Local<Object> buffer_object = args[0].As<Object>();
char *data;
size_t length;
data = Buffer::Data(buffer_object);
length = Buffer::Length(buffer_object);
/* Can't handle OOM for now. */
assert(redisReaderFeed(r->reader, data, length) == REDIS_OK);
} else if (args[0]->IsString()) {
String::Utf8Value str(args[0].As<String>());
redisReplyReaderFeed(r->reader, *str, str.length());
} else {
NanThrowError("Invalid argument");
}
}
NanReturnValue(args.This());
}
NAN_METHOD(Reader::Get) {
NanScope();
Reader *r = ObjectWrap::Unwrap<Reader>(args.This());
void *index = NULL;
Local<Value> reply;
int i;
if (redisReaderGetReply(r->reader,&index) == REDIS_OK) {
if (index == 0) {
NanReturnUndefined();
} else {
/* Complete replies should always have a root object at index 1. */
assert((size_t)index == 1);
reply = NanNew<Value>(r->handle[1]);
/* Dispose and clear used handles. */
for (i = 1; i < 3; i++) {
NanDisposePersistent(r->handle[i]);
}
}
} else {
NanThrowError(r->reader->errstr);
}
NanReturnValue(reply);
}

View File

@ -1,59 +0,0 @@
#include <v8.h>
#include <node.h>
#include <hiredis/hiredis.h>
#include "nan.h"
#if NODE_MODULE_VERSION < 12
#define _USE_CUSTOM_BUFFER_POOL 1
#else
#define _USE_CUSTOM_BUFFER_POOL 0
#endif
namespace hiredis {
using namespace v8;
using namespace node;
class Reader : public ObjectWrap {
public:
Reader(bool);
~Reader();
static void Initialize(Handle<Object> target);
static NAN_METHOD(New);
static NAN_METHOD(Feed);
static NAN_METHOD(Get);
/* Objects created by the reply object functions need to get back to the
* reader when the reply is requested via Reader::Get(). Keep temporary
* objects in this handle. Use an array of handles because replies may
* include nested multi bulks and child-elements need to be added to the
* right respective parent. handle[0] will be unused, so the real index of
* an object in this array can be returned from the reply object functions.
* The returned value needs to be non-zero to distinguish complete replies
* from incomplete replies. These are persistent handles because
* Reader::Get might not return a full reply and the objects need to be
* kept around for subsequent calls. */
Persistent<Value> handle[9];
/* Helper function to create string/buffer objects. */
Local<Value> createString(char *str, size_t len);
private:
redisReader *reader;
/* Determines whether to return strings or buffers for single line and bulk
* replies. This defaults to false, so strings are returned by default. */
bool return_buffers;
#if _USE_CUSTOM_BUFFER_POOL
Local<Value> createBufferFromPool(char *str, size_t len);
Persistent<Function> buffer_fn;
Persistent<Object> buffer_pool;
size_t buffer_pool_length;
size_t buffer_pool_offset;
#endif
};
};

View File

@ -1,197 +0,0 @@
var assert = require("assert"),
hiredis = require("../hiredis");
var passed = 0;
var failed = 0;
function test(str, fn) {
try {
fn();
passed++;
} catch (err) {
console.log("\x1B[1;31m" + str + " failed!\x1B[0m");
console.log(err.stack + "\n");
failed++;
}
}
test("CreateReader", function() {
var reader = new hiredis.Reader();
assert.notEqual(reader, null);
});
test("StatusReply", function() {
var reader = new hiredis.Reader();
reader.feed("+OK\r\n");
assert.equal("OK", reader.get());
});
test("StatusReplyAsBuffer", function() {
var reader = new hiredis.Reader({ return_buffers: true });
reader.feed("+OK\r\n");
var reply = reader.get();
assert.ok(Buffer.isBuffer(reply));
assert.equal("OK", reply.toString());
});
test("IntegerReply", function() {
var reader = new hiredis.Reader();
reader.feed(":1\r\n");
assert.equal(1, reader.get());
});
// This test fails since v8 doesn't to 64-bit integers...
test("LargeIntegerReply", function() {
var reader = new hiredis.Reader();
reader.feed(":9223372036854775807\r\n");
assert.equal("9223372036854775807", String(reader.get()));
});
test("ErrorReply", function() {
var reader = new hiredis.Reader();
reader.feed("-ERR foo\r\n");
var reply = reader.get();
assert.equal(Error, reply.constructor);
assert.equal("ERR foo", reply.message);
});
test("ErrorReplyWithReturnBuffers", function() {
var reader = new hiredis.Reader({ return_buffers: true });
reader.feed("-ERR foo\r\n");
var reply = reader.get();
assert.equal(Error, reply.constructor);
assert.equal("ERR foo", reply.message);
});
test("NullBulkReply", function() {
var reader = new hiredis.Reader();
reader.feed("$-1\r\n");
assert.equal(null, reader.get());
});
test("EmptyBulkReply", function() {
var reader = new hiredis.Reader();
reader.feed("$0\r\n\r\n");
assert.equal("", reader.get());
});
test("BulkReply", function() {
var reader = new hiredis.Reader();
reader.feed("$3\r\nfoo\r\n");
assert.equal("foo", reader.get());
});
test("BulkReplyAsBuffer", function() {
var reader = new hiredis.Reader({ return_buffers: true });
reader.feed("$3\r\nfoo\r\n");
var reply = reader.get();
assert.ok(Buffer.isBuffer(reply));
assert.equal("foo", reply.toString());
});
test("BulkReplyWithEncoding", function() {
var reader = new hiredis.Reader();
reader.feed("$" + Buffer.byteLength("☃") + "\r\n☃\r\n");
assert.equal("☃", reader.get());
});
test("NullMultiBulkReply", function() {
var reader = new hiredis.Reader();
reader.feed("*-1\r\n");
assert.equal(null, reader.get());
});
test("EmptyMultiBulkReply", function() {
var reader = new hiredis.Reader();
reader.feed("*0\r\n");
assert.deepEqual([], reader.get());
});
test("MultiBulkReply", function() {
var reader = new hiredis.Reader();
reader.feed("*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n");
assert.deepEqual(["foo", "bar"], reader.get());
});
test("NestedMultiBulkReply", function() {
var reader = new hiredis.Reader();
reader.feed("*2\r\n*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$3\r\nqux\r\n");
assert.deepEqual([["foo", "bar"], "qux"], reader.get());
});
test("DeeplyNestedMultiBulkReply", function() {
var i;
var reader = new hiredis.Reader();
var expected = 1;
for (i = 0; i < 8; i++) {
reader.feed("*1\r\n");
expected = [expected];
}
reader.feed(":1\r\n");
assert.deepEqual(reader.get(), expected);
});
test("TooDeeplyNestedMultiBulkReply", function() {
var i;
var reader = new hiredis.Reader();
for (i = 0; i < 9; i++) {
reader.feed("*1\r\n");
}
reader.feed(":1\r\n");
assert.throws(
function() {
reader.get();
},
/nested multi/
);
});
test("MultiBulkReplyWithNonStringValues", function() {
var reader = new hiredis.Reader();
reader.feed("*3\r\n:1\r\n+OK\r\n$-1\r\n");
assert.deepEqual([1, "OK", null], reader.get());
});
test("FeedWithBuffer", function() {
var reader = new hiredis.Reader();
reader.feed(new Buffer("$3\r\nfoo\r\n"));
assert.deepEqual("foo", reader.get());
});
test("UndefinedReplyOnIncompleteFeed", function() {
var reader = new hiredis.Reader();
reader.feed("$3\r\nfoo");
assert.deepEqual(undefined, reader.get());
reader.feed("\r\n");
assert.deepEqual("foo", reader.get());
});
test("Leaks", function(beforeExit) {
/* The "leaks" utility is only available on OSX. */
if (process.platform != "darwin") return;
var done = 0;
var leaks = require('child_process').spawn("leaks", [process.pid]);
leaks.stdout.on("data", function(data) {
var str = data.toString();
var notice = "Node 0.2.5 always leaks 16 bytes (this is " + process.versions.node + ")";
var matches;
if ((matches = /(\d+) leaks?/i.exec(str)) != null) {
if (parseInt(matches[1]) > 0) {
console.log(str);
console.log('\x1B[31mNotice: ' + notice + '\x1B[0m');
}
}
done = 1;
});
process.on('exit', function() {
assert.ok(done, "Leaks test should have completed");
});
});

View File

@ -1,5 +0,0 @@
language: node_js
node_js:
- "0.10"
before_install:
- "curl -L http://git.io/ejPSng | /bin/sh"

22
labs/node_modules/meteor-npm/LICENSE generated vendored
View File

@ -1,22 +0,0 @@
(The MIT License)
Copyright (c) 2013 Arunoda Susiripala <arunoda.susiripala@gmail.com>
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.

View File

@ -1,147 +0,0 @@
# Complete NPM integration for Meteor [![Build Status](https://travis-ci.org/arunoda/meteor-npm.png?branch=master)](https://travis-ci.org/arunoda/meteor-npm)
See MeteorHacks article on [Complete NPM integration for Meteor](http://meteorhacks.com/complete-npm-integration-for-meteor.html)
#### If you are migrating from Meteor 0.6.5.x to 0.6.6.x, please check this [issue](https://github.com/arunoda/meteor-npm/issues/12).
---
### Adding NPM support to your app
mrt add npm
### Create packages.json file for listing dependencies.
{
"redis": "0.8.2",
"github": "0.1.8"
}
### Example on using npm module inside a Meteor method
~~~js
if (Meteor.isClient) {
getGists = function getGists(user, callback) {
Meteor.call('getGists', user, callback);
}
}
if (Meteor.isServer) {
Meteor.methods({
'getGists': function getGists(user) {
var GithubApi = Meteor.require('github');
var github = new GithubApi({
version: "3.0.0"
});
var gists = Async.runSync(function(done) {
github.gists.getFromUser({user: 'arunoda'}, function(err, data) {
done(null, data);
});
});
return gists.result;
}
});
}
~~~
## API
> Available in the Server Side only
### Meteor.require(npmModuleName)
This method loads NPM modules you've specified in the `packages.json` file.
~~~
var Github = Meteor.require('github');
~~~
## Async Utilities
> Available in the Server Side only
Meteor APIs are executed synchronously. Most of the NodeJS modules works asynchronously.
So we need a way to bride the gap. Async Utilities comes to rescue you.
### Async.runSync(function)
`Async.runSync()` pause the execution until you invoke `done()` callback as shown below.
~~~
var response = Async.runSync(function(done) {
setTimeout(function() {
done(null, 1001);
}, 100);
});
console.log(response.result); // 1001
~~~
`done()` callback takes 2 arguments. `error` and the `result` object. You can get them as the return value of the `Async.runSync()` as shown as response in the above example.
return value is an object and it has 2 fields. `error` and `result`.
### Meteor.sync(function)
Same as `Async.runSync` but deprecated.
### Async.wrap(function)
Wrap an asynchronous function and allow it to be run inside Meteor without callbacks.
~~~
//declare a simple async function
function delayedMessge(delay, message, callback) {
setTimeout(function() {
callback(null, message);
}, delay);
}
//wrapping
var wrappedDelayedMessage = Async.wrap(delayedMessge);
//usage
Meteor.methods({
'delayedEcho': function(message) {
var response = wrappedDelayedMessage(500, message);
return response;
}
});
~~~
If the callback has a result, it will be returned from the wrapped function. If there is an error, it will be thrown.
> `Async.wrap(function)` is very similar to `Meteor._wrapAsync`.
### Async.wrap(object, functionName)
Very similar to `Async.wrap(function)`,
but this API can be used to wrap an instance method of an object.
~~~
var github = new GithubApi({
version: "3.0.0"
});
//wrapping github.user.getFrom
var wrappedGetFrom = Async.wrap(github.user, 'getFrom');
~~~
### Async.wrap(object, functionNameList)
Very similar to `Async.wrap(object, functionName)`,
but this API can be used to wrap **multiple** instance methods of an object.
~~~
var github = new GithubApi({
version: "3.0.0"
});
//wrapping github.user.getFrom and github.user.getEmails
var wrappedGithubUser = Async.wrap(github.user, ['getFrom', 'getEmails']);
//usage
var profile = wrappedGithubUser.getFrom('arunoda');
var emails = wrappedGithubUser.getEmails();
~~~

View File

@ -1,49 +0,0 @@
#!/usr/bin/env node
var fs = require('fs');
var rimraf = require('rimraf');
var mkdirp = require('mkdirp');
var path = require('path');
//check, whether exists in a meteor project
if(!fs.existsSync('.meteor/packages')) {
console.error('Seems like you are not inside a meteor project');
process.exit(1);
}
//remove packages/npm
rimraf.sync('packages/npm');
//mkdir packages/npm
mkdirp.sync('packages/npm');
//copy package.js, packages.json, index.js
copySync(path.resolve(__dirname, '../package.js'), './packages/npm/package.js');
copySync(path.resolve(__dirname, '../index.js'), './packages/npm/index.js');
copySync(path.resolve(__dirname, '../test.js'), './packages/npm/test.js');
//add npm to ./.meteor/packaegs for versions grater than 0.6.5
var meteorPackages = fs.readFileSync('.meteor/packages', 'utf8');
if(meteorPackages.match(/\nstandard-app-packages/)) {
if(!meteorPackages.match(/\nnpm/)) {
var content = 'npm\n';
if(!meteorPackages.match(/\n$/)) {
content = '\n' + npm;
}
fs.appendFileSync('.meteor/packages', content);
}
}
//remove npm from packages/.gitignore if exists
if(fs.existsSync('packages/.gitignore')) {
var packagesGitignore = fs.readFileSync('packages/.gitignore', 'utf8');
packagesGitignore = packagesGitignore.replace(/npm/, '');
fs.writeFileSync('packages/.gitignore', packagesGitignore);
}
console.log('add npm modules to "packages.json" & have fun!');
function copySync(src, dest) {
var content = fs.readFileSync(src, 'utf8');
fs.writeFileSync(dest, content);
}

View File

@ -1,88 +0,0 @@
var Future = Npm.require('fibers/future');
Async = {};
Meteor.require = function(moduleName) {
var module = Npm.require(moduleName);
return module;
};
Async.runSync = Meteor.sync = function(asynFunction) {
var future = new Future();
var sent = false;
var payload;
var wrappedAsyncFunction = Meteor.bindEnvironment(asynFunction, function(err) {
console.error('Error inside the Async.runSync: ' + err.message);
returnFuture(err);
});
setTimeout(function() {
wrappedAsyncFunction(returnFuture);
}, 0);
future.wait();
sent = true;
function returnFuture(error, result) {
if(!sent) {
payload = { result: result, error: error};
future.return();
}
}
return payload;
};
Async.wrap = function(arg1, arg2) {
if(typeof arg1 == 'function') {
var func = arg1;
return wrapFunction(func);
} else if(typeof arg1 == 'object' && typeof arg2 == 'string') {
var obj = arg1;
var funcName = arg2;
return wrapObject(obj, [funcName])[funcName];
} else if(typeof arg1 == 'object' && arg2 instanceof Array) {
var obj = arg1;
var funcNameList = arg2;
return wrapObject(obj, funcNameList);
} else {
throw new Error('unsupported argument list');
}
function wrapObject(obj, funcNameList) {
var returnObj = {};
funcNameList.forEach(function(funcName) {
if(obj[funcName]) {
var func = obj[funcName].bind(obj);
returnObj[funcName] = wrapFunction(func);
} else {
throw new Error('instance method not exists: ' + funcName);
}
});
return returnObj;
}
function wrapFunction(func) {
return function() {
var args = arguments;
response = Meteor.sync(function(done) {
Array.prototype.push.call(args, done);
func.apply(null, args);
});
if(response.error) {
//we need to wrap a new error here something throw error object comes with response does not
//print the correct error to the console, if there is not try catch block
var error = new Error(response.error.message);
for(var key in response.error) {
if(error[key] === undefined) {
error[key] = response.error[key];
}
}
throw error;
} else {
return response.result;
}
};
}
};

View File

@ -1,2 +0,0 @@
node_modules/
npm-debug.log

View File

@ -1,5 +0,0 @@
language: node_js
node_js:
- 0.6
- 0.8
- 0.9

View File

@ -1,21 +0,0 @@
Copyright 2010 James Halliday (mail@substack.net)
This project is free software released under the MIT/X11 license:
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.

View File

@ -1,6 +0,0 @@
var mkdirp = require('mkdirp');
mkdirp('/tmp/foo/bar/baz', function (err) {
if (err) console.error(err)
else console.log('pow!')
});

View File

@ -1,82 +0,0 @@
var path = require('path');
var fs = require('fs');
module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
function mkdirP (p, mode, f, made) {
if (typeof mode === 'function' || mode === undefined) {
f = mode;
mode = 0777 & (~process.umask());
}
if (!made) made = null;
var cb = f || function () {};
if (typeof mode === 'string') mode = parseInt(mode, 8);
p = path.resolve(p);
fs.mkdir(p, mode, function (er) {
if (!er) {
made = made || p;
return cb(null, made);
}
switch (er.code) {
case 'ENOENT':
mkdirP(path.dirname(p), mode, function (er, made) {
if (er) cb(er, made);
else mkdirP(p, mode, cb, made);
});
break;
// In the case of any other error, just see if there's a dir
// there already. If so, then hooray! If not, then something
// is borked.
default:
fs.stat(p, function (er2, stat) {
// if the stat fails, then that's super weird.
// let the original error be the failure reason.
if (er2 || !stat.isDirectory()) cb(er, made)
else cb(null, made);
});
break;
}
});
}
mkdirP.sync = function sync (p, mode, made) {
if (mode === undefined) {
mode = 0777 & (~process.umask());
}
if (!made) made = null;
if (typeof mode === 'string') mode = parseInt(mode, 8);
p = path.resolve(p);
try {
fs.mkdirSync(p, mode);
made = made || p;
}
catch (err0) {
switch (err0.code) {
case 'ENOENT' :
made = sync(path.dirname(p), mode, made);
sync(p, mode, made);
break;
// In the case of any other error, just see if there's a dir
// there already. If so, then hooray! If not, then something
// is borked.
default:
var stat;
try {
stat = fs.statSync(p);
}
catch (err1) {
throw err0;
}
if (!stat.isDirectory()) throw err0;
break;
}
}
return made;
};

View File

@ -1,36 +0,0 @@
{
"name": "mkdirp",
"description": "Recursively mkdir, like `mkdir -p`",
"version": "0.3.5",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"main": "./index",
"keywords": [
"mkdir",
"directory"
],
"repository": {
"type": "git",
"url": "http://github.com/substack/node-mkdirp.git"
},
"scripts": {
"test": "tap test/*.js"
},
"devDependencies": {
"tap": "~0.4.0"
},
"license": "MIT",
"readme": "# mkdirp\n\nLike `mkdir -p`, but in node.js!\n\n[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp)\n\n# example\n\n## pow.js\n\n```js\nvar mkdirp = require('mkdirp');\n \nmkdirp('/tmp/foo/bar/baz', function (err) {\n if (err) console.error(err)\n else console.log('pow!')\n});\n```\n\nOutput\n\n```\npow!\n```\n\nAnd now /tmp/foo/bar/baz exists, huzzah!\n\n# methods\n\n```js\nvar mkdirp = require('mkdirp');\n```\n\n## mkdirp(dir, mode, cb)\n\nCreate a new directory and any necessary subdirectories at `dir` with octal\npermission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\n`cb(err, made)` fires with the error or the first directory `made`\nthat had to be created, if any.\n\n## mkdirp.sync(dir, mode)\n\nSynchronously create a new directory and any necessary subdirectories at `dir`\nwith octal permission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\nReturns the first directory that had to be created, if any.\n\n# install\n\nWith [npm](http://npmjs.org) do:\n\n```\nnpm install mkdirp\n```\n\n# license\n\nMIT\n",
"readmeFilename": "readme.markdown",
"bugs": {
"url": "https://github.com/substack/node-mkdirp/issues"
},
"homepage": "https://github.com/substack/node-mkdirp",
"_id": "mkdirp@0.3.5",
"_shasum": "de3e5f8961c88c787ee1368df849ac4413eca8d7",
"_from": "mkdirp@0.3.x",
"_resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz"
}

View File

@ -1,63 +0,0 @@
# mkdirp
Like `mkdir -p`, but in node.js!
[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp)
# example
## pow.js
```js
var mkdirp = require('mkdirp');
mkdirp('/tmp/foo/bar/baz', function (err) {
if (err) console.error(err)
else console.log('pow!')
});
```
Output
```
pow!
```
And now /tmp/foo/bar/baz exists, huzzah!
# methods
```js
var mkdirp = require('mkdirp');
```
## mkdirp(dir, mode, cb)
Create a new directory and any necessary subdirectories at `dir` with octal
permission string `mode`.
If `mode` isn't specified, it defaults to `0777 & (~process.umask())`.
`cb(err, made)` fires with the error or the first directory `made`
that had to be created, if any.
## mkdirp.sync(dir, mode)
Synchronously create a new directory and any necessary subdirectories at `dir`
with octal permission string `mode`.
If `mode` isn't specified, it defaults to `0777 & (~process.umask())`.
Returns the first directory that had to be created, if any.
# install
With [npm](http://npmjs.org) do:
```
npm install mkdirp
```
# license
MIT

View File

@ -1,38 +0,0 @@
var mkdirp = require('../').mkdirp;
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
var ps = [ '', 'tmp' ];
for (var i = 0; i < 25; i++) {
var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
ps.push(dir);
}
var file = ps.join('/');
test('chmod-pre', function (t) {
var mode = 0744
mkdirp(file, mode, function (er) {
t.ifError(er, 'should not error');
fs.stat(file, function (er, stat) {
t.ifError(er, 'should exist');
t.ok(stat && stat.isDirectory(), 'should be directory');
t.equal(stat && stat.mode & 0777, mode, 'should be 0744');
t.end();
});
});
});
test('chmod', function (t) {
var mode = 0755
mkdirp(file, mode, function (er) {
t.ifError(er, 'should not error');
fs.stat(file, function (er, stat) {
t.ifError(er, 'should exist');
t.ok(stat && stat.isDirectory(), 'should be directory');
t.end();
});
});
});

View File

@ -1,37 +0,0 @@
var mkdirp = require('../').mkdirp;
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
var ps = [ '', 'tmp' ];
for (var i = 0; i < 25; i++) {
var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
ps.push(dir);
}
var file = ps.join('/');
// a file in the way
var itw = ps.slice(0, 3).join('/');
test('clobber-pre', function (t) {
console.error("about to write to "+itw)
fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.');
fs.stat(itw, function (er, stat) {
t.ifError(er)
t.ok(stat && stat.isFile(), 'should be file')
t.end()
})
})
test('clobber', function (t) {
t.plan(2);
mkdirp(file, 0755, function (err) {
t.ok(err);
t.equal(err.code, 'ENOTDIR');
t.end();
});
});

View File

@ -1,28 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('woo', function (t) {
t.plan(2);
var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var file = '/tmp/' + [x,y,z].join('/');
mkdirp(file, 0755, function (err) {
if (err) t.fail(err);
else path.exists(file, function (ex) {
if (!ex) t.fail('file not created')
else fs.stat(file, function (err, stat) {
if (err) t.fail(err)
else {
t.equal(stat.mode & 0777, 0755);
t.ok(stat.isDirectory(), 'target not a directory');
t.end();
}
})
})
});
});

View File

@ -1,32 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('async perm', function (t) {
t.plan(2);
var file = '/tmp/' + (Math.random() * (1<<30)).toString(16);
mkdirp(file, 0755, function (err) {
if (err) t.fail(err);
else path.exists(file, function (ex) {
if (!ex) t.fail('file not created')
else fs.stat(file, function (err, stat) {
if (err) t.fail(err)
else {
t.equal(stat.mode & 0777, 0755);
t.ok(stat.isDirectory(), 'target not a directory');
t.end();
}
})
})
});
});
test('async root perm', function (t) {
mkdirp('/tmp', 0755, function (err) {
if (err) t.fail(err);
t.end();
});
t.end();
});

View File

@ -1,39 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('sync perm', function (t) {
t.plan(2);
var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json';
mkdirp.sync(file, 0755);
path.exists(file, function (ex) {
if (!ex) t.fail('file not created')
else fs.stat(file, function (err, stat) {
if (err) t.fail(err)
else {
t.equal(stat.mode & 0777, 0755);
t.ok(stat.isDirectory(), 'target not a directory');
t.end();
}
})
});
});
test('sync root perm', function (t) {
t.plan(1);
var file = '/tmp';
mkdirp.sync(file, 0755);
path.exists(file, function (ex) {
if (!ex) t.fail('file not created')
else fs.stat(file, function (err, stat) {
if (err) t.fail(err)
else {
t.ok(stat.isDirectory(), 'target not a directory');
t.end();
}
})
});
});

View File

@ -1,41 +0,0 @@
var mkdirp = require('../').mkdirp;
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('race', function (t) {
t.plan(4);
var ps = [ '', 'tmp' ];
for (var i = 0; i < 25; i++) {
var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
ps.push(dir);
}
var file = ps.join('/');
var res = 2;
mk(file, function () {
if (--res === 0) t.end();
});
mk(file, function () {
if (--res === 0) t.end();
});
function mk (file, cb) {
mkdirp(file, 0755, function (err) {
if (err) t.fail(err);
else path.exists(file, function (ex) {
if (!ex) t.fail('file not created')
else fs.stat(file, function (err, stat) {
if (err) t.fail(err)
else {
t.equal(stat.mode & 0777, 0755);
t.ok(stat.isDirectory(), 'target not a directory');
if (cb) cb();
}
})
})
});
}
});

View File

@ -1,32 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('rel', function (t) {
t.plan(2);
var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var cwd = process.cwd();
process.chdir('/tmp');
var file = [x,y,z].join('/');
mkdirp(file, 0755, function (err) {
if (err) t.fail(err);
else path.exists(file, function (ex) {
if (!ex) t.fail('file not created')
else fs.stat(file, function (err, stat) {
if (err) t.fail(err)
else {
process.chdir(cwd);
t.equal(stat.mode & 0777, 0755);
t.ok(stat.isDirectory(), 'target not a directory');
t.end();
}
})
})
});
});

View File

@ -1,25 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('return value', function (t) {
t.plan(4);
var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var file = '/tmp/' + [x,y,z].join('/');
// should return the first dir created.
// By this point, it would be profoundly surprising if /tmp didn't
// already exist, since every other test makes things in there.
mkdirp(file, function (err, made) {
t.ifError(err);
t.equal(made, '/tmp/' + x);
mkdirp(file, function (err, made) {
t.ifError(err);
t.equal(made, null);
});
});
});

View File

@ -1,24 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('return value', function (t) {
t.plan(2);
var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var file = '/tmp/' + [x,y,z].join('/');
// should return the first dir created.
// By this point, it would be profoundly surprising if /tmp didn't
// already exist, since every other test makes things in there.
// Note that this will throw on failure, which will fail the test.
var made = mkdirp.sync(file);
t.equal(made, '/tmp/' + x);
// making the same file again should have no effect.
made = mkdirp.sync(file);
t.equal(made, null);
});

View File

@ -1,18 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('root', function (t) {
// '/' on unix, 'c:/' on windows.
var file = path.resolve('/');
mkdirp(file, 0755, function (err) {
if (err) throw err
fs.stat(file, function (er, stat) {
if (er) throw er
t.ok(stat.isDirectory(), 'target is a directory');
t.end();
})
});
});

View File

@ -1,32 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('sync', function (t) {
t.plan(2);
var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var file = '/tmp/' + [x,y,z].join('/');
try {
mkdirp.sync(file, 0755);
} catch (err) {
t.fail(err);
return t.end();
}
path.exists(file, function (ex) {
if (!ex) t.fail('file not created')
else fs.stat(file, function (err, stat) {
if (err) t.fail(err)
else {
t.equal(stat.mode & 0777, 0755);
t.ok(stat.isDirectory(), 'target not a directory');
t.end();
}
});
});
});

View File

@ -1,28 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('implicit mode from umask', function (t) {
t.plan(2);
var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var file = '/tmp/' + [x,y,z].join('/');
mkdirp(file, function (err) {
if (err) t.fail(err);
else path.exists(file, function (ex) {
if (!ex) t.fail('file not created')
else fs.stat(file, function (err, stat) {
if (err) t.fail(err)
else {
t.equal(stat.mode & 0777, 0777 & (~process.umask()));
t.ok(stat.isDirectory(), 'target not a directory');
t.end();
}
})
})
});
});

View File

@ -1,32 +0,0 @@
var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
test('umask sync modes', function (t) {
t.plan(2);
var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
var file = '/tmp/' + [x,y,z].join('/');
try {
mkdirp.sync(file);
} catch (err) {
t.fail(err);
return t.end();
}
path.exists(file, function (ex) {
if (!ex) t.fail('file not created')
else fs.stat(file, function (err, stat) {
if (err) t.fail(err)
else {
t.equal(stat.mode & 0777, (0777 & (~process.umask())));
t.ok(stat.isDirectory(), 'target not a directory');
t.end();
}
});
});
});

View File

@ -1,6 +0,0 @@
# Authors sorted by whether or not they're me.
Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)
Wayne Larsen <wayne@larsen.st> (http://github.com/wvl)
ritch <skawful@gmail.com>
Marcel Laverdet
Yosef Dinerstein <yosefd@microsoft.com>

View File

@ -1,23 +0,0 @@
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
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.

View File

@ -1,30 +0,0 @@
`rm -rf` for node.
Install with `npm install rimraf`, or just drop rimraf.js somewhere.
## API
`rimraf(f, callback)`
The callback will be called with an error if there is one. Certain
errors are handled for you:
* Windows: `EBUSY` and `ENOTEMPTY` - rimraf will back off a maximum of
`opts.maxBusyTries` times before giving up.
* `ENOENT` - If the file doesn't exist, rimraf will return
successfully, since your desired outcome is already the case.
## rimraf.sync
It can remove stuff synchronously, too. But that's not so good. Use
the async API. It's better.
## CLI
If installed with `npm install rimraf -g` it can be used as a global
command `rimraf <path>` which is useful for cross platform support.
## mkdirp
If you need to create a directory recursively, check out
[mkdirp](https://github.com/substack/node-mkdirp).

View File

@ -1,33 +0,0 @@
#!/usr/bin/env node
var rimraf = require('./')
var help = false
var dashdash = false
var args = process.argv.slice(2).filter(function(arg) {
if (dashdash)
return !!arg
else if (arg === '--')
dashdash = true
else if (arg.match(/^(-+|\/)(h(elp)?|\?)$/))
help = true
else
return !!arg
});
if (help || args.length === 0) {
// If they didn't ask for help, then this is not a "success"
var log = help ? console.log : console.error
log('Usage: rimraf <path>')
log('')
log(' Deletes all files and folders at "path" recursively.')
log('')
log('Options:')
log('')
log(' -h, --help Display this usage info')
process.exit(help ? 0 : 1)
} else {
args.forEach(function(arg) {
rimraf.sync(arg)
})
}

Some files were not shown because too many files have changed in this diff Show More