mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-20 22:16:54 +02:00
add redis dependencies for central controller
This commit is contained in:
parent
d18c33d6df
commit
2bceabdfa5
118 changed files with 31744 additions and 0 deletions
7
controller/thirdparty/hiredis-0.14.1/.gitignore
vendored
Normal file
7
controller/thirdparty/hiredis-0.14.1/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
/hiredis-test
|
||||
/examples/hiredis-example*
|
||||
/*.o
|
||||
/*.so
|
||||
/*.dylib
|
||||
/*.a
|
||||
/*.pc
|
45
controller/thirdparty/hiredis-0.14.1/.travis.yml
vendored
Normal file
45
controller/thirdparty/hiredis-0.14.1/.travis.yml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
language: c
|
||||
sudo: false
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
branches:
|
||||
only:
|
||||
- staging
|
||||
- trying
|
||||
- master
|
||||
|
||||
before_script:
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libc6-dbg
|
||||
- libc6-dev
|
||||
- libc6:i386
|
||||
- libc6-dev-i386
|
||||
- libc6-dbg:i386
|
||||
- gcc-multilib
|
||||
- valgrind
|
||||
|
||||
env:
|
||||
- CFLAGS="-Werror"
|
||||
- PRE="valgrind --track-origins=yes --leak-check=full"
|
||||
- TARGET="32bit" TARGET_VARS="32bit-vars" CFLAGS="-Werror"
|
||||
- TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full"
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
env: PRE="valgrind --track-origins=yes --leak-check=full"
|
||||
|
||||
- os: osx
|
||||
env: TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full"
|
||||
|
||||
script: make $TARGET CFLAGS="$CFLAGS" && make check PRE="$PRE" && make $TARGET_VARS hiredis-example
|
190
controller/thirdparty/hiredis-0.14.1/CHANGELOG.md
vendored
Normal file
190
controller/thirdparty/hiredis-0.14.1/CHANGELOG.md
vendored
Normal file
|
@ -0,0 +1,190 @@
|
|||
**NOTE: BREAKING CHANGES upgrading from 0.13.x to 0.14.x **:
|
||||
|
||||
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
|
||||
protocol errors. This is consistent with the RESP specification. On 32-bit
|
||||
platforms, the upper bound is lowered to `SIZE_MAX`.
|
||||
|
||||
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
||||
|
||||
User code should compare this to `size_t` values as well. If it was used to
|
||||
compare to other values, casting might be necessary or can be removed, if
|
||||
casting was applied before.
|
||||
|
||||
### 0.14.1 (2020-03-13)
|
||||
|
||||
* Adds safe allocation wrappers (CVE-2020-7105, #747, #752) (Michael Grunder)
|
||||
|
||||
### 0.14.0 (2018-09-25)
|
||||
|
||||
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
|
||||
* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])
|
||||
* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622])
|
||||
* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8])
|
||||
* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8])
|
||||
* Fix bulk and multi-bulk length truncation (Justin Brewer [109197])
|
||||
* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94])
|
||||
* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6])
|
||||
* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1])
|
||||
* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b])
|
||||
* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96])
|
||||
* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234])
|
||||
* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129])
|
||||
* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c])
|
||||
* Fix libevent leak (zfz [515228])
|
||||
* Clean up GCC warning (Ichito Nagata [2ec774])
|
||||
* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88])
|
||||
* Solaris compilation fix (Donald Whyte [41b07d])
|
||||
* Reorder linker arguments when building examples (Tustfarm-heart [06eedd])
|
||||
* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999])
|
||||
* libuv use after free fix (Paul Scott [cbb956])
|
||||
* Properly close socket fd on reconnect attempt (WSL [64d1ec])
|
||||
* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78])
|
||||
* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5])
|
||||
* Update libevent (Chris Xin [386802])
|
||||
* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e])
|
||||
* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6])
|
||||
* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3])
|
||||
* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb])
|
||||
* Compatibility fix for strerror_r (Tom Lee [bb1747])
|
||||
* Properly detect integer parse/overflow errors (Justin Brewer [93421f])
|
||||
* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40])
|
||||
* Catch a buffer overflow when formatting the error message
|
||||
* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13
|
||||
* Fix warnings, when compiled with -Wshadow
|
||||
* Make hiredis compile in Cygwin on Windows, now CI-tested
|
||||
|
||||
**BREAKING CHANGES**:
|
||||
|
||||
* Remove backwards compatibility macro's
|
||||
|
||||
This removes the following old function aliases, use the new name now:
|
||||
|
||||
| Old | New |
|
||||
| --------------------------- | ---------------------- |
|
||||
| redisReplyReaderCreate | redisReaderCreate |
|
||||
| redisReplyReaderCreate | redisReaderCreate |
|
||||
| redisReplyReaderFree | redisReaderFree |
|
||||
| redisReplyReaderFeed | redisReaderFeed |
|
||||
| redisReplyReaderGetReply | redisReaderGetReply |
|
||||
| redisReplyReaderSetPrivdata | redisReaderSetPrivdata |
|
||||
| redisReplyReaderGetObject | redisReaderGetObject |
|
||||
| redisReplyReaderGetError | redisReaderGetError |
|
||||
|
||||
* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS`
|
||||
|
||||
Previously it broke some builds for people that had `DEBUG` set to some arbitrary value,
|
||||
due to debugging other software.
|
||||
By renaming we avoid unintentional name clashes.
|
||||
|
||||
Simply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again.
|
||||
|
||||
### 0.13.3 (2015-09-16)
|
||||
|
||||
* Revert "Clear `REDIS_CONNECTED` flag when connection is closed".
|
||||
* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni)
|
||||
|
||||
|
||||
If the `REDIS_CONNECTED` flag is cleared,
|
||||
the async onDisconnect callback function will never be called.
|
||||
This causes problems as the disconnect is never reported back to the user.
|
||||
|
||||
### 0.13.2 (2015-08-25)
|
||||
|
||||
* Prevent crash on pending replies in async code (Thanks, @switch-st)
|
||||
* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs)
|
||||
* Add MacOS X addapter (Thanks, @dizzus)
|
||||
* Add Qt adapter (Thanks, Pietro Cerutti)
|
||||
* Add Ivykis adapter (Thanks, Gergely Nagy)
|
||||
|
||||
All adapters are provided as is and are only tested where possible.
|
||||
|
||||
### 0.13.1 (2015-05-03)
|
||||
|
||||
This is a bug fix release.
|
||||
The new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code.
|
||||
Another commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects.
|
||||
Other non-C99 code can now use hiredis as usual again.
|
||||
Sorry for the inconvenience.
|
||||
|
||||
* Fix memory leak in async reply handling (Salvatore Sanfilippo)
|
||||
* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa)
|
||||
|
||||
### 0.13.0 (2015-04-16)
|
||||
|
||||
This release adds a minimal Windows compatibility layer.
|
||||
The parser, standalone since v0.12.0, can now be compiled on Windows
|
||||
(and thus used in other client libraries as well)
|
||||
|
||||
* Windows compatibility layer for parser code (tzickel)
|
||||
* Properly escape data printed to PKGCONF file (Dan Skorupski)
|
||||
* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff)
|
||||
* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra)
|
||||
|
||||
### 0.12.1 (2015-01-26)
|
||||
|
||||
* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location
|
||||
* Fix `make test` as 32 bit build on 64 bit platform
|
||||
|
||||
### 0.12.0 (2015-01-22)
|
||||
|
||||
* Add optional KeepAlive support
|
||||
|
||||
* Try again on EINTR errors
|
||||
|
||||
* Add libuv adapter
|
||||
|
||||
* Add IPv6 support
|
||||
|
||||
* Remove possibility of multiple close on same fd
|
||||
|
||||
* Add ability to bind source address on connect
|
||||
|
||||
* Add redisConnectFd() and redisFreeKeepFd()
|
||||
|
||||
* Fix getaddrinfo() memory leak
|
||||
|
||||
* Free string if it is unused (fixes memory leak)
|
||||
|
||||
* Improve redisAppendCommandArgv performance 2.5x
|
||||
|
||||
* Add support for SO_REUSEADDR
|
||||
|
||||
* Fix redisvFormatCommand format parsing
|
||||
|
||||
* Add GLib 2.0 adapter
|
||||
|
||||
* Refactor reading code into read.c
|
||||
|
||||
* Fix errno error buffers to not clobber errors
|
||||
|
||||
* Generate pkgconf during build
|
||||
|
||||
* Silence _BSD_SOURCE warnings
|
||||
|
||||
* Improve digit counting for multibulk creation
|
||||
|
||||
|
||||
### 0.11.0
|
||||
|
||||
* Increase the maximum multi-bulk reply depth to 7.
|
||||
|
||||
* 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.
|
||||
|
29
controller/thirdparty/hiredis-0.14.1/COPYING
vendored
Normal file
29
controller/thirdparty/hiredis-0.14.1/COPYING
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
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.
|
214
controller/thirdparty/hiredis-0.14.1/Makefile
vendored
Normal file
214
controller/thirdparty/hiredis-0.14.1/Makefile
vendored
Normal file
|
@ -0,0 +1,214 @@
|
|||
# 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 read.o alloc.o
|
||||
EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib
|
||||
TESTS=hiredis-test
|
||||
LIBNAME=libhiredis
|
||||
PKGCONFNAME=hiredis.pc
|
||||
|
||||
HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')
|
||||
HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}')
|
||||
HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}')
|
||||
HIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}')
|
||||
|
||||
# Installation related variables and target
|
||||
PREFIX?=/usr/local
|
||||
INCLUDE_PATH?=include/hiredis
|
||||
LIBRARY_PATH?=lib
|
||||
PKGCONF_PATH?=pkgconfig
|
||||
INSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH)
|
||||
INSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH)
|
||||
INSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH)
|
||||
|
||||
# redis-server configuration used for testing
|
||||
REDIS_PORT=56379
|
||||
REDIS_SERVER=redis-server
|
||||
define REDIS_TEST_CONFIG
|
||||
daemonize yes
|
||||
pidfile /tmp/hiredis-test-redis.pid
|
||||
port $(REDIS_PORT)
|
||||
bind 127.0.0.1
|
||||
unixsocket /tmp/hiredis-test-redis.sock
|
||||
endef
|
||||
export REDIS_TEST_CONFIG
|
||||
|
||||
# Fallback to gcc when $CC is not in $PATH.
|
||||
CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
|
||||
CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
|
||||
OPTIMIZATION?=-O3
|
||||
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
|
||||
DEBUG_FLAGS?= -g -ggdb
|
||||
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS)
|
||||
REAL_LDFLAGS=$(LDFLAGS)
|
||||
|
||||
DYLIBSUFFIX=so
|
||||
STLIBSUFFIX=a
|
||||
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
|
||||
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)
|
||||
endif
|
||||
ifeq ($(uname_S),Darwin)
|
||||
DYLIBSUFFIX=dylib
|
||||
DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX)
|
||||
DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
|
||||
endif
|
||||
|
||||
all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)
|
||||
|
||||
# Deps (use make dep to generate this)
|
||||
alloc.o: alloc.c fmacros.h alloc.h
|
||||
async.o: async.c fmacros.h alloc.h async.h hiredis.h read.h sds.h net.h dict.c dict.h
|
||||
dict.o: dict.c fmacros.h alloc.h dict.h
|
||||
hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h alloc.h net.h
|
||||
net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h
|
||||
read.o: read.c fmacros.h read.h sds.h
|
||||
sds.o: sds.c sds.h sdsalloc.h
|
||||
test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h
|
||||
|
||||
$(DYLIBNAME): $(OBJ)
|
||||
$(DYLIB_MAKE_CMD) $(OBJ)
|
||||
|
||||
$(STLIBNAME): $(OBJ)
|
||||
$(STLIB_MAKE_CMD) $(OBJ)
|
||||
|
||||
dynamic: $(DYLIBNAME)
|
||||
static: $(STLIBNAME)
|
||||
|
||||
# Binaries:
|
||||
hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -levent $(STLIBNAME)
|
||||
|
||||
hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME)
|
||||
|
||||
hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME)
|
||||
|
||||
hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -livykis $(STLIBNAME)
|
||||
|
||||
hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME)
|
||||
|
||||
ifndef AE_DIR
|
||||
hiredis-example-ae:
|
||||
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
|
||||
@false
|
||||
else
|
||||
hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME)
|
||||
endif
|
||||
|
||||
ifndef LIBUV_DIR
|
||||
hiredis-example-libuv:
|
||||
@echo "Please specify LIBUV_DIR (e.g. ../libuv/)"
|
||||
@false
|
||||
else
|
||||
hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME)
|
||||
endif
|
||||
|
||||
ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),)
|
||||
hiredis-example-qt:
|
||||
@echo "Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR"
|
||||
@false
|
||||
else
|
||||
hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME)
|
||||
$(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \
|
||||
$(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore
|
||||
$(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \
|
||||
$(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore
|
||||
$(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore
|
||||
endif
|
||||
|
||||
hiredis-example: examples/example.c $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(STLIBNAME)
|
||||
|
||||
examples: $(EXAMPLES)
|
||||
|
||||
hiredis-test: test.o $(STLIBNAME)
|
||||
|
||||
hiredis-%: %.o $(STLIBNAME)
|
||||
$(CC) $(REAL_CFLAGS) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)
|
||||
|
||||
test: hiredis-test
|
||||
./hiredis-test
|
||||
|
||||
check: hiredis-test
|
||||
@echo "$$REDIS_TEST_CONFIG" | $(REDIS_SERVER) -
|
||||
$(PRE) ./hiredis-test -h 127.0.0.1 -p $(REDIS_PORT) -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) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov
|
||||
|
||||
dep:
|
||||
$(CC) -MM *.c
|
||||
|
||||
INSTALL?= cp -pPR
|
||||
|
||||
$(PKGCONFNAME): hiredis.h
|
||||
@echo "Generating $@ for pkgconfig..."
|
||||
@echo prefix=$(PREFIX) > $@
|
||||
@echo exec_prefix=\$${prefix} >> $@
|
||||
@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@
|
||||
@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
||||
@echo >> $@
|
||||
@echo Name: hiredis >> $@
|
||||
@echo Description: Minimalistic C client library for Redis. >> $@
|
||||
@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@
|
||||
@echo Libs: -L\$${libdir} -lhiredis >> $@
|
||||
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
|
||||
|
||||
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
|
||||
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)
|
||||
$(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH)
|
||||
$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters
|
||||
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)
|
||||
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
|
||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
||||
$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
||||
|
||||
32bit:
|
||||
@echo ""
|
||||
@echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
|
||||
@echo ""
|
||||
$(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
|
||||
|
||||
32bit-vars:
|
||||
$(eval CFLAGS=-m32)
|
||||
$(eval 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 32bit-vars gprof gcov noopt
|
410
controller/thirdparty/hiredis-0.14.1/README.md
vendored
Normal file
410
controller/thirdparty/hiredis-0.14.1/README.md
vendored
Normal file
|
@ -0,0 +1,410 @@
|
|||
[](https://travis-ci.org/redis/hiredis)
|
||||
|
||||
**This Readme reflects the latest changed in the master branch. See [v0.14.1](https://github.com/redis/hiredis/tree/v0.14.1) for the Readme and documentation for the latest release.**
|
||||
|
||||
# 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 a 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*.
|
||||
|
||||
## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x
|
||||
|
||||
Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
|
||||
protocol errors. This is consistent with the RESP specification. On 32-bit
|
||||
platforms, the upper bound is lowered to `SIZE_MAX`.
|
||||
|
||||
Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
||||
|
||||
User code should compare this to `size_t` values as well. If it was used to
|
||||
compare to other values, casting might be necessary or can be removed, if
|
||||
casting was applied before.
|
||||
|
||||
For a detailed list of changes please view our [Changelog](CHANGELOG.md).
|
||||
|
||||
## Synchronous API
|
||||
|
||||
To consume the synchronous API, there are only a few function calls that need to be introduced:
|
||||
|
||||
```c
|
||||
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 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:
|
||||
```c
|
||||
redisContext *c = redisConnect("127.0.0.1", 6379);
|
||||
if (c == NULL || c->err) {
|
||||
if (c) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
// handle error
|
||||
} else {
|
||||
printf("Can't allocate redis context\n");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Note: A `redisContext` is not thread-safe.*
|
||||
|
||||
### 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:
|
||||
```c
|
||||
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:
|
||||
```c
|
||||
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:
|
||||
```c
|
||||
reply = redisCommand(context, "SET foo %b", value, (size_t) 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:
|
||||
```c
|
||||
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-reply 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) frees 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:
|
||||
```c
|
||||
void redisFree(redisContext *c);
|
||||
```
|
||||
This function immediately closes the socket and then frees 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:
|
||||
```c
|
||||
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:
|
||||
```c
|
||||
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)`):
|
||||
```c
|
||||
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:
|
||||
```c
|
||||
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.
|
||||
|
||||
*Note: A `redisAsyncContext` is not thread-safe.*
|
||||
|
||||
```c
|
||||
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:
|
||||
```c
|
||||
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 freed 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:
|
||||
```c
|
||||
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:
|
||||
```c
|
||||
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:
|
||||
```c
|
||||
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 freed. When the callback
|
||||
for a command is non-`NULL`, the memory is freed 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:
|
||||
```c
|
||||
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 freed.
|
||||
|
||||
### 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:
|
||||
```c
|
||||
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).
|
||||
|
||||
The parser limits the level of nesting for multi bulk payloads to 7. If the
|
||||
multi bulk nesting level is higher than this, the parser returns an 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
|
||||
KiB 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:
|
||||
```c
|
||||
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.
|
||||
Hiredis is currently maintained by Matt Stancliff (matt at genges dot com) and
|
||||
Jan-Erik Rediger (janerik at fnordig dot com)
|
127
controller/thirdparty/hiredis-0.14.1/adapters/ae.h
vendored
Normal file
127
controller/thirdparty/hiredis-0.14.1/adapters/ae.h
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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_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*)hi_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
|
153
controller/thirdparty/hiredis-0.14.1/adapters/glib.h
vendored
Normal file
153
controller/thirdparty/hiredis-0.14.1/adapters/glib.h
vendored
Normal file
|
@ -0,0 +1,153 @@
|
|||
#ifndef __HIREDIS_GLIB_H__
|
||||
#define __HIREDIS_GLIB_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GSource source;
|
||||
redisAsyncContext *ac;
|
||||
GPollFD poll_fd;
|
||||
} RedisSource;
|
||||
|
||||
static void
|
||||
redis_source_add_read (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
g_return_if_fail(source);
|
||||
source->poll_fd.events |= G_IO_IN;
|
||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_del_read (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
g_return_if_fail(source);
|
||||
source->poll_fd.events &= ~G_IO_IN;
|
||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_add_write (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
g_return_if_fail(source);
|
||||
source->poll_fd.events |= G_IO_OUT;
|
||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_del_write (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
g_return_if_fail(source);
|
||||
source->poll_fd.events &= ~G_IO_OUT;
|
||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_cleanup (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
|
||||
g_return_if_fail(source);
|
||||
|
||||
redis_source_del_read(source);
|
||||
redis_source_del_write(source);
|
||||
/*
|
||||
* It is not our responsibility to remove ourself from the
|
||||
* current main loop. However, we will remove the GPollFD.
|
||||
*/
|
||||
if (source->poll_fd.fd >= 0) {
|
||||
g_source_remove_poll((GSource *)data, &source->poll_fd);
|
||||
source->poll_fd.fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
redis_source_prepare (GSource *source,
|
||||
gint *timeout_)
|
||||
{
|
||||
RedisSource *redis = (RedisSource *)source;
|
||||
*timeout_ = -1;
|
||||
return !!(redis->poll_fd.events & redis->poll_fd.revents);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
redis_source_check (GSource *source)
|
||||
{
|
||||
RedisSource *redis = (RedisSource *)source;
|
||||
return !!(redis->poll_fd.events & redis->poll_fd.revents);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
redis_source_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
RedisSource *redis = (RedisSource *)source;
|
||||
|
||||
if ((redis->poll_fd.revents & G_IO_OUT)) {
|
||||
redisAsyncHandleWrite(redis->ac);
|
||||
redis->poll_fd.revents &= ~G_IO_OUT;
|
||||
}
|
||||
|
||||
if ((redis->poll_fd.revents & G_IO_IN)) {
|
||||
redisAsyncHandleRead(redis->ac);
|
||||
redis->poll_fd.revents &= ~G_IO_IN;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
return callback(user_data);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_finalize (GSource *source)
|
||||
{
|
||||
RedisSource *redis = (RedisSource *)source;
|
||||
|
||||
if (redis->poll_fd.fd >= 0) {
|
||||
g_source_remove_poll(source, &redis->poll_fd);
|
||||
redis->poll_fd.fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static GSource *
|
||||
redis_source_new (redisAsyncContext *ac)
|
||||
{
|
||||
static GSourceFuncs source_funcs = {
|
||||
.prepare = redis_source_prepare,
|
||||
.check = redis_source_check,
|
||||
.dispatch = redis_source_dispatch,
|
||||
.finalize = redis_source_finalize,
|
||||
};
|
||||
redisContext *c = &ac->c;
|
||||
RedisSource *source;
|
||||
|
||||
g_return_val_if_fail(ac != NULL, NULL);
|
||||
|
||||
source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);
|
||||
source->ac = ac;
|
||||
source->poll_fd.fd = c->fd;
|
||||
source->poll_fd.events = 0;
|
||||
source->poll_fd.revents = 0;
|
||||
g_source_add_poll((GSource *)source, &source->poll_fd);
|
||||
|
||||
ac->ev.addRead = redis_source_add_read;
|
||||
ac->ev.delRead = redis_source_del_read;
|
||||
ac->ev.addWrite = redis_source_add_write;
|
||||
ac->ev.delWrite = redis_source_del_write;
|
||||
ac->ev.cleanup = redis_source_cleanup;
|
||||
ac->ev.data = source;
|
||||
|
||||
return (GSource *)source;
|
||||
}
|
||||
|
||||
#endif /* __HIREDIS_GLIB_H__ */
|
81
controller/thirdparty/hiredis-0.14.1/adapters/ivykis.h
vendored
Normal file
81
controller/thirdparty/hiredis-0.14.1/adapters/ivykis.h
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
#ifndef __HIREDIS_IVYKIS_H__
|
||||
#define __HIREDIS_IVYKIS_H__
|
||||
#include <iv.h>
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
typedef struct redisIvykisEvents {
|
||||
redisAsyncContext *context;
|
||||
struct iv_fd fd;
|
||||
} redisIvykisEvents;
|
||||
|
||||
static void redisIvykisReadEvent(void *arg) {
|
||||
redisAsyncContext *context = (redisAsyncContext *)arg;
|
||||
redisAsyncHandleRead(context);
|
||||
}
|
||||
|
||||
static void redisIvykisWriteEvent(void *arg) {
|
||||
redisAsyncContext *context = (redisAsyncContext *)arg;
|
||||
redisAsyncHandleWrite(context);
|
||||
}
|
||||
|
||||
static void redisIvykisAddRead(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent);
|
||||
}
|
||||
|
||||
static void redisIvykisDelRead(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
iv_fd_set_handler_in(&e->fd, NULL);
|
||||
}
|
||||
|
||||
static void redisIvykisAddWrite(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent);
|
||||
}
|
||||
|
||||
static void redisIvykisDelWrite(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
iv_fd_set_handler_out(&e->fd, NULL);
|
||||
}
|
||||
|
||||
static void redisIvykisCleanup(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
|
||||
iv_fd_unregister(&e->fd);
|
||||
free(e);
|
||||
}
|
||||
|
||||
static int redisIvykisAttach(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisIvykisEvents *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 = (redisIvykisEvents*)hi_malloc(sizeof(*e));
|
||||
e->context = ac;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
ac->ev.addRead = redisIvykisAddRead;
|
||||
ac->ev.delRead = redisIvykisDelRead;
|
||||
ac->ev.addWrite = redisIvykisAddWrite;
|
||||
ac->ev.delWrite = redisIvykisDelWrite;
|
||||
ac->ev.cleanup = redisIvykisCleanup;
|
||||
ac->ev.data = e;
|
||||
|
||||
/* Initialize and install read/write events */
|
||||
IV_FD_INIT(&e->fd);
|
||||
e->fd.fd = c->fd;
|
||||
e->fd.handler_in = redisIvykisReadEvent;
|
||||
e->fd.handler_out = redisIvykisWriteEvent;
|
||||
e->fd.handler_err = NULL;
|
||||
e->fd.cookie = e->context;
|
||||
|
||||
iv_fd_register(&e->fd);
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
147
controller/thirdparty/hiredis-0.14.1/adapters/libev.h
vendored
Normal file
147
controller/thirdparty/hiredis-0.14.1/adapters/libev.h
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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_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*)hi_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
|
108
controller/thirdparty/hiredis-0.14.1/adapters/libevent.h
vendored
Normal file
108
controller/thirdparty/hiredis-0.14.1/adapters/libevent.h
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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_LIBEVENT_H__
|
||||
#define __HIREDIS_LIBEVENT_H__
|
||||
#include <event2/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_free(e->rev);
|
||||
event_free(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*)hi_calloc(1, 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 */
|
||||
e->rev = event_new(base, c->fd, EV_READ, redisLibeventReadEvent, e);
|
||||
e->wev = event_new(base, c->fd, EV_WRITE, redisLibeventWriteEvent, e);
|
||||
event_add(e->rev, NULL);
|
||||
event_add(e->wev, NULL);
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
122
controller/thirdparty/hiredis-0.14.1/adapters/libuv.h
vendored
Normal file
122
controller/thirdparty/hiredis-0.14.1/adapters/libuv.h
vendored
Normal file
|
@ -0,0 +1,122 @@
|
|||
#ifndef __HIREDIS_LIBUV_H__
|
||||
#define __HIREDIS_LIBUV_H__
|
||||
#include <stdlib.h>
|
||||
#include <uv.h>
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct redisLibuvEvents {
|
||||
redisAsyncContext* context;
|
||||
uv_poll_t handle;
|
||||
int events;
|
||||
} redisLibuvEvents;
|
||||
|
||||
|
||||
static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
|
||||
|
||||
if (status != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->context != NULL && (events & UV_READABLE)) {
|
||||
redisAsyncHandleRead(p->context);
|
||||
}
|
||||
if (p->context != NULL && (events & UV_WRITABLE)) {
|
||||
redisAsyncHandleWrite(p->context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvAddRead(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->events |= UV_READABLE;
|
||||
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvDelRead(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->events &= ~UV_READABLE;
|
||||
|
||||
if (p->events) {
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
} else {
|
||||
uv_poll_stop(&p->handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvAddWrite(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->events |= UV_WRITABLE;
|
||||
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvDelWrite(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->events &= ~UV_WRITABLE;
|
||||
|
||||
if (p->events) {
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
} else {
|
||||
uv_poll_stop(&p->handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void on_close(uv_handle_t* handle) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvCleanup(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->context = NULL; // indicate that context might no longer exist
|
||||
uv_close((uv_handle_t*)&p->handle, on_close);
|
||||
}
|
||||
|
||||
|
||||
static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
if (ac->ev.data != NULL) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
ac->ev.addRead = redisLibuvAddRead;
|
||||
ac->ev.delRead = redisLibuvDelRead;
|
||||
ac->ev.addWrite = redisLibuvAddWrite;
|
||||
ac->ev.delWrite = redisLibuvDelWrite;
|
||||
ac->ev.cleanup = redisLibuvCleanup;
|
||||
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p));
|
||||
|
||||
if (!p) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
if (uv_poll_init(loop, &p->handle, c->fd) != 0) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
ac->ev.data = p;
|
||||
p->handle.data = p;
|
||||
p->context = ac;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
114
controller/thirdparty/hiredis-0.14.1/adapters/macosx.h
vendored
Normal file
114
controller/thirdparty/hiredis-0.14.1/adapters/macosx.h
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// Created by Дмитрий Бахвалов on 13.07.15.
|
||||
// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __HIREDIS_MACOSX_H__
|
||||
#define __HIREDIS_MACOSX_H__
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
typedef struct {
|
||||
redisAsyncContext *context;
|
||||
CFSocketRef socketRef;
|
||||
CFRunLoopSourceRef sourceRef;
|
||||
} RedisRunLoop;
|
||||
|
||||
static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
|
||||
if( redisRunLoop != NULL ) {
|
||||
if( redisRunLoop->sourceRef != NULL ) {
|
||||
CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);
|
||||
CFRelease(redisRunLoop->sourceRef);
|
||||
}
|
||||
if( redisRunLoop->socketRef != NULL ) {
|
||||
CFSocketInvalidate(redisRunLoop->socketRef);
|
||||
CFRelease(redisRunLoop->socketRef);
|
||||
}
|
||||
free(redisRunLoop);
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static void redisMacOSAddRead(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
|
||||
}
|
||||
|
||||
static void redisMacOSDelRead(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
|
||||
}
|
||||
|
||||
static void redisMacOSAddWrite(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
|
||||
}
|
||||
|
||||
static void redisMacOSDelWrite(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
|
||||
}
|
||||
|
||||
static void redisMacOSCleanup(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
freeRedisRunLoop(redisRunLoop);
|
||||
}
|
||||
|
||||
static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {
|
||||
redisAsyncContext* context = (redisAsyncContext*) info;
|
||||
|
||||
switch (callbackType) {
|
||||
case kCFSocketReadCallBack:
|
||||
redisAsyncHandleRead(context);
|
||||
break;
|
||||
|
||||
case kCFSocketWriteCallBack:
|
||||
redisAsyncHandleWrite(context);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {
|
||||
redisContext *redisCtx = &(redisAsyncCtx->c);
|
||||
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
|
||||
|
||||
RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop));
|
||||
if( !redisRunLoop ) return REDIS_ERR;
|
||||
|
||||
/* Setup redis stuff */
|
||||
redisRunLoop->context = redisAsyncCtx;
|
||||
|
||||
redisAsyncCtx->ev.addRead = redisMacOSAddRead;
|
||||
redisAsyncCtx->ev.delRead = redisMacOSDelRead;
|
||||
redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;
|
||||
redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;
|
||||
redisAsyncCtx->ev.cleanup = redisMacOSCleanup;
|
||||
redisAsyncCtx->ev.data = redisRunLoop;
|
||||
|
||||
/* Initialize and install read/write events */
|
||||
CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };
|
||||
|
||||
redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,
|
||||
kCFSocketReadCallBack | kCFSocketWriteCallBack,
|
||||
redisMacOSAsyncCallback,
|
||||
&socketCtx);
|
||||
if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);
|
||||
|
||||
redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);
|
||||
if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);
|
||||
|
||||
CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
135
controller/thirdparty/hiredis-0.14.1/adapters/qt.h
vendored
Normal file
135
controller/thirdparty/hiredis-0.14.1/adapters/qt.h
vendored
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*-
|
||||
* Copyright (C) 2014 Pietro Cerutti <gahr@gahr.ch>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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_QT_H__
|
||||
#define __HIREDIS_QT_H__
|
||||
#include <QSocketNotifier>
|
||||
#include "../async.h"
|
||||
|
||||
static void RedisQtAddRead(void *);
|
||||
static void RedisQtDelRead(void *);
|
||||
static void RedisQtAddWrite(void *);
|
||||
static void RedisQtDelWrite(void *);
|
||||
static void RedisQtCleanup(void *);
|
||||
|
||||
class RedisQtAdapter : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
friend
|
||||
void RedisQtAddRead(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->addRead();
|
||||
}
|
||||
|
||||
friend
|
||||
void RedisQtDelRead(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->delRead();
|
||||
}
|
||||
|
||||
friend
|
||||
void RedisQtAddWrite(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->addWrite();
|
||||
}
|
||||
|
||||
friend
|
||||
void RedisQtDelWrite(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->delWrite();
|
||||
}
|
||||
|
||||
friend
|
||||
void RedisQtCleanup(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->cleanup();
|
||||
}
|
||||
|
||||
public:
|
||||
RedisQtAdapter(QObject * parent = 0)
|
||||
: QObject(parent), m_ctx(0), m_read(0), m_write(0) { }
|
||||
|
||||
~RedisQtAdapter() {
|
||||
if (m_ctx != 0) {
|
||||
m_ctx->ev.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int setContext(redisAsyncContext * ac) {
|
||||
if (ac->ev.data != NULL) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
m_ctx = ac;
|
||||
m_ctx->ev.data = this;
|
||||
m_ctx->ev.addRead = RedisQtAddRead;
|
||||
m_ctx->ev.delRead = RedisQtDelRead;
|
||||
m_ctx->ev.addWrite = RedisQtAddWrite;
|
||||
m_ctx->ev.delWrite = RedisQtDelWrite;
|
||||
m_ctx->ev.cleanup = RedisQtCleanup;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
void addRead() {
|
||||
if (m_read) return;
|
||||
m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0);
|
||||
connect(m_read, SIGNAL(activated(int)), this, SLOT(read()));
|
||||
}
|
||||
|
||||
void delRead() {
|
||||
if (!m_read) return;
|
||||
delete m_read;
|
||||
m_read = 0;
|
||||
}
|
||||
|
||||
void addWrite() {
|
||||
if (m_write) return;
|
||||
m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0);
|
||||
connect(m_write, SIGNAL(activated(int)), this, SLOT(write()));
|
||||
}
|
||||
|
||||
void delWrite() {
|
||||
if (!m_write) return;
|
||||
delete m_write;
|
||||
m_write = 0;
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
delRead();
|
||||
delWrite();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void read() { redisAsyncHandleRead(m_ctx); }
|
||||
void write() { redisAsyncHandleWrite(m_ctx); }
|
||||
|
||||
private:
|
||||
redisAsyncContext * m_ctx;
|
||||
QSocketNotifier * m_read;
|
||||
QSocketNotifier * m_write;
|
||||
};
|
||||
|
||||
#endif /* !__HIREDIS_QT_H__ */
|
65
controller/thirdparty/hiredis-0.14.1/alloc.c
vendored
Normal file
65
controller/thirdparty/hiredis-0.14.1/alloc.c
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder 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 "alloc.h"
|
||||
#include <string.h>
|
||||
|
||||
void *hi_malloc(size_t size) {
|
||||
void *ptr = malloc(size);
|
||||
if (ptr == NULL)
|
||||
HIREDIS_OOM_HANDLER;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *hi_calloc(size_t nmemb, size_t size) {
|
||||
void *ptr = calloc(nmemb, size);
|
||||
if (ptr == NULL)
|
||||
HIREDIS_OOM_HANDLER;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *hi_realloc(void *ptr, size_t size) {
|
||||
void *newptr = realloc(ptr, size);
|
||||
if (newptr == NULL)
|
||||
HIREDIS_OOM_HANDLER;
|
||||
|
||||
return newptr;
|
||||
}
|
||||
|
||||
char *hi_strdup(const char *str) {
|
||||
char *newstr = strdup(str);
|
||||
if (newstr == NULL)
|
||||
HIREDIS_OOM_HANDLER;
|
||||
|
||||
return newstr;
|
||||
}
|
53
controller/thirdparty/hiredis-0.14.1/alloc.h
vendored
Normal file
53
controller/thirdparty/hiredis-0.14.1/alloc.h
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder 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_ALLOC_H
|
||||
#define HIREDIS_ALLOC_H
|
||||
|
||||
#include <stdlib.h> /* for size_t */
|
||||
|
||||
#ifndef HIREDIS_OOM_HANDLER
|
||||
#define HIREDIS_OOM_HANDLER abort()
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void *hi_malloc(size_t size);
|
||||
void *hi_calloc(size_t nmemb, size_t size);
|
||||
void *hi_realloc(void *ptr, size_t size);
|
||||
char *hi_strdup(const char *str);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HIREDIS_ALLOC_H */
|
23
controller/thirdparty/hiredis-0.14.1/appveyor.yml
vendored
Normal file
23
controller/thirdparty/hiredis-0.14.1/appveyor.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin)
|
||||
environment:
|
||||
matrix:
|
||||
- CYG_BASH: C:\cygwin64\bin\bash
|
||||
CC: gcc
|
||||
- CYG_BASH: C:\cygwin\bin\bash
|
||||
CC: gcc
|
||||
TARGET: 32bit
|
||||
TARGET_VARS: 32bit-vars
|
||||
|
||||
clone_depth: 1
|
||||
|
||||
# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
|
||||
# Install needed build dependencies
|
||||
install:
|
||||
- '%CYG_BASH% -lc "cygcheck -dc cygwin"'
|
||||
|
||||
build_script:
|
||||
- 'echo building...'
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; make LDFLAGS=$LDFLAGS CC=$CC $TARGET CFLAGS=$CFLAGS && make LDFLAGS=$LDFLAGS CC=$CC $TARGET_VARS hiredis-example"'
|
717
controller/thirdparty/hiredis-0.14.1/async.c
vendored
Normal file
717
controller/thirdparty/hiredis-0.14.1/async.c
vendored
Normal file
|
@ -0,0 +1,717 @@
|
|||
/*
|
||||
* 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 "alloc.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 */
|
||||
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
|
||||
|
||||
/* Functions managing dictionary of callbacks for pub/sub. */
|
||||
static unsigned int callbackHash(const void *key) {
|
||||
return dictGenHashFunction((const unsigned char *)key,
|
||||
sdslen((const sds)key));
|
||||
}
|
||||
|
||||
static void *callbackValDup(void *privdata, const void *src) {
|
||||
((void) privdata);
|
||||
redisCallback *dup = hi_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((const sds)key1);
|
||||
l2 = sdslen((const 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;
|
||||
|
||||
ac = realloc(c,sizeof(redisAsyncContext));
|
||||
if (ac == NULL)
|
||||
return NULL;
|
||||
|
||||
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) {
|
||||
if (!ac)
|
||||
return;
|
||||
|
||||
redisContext *c = &(ac->c);
|
||||
ac->err = c->err;
|
||||
ac->errstr = c->errstr;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
|
||||
redisContext *c;
|
||||
redisAsyncContext *ac;
|
||||
|
||||
c = redisConnectNonBlock(ip,port);
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
ac = redisAsyncInitialize(c);
|
||||
if (ac == NULL) {
|
||||
redisFree(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__redisAsyncCopyError(ac);
|
||||
return ac;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
|
||||
const char *source_addr) {
|
||||
redisContext *c = redisConnectBindNonBlock(ip,port,source_addr);
|
||||
redisAsyncContext *ac = redisAsyncInitialize(c);
|
||||
__redisAsyncCopyError(ac);
|
||||
return ac;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
||||
const char *source_addr) {
|
||||
redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr);
|
||||
redisAsyncContext *ac = redisAsyncInitialize(c);
|
||||
__redisAsyncCopyError(ac);
|
||||
return ac;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
|
||||
redisContext *c;
|
||||
redisAsyncContext *ac;
|
||||
|
||||
c = redisConnectUnixNonBlock(path);
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
ac = redisAsyncInitialize(c);
|
||||
if (ac == NULL) {
|
||||
redisFree(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__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 (cb == NULL)
|
||||
return REDIS_ERR_OOM;
|
||||
|
||||
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. */
|
||||
int ret = __redisShiftCallback(&ac->replies,NULL);
|
||||
assert(ret == 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;
|
||||
redisCallback *cb;
|
||||
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) {
|
||||
cb = dictGetEntryVal(de);
|
||||
|
||||
/* If this is an subscribe reply decrease pending counter. */
|
||||
if (strcasecmp(stype+pvariant,"subscribe") == 0) {
|
||||
cb->pending_subs -= 1;
|
||||
}
|
||||
|
||||
memcpy(dstcb,cb,sizeof(*dstcb));
|
||||
|
||||
/* If this is an unsubscribe message, remove it. */
|
||||
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
|
||||
if (cb->pending_subs == 0)
|
||||
dictDelete(callbacks,sname);
|
||||
|
||||
/* If this was the last unsubscribe message, revert to
|
||||
* non-subscribe mode. */
|
||||
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
|
||||
|
||||
/* Unset subscribed flag only when no pipelined pending subscribe. */
|
||||
if (reply->element[2]->integer == 0
|
||||
&& dictSize(ac->sub.channels) == 0
|
||||
&& dictSize(ac->sub.patterns) == 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 = {NULL, NULL, 0, NULL};
|
||||
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
|
||||
&& ac->replies.head == NULL) {
|
||||
__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);
|
||||
c->reader->fn->freeObject(reply);
|
||||
__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 successful, 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) == 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 const char *nextArgument(const char *start, const char **str, size_t *len) {
|
||||
const 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, const char *cmd, size_t len) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
struct dict *cbdict;
|
||||
dictEntry *de;
|
||||
redisCallback *existcb;
|
||||
int pvariant, hasnext;
|
||||
const char *cstr, *astr;
|
||||
size_t clen, alen;
|
||||
const char *p;
|
||||
sds sname;
|
||||
int ret;
|
||||
|
||||
/* 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;
|
||||
cb.pending_subs = 1;
|
||||
|
||||
/* 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)
|
||||
cbdict = ac->sub.patterns;
|
||||
else
|
||||
cbdict = ac->sub.channels;
|
||||
|
||||
de = dictFind(cbdict,sname);
|
||||
|
||||
if (de != NULL) {
|
||||
existcb = dictGetEntryVal(de);
|
||||
cb.pending_subs = existcb->pending_subs + 1;
|
||||
}
|
||||
|
||||
ret = dictReplace(cbdict,sname,&cb);
|
||||
|
||||
if (ret == 0) sdsfree(sname);
|
||||
}
|
||||
} 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);
|
||||
|
||||
/* We don't want to pass -1 or -2 to future functions as a length. */
|
||||
if (len < 0)
|
||||
return REDIS_ERR;
|
||||
|
||||
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) {
|
||||
sds cmd;
|
||||
int len;
|
||||
int status;
|
||||
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
|
||||
if (len < 0)
|
||||
return REDIS_ERR;
|
||||
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
sdsfree(cmd);
|
||||
return status;
|
||||
}
|
||||
|
||||
int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
|
||||
int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
return status;
|
||||
}
|
130
controller/thirdparty/hiredis-0.14.1/async.h
vendored
Normal file
130
controller/thirdparty/hiredis-0.14.1/async.h
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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;
|
||||
int pending_subs;
|
||||
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 *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
|
||||
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
||||
const char *source_addr);
|
||||
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);
|
||||
int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
339
controller/thirdparty/hiredis-0.14.1/dict.c
vendored
Normal file
339
controller/thirdparty/hiredis-0.14.1/dict.c
vendored
Normal file
|
@ -0,0 +1,339 @@
|
|||
/* 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 "alloc.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 = hi_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 = hi_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 succeed. */
|
||||
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 = hi_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 initial 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;
|
||||
}
|
||||
|
126
controller/thirdparty/hiredis-0.14.1/dict.h
vendored
Normal file
126
controller/thirdparty/hiredis-0.14.1/dict.h
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
/* 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 */
|
62
controller/thirdparty/hiredis-0.14.1/examples/example-ae.c
vendored
Normal file
62
controller/thirdparty/hiredis-0.14.1/examples/example-ae.c
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
#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);
|
||||
aeStop(loop);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
aeStop(loop);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Disconnected...\n");
|
||||
aeStop(loop);
|
||||
}
|
||||
|
||||
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(64);
|
||||
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;
|
||||
}
|
||||
|
73
controller/thirdparty/hiredis-0.14.1/examples/example-glib.c
vendored
Normal file
73
controller/thirdparty/hiredis-0.14.1/examples/example-glib.c
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/glib.h>
|
||||
|
||||
static GMainLoop *mainloop;
|
||||
|
||||
static void
|
||||
connect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,
|
||||
int status)
|
||||
{
|
||||
if (status != REDIS_OK) {
|
||||
g_printerr("Failed to connect: %s\n", ac->errstr);
|
||||
g_main_loop_quit(mainloop);
|
||||
} else {
|
||||
g_printerr("Connected...\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
disconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,
|
||||
int status)
|
||||
{
|
||||
if (status != REDIS_OK) {
|
||||
g_error("Failed to disconnect: %s", ac->errstr);
|
||||
} else {
|
||||
g_printerr("Disconnected...\n");
|
||||
g_main_loop_quit(mainloop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
command_cb(redisAsyncContext *ac,
|
||||
gpointer r,
|
||||
gpointer user_data G_GNUC_UNUSED)
|
||||
{
|
||||
redisReply *reply = r;
|
||||
|
||||
if (reply) {
|
||||
g_print("REPLY: %s\n", reply->str);
|
||||
}
|
||||
|
||||
redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
gint
|
||||
main (gint argc G_GNUC_UNUSED,
|
||||
gchar *argv[] G_GNUC_UNUSED)
|
||||
{
|
||||
redisAsyncContext *ac;
|
||||
GMainContext *context = NULL;
|
||||
GSource *source;
|
||||
|
||||
ac = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (ac->err) {
|
||||
g_printerr("%s\n", ac->errstr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
source = redis_source_new(ac);
|
||||
mainloop = g_main_loop_new(context, FALSE);
|
||||
g_source_attach(source, context);
|
||||
|
||||
redisAsyncSetConnectCallback(ac, connect_cb);
|
||||
redisAsyncSetDisconnectCallback(ac, disconnect_cb);
|
||||
redisAsyncCommand(ac, command_cb, NULL, "SET key 1234");
|
||||
redisAsyncCommand(ac, command_cb, NULL, "GET key");
|
||||
|
||||
g_main_loop_run(mainloop);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
58
controller/thirdparty/hiredis-0.14.1/examples/example-ivykis.c
vendored
Normal file
58
controller/thirdparty/hiredis-0.14.1/examples/example-ivykis.c
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/ivykis.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);
|
||||
|
||||
iv_init();
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisIvykisAttach(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");
|
||||
|
||||
iv_main();
|
||||
|
||||
iv_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
52
controller/thirdparty/hiredis-0.14.1/examples/example-libev.c
vendored
Normal file
52
controller/thirdparty/hiredis-0.14.1/examples/example-libev.c
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
#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;
|
||||
}
|
53
controller/thirdparty/hiredis-0.14.1/examples/example-libevent.c
vendored
Normal file
53
controller/thirdparty/hiredis-0.14.1/examples/example-libevent.c
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
#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;
|
||||
}
|
53
controller/thirdparty/hiredis-0.14.1/examples/example-libuv.c
vendored
Normal file
53
controller/thirdparty/hiredis-0.14.1/examples/example-libuv.c
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/libuv.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);
|
||||
uv_loop_t* loop = uv_default_loop();
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisLibuvAttach(c,loop);
|
||||
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");
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
return 0;
|
||||
}
|
66
controller/thirdparty/hiredis-0.14.1/examples/example-macosx.c
vendored
Normal file
66
controller/thirdparty/hiredis-0.14.1/examples/example-macosx.c
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// Created by Дмитрий Бахвалов on 13.07.15.
|
||||
// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/macosx.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;
|
||||
}
|
||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
CFRunLoopRef loop = CFRunLoopGetCurrent();
|
||||
if( !loop ) {
|
||||
printf("Error: Cannot get current run loop\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisMacOSAttach(c, loop);
|
||||
|
||||
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");
|
||||
|
||||
CFRunLoopRun();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
46
controller/thirdparty/hiredis-0.14.1/examples/example-qt.cpp
vendored
Normal file
46
controller/thirdparty/hiredis-0.14.1/examples/example-qt.cpp
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTimer>
|
||||
|
||||
#include "example-qt.h"
|
||||
|
||||
void getCallback(redisAsyncContext *, void * r, void * privdata) {
|
||||
|
||||
redisReply * reply = static_cast<redisReply *>(r);
|
||||
ExampleQt * ex = static_cast<ExampleQt *>(privdata);
|
||||
if (reply == nullptr || ex == nullptr) return;
|
||||
|
||||
cout << "key: " << reply->str << endl;
|
||||
|
||||
ex->finish();
|
||||
}
|
||||
|
||||
void ExampleQt::run() {
|
||||
|
||||
m_ctx = redisAsyncConnect("localhost", 6379);
|
||||
|
||||
if (m_ctx->err) {
|
||||
cerr << "Error: " << m_ctx->errstr << endl;
|
||||
redisAsyncFree(m_ctx);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
m_adapter.setContext(m_ctx);
|
||||
|
||||
redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value);
|
||||
redisAsyncCommand(m_ctx, getCallback, this, "GET key");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
ExampleQt example(argv[argc-1]);
|
||||
|
||||
QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit()));
|
||||
QTimer::singleShot(0, &example, SLOT(run()));
|
||||
|
||||
return app.exec();
|
||||
}
|
32
controller/thirdparty/hiredis-0.14.1/examples/example-qt.h
vendored
Normal file
32
controller/thirdparty/hiredis-0.14.1/examples/example-qt.h
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef __HIREDIS_EXAMPLE_QT_H
|
||||
#define __HIREDIS_EXAMPLE_QT_H
|
||||
|
||||
#include <adapters/qt.h>
|
||||
|
||||
class ExampleQt : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ExampleQt(const char * value, QObject * parent = 0)
|
||||
: QObject(parent), m_value(value) {}
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
|
||||
private:
|
||||
void finish() { emit finished(); }
|
||||
|
||||
private:
|
||||
const char * m_value;
|
||||
redisAsyncContext * m_ctx;
|
||||
RedisQtAdapter m_adapter;
|
||||
|
||||
friend
|
||||
void getCallback(redisAsyncContext *, void *, void *);
|
||||
};
|
||||
|
||||
#endif /* !__HIREDIS_EXAMPLE_QT_H */
|
78
controller/thirdparty/hiredis-0.14.1/examples/example.c
vendored
Normal file
78
controller/thirdparty/hiredis-0.14.1/examples/example.c
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j;
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
|
||||
int port = (argc > 2) ? atoi(argv[2]) : 6379;
|
||||
|
||||
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
|
||||
c = redisConnectWithTimeout(hostname, port, timeout);
|
||||
if (c == NULL || c->err) {
|
||||
if (c) {
|
||||
printf("Connection error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
} else {
|
||||
printf("Connection error: can't allocate redis context\n");
|
||||
}
|
||||
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", (size_t) 3, "hello", (size_t) 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,"%u",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);
|
||||
|
||||
/* Disconnects and frees the context */
|
||||
redisFree(c);
|
||||
|
||||
return 0;
|
||||
}
|
12
controller/thirdparty/hiredis-0.14.1/fmacros.h
vendored
Normal file
12
controller/thirdparty/hiredis-0.14.1/fmacros.h
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef __HIREDIS_FMACRO_H
|
||||
#define __HIREDIS_FMACRO_H
|
||||
|
||||
#define _XOPEN_SOURCE 600
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
/* Enable TCP_KEEPALIVE */
|
||||
#define _DARWIN_C_SOURCE
|
||||
#endif
|
||||
|
||||
#endif
|
1006
controller/thirdparty/hiredis-0.14.1/hiredis.c
vendored
Normal file
1006
controller/thirdparty/hiredis-0.14.1/hiredis.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
200
controller/thirdparty/hiredis-0.14.1/hiredis.h
vendored
Normal file
200
controller/thirdparty/hiredis-0.14.1/hiredis.h
vendored
Normal file
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
||||
* Jan-Erik Rediger <janerik at fnordig 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 "read.h"
|
||||
#include <stdarg.h> /* for va_list */
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
#include <stdint.h> /* uintXX_t, etc */
|
||||
#include "sds.h" /* for sds */
|
||||
#include "alloc.h" /* for allocation wrappers */
|
||||
|
||||
#define HIREDIS_MAJOR 0
|
||||
#define HIREDIS_MINOR 14
|
||||
#define HIREDIS_PATCH 1
|
||||
#define HIREDIS_SONAME 0.14
|
||||
|
||||
/* 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
|
||||
|
||||
/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
|
||||
#define REDIS_REUSEADDR 0x80
|
||||
|
||||
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
|
||||
|
||||
/* number of times we retry to connect in the case of EADDRNOTAVAIL and
|
||||
* SO_REUSEADDR is being used. */
|
||||
#define REDIS_CONNECT_RETRIES 10
|
||||
|
||||
#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 */
|
||||
size_t 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;
|
||||
|
||||
redisReader *redisReaderCreate(void);
|
||||
|
||||
/* 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);
|
||||
int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
|
||||
void redisFreeCommand(char *cmd);
|
||||
void redisFreeSdsCommand(sds cmd);
|
||||
|
||||
enum redisConnectionType {
|
||||
REDIS_CONN_TCP,
|
||||
REDIS_CONN_UNIX
|
||||
};
|
||||
|
||||
/* 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 */
|
||||
|
||||
enum redisConnectionType connection_type;
|
||||
struct timeval *timeout;
|
||||
|
||||
struct {
|
||||
char *host;
|
||||
char *source_addr;
|
||||
int port;
|
||||
} tcp;
|
||||
|
||||
struct {
|
||||
char *path;
|
||||
} unix_sock;
|
||||
|
||||
} redisContext;
|
||||
|
||||
redisContext *redisConnect(const char *ip, int port);
|
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
|
||||
redisContext *redisConnectNonBlock(const char *ip, int port);
|
||||
redisContext *redisConnectBindNonBlock(const char *ip, int port,
|
||||
const char *source_addr);
|
||||
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
|
||||
const char *source_addr);
|
||||
redisContext *redisConnectUnix(const char *path);
|
||||
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
|
||||
redisContext *redisConnectUnixNonBlock(const char *path);
|
||||
redisContext *redisConnectFd(int fd);
|
||||
|
||||
/**
|
||||
* Reconnect the given context using the saved information.
|
||||
*
|
||||
* This re-uses the exact same connect options as in the initial connection.
|
||||
* host, ip (or path), timeout and bind address are reused,
|
||||
* flags are used unmodified from the existing context.
|
||||
*
|
||||
* Returns REDIS_OK on successful connect or REDIS_ERR otherwise.
|
||||
*/
|
||||
int redisReconnect(redisContext *c);
|
||||
|
||||
int redisSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisEnableKeepAlive(redisContext *c);
|
||||
void redisFree(redisContext *c);
|
||||
int redisFreeKeepFd(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 formatted command to the output buffer. Use these functions in blocking mode
|
||||
* to get a pipeline of commands. */
|
||||
int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
|
||||
|
||||
/* 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
|
477
controller/thirdparty/hiredis-0.14.1/net.c
vendored
Normal file
477
controller/thirdparty/hiredis-0.14.1/net.c
vendored
Normal file
|
@ -0,0 +1,477 @@
|
|||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
||||
* Jan-Erik Rediger <janerik at fnordig 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 <stdlib.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "sds.h"
|
||||
|
||||
/* Defined in hiredis.c */
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
static void redisContextCloseFd(redisContext *c) {
|
||||
if (c && c->fd >= 0) {
|
||||
close(c->fd);
|
||||
c->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
|
||||
int errorno = errno; /* snprintf() may change errno */
|
||||
char buf[128] = { 0 };
|
||||
size_t len = 0;
|
||||
|
||||
if (prefix != NULL)
|
||||
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
|
||||
strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
|
||||
__redisSetError(c,type,buf);
|
||||
}
|
||||
|
||||
static int redisSetReuseAddr(redisContext *c) {
|
||||
int on = 1;
|
||||
if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
redisContextCloseFd(c);
|
||||
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;
|
||||
}
|
||||
c->fd = s;
|
||||
if (type == AF_INET) {
|
||||
if (redisSetReuseAddr(c) == REDIS_ERR) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisSetBlocking(redisContext *c, 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(c->fd, F_GETFL)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (blocking)
|
||||
flags &= ~O_NONBLOCK;
|
||||
else
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if (fcntl(c->fd, F_SETFL, flags) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisKeepAlive(redisContext *c, int interval) {
|
||||
int val = 1;
|
||||
int fd = c->fd;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = interval;
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#else
|
||||
#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = interval/3;
|
||||
if (val == 0) val = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisSetTcpNoDelay(redisContext *c) {
|
||||
int yes = 1;
|
||||
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
|
||||
|
||||
static int redisContextTimeoutMsec(redisContext *c, long *result)
|
||||
{
|
||||
const struct timeval *timeout = c->timeout;
|
||||
long msec = -1;
|
||||
|
||||
/* Only use timeout when not NULL. */
|
||||
if (timeout != NULL) {
|
||||
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
|
||||
*result = msec;
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
|
||||
|
||||
if (msec < 0 || msec > INT_MAX) {
|
||||
msec = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
*result = msec;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisContextWaitReady(redisContext *c, long msec) {
|
||||
struct pollfd wfd[1];
|
||||
|
||||
wfd[0].fd = c->fd;
|
||||
wfd[0].events = POLLOUT;
|
||||
|
||||
if (errno == EINPROGRESS) {
|
||||
int res;
|
||||
|
||||
if ((res = poll(wfd, 1, msec)) == -1) {
|
||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
} else if (res == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (redisCheckSocketError(c) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisCheckSocketError(redisContext *c) {
|
||||
int err = 0;
|
||||
socklen_t errlen = sizeof(err);
|
||||
|
||||
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
errno = err;
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextSetTimeout(redisContext *c, const 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;
|
||||
}
|
||||
|
||||
static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout,
|
||||
const char *source_addr) {
|
||||
int s, rv, n;
|
||||
char _port[6]; /* strlen("65535"); */
|
||||
struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
int reuseaddr = (c->flags & REDIS_REUSEADDR);
|
||||
int reuses = 0;
|
||||
long timeout_msec = -1;
|
||||
|
||||
servinfo = NULL;
|
||||
c->connection_type = REDIS_CONN_TCP;
|
||||
c->tcp.port = port;
|
||||
|
||||
/* We need to take possession of the passed parameters
|
||||
* to make them reusable for a reconnect.
|
||||
* We also carefully check we don't free data we already own,
|
||||
* as in the case of the reconnect method.
|
||||
*
|
||||
* This is a bit ugly, but atleast it works and doesn't leak memory.
|
||||
**/
|
||||
if (c->tcp.host != addr) {
|
||||
free(c->tcp.host);
|
||||
|
||||
c->tcp.host = hi_strdup(addr);
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (c->timeout != timeout) {
|
||||
if (c->timeout == NULL)
|
||||
c->timeout = hi_malloc(sizeof(struct timeval));
|
||||
|
||||
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
||||
}
|
||||
} else {
|
||||
free(c->timeout);
|
||||
c->timeout = NULL;
|
||||
}
|
||||
|
||||
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
|
||||
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (source_addr == NULL) {
|
||||
free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = NULL;
|
||||
} else if (c->tcp.source_addr != source_addr) {
|
||||
free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = hi_strdup(source_addr);
|
||||
}
|
||||
|
||||
snprintf(_port, 6, "%d", port);
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
/* Try with IPv6 if no IPv4 address was found. We do it in this order since
|
||||
* in a Redis client you can't afford to test if you have IPv6 connectivity
|
||||
* as this would add latency to every connect. Otherwise a more sensible
|
||||
* route could be: Use IPv6 if both addresses are available and there is IPv6
|
||||
* connectivity. */
|
||||
if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
|
||||
hints.ai_family = AF_INET6;
|
||||
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) {
|
||||
addrretry:
|
||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
||||
continue;
|
||||
|
||||
c->fd = s;
|
||||
if (redisSetBlocking(c,0) != REDIS_OK)
|
||||
goto error;
|
||||
if (c->tcp.source_addr) {
|
||||
int bound = 0;
|
||||
/* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
|
||||
if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
|
||||
char buf[128];
|
||||
snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
|
||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (reuseaddr) {
|
||||
n = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
|
||||
sizeof(n)) < 0) {
|
||||
freeaddrinfo(bservinfo);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
for (b = bservinfo; b != NULL; b = b->ai_next) {
|
||||
if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
|
||||
bound = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(bservinfo);
|
||||
if (!bound) {
|
||||
char buf[128];
|
||||
snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
|
||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
||||
if (errno == EHOSTUNREACH) {
|
||||
redisContextCloseFd(c);
|
||||
continue;
|
||||
} else if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else if (errno == EADDRNOTAVAIL && reuseaddr) {
|
||||
if (++reuses >= REDIS_CONNECT_RETRIES) {
|
||||
goto error;
|
||||
} else {
|
||||
redisContextCloseFd(c);
|
||||
goto addrretry;
|
||||
}
|
||||
} else {
|
||||
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
||||
goto error;
|
||||
if (redisSetTcpNoDelay(c) != REDIS_OK)
|
||||
goto error;
|
||||
|
||||
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:
|
||||
if(servinfo) {
|
||||
freeaddrinfo(servinfo);
|
||||
}
|
||||
|
||||
return rv; // Need to return REDIS_OK if alright
|
||||
}
|
||||
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout) {
|
||||
return _redisContextConnectTcp(c, addr, port, timeout, NULL);
|
||||
}
|
||||
|
||||
int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout,
|
||||
const char *source_addr) {
|
||||
return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
|
||||
}
|
||||
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
struct sockaddr_un sa;
|
||||
long timeout_msec = -1;
|
||||
|
||||
if (redisCreateSocket(c,AF_UNIX) < 0)
|
||||
return REDIS_ERR;
|
||||
if (redisSetBlocking(c,0) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
c->connection_type = REDIS_CONN_UNIX;
|
||||
if (c->unix_sock.path != path)
|
||||
c->unix_sock.path = hi_strdup(path);
|
||||
|
||||
if (timeout) {
|
||||
if (c->timeout != timeout) {
|
||||
if (c->timeout == NULL)
|
||||
c->timeout = hi_malloc(sizeof(struct timeval));
|
||||
|
||||
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
||||
}
|
||||
} else {
|
||||
free(c->timeout);
|
||||
c->timeout = NULL;
|
||||
}
|
||||
|
||||
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
sa.sun_family = AF_UNIX;
|
||||
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
||||
if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
||||
if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset socket to be blocking after connect(2). */
|
||||
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
}
|
49
controller/thirdparty/hiredis-0.14.1/net.h
vendored
Normal file
49
controller/thirdparty/hiredis-0.14.1/net.h
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
||||
* Jan-Erik Rediger <janerik at fnordig 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"
|
||||
|
||||
int redisCheckSocketError(redisContext *c);
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
|
||||
int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout,
|
||||
const char *source_addr);
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
|
||||
int redisKeepAlive(redisContext *c, int interval);
|
||||
|
||||
#endif
|
598
controller/thirdparty/hiredis-0.14.1/read.c
vendored
Normal file
598
controller/thirdparty/hiredis-0.14.1/read.c
vendored
Normal file
|
@ -0,0 +1,598 @@
|
|||
/*
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "read.h"
|
||||
#include "sds.h"
|
||||
|
||||
static void __redisReaderSetError(redisReader *r, int type, const char *str) {
|
||||
size_t len;
|
||||
|
||||
if (r->reply != NULL && r->fn && r->fn->freeObject) {
|
||||
r->fn->freeObject(r->reply);
|
||||
r->reply = NULL;
|
||||
}
|
||||
|
||||
/* Clear input buffer on errors. */
|
||||
sdsfree(r->buf);
|
||||
r->buf = NULL;
|
||||
r->pos = r->len = 0;
|
||||
|
||||
/* Reset task stack. */
|
||||
r->ridx = -1;
|
||||
|
||||
/* Set error. */
|
||||
r->err = type;
|
||||
len = strlen(str);
|
||||
len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
|
||||
memcpy(r->errstr,str,len);
|
||||
r->errstr[len] = '\0';
|
||||
}
|
||||
|
||||
static size_t chrtos(char *buf, size_t size, char byte) {
|
||||
size_t len = 0;
|
||||
|
||||
switch(byte) {
|
||||
case '\\':
|
||||
case '"':
|
||||
len = snprintf(buf,size,"\"\\%c\"",byte);
|
||||
break;
|
||||
case '\n': len = snprintf(buf,size,"\"\\n\""); break;
|
||||
case '\r': len = snprintf(buf,size,"\"\\r\""); break;
|
||||
case '\t': len = snprintf(buf,size,"\"\\t\""); break;
|
||||
case '\a': len = snprintf(buf,size,"\"\\a\""); break;
|
||||
case '\b': len = snprintf(buf,size,"\"\\b\""); break;
|
||||
default:
|
||||
if (isprint(byte))
|
||||
len = snprintf(buf,size,"\"%c\"",byte);
|
||||
else
|
||||
len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
|
||||
break;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
|
||||
char cbuf[8], sbuf[128];
|
||||
|
||||
chrtos(cbuf,sizeof(cbuf),byte);
|
||||
snprintf(sbuf,sizeof(sbuf),
|
||||
"Protocol error, got %s as reply type byte", cbuf);
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
|
||||
}
|
||||
|
||||
static void __redisReaderSetErrorOOM(redisReader *r) {
|
||||
__redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
|
||||
}
|
||||
|
||||
static char *readBytes(redisReader *r, unsigned int bytes) {
|
||||
char *p;
|
||||
if (r->len-r->pos >= bytes) {
|
||||
p = r->buf+r->pos;
|
||||
r->pos += bytes;
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find pointer to \r\n. */
|
||||
static char *seekNewline(char *s, size_t len) {
|
||||
int pos = 0;
|
||||
int _len = len-1;
|
||||
|
||||
/* Position should be < len-1 because the character at "pos" should be
|
||||
* followed by a \n. Note that strchr cannot be used because it doesn't
|
||||
* allow to search a limited length and the buffer that is being searched
|
||||
* might not have a trailing NULL character. */
|
||||
while (pos < _len) {
|
||||
while(pos < _len && s[pos] != '\r') pos++;
|
||||
if (pos==_len) {
|
||||
/* Not found. */
|
||||
return NULL;
|
||||
} else {
|
||||
if (s[pos+1] == '\n') {
|
||||
/* Found. */
|
||||
return s+pos;
|
||||
} else {
|
||||
/* Continue searching. */
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert a string into a long long. Returns REDIS_OK if the string could be
|
||||
* parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value
|
||||
* will be set to the parsed value when appropriate.
|
||||
*
|
||||
* Note that this function demands that the string strictly represents
|
||||
* a long long: no spaces or other characters before or after the string
|
||||
* representing the number are accepted, nor zeroes at the start if not
|
||||
* for the string "0" representing the zero number.
|
||||
*
|
||||
* Because of its strictness, it is safe to use this function to check if
|
||||
* you can convert a string into a long long, and obtain back the string
|
||||
* from the number without any loss in the string representation. */
|
||||
static int string2ll(const char *s, size_t slen, long long *value) {
|
||||
const char *p = s;
|
||||
size_t plen = 0;
|
||||
int negative = 0;
|
||||
unsigned long long v;
|
||||
|
||||
if (plen == slen)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Special case: first and only digit is 0. */
|
||||
if (slen == 1 && p[0] == '0') {
|
||||
if (value != NULL) *value = 0;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
if (p[0] == '-') {
|
||||
negative = 1;
|
||||
p++; plen++;
|
||||
|
||||
/* Abort on only a negative sign. */
|
||||
if (plen == slen)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* First digit should be 1-9, otherwise the string should just be 0. */
|
||||
if (p[0] >= '1' && p[0] <= '9') {
|
||||
v = p[0]-'0';
|
||||
p++; plen++;
|
||||
} else if (p[0] == '0' && slen == 1) {
|
||||
*value = 0;
|
||||
return REDIS_OK;
|
||||
} else {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
while (plen < slen && p[0] >= '0' && p[0] <= '9') {
|
||||
if (v > (ULLONG_MAX / 10)) /* Overflow. */
|
||||
return REDIS_ERR;
|
||||
v *= 10;
|
||||
|
||||
if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
|
||||
return REDIS_ERR;
|
||||
v += p[0]-'0';
|
||||
|
||||
p++; plen++;
|
||||
}
|
||||
|
||||
/* Return if not all bytes were used. */
|
||||
if (plen < slen)
|
||||
return REDIS_ERR;
|
||||
|
||||
if (negative) {
|
||||
if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
|
||||
return REDIS_ERR;
|
||||
if (value != NULL) *value = -v;
|
||||
} else {
|
||||
if (v > LLONG_MAX) /* Overflow. */
|
||||
return REDIS_ERR;
|
||||
if (value != NULL) *value = v;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static char *readLine(redisReader *r, int *_len) {
|
||||
char *p, *s;
|
||||
int len;
|
||||
|
||||
p = r->buf+r->pos;
|
||||
s = seekNewline(p,(r->len-r->pos));
|
||||
if (s != NULL) {
|
||||
len = s-(r->buf+r->pos);
|
||||
r->pos += len+2; /* skip \r\n */
|
||||
if (_len) *_len = len;
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void moveToNextTask(redisReader *r) {
|
||||
redisReadTask *cur, *prv;
|
||||
while (r->ridx >= 0) {
|
||||
/* Return a.s.a.p. when the stack is now empty. */
|
||||
if (r->ridx == 0) {
|
||||
r->ridx--;
|
||||
return;
|
||||
}
|
||||
|
||||
cur = &(r->rstack[r->ridx]);
|
||||
prv = &(r->rstack[r->ridx-1]);
|
||||
assert(prv->type == REDIS_REPLY_ARRAY);
|
||||
if (cur->idx == prv->elements-1) {
|
||||
r->ridx--;
|
||||
} else {
|
||||
/* Reset the type because the next item can be anything */
|
||||
assert(cur->idx < prv->elements);
|
||||
cur->type = -1;
|
||||
cur->elements = -1;
|
||||
cur->idx++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int processLineItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
void *obj;
|
||||
char *p;
|
||||
int len;
|
||||
|
||||
if ((p = readLine(r,&len)) != NULL) {
|
||||
if (cur->type == REDIS_REPLY_INTEGER) {
|
||||
if (r->fn && r->fn->createInteger) {
|
||||
long long v;
|
||||
if (string2ll(p, len, &v) == REDIS_ERR) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Bad integer value");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
obj = r->fn->createInteger(cur,v);
|
||||
} else {
|
||||
obj = (void*)REDIS_REPLY_INTEGER;
|
||||
}
|
||||
} else {
|
||||
/* Type will be error or status. */
|
||||
if (r->fn && r->fn->createString)
|
||||
obj = r->fn->createString(cur,p,len);
|
||||
else
|
||||
obj = (void*)(size_t)(cur->type);
|
||||
}
|
||||
|
||||
if (obj == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Set reply if this is the root object. */
|
||||
if (r->ridx == 0) r->reply = obj;
|
||||
moveToNextTask(r);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int processBulkItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
void *obj = NULL;
|
||||
char *p, *s;
|
||||
long long len;
|
||||
unsigned long bytelen;
|
||||
int success = 0;
|
||||
|
||||
p = r->buf+r->pos;
|
||||
s = seekNewline(p,r->len-r->pos);
|
||||
if (s != NULL) {
|
||||
p = r->buf+r->pos;
|
||||
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
|
||||
|
||||
if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Bad bulk string length");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Bulk string length out of range");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (len == -1) {
|
||||
/* The nil object can always be created. */
|
||||
if (r->fn && r->fn->createNil)
|
||||
obj = r->fn->createNil(cur);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_NIL;
|
||||
success = 1;
|
||||
} else {
|
||||
/* Only continue when the buffer contains the entire bulk item. */
|
||||
bytelen += len+2; /* include \r\n */
|
||||
if (r->pos+bytelen <= r->len) {
|
||||
if (r->fn && r->fn->createString)
|
||||
obj = r->fn->createString(cur,s+2,len);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_STRING;
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Proceed when obj was created. */
|
||||
if (success) {
|
||||
if (obj == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
r->pos += bytelen;
|
||||
|
||||
/* Set reply if this is the root object. */
|
||||
if (r->ridx == 0) r->reply = obj;
|
||||
moveToNextTask(r);
|
||||
return REDIS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int processMultiBulkItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
void *obj;
|
||||
char *p;
|
||||
long long elements;
|
||||
int root = 0, len;
|
||||
|
||||
/* Set error for nested multi bulks with depth > 7 */
|
||||
if (r->ridx == 8) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"No support for nested multi bulk replies with depth > 7");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if ((p = readLine(r,&len)) != NULL) {
|
||||
if (string2ll(p, len, &elements) == REDIS_ERR) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Bad multi-bulk length");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
root = (r->ridx == 0);
|
||||
|
||||
if (elements < -1 || elements > INT_MAX) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Multi-bulk length out of range");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (elements == -1) {
|
||||
if (r->fn && r->fn->createNil)
|
||||
obj = r->fn->createNil(cur);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_NIL;
|
||||
|
||||
if (obj == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
moveToNextTask(r);
|
||||
} else {
|
||||
if (r->fn && r->fn->createArray)
|
||||
obj = r->fn->createArray(cur,elements);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_ARRAY;
|
||||
|
||||
if (obj == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Modify task stack when there are more than 0 elements. */
|
||||
if (elements > 0) {
|
||||
cur->elements = elements;
|
||||
cur->obj = obj;
|
||||
r->ridx++;
|
||||
r->rstack[r->ridx].type = -1;
|
||||
r->rstack[r->ridx].elements = -1;
|
||||
r->rstack[r->ridx].idx = 0;
|
||||
r->rstack[r->ridx].obj = NULL;
|
||||
r->rstack[r->ridx].parent = cur;
|
||||
r->rstack[r->ridx].privdata = r->privdata;
|
||||
} else {
|
||||
moveToNextTask(r);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set reply if this is the root object. */
|
||||
if (root) r->reply = obj;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int processItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
char *p;
|
||||
|
||||
/* check if we need to read type */
|
||||
if (cur->type < 0) {
|
||||
if ((p = readBytes(r,1)) != NULL) {
|
||||
switch (p[0]) {
|
||||
case '-':
|
||||
cur->type = REDIS_REPLY_ERROR;
|
||||
break;
|
||||
case '+':
|
||||
cur->type = REDIS_REPLY_STATUS;
|
||||
break;
|
||||
case ':':
|
||||
cur->type = REDIS_REPLY_INTEGER;
|
||||
break;
|
||||
case '$':
|
||||
cur->type = REDIS_REPLY_STRING;
|
||||
break;
|
||||
case '*':
|
||||
cur->type = REDIS_REPLY_ARRAY;
|
||||
break;
|
||||
default:
|
||||
__redisReaderSetErrorProtocolByte(r,*p);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
} else {
|
||||
/* could not consume 1 byte */
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* process typed item */
|
||||
switch(cur->type) {
|
||||
case REDIS_REPLY_ERROR:
|
||||
case REDIS_REPLY_STATUS:
|
||||
case REDIS_REPLY_INTEGER:
|
||||
return processLineItem(r);
|
||||
case REDIS_REPLY_STRING:
|
||||
return processBulkItem(r);
|
||||
case REDIS_REPLY_ARRAY:
|
||||
return processMultiBulkItem(r);
|
||||
default:
|
||||
assert(NULL);
|
||||
return REDIS_ERR; /* Avoid warning. */
|
||||
}
|
||||
}
|
||||
|
||||
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
|
||||
redisReader *r;
|
||||
|
||||
r = calloc(1,sizeof(redisReader));
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
r->fn = fn;
|
||||
r->buf = sdsempty();
|
||||
r->maxbuf = REDIS_READER_MAX_BUF;
|
||||
if (r->buf == NULL) {
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r->ridx = -1;
|
||||
return r;
|
||||
}
|
||||
|
||||
void redisReaderFree(redisReader *r) {
|
||||
if (r == NULL)
|
||||
return;
|
||||
if (r->reply != NULL && r->fn && r->fn->freeObject)
|
||||
r->fn->freeObject(r->reply);
|
||||
sdsfree(r->buf);
|
||||
free(r);
|
||||
}
|
||||
|
||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
|
||||
sds newbuf;
|
||||
|
||||
/* Return early when this reader is in an erroneous state. */
|
||||
if (r->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Copy the provided buffer. */
|
||||
if (buf != NULL && len >= 1) {
|
||||
/* Destroy internal buffer when it is empty and is quite large. */
|
||||
if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
|
||||
sdsfree(r->buf);
|
||||
r->buf = sdsempty();
|
||||
r->pos = 0;
|
||||
|
||||
/* r->buf should not be NULL since we just free'd a larger one. */
|
||||
assert(r->buf != NULL);
|
||||
}
|
||||
|
||||
newbuf = sdscatlen(r->buf,buf,len);
|
||||
if (newbuf == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
r->buf = newbuf;
|
||||
r->len = sdslen(r->buf);
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisReaderGetReply(redisReader *r, void **reply) {
|
||||
/* Default target pointer to NULL. */
|
||||
if (reply != NULL)
|
||||
*reply = NULL;
|
||||
|
||||
/* Return early when this reader is in an erroneous state. */
|
||||
if (r->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* When the buffer is empty, there will never be a reply. */
|
||||
if (r->len == 0)
|
||||
return REDIS_OK;
|
||||
|
||||
/* Set first item to process when the stack is empty. */
|
||||
if (r->ridx == -1) {
|
||||
r->rstack[0].type = -1;
|
||||
r->rstack[0].elements = -1;
|
||||
r->rstack[0].idx = -1;
|
||||
r->rstack[0].obj = NULL;
|
||||
r->rstack[0].parent = NULL;
|
||||
r->rstack[0].privdata = r->privdata;
|
||||
r->ridx = 0;
|
||||
}
|
||||
|
||||
/* Process items in reply. */
|
||||
while (r->ridx >= 0)
|
||||
if (processItem(r) != REDIS_OK)
|
||||
break;
|
||||
|
||||
/* Return ASAP when an error occurred. */
|
||||
if (r->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Discard part of the buffer when we've consumed at least 1k, to avoid
|
||||
* doing unnecessary calls to memmove() in sds.c. */
|
||||
if (r->pos >= 1024) {
|
||||
sdsrange(r->buf,r->pos,-1);
|
||||
r->pos = 0;
|
||||
r->len = sdslen(r->buf);
|
||||
}
|
||||
|
||||
/* Emit a reply when there is one. */
|
||||
if (r->ridx == -1) {
|
||||
if (reply != NULL)
|
||||
*reply = r->reply;
|
||||
r->reply = NULL;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
111
controller/thirdparty/hiredis-0.14.1/read.h
vendored
Normal file
111
controller/thirdparty/hiredis-0.14.1/read.h
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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_READ_H
|
||||
#define __HIREDIS_READ_H
|
||||
#include <stdio.h> /* for size_t */
|
||||
|
||||
#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 occurred. 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... */
|
||||
|
||||
#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
|
||||
|
||||
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;
|
||||
|
||||
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 *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);
|
||||
void redisReaderFree(redisReader *r);
|
||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
|
||||
int redisReaderGetReply(redisReader *r, void **reply);
|
||||
|
||||
#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
|
||||
#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)
|
||||
#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
1272
controller/thirdparty/hiredis-0.14.1/sds.c
vendored
Normal file
1272
controller/thirdparty/hiredis-0.14.1/sds.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
273
controller/thirdparty/hiredis-0.14.1/sds.h
vendored
Normal file
273
controller/thirdparty/hiredis-0.14.1/sds.h
vendored
Normal file
|
@ -0,0 +1,273 @@
|
|||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2015, Oran Agra
|
||||
* Copyright (c) 2015, Redis Labs, Inc
|
||||
* 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
|
||||
|
||||
#define SDS_MAX_PREALLOC (1024*1024)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef char *sds;
|
||||
|
||||
/* Note: sdshdr5 is never used, we just access the flags byte directly.
|
||||
* However is here to document the layout of type 5 SDS strings. */
|
||||
struct __attribute__ ((__packed__)) sdshdr5 {
|
||||
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr8 {
|
||||
uint8_t len; /* used */
|
||||
uint8_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr16 {
|
||||
uint16_t len; /* used */
|
||||
uint16_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr32 {
|
||||
uint32_t len; /* used */
|
||||
uint32_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr64 {
|
||||
uint64_t len; /* used */
|
||||
uint64_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
|
||||
#define SDS_TYPE_5 0
|
||||
#define SDS_TYPE_8 1
|
||||
#define SDS_TYPE_16 2
|
||||
#define SDS_TYPE_32 3
|
||||
#define SDS_TYPE_64 4
|
||||
#define SDS_TYPE_MASK 7
|
||||
#define SDS_TYPE_BITS 3
|
||||
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
|
||||
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
|
||||
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
|
||||
|
||||
static inline size_t sdslen(const sds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
return SDS_TYPE_5_LEN(flags);
|
||||
case SDS_TYPE_8:
|
||||
return SDS_HDR(8,s)->len;
|
||||
case SDS_TYPE_16:
|
||||
return SDS_HDR(16,s)->len;
|
||||
case SDS_TYPE_32:
|
||||
return SDS_HDR(32,s)->len;
|
||||
case SDS_TYPE_64:
|
||||
return SDS_HDR(64,s)->len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t sdsavail(const sds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5: {
|
||||
return 0;
|
||||
}
|
||||
case SDS_TYPE_8: {
|
||||
SDS_HDR_VAR(8,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case SDS_TYPE_16: {
|
||||
SDS_HDR_VAR(16,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case SDS_TYPE_32: {
|
||||
SDS_HDR_VAR(32,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case SDS_TYPE_64: {
|
||||
SDS_HDR_VAR(64,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sdssetlen(sds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len = newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len = newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len = newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len = newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sdsinclen(sds s, size_t inc) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len += inc;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len += inc;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len += inc;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len += inc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* sdsalloc() = sdsavail() + sdslen() */
|
||||
static inline size_t sdsalloc(const sds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
return SDS_TYPE_5_LEN(flags);
|
||||
case SDS_TYPE_8:
|
||||
return SDS_HDR(8,s)->alloc;
|
||||
case SDS_TYPE_16:
|
||||
return SDS_HDR(16,s)->alloc;
|
||||
case SDS_TYPE_32:
|
||||
return SDS_HDR(32,s)->alloc;
|
||||
case SDS_TYPE_64:
|
||||
return SDS_HDR(64,s)->alloc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sdssetalloc(sds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
/* Nothing to do, this type has no total allocation info. */
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->alloc = newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->alloc = newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->alloc = newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->alloc = newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen);
|
||||
sds sdsnew(const char *init);
|
||||
sds sdsempty(void);
|
||||
sds sdsdup(const sds s);
|
||||
void sdsfree(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 sdscatsds(sds s, const sds t);
|
||||
sds sdscpylen(sds s, const char *t, size_t len);
|
||||
sds sdscpy(sds s, const 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 sdscatfmt(sds s, char const *fmt, ...);
|
||||
sds sdstrim(sds s, const char *cset);
|
||||
void sdsrange(sds s, int start, int end);
|
||||
void sdsupdatelen(sds s);
|
||||
void sdsclear(sds s);
|
||||
int sdscmp(const sds s1, const sds s2);
|
||||
sds *sdssplitlen(const char *s, int len, const 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, const char *p, size_t len);
|
||||
sds *sdssplitargs(const char *line, int *argc);
|
||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
|
||||
sds sdsjoin(char **argv, int argc, char *sep);
|
||||
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
|
||||
|
||||
/* Low level functions exposed to the user API */
|
||||
sds sdsMakeRoomFor(sds s, size_t addlen);
|
||||
void sdsIncrLen(sds s, int incr);
|
||||
sds sdsRemoveFreeSpace(sds s);
|
||||
size_t sdsAllocSize(sds s);
|
||||
void *sdsAllocPtr(sds s);
|
||||
|
||||
/* Export the allocator used by SDS to the program using SDS.
|
||||
* Sometimes the program SDS is linked to, may use a different set of
|
||||
* allocators, but may want to allocate or free things that SDS will
|
||||
* respectively free or allocate. */
|
||||
void *sds_malloc(size_t size);
|
||||
void *sds_realloc(void *ptr, size_t size);
|
||||
void sds_free(void *ptr);
|
||||
|
||||
#ifdef REDIS_TEST
|
||||
int sdsTest(int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
#endif
|
42
controller/thirdparty/hiredis-0.14.1/sdsalloc.h
vendored
Normal file
42
controller/thirdparty/hiredis-0.14.1/sdsalloc.h
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2015, Oran Agra
|
||||
* Copyright (c) 2015, Redis Labs, Inc
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* SDS allocator selection.
|
||||
*
|
||||
* This file is used in order to change the SDS allocator at compile time.
|
||||
* Just define the following defines to what you want to use. Also add
|
||||
* the include of your alternate allocator if needed (not needed in order
|
||||
* to use the default libc allocator). */
|
||||
|
||||
#define s_malloc malloc
|
||||
#define s_realloc realloc
|
||||
#define s_free free
|
923
controller/thirdparty/hiredis-0.14.1/test.c
vendored
Normal file
923
controller/thirdparty/hiredis-0.14.1/test.c
vendored
Normal file
|
@ -0,0 +1,923 @@
|
|||
#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 <limits.h>
|
||||
|
||||
#include "hiredis.h"
|
||||
#include "net.h"
|
||||
|
||||
enum connection_type {
|
||||
CONN_TCP,
|
||||
CONN_UNIX,
|
||||
CONN_FD
|
||||
};
|
||||
|
||||
struct config {
|
||||
enum connection_type type;
|
||||
|
||||
struct {
|
||||
const char *host;
|
||||
int port;
|
||||
struct timeval timeout;
|
||||
} tcp;
|
||||
|
||||
struct {
|
||||
const char *path;
|
||||
} unix_sock;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* The assert() calls below have side effects, so we need assert()
|
||||
* even if we are compiling without asserts (-DNDEBUG). */
|
||||
#ifdef NDEBUG
|
||||
#undef assert
|
||||
#define assert(e) (void)(e)
|
||||
#endif
|
||||
|
||||
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 int disconnect(redisContext *c, int keep_fd) {
|
||||
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, but keep the fd if requested. */
|
||||
if (keep_fd)
|
||||
return redisFreeKeepFd(c);
|
||||
redisFree(c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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_sock.path);
|
||||
} else if (config.type == CONN_FD) {
|
||||
/* Create a dummy connection just to get an fd to inherit */
|
||||
redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path);
|
||||
if (dummy_ctx) {
|
||||
int fd = disconnect(dummy_ctx, 1);
|
||||
printf("Connecting to inherited fd %d\n", fd);
|
||||
c = redisConnectFd(fd);
|
||||
}
|
||||
} else {
|
||||
assert(NULL);
|
||||
}
|
||||
|
||||
if (c == NULL) {
|
||||
printf("Connection error: can't allocate redis context\n");
|
||||
exit(1);
|
||||
} else if (c->err) {
|
||||
printf("Connection error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
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",(size_t)3,"b\0r",(size_t)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",(size_t)3,"",(size_t)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",(size_t)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);
|
||||
|
||||
sds sds_cmd;
|
||||
|
||||
sds_cmd = sdsempty();
|
||||
test("Format command into sds by passing argc/argv without lengths: ");
|
||||
len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL);
|
||||
test_cond(strncmp(sds_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));
|
||||
sdsfree(sds_cmd);
|
||||
|
||||
sds_cmd = sdsempty();
|
||||
test("Format command into sds by passing argc/argv with lengths: ");
|
||||
len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens);
|
||||
test_cond(strncmp(sds_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));
|
||||
sdsfree(sds_cmd);
|
||||
}
|
||||
|
||||
static void test_append_formatted_commands(struct config config) {
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
char *cmd;
|
||||
int len;
|
||||
|
||||
c = connect(config);
|
||||
|
||||
test("Append format command: ");
|
||||
|
||||
len = redisFormatCommand(&cmd, "SET foo bar");
|
||||
|
||||
test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK);
|
||||
|
||||
assert(redisGetReply(c, (void*)&reply) == REDIS_OK);
|
||||
|
||||
free(cmd);
|
||||
freeReplyObject(reply);
|
||||
|
||||
disconnect(c, 0);
|
||||
}
|
||||
|
||||
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("Correctly parses LLONG_MAX: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, ":9223372036854775807\r\n",22);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_OK &&
|
||||
((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
|
||||
((redisReply*)reply)->integer == LLONG_MAX);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Set error when > LLONG_MAX: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, ":9223372036854775808\r\n",22);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strcasecmp(reader->errstr,"Bad integer value") == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Correctly parses LLONG_MIN: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, ":-9223372036854775808\r\n",23);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_OK &&
|
||||
((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
|
||||
((redisReply*)reply)->integer == LLONG_MIN);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Set error when < LLONG_MIN: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, ":-9223372036854775809\r\n",23);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strcasecmp(reader->errstr,"Bad integer value") == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Set error when array < -1: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Set error when bulk < -1: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, "$-2\r\nasdf\r\n",11);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Set error when array > INT_MAX: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
#if LLONG_MAX > SIZE_MAX
|
||||
test("Set error when bulk > SIZE_MAX: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
#endif
|
||||
|
||||
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_free_null(void) {
|
||||
void *redisCtx = NULL;
|
||||
void *reply = NULL;
|
||||
|
||||
test("Don't fail when redisFree is passed a NULL value: ");
|
||||
redisFree(redisCtx);
|
||||
test_cond(redisCtx == NULL);
|
||||
|
||||
test("Don't fail when freeReplyObject is passed a NULL value: ");
|
||||
freeReplyObject(reply);
|
||||
test_cond(reply == NULL);
|
||||
}
|
||||
|
||||
static void test_blocking_connection_errors(void) {
|
||||
redisContext *c;
|
||||
|
||||
test("Returns error when host cannot be resolved: ");
|
||||
c = redisConnect((char*)"idontexist.test", 6379);
|
||||
test_cond(c->err == REDIS_ERR_OTHER &&
|
||||
(strcmp(c->errstr,"Name or service not known") == 0 ||
|
||||
strcmp(c->errstr,"Can't resolve: idontexist.test") == 0 ||
|
||||
strcmp(c->errstr,"nodename nor servname provided, or not known") == 0 ||
|
||||
strcmp(c->errstr,"No address associated with hostname") == 0 ||
|
||||
strcmp(c->errstr,"Temporary failure in name resolution") == 0 ||
|
||||
strcmp(c->errstr,"hostname nor servname provided, or not known") == 0 ||
|
||||
strcmp(c->errstr,"no address associated with name") == 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_sock 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",(size_t)3,"hello\x00world",(size_t)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, 0);
|
||||
}
|
||||
|
||||
static void test_blocking_connection_timeouts(struct config config) {
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
ssize_t s;
|
||||
const char *cmd = "DEBUG SLEEP 3\r\n";
|
||||
struct timeval tv;
|
||||
|
||||
c = connect(config);
|
||||
test("Successfully completes a command when the timeout is not exceeded: ");
|
||||
reply = redisCommand(c,"SET foo fast");
|
||||
freeReplyObject(reply);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
redisSetTimeout(c, tv);
|
||||
reply = redisCommand(c, "GET foo");
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0);
|
||||
freeReplyObject(reply);
|
||||
disconnect(c, 0);
|
||||
|
||||
c = connect(config);
|
||||
test("Does not return a reply when the command times out: ");
|
||||
s = write(c->fd, cmd, strlen(cmd));
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
redisSetTimeout(c, tv);
|
||||
reply = redisCommand(c, "GET foo");
|
||||
test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Resource temporarily unavailable") == 0);
|
||||
freeReplyObject(reply);
|
||||
|
||||
test("Reconnect properly reconnects after a timeout: ");
|
||||
redisReconnect(c);
|
||||
reply = redisCommand(c, "PING");
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
|
||||
freeReplyObject(reply);
|
||||
|
||||
test("Reconnect properly uses owned parameters: ");
|
||||
config.tcp.host = "foo";
|
||||
config.unix_sock.path = "foo";
|
||||
redisReconnect(c);
|
||||
reply = redisCommand(c, "PING");
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
|
||||
freeReplyObject(reply);
|
||||
|
||||
disconnect(c, 0);
|
||||
}
|
||||
|
||||
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 || (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_invalid_timeout_errors(struct config config) {
|
||||
redisContext *c;
|
||||
|
||||
test("Set error when an invalid timeout usec value is given to redisConnectWithTimeout: ");
|
||||
|
||||
config.tcp.timeout.tv_sec = 0;
|
||||
config.tcp.timeout.tv_usec = 10000001;
|
||||
|
||||
c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
|
||||
|
||||
test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
|
||||
redisFree(c);
|
||||
|
||||
test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: ");
|
||||
|
||||
config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1;
|
||||
config.tcp.timeout.tv_usec = 0;
|
||||
|
||||
c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
|
||||
|
||||
test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
|
||||
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);
|
||||
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
t1 = usec();
|
||||
for (i = 0; i < num; i++) {
|
||||
replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
|
||||
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
printf("\t(%dx INCRBY: %.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);
|
||||
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
for (i = 0; i < num; i++)
|
||||
redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
|
||||
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_INTEGER);
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
disconnect(c, 0);
|
||||
}
|
||||
|
||||
// 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_sock = {
|
||||
.path = "/tmp/redis.sock"
|
||||
}
|
||||
};
|
||||
int throughput = 1;
|
||||
int test_inherit_fd = 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_sock.path = argv[0];
|
||||
} else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
|
||||
throughput = 0;
|
||||
} else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
|
||||
test_inherit_fd = 0;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid argument: %s\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
argv++; argc--;
|
||||
}
|
||||
|
||||
test_format_commands();
|
||||
test_reply_reader();
|
||||
test_blocking_connection_errors();
|
||||
test_free_null();
|
||||
|
||||
printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
|
||||
cfg.type = CONN_TCP;
|
||||
test_blocking_connection(cfg);
|
||||
test_blocking_connection_timeouts(cfg);
|
||||
test_blocking_io_errors(cfg);
|
||||
test_invalid_timeout_errors(cfg);
|
||||
test_append_formatted_commands(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
|
||||
printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path);
|
||||
cfg.type = CONN_UNIX;
|
||||
test_blocking_connection(cfg);
|
||||
test_blocking_connection_timeouts(cfg);
|
||||
test_blocking_io_errors(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
|
||||
if (test_inherit_fd) {
|
||||
printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path);
|
||||
cfg.type = CONN_FD;
|
||||
test_blocking_connection(cfg);
|
||||
}
|
||||
|
||||
|
||||
if (fails) {
|
||||
printf("*** %d TESTS FAILED ***\n", fails);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("ALL TESTS PASSED\n");
|
||||
return 0;
|
||||
}
|
42
controller/thirdparty/hiredis-0.14.1/win32.h
vendored
Normal file
42
controller/thirdparty/hiredis-0.14.1/win32.h
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef _WIN32_HELPER_INCLUDE
|
||||
#define _WIN32_HELPER_INCLUDE
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#ifndef inline
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
#ifndef va_copy
|
||||
#define va_copy(d,s) ((d) = (s))
|
||||
#endif
|
||||
|
||||
#ifndef snprintf
|
||||
#define snprintf c99_snprintf
|
||||
|
||||
__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap)
|
||||
{
|
||||
int count = -1;
|
||||
|
||||
if (size != 0)
|
||||
count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
|
||||
if (count == -1)
|
||||
count = _vscprintf(format, ap);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
__inline int c99_snprintf(char* str, size_t size, const char* format, ...)
|
||||
{
|
||||
int count;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
count = c99_vsnprintf(str, size, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
32
controller/thirdparty/redis-plus-plus-1.1.1/.gitignore
vendored
Normal file
32
controller/thirdparty/redis-plus-plus-1.1.1/.gitignore
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
50
controller/thirdparty/redis-plus-plus-1.1.1/CMakeLists.txt
vendored
Normal file
50
controller/thirdparty/redis-plus-plus-1.1.1/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
project(redis++)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
cmake_minimum_required(VERSION 3.0.0)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 2.8.0)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -W -Werror -fPIC")
|
||||
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
set(PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src/sw/redis++)
|
||||
|
||||
file(GLOB PROJECT_SOURCE_FILES "${PROJECT_SOURCE_DIR}/*.cpp")
|
||||
|
||||
set(STATIC_LIB static)
|
||||
set(SHARED_LIB shared)
|
||||
|
||||
add_library(${STATIC_LIB} STATIC ${PROJECT_SOURCE_FILES})
|
||||
add_library(${SHARED_LIB} SHARED ${PROJECT_SOURCE_FILES})
|
||||
|
||||
# hiredis dependency
|
||||
find_path(HIREDIS_HEADER hiredis)
|
||||
target_include_directories(${STATIC_LIB} PUBLIC ${HIREDIS_HEADER})
|
||||
target_include_directories(${SHARED_LIB} PUBLIC ${HIREDIS_HEADER})
|
||||
|
||||
find_library(HIREDIS_LIB hiredis)
|
||||
target_link_libraries(${SHARED_LIB} ${HIREDIS_LIB})
|
||||
|
||||
set_target_properties(${STATIC_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||
set_target_properties(${SHARED_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||
|
||||
set_target_properties(${STATIC_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
|
||||
set_target_properties(${SHARED_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
# Install static lib.
|
||||
install(TARGETS ${STATIC_LIB}
|
||||
ARCHIVE DESTINATION lib)
|
||||
|
||||
# Install shared lib.
|
||||
install(TARGETS ${SHARED_LIB}
|
||||
LIBRARY DESTINATION lib)
|
||||
|
||||
#Install headers.
|
||||
set(HEADER_PATH "sw/redis++")
|
||||
file(GLOB HEADERS "${PROJECT_SOURCE_DIR}/*.h*")
|
||||
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${HEADER_PATH})
|
201
controller/thirdparty/redis-plus-plus-1.1.1/LICENSE
vendored
Normal file
201
controller/thirdparty/redis-plus-plus-1.1.1/LICENSE
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
1776
controller/thirdparty/redis-plus-plus-1.1.1/README.md
vendored
Normal file
1776
controller/thirdparty/redis-plus-plus-1.1.1/README.md
vendored
Normal file
File diff suppressed because it is too large
Load diff
376
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command.cpp
vendored
Normal file
376
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command.cpp
vendored
Normal file
|
@ -0,0 +1,376 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "command.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace cmd {
|
||||
|
||||
// KEY commands.
|
||||
|
||||
void restore(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
bool replace) {
|
||||
CmdArgs args;
|
||||
args << "RESTORE" << key << ttl << val;
|
||||
|
||||
if (replace) {
|
||||
args << "REPLACE";
|
||||
}
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
// STRING commands.
|
||||
|
||||
void bitop(Connection &connection,
|
||||
BitOp op,
|
||||
const StringView &destination,
|
||||
const StringView &key) {
|
||||
CmdArgs args;
|
||||
|
||||
detail::set_bitop(args, op);
|
||||
|
||||
args << destination << key;
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void set(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
UpdateType type) {
|
||||
CmdArgs args;
|
||||
args << "SET" << key << val;
|
||||
|
||||
if (ttl > 0) {
|
||||
args << "PX" << ttl;
|
||||
}
|
||||
|
||||
detail::set_update_type(args, type);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
// LIST commands.
|
||||
|
||||
void linsert(Connection &connection,
|
||||
const StringView &key,
|
||||
InsertPosition position,
|
||||
const StringView &pivot,
|
||||
const StringView &val) {
|
||||
std::string pos;
|
||||
switch (position) {
|
||||
case InsertPosition::BEFORE:
|
||||
pos = "BEFORE";
|
||||
break;
|
||||
|
||||
case InsertPosition::AFTER:
|
||||
pos = "AFTER";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
connection.send("LINSERT %b %s %b %b",
|
||||
key.data(), key.size(),
|
||||
pos.c_str(),
|
||||
pivot.data(), pivot.size(),
|
||||
val.data(), val.size());
|
||||
}
|
||||
|
||||
// GEO commands.
|
||||
|
||||
void geodist(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &member1,
|
||||
const StringView &member2,
|
||||
GeoUnit unit) {
|
||||
CmdArgs args;
|
||||
args << "GEODIST" << key << member1 << member2;
|
||||
|
||||
detail::set_geo_unit(args, unit);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadius_store(Connection &connection,
|
||||
const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUS" << key << loc.first << loc.second;
|
||||
|
||||
detail::set_georadius_store_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadius(Connection &connection,
|
||||
const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
long long count,
|
||||
bool asc,
|
||||
bool with_coord,
|
||||
bool with_dist,
|
||||
bool with_hash) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUS" << key << loc.first << loc.second;
|
||||
|
||||
detail::set_georadius_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
count,
|
||||
asc,
|
||||
with_coord,
|
||||
with_dist,
|
||||
with_hash);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadiusbymember(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
long long count,
|
||||
bool asc,
|
||||
bool with_coord,
|
||||
bool with_dist,
|
||||
bool with_hash) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUSBYMEMBER" << key << member;
|
||||
|
||||
detail::set_georadius_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
count,
|
||||
asc,
|
||||
with_coord,
|
||||
with_dist,
|
||||
with_hash);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadiusbymember_store(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUSBYMEMBER" << key << member;
|
||||
|
||||
detail::set_georadius_store_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
// Stream commands.
|
||||
|
||||
void xtrim(Connection &connection, const StringView &key, long long count, bool approx) {
|
||||
CmdArgs args;
|
||||
args << "XTRIM" << key << "MAXLEN";
|
||||
|
||||
if (approx) {
|
||||
args << "~";
|
||||
}
|
||||
|
||||
args << count;
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
void set_bitop(CmdArgs &args, BitOp op) {
|
||||
args << "BITOP";
|
||||
|
||||
switch (op) {
|
||||
case BitOp::AND:
|
||||
args << "AND";
|
||||
break;
|
||||
|
||||
case BitOp::OR:
|
||||
args << "OR";
|
||||
break;
|
||||
|
||||
case BitOp::XOR:
|
||||
args << "XOR";
|
||||
break;
|
||||
|
||||
case BitOp::NOT:
|
||||
args << "NOT";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown bit operations");
|
||||
}
|
||||
}
|
||||
|
||||
void set_update_type(CmdArgs &args, UpdateType type) {
|
||||
switch (type) {
|
||||
case UpdateType::EXIST:
|
||||
args << "XX";
|
||||
break;
|
||||
|
||||
case UpdateType::NOT_EXIST:
|
||||
args << "NX";
|
||||
break;
|
||||
|
||||
case UpdateType::ALWAYS:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown update type");
|
||||
}
|
||||
}
|
||||
|
||||
void set_aggregation_type(CmdArgs &args, Aggregation aggr) {
|
||||
args << "AGGREGATE";
|
||||
|
||||
switch (aggr) {
|
||||
case Aggregation::SUM:
|
||||
args << "SUM";
|
||||
break;
|
||||
|
||||
case Aggregation::MIN:
|
||||
args << "MIN";
|
||||
break;
|
||||
|
||||
case Aggregation::MAX:
|
||||
args << "MAX";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown aggregation type");
|
||||
}
|
||||
}
|
||||
|
||||
void set_geo_unit(CmdArgs &args, GeoUnit unit) {
|
||||
switch (unit) {
|
||||
case GeoUnit::M:
|
||||
args << "m";
|
||||
break;
|
||||
|
||||
case GeoUnit::KM:
|
||||
args << "km";
|
||||
break;
|
||||
|
||||
case GeoUnit::MI:
|
||||
args << "mi";
|
||||
break;
|
||||
|
||||
case GeoUnit::FT:
|
||||
args << "ft";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown geo unit type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void set_georadius_store_parameters(CmdArgs &args,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
args << radius;
|
||||
|
||||
detail::set_geo_unit(args, unit);
|
||||
|
||||
args << "COUNT" << count;
|
||||
|
||||
if (store_dist) {
|
||||
args << "STOREDIST";
|
||||
} else {
|
||||
args << "STORE";
|
||||
}
|
||||
|
||||
args << destination;
|
||||
}
|
||||
|
||||
void set_georadius_parameters(CmdArgs &args,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
long long count,
|
||||
bool asc,
|
||||
bool with_coord,
|
||||
bool with_dist,
|
||||
bool with_hash) {
|
||||
args << radius;
|
||||
|
||||
detail::set_geo_unit(args, unit);
|
||||
|
||||
if (with_coord) {
|
||||
args << "WITHCOORD";
|
||||
}
|
||||
|
||||
if (with_dist) {
|
||||
args << "WITHDIST";
|
||||
}
|
||||
|
||||
if (with_hash) {
|
||||
args << "WITHHASH";
|
||||
}
|
||||
|
||||
args << "COUNT" << count;
|
||||
|
||||
if (asc) {
|
||||
args << "ASC";
|
||||
} else {
|
||||
args << "DESC";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
2233
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command.h
vendored
Normal file
2233
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
180
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_args.h
vendored
Normal file
180
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_args.h
vendored
Normal file
|
@ -0,0 +1,180 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class CmdArgs {
|
||||
public:
|
||||
template <typename Arg>
|
||||
CmdArgs& append(Arg &&arg);
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
CmdArgs& append(Arg &&arg, Args &&...args);
|
||||
|
||||
// All overloads of operator<< are for internal use only.
|
||||
CmdArgs& operator<<(const StringView &arg);
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<typename std::decay<T>::type>::value,
|
||||
int>::type = 0>
|
||||
CmdArgs& operator<<(T &&arg);
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& operator<<(const std::pair<Iter, Iter> &range);
|
||||
|
||||
template <std::size_t N, typename ...Args>
|
||||
auto operator<<(const std::tuple<Args...> &) ->
|
||||
typename std::enable_if<N == sizeof...(Args), CmdArgs&>::type {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <std::size_t N = 0, typename ...Args>
|
||||
auto operator<<(const std::tuple<Args...> &arg) ->
|
||||
typename std::enable_if<N < sizeof...(Args), CmdArgs&>::type;
|
||||
|
||||
const char** argv() {
|
||||
return _argv.data();
|
||||
}
|
||||
|
||||
const std::size_t* argv_len() {
|
||||
return _argv_len.data();
|
||||
}
|
||||
|
||||
std::size_t size() const {
|
||||
return _argv.size();
|
||||
}
|
||||
|
||||
private:
|
||||
// Deep copy.
|
||||
CmdArgs& _append(std::string arg);
|
||||
|
||||
// Shallow copy.
|
||||
CmdArgs& _append(const StringView &arg);
|
||||
|
||||
// Shallow copy.
|
||||
CmdArgs& _append(const char *arg);
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<typename std::decay<T>::type>::value,
|
||||
int>::type = 0>
|
||||
CmdArgs& _append(T &&arg) {
|
||||
return operator<<(std::forward<T>(arg));
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& _append(std::true_type, const std::pair<Iter, Iter> &range);
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& _append(std::false_type, const std::pair<Iter, Iter> &range);
|
||||
|
||||
std::vector<const char *> _argv;
|
||||
std::vector<std::size_t> _argv_len;
|
||||
|
||||
std::list<std::string> _args;
|
||||
};
|
||||
|
||||
template <typename Arg>
|
||||
inline CmdArgs& CmdArgs::append(Arg &&arg) {
|
||||
return _append(std::forward<Arg>(arg));
|
||||
}
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
inline CmdArgs& CmdArgs::append(Arg &&arg, Args &&...args) {
|
||||
_append(std::forward<Arg>(arg));
|
||||
|
||||
return append(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
inline CmdArgs& CmdArgs::operator<<(const StringView &arg) {
|
||||
_argv.push_back(arg.data());
|
||||
_argv_len.push_back(arg.size());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
inline CmdArgs& CmdArgs::operator<<(const std::pair<Iter, Iter> &range) {
|
||||
return _append(IsKvPair<typename std::decay<decltype(*std::declval<Iter>())>::type>(), range);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<typename std::decay<T>::type>::value,
|
||||
int>::type>
|
||||
inline CmdArgs& CmdArgs::operator<<(T &&arg) {
|
||||
return _append(std::to_string(std::forward<T>(arg)));
|
||||
}
|
||||
|
||||
template <std::size_t N, typename ...Args>
|
||||
auto CmdArgs::operator<<(const std::tuple<Args...> &arg) ->
|
||||
typename std::enable_if<N < sizeof...(Args), CmdArgs&>::type {
|
||||
operator<<(std::get<N>(arg));
|
||||
|
||||
return operator<<<N + 1, Args...>(arg);
|
||||
}
|
||||
|
||||
inline CmdArgs& CmdArgs::_append(std::string arg) {
|
||||
_args.push_back(std::move(arg));
|
||||
return operator<<(_args.back());
|
||||
}
|
||||
|
||||
inline CmdArgs& CmdArgs::_append(const StringView &arg) {
|
||||
return operator<<(arg);
|
||||
}
|
||||
|
||||
inline CmdArgs& CmdArgs::_append(const char *arg) {
|
||||
return operator<<(arg);
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& CmdArgs::_append(std::false_type, const std::pair<Iter, Iter> &range) {
|
||||
auto first = range.first;
|
||||
auto last = range.second;
|
||||
while (first != last) {
|
||||
*this << *first;
|
||||
++first;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& CmdArgs::_append(std::true_type, const std::pair<Iter, Iter> &range) {
|
||||
auto first = range.first;
|
||||
auto last = range.second;
|
||||
while (first != last) {
|
||||
*this << first->first << first->second;
|
||||
++first;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H
|
201
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_options.cpp
vendored
Normal file
201
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_options.cpp
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "command_options.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string NEGATIVE_INFINITY_NUMERIC = "-inf";
|
||||
const std::string POSITIVE_INFINITY_NUMERIC = "+inf";
|
||||
|
||||
const std::string NEGATIVE_INFINITY_STRING = "-";
|
||||
const std::string POSITIVE_INFINITY_STRING = "+";
|
||||
|
||||
std::string unbound(const std::string &bnd);
|
||||
|
||||
std::string bound(const std::string &bnd);
|
||||
|
||||
}
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
const std::string& UnboundedInterval<double>::min() const {
|
||||
return NEGATIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
const std::string& UnboundedInterval<double>::max() const {
|
||||
return POSITIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
BoundedInterval<double>::BoundedInterval(double min, double max, BoundType type) :
|
||||
_min(std::to_string(min)),
|
||||
_max(std::to_string(max)) {
|
||||
switch (type) {
|
||||
case BoundType::CLOSED:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(_min);
|
||||
_max = unbound(_max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
_min = unbound(_min);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
_max = unbound(_max);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknow BoundType");
|
||||
}
|
||||
}
|
||||
|
||||
LeftBoundedInterval<double>::LeftBoundedInterval(double min, BoundType type) :
|
||||
_min(std::to_string(min)) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(_min);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or RIGHT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& LeftBoundedInterval<double>::max() const {
|
||||
return POSITIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
RightBoundedInterval<double>::RightBoundedInterval(double max, BoundType type) :
|
||||
_max(std::to_string(max)) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_max = unbound(_max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or LEFT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& RightBoundedInterval<double>::min() const {
|
||||
return NEGATIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
const std::string& UnboundedInterval<std::string>::min() const {
|
||||
return NEGATIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
const std::string& UnboundedInterval<std::string>::max() const {
|
||||
return POSITIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
BoundedInterval<std::string>::BoundedInterval(const std::string &min,
|
||||
const std::string &max,
|
||||
BoundType type) {
|
||||
switch (type) {
|
||||
case BoundType::CLOSED:
|
||||
_min = bound(min);
|
||||
_max = bound(max);
|
||||
break;
|
||||
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(min);
|
||||
_max = unbound(max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
_min = unbound(min);
|
||||
_max = bound(max);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
_min = bound(min);
|
||||
_max = unbound(max);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknow BoundType");
|
||||
}
|
||||
}
|
||||
|
||||
LeftBoundedInterval<std::string>::LeftBoundedInterval(const std::string &min, BoundType type) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(min);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
_min = bound(min);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or RIGHT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& LeftBoundedInterval<std::string>::max() const {
|
||||
return POSITIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
RightBoundedInterval<std::string>::RightBoundedInterval(const std::string &max, BoundType type) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_max = unbound(max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
_max = bound(max);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or LEFT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& RightBoundedInterval<std::string>::min() const {
|
||||
return NEGATIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::string unbound(const std::string &bnd) {
|
||||
return "(" + bnd;
|
||||
}
|
||||
|
||||
std::string bound(const std::string &bnd) {
|
||||
return "[" + bnd;
|
||||
}
|
||||
|
||||
}
|
211
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_options.h
vendored
Normal file
211
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_options.h
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H
|
||||
|
||||
#include <string>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
enum class UpdateType {
|
||||
EXIST,
|
||||
NOT_EXIST,
|
||||
ALWAYS
|
||||
};
|
||||
|
||||
enum class InsertPosition {
|
||||
BEFORE,
|
||||
AFTER
|
||||
};
|
||||
|
||||
enum class BoundType {
|
||||
CLOSED,
|
||||
OPEN,
|
||||
LEFT_OPEN,
|
||||
RIGHT_OPEN
|
||||
};
|
||||
|
||||
// (-inf, +inf)
|
||||
template <typename T>
|
||||
class UnboundedInterval;
|
||||
|
||||
// [min, max], (min, max), (min, max], [min, max)
|
||||
template <typename T>
|
||||
class BoundedInterval;
|
||||
|
||||
// [min, +inf), (min, +inf)
|
||||
template <typename T>
|
||||
class LeftBoundedInterval;
|
||||
|
||||
// (-inf, max], (-inf, max)
|
||||
template <typename T>
|
||||
class RightBoundedInterval;
|
||||
|
||||
template <>
|
||||
class UnboundedInterval<double> {
|
||||
public:
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const;
|
||||
};
|
||||
|
||||
template <>
|
||||
class BoundedInterval<double> {
|
||||
public:
|
||||
BoundedInterval(double min, double max, BoundType type);
|
||||
|
||||
const std::string& min() const {
|
||||
return _min;
|
||||
}
|
||||
|
||||
const std::string& max() const {
|
||||
return _max;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _min;
|
||||
std::string _max;
|
||||
};
|
||||
|
||||
template <>
|
||||
class LeftBoundedInterval<double> {
|
||||
public:
|
||||
LeftBoundedInterval(double min, BoundType type);
|
||||
|
||||
const std::string& min() const {
|
||||
return _min;
|
||||
}
|
||||
|
||||
const std::string& max() const;
|
||||
|
||||
private:
|
||||
std::string _min;
|
||||
};
|
||||
|
||||
template <>
|
||||
class RightBoundedInterval<double> {
|
||||
public:
|
||||
RightBoundedInterval(double max, BoundType type);
|
||||
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const {
|
||||
return _max;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _max;
|
||||
};
|
||||
|
||||
template <>
|
||||
class UnboundedInterval<std::string> {
|
||||
public:
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const;
|
||||
};
|
||||
|
||||
template <>
|
||||
class BoundedInterval<std::string> {
|
||||
public:
|
||||
BoundedInterval(const std::string &min, const std::string &max, BoundType type);
|
||||
|
||||
const std::string& min() const {
|
||||
return _min;
|
||||
}
|
||||
|
||||
const std::string& max() const {
|
||||
return _max;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _min;
|
||||
std::string _max;
|
||||
};
|
||||
|
||||
template <>
|
||||
class LeftBoundedInterval<std::string> {
|
||||
public:
|
||||
LeftBoundedInterval(const std::string &min, BoundType type);
|
||||
|
||||
const std::string& min() const {
|
||||
return _min;
|
||||
}
|
||||
|
||||
const std::string& max() const;
|
||||
|
||||
private:
|
||||
std::string _min;
|
||||
};
|
||||
|
||||
template <>
|
||||
class RightBoundedInterval<std::string> {
|
||||
public:
|
||||
RightBoundedInterval(const std::string &max, BoundType type);
|
||||
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const {
|
||||
return _max;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _max;
|
||||
};
|
||||
|
||||
struct LimitOptions {
|
||||
long long offset = 0;
|
||||
long long count = -1;
|
||||
};
|
||||
|
||||
enum class Aggregation {
|
||||
SUM,
|
||||
MIN,
|
||||
MAX
|
||||
};
|
||||
|
||||
enum class BitOp {
|
||||
AND,
|
||||
OR,
|
||||
XOR,
|
||||
NOT
|
||||
};
|
||||
|
||||
enum class GeoUnit {
|
||||
M,
|
||||
KM,
|
||||
MI,
|
||||
FT
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct WithCoord : TupleWithType<std::pair<double, double>, T> {};
|
||||
|
||||
template <typename T>
|
||||
struct WithDist : TupleWithType<double, T> {};
|
||||
|
||||
template <typename T>
|
||||
struct WithHash : TupleWithType<long long, T> {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H
|
305
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection.cpp
vendored
Normal file
305
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection.cpp
vendored
Normal file
|
@ -0,0 +1,305 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "connection.h"
|
||||
#include <cassert>
|
||||
#include "reply.h"
|
||||
#include "command.h"
|
||||
#include "command_args.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
ConnectionOptions::ConnectionOptions(const std::string &uri) :
|
||||
ConnectionOptions(_parse_options(uri)) {}
|
||||
|
||||
ConnectionOptions ConnectionOptions::_parse_options(const std::string &uri) const {
|
||||
std::string type;
|
||||
std::string path;
|
||||
std::tie(type, path) = _split_string(uri, "://");
|
||||
|
||||
if (path.empty()) {
|
||||
throw Error("Invalid URI: no path");
|
||||
}
|
||||
|
||||
if (type == "tcp") {
|
||||
return _parse_tcp_options(path);
|
||||
} else if (type == "unix") {
|
||||
return _parse_unix_options(path);
|
||||
} else {
|
||||
throw Error("Invalid URI: invalid type");
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionOptions ConnectionOptions::_parse_tcp_options(const std::string &path) const {
|
||||
ConnectionOptions options;
|
||||
|
||||
options.type = ConnectionType::TCP;
|
||||
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::tie(host, port) = _split_string(path, ":");
|
||||
|
||||
options.host = host;
|
||||
try {
|
||||
if (!port.empty()) {
|
||||
options.port = std::stoi(port);
|
||||
} // else use default port, i.e. 6379.
|
||||
} catch (const std::exception &) {
|
||||
throw Error("Invalid URL: invalid port");
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
ConnectionOptions ConnectionOptions::_parse_unix_options(const std::string &path) const {
|
||||
ConnectionOptions options;
|
||||
|
||||
options.type = ConnectionType::UNIX;
|
||||
options.path = path;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
auto ConnectionOptions::_split_string(const std::string &str, const std::string &delimiter) const ->
|
||||
std::pair<std::string, std::string> {
|
||||
auto pos = str.rfind(delimiter);
|
||||
if (pos == std::string::npos) {
|
||||
return {str, ""};
|
||||
}
|
||||
|
||||
return {str.substr(0, pos), str.substr(pos + delimiter.size())};
|
||||
}
|
||||
|
||||
class Connection::Connector {
|
||||
public:
|
||||
explicit Connector(const ConnectionOptions &opts);
|
||||
|
||||
ContextUPtr connect() const;
|
||||
|
||||
private:
|
||||
ContextUPtr _connect() const;
|
||||
|
||||
redisContext* _connect_tcp() const;
|
||||
|
||||
redisContext* _connect_unix() const;
|
||||
|
||||
void _set_socket_timeout(redisContext &ctx) const;
|
||||
|
||||
void _enable_keep_alive(redisContext &ctx) const;
|
||||
|
||||
timeval _to_timeval(const std::chrono::milliseconds &dur) const;
|
||||
|
||||
const ConnectionOptions &_opts;
|
||||
};
|
||||
|
||||
Connection::Connector::Connector(const ConnectionOptions &opts) : _opts(opts) {}
|
||||
|
||||
Connection::ContextUPtr Connection::Connector::connect() const {
|
||||
auto ctx = _connect();
|
||||
|
||||
assert(ctx);
|
||||
|
||||
if (ctx->err != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to connect to Redis");
|
||||
}
|
||||
|
||||
_set_socket_timeout(*ctx);
|
||||
|
||||
_enable_keep_alive(*ctx);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
Connection::ContextUPtr Connection::Connector::_connect() const {
|
||||
redisContext *context = nullptr;
|
||||
switch (_opts.type) {
|
||||
case ConnectionType::TCP:
|
||||
context = _connect_tcp();
|
||||
break;
|
||||
|
||||
case ConnectionType::UNIX:
|
||||
context = _connect_unix();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Never goes here.
|
||||
throw Error("Unkonw connection type");
|
||||
}
|
||||
|
||||
if (context == nullptr) {
|
||||
throw Error("Failed to allocate memory for connection.");
|
||||
}
|
||||
|
||||
return ContextUPtr(context);
|
||||
}
|
||||
|
||||
redisContext* Connection::Connector::_connect_tcp() const {
|
||||
if (_opts.connect_timeout > std::chrono::milliseconds(0)) {
|
||||
return redisConnectWithTimeout(_opts.host.c_str(),
|
||||
_opts.port,
|
||||
_to_timeval(_opts.connect_timeout));
|
||||
} else {
|
||||
return redisConnect(_opts.host.c_str(), _opts.port);
|
||||
}
|
||||
}
|
||||
|
||||
redisContext* Connection::Connector::_connect_unix() const {
|
||||
if (_opts.connect_timeout > std::chrono::milliseconds(0)) {
|
||||
return redisConnectUnixWithTimeout(
|
||||
_opts.path.c_str(),
|
||||
_to_timeval(_opts.connect_timeout));
|
||||
} else {
|
||||
return redisConnectUnix(_opts.path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Connector::_set_socket_timeout(redisContext &ctx) const {
|
||||
if (_opts.socket_timeout <= std::chrono::milliseconds(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (redisSetTimeout(&ctx, _to_timeval(_opts.socket_timeout)) != REDIS_OK) {
|
||||
throw_error(ctx, "Failed to set socket timeout");
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Connector::_enable_keep_alive(redisContext &ctx) const {
|
||||
if (!_opts.keep_alive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (redisEnableKeepAlive(&ctx) != REDIS_OK) {
|
||||
throw_error(ctx, "Failed to enable keep alive option");
|
||||
}
|
||||
}
|
||||
|
||||
timeval Connection::Connector::_to_timeval(const std::chrono::milliseconds &dur) const {
|
||||
auto sec = std::chrono::duration_cast<std::chrono::seconds>(dur);
|
||||
auto msec = std::chrono::duration_cast<std::chrono::microseconds>(dur - sec);
|
||||
|
||||
return {
|
||||
static_cast<std::time_t>(sec.count()),
|
||||
static_cast<suseconds_t>(msec.count())
|
||||
};
|
||||
}
|
||||
|
||||
void swap(Connection &lhs, Connection &rhs) noexcept {
|
||||
std::swap(lhs._ctx, rhs._ctx);
|
||||
std::swap(lhs._last_active, rhs._last_active);
|
||||
std::swap(lhs._opts, rhs._opts);
|
||||
}
|
||||
|
||||
Connection::Connection(const ConnectionOptions &opts) :
|
||||
_ctx(Connector(opts).connect()),
|
||||
_last_active(std::chrono::steady_clock::now()),
|
||||
_opts(opts) {
|
||||
assert(_ctx && !broken());
|
||||
|
||||
_set_options();
|
||||
}
|
||||
|
||||
void Connection::reconnect() {
|
||||
Connection connection(_opts);
|
||||
|
||||
swap(*this, connection);
|
||||
}
|
||||
|
||||
void Connection::send(int argc, const char **argv, const std::size_t *argv_len) {
|
||||
auto ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
if (redisAppendCommandArgv(ctx,
|
||||
argc,
|
||||
argv,
|
||||
argv_len) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to send command");
|
||||
}
|
||||
|
||||
assert(!broken());
|
||||
}
|
||||
|
||||
void Connection::send(CmdArgs &args) {
|
||||
auto ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
if (redisAppendCommandArgv(ctx,
|
||||
args.size(),
|
||||
args.argv(),
|
||||
args.argv_len()) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to send command");
|
||||
}
|
||||
|
||||
assert(!broken());
|
||||
}
|
||||
|
||||
ReplyUPtr Connection::recv() {
|
||||
auto *ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
void *r = nullptr;
|
||||
if (redisGetReply(ctx, &r) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to get reply");
|
||||
}
|
||||
|
||||
assert(!broken() && r != nullptr);
|
||||
|
||||
auto reply = ReplyUPtr(static_cast<redisReply*>(r));
|
||||
|
||||
if (reply::is_error(*reply)) {
|
||||
throw_error(*reply);
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void Connection::_set_options() {
|
||||
_auth();
|
||||
|
||||
_select_db();
|
||||
}
|
||||
|
||||
void Connection::_auth() {
|
||||
if (_opts.password.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd::auth(*this, _opts.password);
|
||||
|
||||
auto reply = recv();
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Connection::_select_db() {
|
||||
if (_opts.db == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd::select(*this, _opts.db);
|
||||
|
||||
auto reply = recv();
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
194
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection.h
vendored
Normal file
194
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection.h
vendored
Normal file
|
@ -0,0 +1,194 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_H
|
||||
#define SEWENEW_REDISPLUSPLUS_CONNECTION_H
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "errors.h"
|
||||
#include "reply.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
enum class ConnectionType {
|
||||
TCP = 0,
|
||||
UNIX
|
||||
};
|
||||
|
||||
struct ConnectionOptions {
|
||||
public:
|
||||
ConnectionOptions() = default;
|
||||
|
||||
explicit ConnectionOptions(const std::string &uri);
|
||||
|
||||
ConnectionOptions(const ConnectionOptions &) = default;
|
||||
ConnectionOptions& operator=(const ConnectionOptions &) = default;
|
||||
|
||||
ConnectionOptions(ConnectionOptions &&) = default;
|
||||
ConnectionOptions& operator=(ConnectionOptions &&) = default;
|
||||
|
||||
~ConnectionOptions() = default;
|
||||
|
||||
ConnectionType type = ConnectionType::TCP;
|
||||
|
||||
std::string host;
|
||||
|
||||
int port = 6379;
|
||||
|
||||
std::string path;
|
||||
|
||||
std::string password;
|
||||
|
||||
int db = 0;
|
||||
|
||||
bool keep_alive = false;
|
||||
|
||||
std::chrono::milliseconds connect_timeout{0};
|
||||
|
||||
std::chrono::milliseconds socket_timeout{0};
|
||||
|
||||
private:
|
||||
ConnectionOptions _parse_options(const std::string &uri) const;
|
||||
|
||||
ConnectionOptions _parse_tcp_options(const std::string &path) const;
|
||||
|
||||
ConnectionOptions _parse_unix_options(const std::string &path) const;
|
||||
|
||||
auto _split_string(const std::string &str, const std::string &delimiter) const ->
|
||||
std::pair<std::string, std::string>;
|
||||
};
|
||||
|
||||
class CmdArgs;
|
||||
|
||||
class Connection {
|
||||
public:
|
||||
explicit Connection(const ConnectionOptions &opts);
|
||||
|
||||
Connection(const Connection &) = delete;
|
||||
Connection& operator=(const Connection &) = delete;
|
||||
|
||||
Connection(Connection &&) = default;
|
||||
Connection& operator=(Connection &&) = default;
|
||||
|
||||
~Connection() = default;
|
||||
|
||||
// Check if the connection is broken. Client needs to do this check
|
||||
// before sending some command to the connection. If it's broken,
|
||||
// client needs to reconnect it.
|
||||
bool broken() const noexcept {
|
||||
return _ctx->err != REDIS_OK;
|
||||
}
|
||||
|
||||
void reset() noexcept {
|
||||
_ctx->err = 0;
|
||||
}
|
||||
|
||||
void reconnect();
|
||||
|
||||
auto last_active() const
|
||||
-> std::chrono::time_point<std::chrono::steady_clock> {
|
||||
return _last_active;
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
void send(const char *format, Args &&...args);
|
||||
|
||||
void send(int argc, const char **argv, const std::size_t *argv_len);
|
||||
|
||||
void send(CmdArgs &args);
|
||||
|
||||
ReplyUPtr recv();
|
||||
|
||||
const ConnectionOptions& options() const {
|
||||
return _opts;
|
||||
}
|
||||
|
||||
friend void swap(Connection &lhs, Connection &rhs) noexcept;
|
||||
|
||||
private:
|
||||
class Connector;
|
||||
|
||||
struct ContextDeleter {
|
||||
void operator()(redisContext *context) const {
|
||||
if (context != nullptr) {
|
||||
redisFree(context);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
using ContextUPtr = std::unique_ptr<redisContext, ContextDeleter>;
|
||||
|
||||
void _set_options();
|
||||
|
||||
void _auth();
|
||||
|
||||
void _select_db();
|
||||
|
||||
redisContext* _context();
|
||||
|
||||
ContextUPtr _ctx;
|
||||
|
||||
// The time that the connection is created or the time that
|
||||
// the connection is used, i.e. *context()* is called.
|
||||
std::chrono::time_point<std::chrono::steady_clock> _last_active{};
|
||||
|
||||
ConnectionOptions _opts;
|
||||
};
|
||||
|
||||
using ConnectionSPtr = std::shared_ptr<Connection>;
|
||||
|
||||
enum class Role {
|
||||
MASTER,
|
||||
SLAVE
|
||||
};
|
||||
|
||||
// Inline implementaions.
|
||||
|
||||
template <typename ...Args>
|
||||
inline void Connection::send(const char *format, Args &&...args) {
|
||||
auto ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
if (redisAppendCommand(ctx,
|
||||
format,
|
||||
std::forward<Args>(args)...) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to send command");
|
||||
}
|
||||
|
||||
assert(!broken());
|
||||
}
|
||||
|
||||
inline redisContext* Connection::_context() {
|
||||
_last_active = std::chrono::steady_clock::now();
|
||||
|
||||
return _ctx.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_H
|
249
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.cpp
vendored
Normal file
249
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.cpp
vendored
Normal file
|
@ -0,0 +1,249 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "connection_pool.h"
|
||||
#include <cassert>
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
ConnectionPool::ConnectionPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts) :
|
||||
_opts(connection_opts),
|
||||
_pool_opts(pool_opts) {
|
||||
if (_pool_opts.size == 0) {
|
||||
throw Error("CANNOT create an empty pool");
|
||||
}
|
||||
|
||||
// Lazily create connections.
|
||||
}
|
||||
|
||||
ConnectionPool::ConnectionPool(SimpleSentinel sentinel,
|
||||
const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts) :
|
||||
_opts(connection_opts),
|
||||
_pool_opts(pool_opts),
|
||||
_sentinel(std::move(sentinel)) {
|
||||
// In this case, the connection must be of TCP type.
|
||||
if (_opts.type != ConnectionType::TCP) {
|
||||
throw Error("Sentinel only supports TCP connection");
|
||||
}
|
||||
|
||||
if (_opts.connect_timeout == std::chrono::milliseconds(0)
|
||||
|| _opts.socket_timeout == std::chrono::milliseconds(0)) {
|
||||
throw Error("With sentinel, connection timeout and socket timeout cannot be 0");
|
||||
}
|
||||
|
||||
// Cleanup connection options.
|
||||
_update_connection_opts("", -1);
|
||||
|
||||
assert(_sentinel);
|
||||
}
|
||||
|
||||
ConnectionPool::ConnectionPool(ConnectionPool &&that) {
|
||||
std::lock_guard<std::mutex> lock(that._mutex);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
ConnectionPool& ConnectionPool::operator=(ConnectionPool &&that) {
|
||||
if (this != &that) {
|
||||
std::lock(_mutex, that._mutex);
|
||||
std::lock_guard<std::mutex> lock_this(_mutex, std::adopt_lock);
|
||||
std::lock_guard<std::mutex> lock_that(that._mutex, std::adopt_lock);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Connection ConnectionPool::fetch() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
if (_pool.empty()) {
|
||||
if (_used_connections == _pool_opts.size) {
|
||||
_wait_for_connection(lock);
|
||||
} else {
|
||||
// Lazily create a new connection.
|
||||
auto connection = _create();
|
||||
|
||||
++_used_connections;
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
// _pool is NOT empty.
|
||||
auto connection = _fetch();
|
||||
|
||||
auto connection_lifetime = _pool_opts.connection_lifetime;
|
||||
|
||||
if (_sentinel) {
|
||||
auto opts = _opts;
|
||||
auto role_changed = _role_changed(connection.options());
|
||||
auto sentinel = _sentinel;
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (role_changed || _need_reconnect(connection, connection_lifetime)) {
|
||||
try {
|
||||
connection = _create(sentinel, opts, false);
|
||||
} catch (const Error &e) {
|
||||
// Failed to reconnect, return it to the pool, and retry latter.
|
||||
release(std::move(connection));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (_need_reconnect(connection, connection_lifetime)) {
|
||||
try {
|
||||
connection.reconnect();
|
||||
} catch (const Error &e) {
|
||||
// Failed to reconnect, return it to the pool, and retry latter.
|
||||
release(std::move(connection));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
ConnectionOptions ConnectionPool::connection_options() {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
return _opts;
|
||||
}
|
||||
|
||||
void ConnectionPool::release(Connection connection) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
_pool.push_back(std::move(connection));
|
||||
}
|
||||
|
||||
_cv.notify_one();
|
||||
}
|
||||
|
||||
Connection ConnectionPool::create() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
auto opts = _opts;
|
||||
|
||||
if (_sentinel) {
|
||||
auto sentinel = _sentinel;
|
||||
|
||||
lock.unlock();
|
||||
|
||||
return _create(sentinel, opts, false);
|
||||
} else {
|
||||
lock.unlock();
|
||||
|
||||
return Connection(opts);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionPool::_move(ConnectionPool &&that) {
|
||||
_opts = std::move(that._opts);
|
||||
_pool_opts = std::move(that._pool_opts);
|
||||
_pool = std::move(that._pool);
|
||||
_used_connections = that._used_connections;
|
||||
_sentinel = std::move(that._sentinel);
|
||||
}
|
||||
|
||||
Connection ConnectionPool::_create() {
|
||||
if (_sentinel) {
|
||||
// Get Redis host and port info from sentinel.
|
||||
return _create(_sentinel, _opts, true);
|
||||
}
|
||||
|
||||
return Connection(_opts);
|
||||
}
|
||||
|
||||
Connection ConnectionPool::_create(SimpleSentinel &sentinel,
|
||||
const ConnectionOptions &opts,
|
||||
bool locked) {
|
||||
try {
|
||||
auto connection = sentinel.create(opts);
|
||||
|
||||
std::unique_lock<std::mutex> lock(_mutex, std::defer_lock);
|
||||
if (!locked) {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
const auto &connection_opts = connection.options();
|
||||
if (_role_changed(connection_opts)) {
|
||||
// Master/Slave has been changed, reconnect all connections.
|
||||
_update_connection_opts(connection_opts.host, connection_opts.port);
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (const StopIterError &e) {
|
||||
throw Error("Failed to create connection with sentinel");
|
||||
}
|
||||
}
|
||||
|
||||
Connection ConnectionPool::_fetch() {
|
||||
assert(!_pool.empty());
|
||||
|
||||
auto connection = std::move(_pool.front());
|
||||
_pool.pop_front();
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
void ConnectionPool::_wait_for_connection(std::unique_lock<std::mutex> &lock) {
|
||||
auto timeout = _pool_opts.wait_timeout;
|
||||
if (timeout > std::chrono::milliseconds(0)) {
|
||||
// Wait until _pool is no longer empty or timeout.
|
||||
if (!_cv.wait_for(lock,
|
||||
timeout,
|
||||
[this] { return !(this->_pool).empty(); })) {
|
||||
throw Error("Failed to fetch a connection in "
|
||||
+ std::to_string(timeout.count()) + " milliseconds");
|
||||
}
|
||||
} else {
|
||||
// Wait forever.
|
||||
_cv.wait(lock, [this] { return !(this->_pool).empty(); });
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectionPool::_need_reconnect(const Connection &connection,
|
||||
const std::chrono::milliseconds &connection_lifetime) const {
|
||||
if (connection.broken()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (connection_lifetime > std::chrono::milliseconds(0)) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now - connection.last_active() > connection_lifetime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
115
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.h
vendored
Normal file
115
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.h
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H
|
||||
#define SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include "connection.h"
|
||||
#include "sentinel.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
struct ConnectionPoolOptions {
|
||||
// Max number of connections, including both in-use and idle ones.
|
||||
std::size_t size = 1;
|
||||
|
||||
// Max time to wait for a connection. 0ms means client waits forever.
|
||||
std::chrono::milliseconds wait_timeout{0};
|
||||
|
||||
// Max lifetime of a connection. 0ms means we never expire the connection.
|
||||
std::chrono::milliseconds connection_lifetime{0};
|
||||
};
|
||||
|
||||
class ConnectionPool {
|
||||
public:
|
||||
ConnectionPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts);
|
||||
|
||||
ConnectionPool(SimpleSentinel sentinel,
|
||||
const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts);
|
||||
|
||||
ConnectionPool() = default;
|
||||
|
||||
ConnectionPool(ConnectionPool &&that);
|
||||
ConnectionPool& operator=(ConnectionPool &&that);
|
||||
|
||||
ConnectionPool(const ConnectionPool &) = delete;
|
||||
ConnectionPool& operator=(const ConnectionPool &) = delete;
|
||||
|
||||
~ConnectionPool() = default;
|
||||
|
||||
// Fetch a connection from pool.
|
||||
Connection fetch();
|
||||
|
||||
ConnectionOptions connection_options();
|
||||
|
||||
void release(Connection connection);
|
||||
|
||||
// Create a new connection.
|
||||
Connection create();
|
||||
|
||||
private:
|
||||
void _move(ConnectionPool &&that);
|
||||
|
||||
// NOT thread-safe
|
||||
Connection _create();
|
||||
|
||||
Connection _create(SimpleSentinel &sentinel, const ConnectionOptions &opts, bool locked);
|
||||
|
||||
Connection _fetch();
|
||||
|
||||
void _wait_for_connection(std::unique_lock<std::mutex> &lock);
|
||||
|
||||
bool _need_reconnect(const Connection &connection,
|
||||
const std::chrono::milliseconds &connection_lifetime) const;
|
||||
|
||||
void _update_connection_opts(const std::string &host, int port) {
|
||||
_opts.host = host;
|
||||
_opts.port = port;
|
||||
}
|
||||
|
||||
bool _role_changed(const ConnectionOptions &opts) const {
|
||||
return opts.port != _opts.port || opts.host != _opts.host;
|
||||
}
|
||||
|
||||
ConnectionOptions _opts;
|
||||
|
||||
ConnectionPoolOptions _pool_opts;
|
||||
|
||||
std::deque<Connection> _pool;
|
||||
|
||||
std::size_t _used_connections = 0;
|
||||
|
||||
std::mutex _mutex;
|
||||
|
||||
std::condition_variable _cv;
|
||||
|
||||
SimpleSentinel _sentinel;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H
|
96
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/crc16.cpp
vendored
Normal file
96
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/crc16.cpp
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2001-2010 Georges Menie (www.menie.org)
|
||||
* Copyright 2010-2012 Salvatore Sanfilippo (adapted to Redis coding style)
|
||||
* 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 the University of California, Berkeley 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 REGENTS 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 REGENTS AND 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.
|
||||
*/
|
||||
|
||||
/* CRC16 implementation according to CCITT standards.
|
||||
*
|
||||
* Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the
|
||||
* following parameters:
|
||||
*
|
||||
* Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN"
|
||||
* Width : 16 bit
|
||||
* Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1)
|
||||
* Initialization : 0000
|
||||
* Reflect Input byte : False
|
||||
* Reflect Output CRC : False
|
||||
* Xor constant to output CRC : 0000
|
||||
* Output for "123456789" : 31C3
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
static const uint16_t crc16tab[256]= {
|
||||
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
|
||||
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
|
||||
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
|
||||
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
|
||||
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
|
||||
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
|
||||
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
|
||||
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
|
||||
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
|
||||
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
|
||||
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
|
||||
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
|
||||
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
|
||||
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
|
||||
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
|
||||
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
|
||||
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
|
||||
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
|
||||
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
|
||||
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
|
||||
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
|
||||
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
|
||||
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
|
||||
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
|
||||
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
|
||||
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
|
||||
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
|
||||
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
|
||||
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
|
||||
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
|
||||
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
|
||||
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
|
||||
};
|
||||
|
||||
uint16_t crc16(const char *buf, int len) {
|
||||
int counter;
|
||||
uint16_t crc = 0;
|
||||
for (counter = 0; counter < len; counter++)
|
||||
crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF];
|
||||
return crc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
136
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/errors.cpp
vendored
Normal file
136
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/errors.cpp
vendored
Normal file
|
@ -0,0 +1,136 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "errors.h"
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include "shards.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace sw::redis;
|
||||
|
||||
std::pair<ReplyErrorType, std::string> parse_error(const std::string &msg);
|
||||
|
||||
std::unordered_map<std::string, ReplyErrorType> error_map = {
|
||||
{"MOVED", ReplyErrorType::MOVED},
|
||||
{"ASK", ReplyErrorType::ASK}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
void throw_error(redisContext &context, const std::string &err_info) {
|
||||
auto err_code = context.err;
|
||||
const auto *err_str = context.errstr;
|
||||
if (err_str == nullptr) {
|
||||
throw Error(err_info + ": null error message: " + std::to_string(err_code));
|
||||
}
|
||||
|
||||
auto err_msg = err_info + ": " + err_str;
|
||||
|
||||
switch (err_code) {
|
||||
case REDIS_ERR_IO:
|
||||
if (errno == EAGAIN || errno == EINTR) {
|
||||
throw TimeoutError(err_msg);
|
||||
} else {
|
||||
throw IoError(err_msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case REDIS_ERR_EOF:
|
||||
throw ClosedError(err_msg);
|
||||
break;
|
||||
|
||||
case REDIS_ERR_PROTOCOL:
|
||||
throw ProtoError(err_msg);
|
||||
break;
|
||||
|
||||
case REDIS_ERR_OOM:
|
||||
throw OomError(err_msg);
|
||||
break;
|
||||
|
||||
case REDIS_ERR_OTHER:
|
||||
throw Error(err_msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error(err_info + ": Unknown error code");
|
||||
}
|
||||
}
|
||||
|
||||
void throw_error(const redisReply &reply) {
|
||||
assert(reply.type == REDIS_REPLY_ERROR);
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw Error("Null error reply");
|
||||
}
|
||||
|
||||
auto err_str = std::string(reply.str, reply.len);
|
||||
|
||||
auto err_type = ReplyErrorType::ERR;
|
||||
std::string err_msg;
|
||||
std::tie(err_type, err_msg) = parse_error(err_str);
|
||||
|
||||
switch (err_type) {
|
||||
case ReplyErrorType::MOVED:
|
||||
throw MovedError(err_msg);
|
||||
break;
|
||||
|
||||
case ReplyErrorType::ASK:
|
||||
throw AskError(err_msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ReplyError(err_str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace sw::redis;
|
||||
|
||||
std::pair<ReplyErrorType, std::string> parse_error(const std::string &err) {
|
||||
// The error contains an Error Prefix, and an optional error message.
|
||||
auto idx = err.find_first_of(" \n");
|
||||
|
||||
if (idx == std::string::npos) {
|
||||
throw ProtoError("No Error Prefix: " + err);
|
||||
}
|
||||
|
||||
auto err_prefix = err.substr(0, idx);
|
||||
auto err_type = ReplyErrorType::ERR;
|
||||
|
||||
auto iter = error_map.find(err_prefix);
|
||||
if (iter != error_map.end()) {
|
||||
// Specific error.
|
||||
err_type = iter->second;
|
||||
} // else Generic error.
|
||||
|
||||
return {err_type, err.substr(idx + 1)};
|
||||
}
|
||||
|
||||
}
|
159
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/errors.h
vendored
Normal file
159
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/errors.h
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_ERRORS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_ERRORS_H
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <hiredis/hiredis.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
enum ReplyErrorType {
|
||||
ERR,
|
||||
MOVED,
|
||||
ASK
|
||||
};
|
||||
|
||||
class Error : public std::exception {
|
||||
public:
|
||||
explicit Error(const std::string &msg) : _msg(msg) {}
|
||||
|
||||
Error(const Error &) = default;
|
||||
Error& operator=(const Error &) = default;
|
||||
|
||||
Error(Error &&) = default;
|
||||
Error& operator=(Error &&) = default;
|
||||
|
||||
virtual ~Error() = default;
|
||||
|
||||
virtual const char* what() const noexcept {
|
||||
return _msg.data();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _msg;
|
||||
};
|
||||
|
||||
class IoError : public Error {
|
||||
public:
|
||||
explicit IoError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
IoError(const IoError &) = default;
|
||||
IoError& operator=(const IoError &) = default;
|
||||
|
||||
IoError(IoError &&) = default;
|
||||
IoError& operator=(IoError &&) = default;
|
||||
|
||||
virtual ~IoError() = default;
|
||||
};
|
||||
|
||||
class TimeoutError : public IoError {
|
||||
public:
|
||||
explicit TimeoutError(const std::string &msg) : IoError(msg) {}
|
||||
|
||||
TimeoutError(const TimeoutError &) = default;
|
||||
TimeoutError& operator=(const TimeoutError &) = default;
|
||||
|
||||
TimeoutError(TimeoutError &&) = default;
|
||||
TimeoutError& operator=(TimeoutError &&) = default;
|
||||
|
||||
virtual ~TimeoutError() = default;
|
||||
};
|
||||
|
||||
class ClosedError : public Error {
|
||||
public:
|
||||
explicit ClosedError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
ClosedError(const ClosedError &) = default;
|
||||
ClosedError& operator=(const ClosedError &) = default;
|
||||
|
||||
ClosedError(ClosedError &&) = default;
|
||||
ClosedError& operator=(ClosedError &&) = default;
|
||||
|
||||
virtual ~ClosedError() = default;
|
||||
};
|
||||
|
||||
class ProtoError : public Error {
|
||||
public:
|
||||
explicit ProtoError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
ProtoError(const ProtoError &) = default;
|
||||
ProtoError& operator=(const ProtoError &) = default;
|
||||
|
||||
ProtoError(ProtoError &&) = default;
|
||||
ProtoError& operator=(ProtoError &&) = default;
|
||||
|
||||
virtual ~ProtoError() = default;
|
||||
};
|
||||
|
||||
class OomError : public Error {
|
||||
public:
|
||||
explicit OomError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
OomError(const OomError &) = default;
|
||||
OomError& operator=(const OomError &) = default;
|
||||
|
||||
OomError(OomError &&) = default;
|
||||
OomError& operator=(OomError &&) = default;
|
||||
|
||||
virtual ~OomError() = default;
|
||||
};
|
||||
|
||||
class ReplyError : public Error {
|
||||
public:
|
||||
explicit ReplyError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
ReplyError(const ReplyError &) = default;
|
||||
ReplyError& operator=(const ReplyError &) = default;
|
||||
|
||||
ReplyError(ReplyError &&) = default;
|
||||
ReplyError& operator=(ReplyError &&) = default;
|
||||
|
||||
virtual ~ReplyError() = default;
|
||||
};
|
||||
|
||||
class WatchError : public Error {
|
||||
public:
|
||||
explicit WatchError() : Error("Watched key has been modified") {}
|
||||
|
||||
WatchError(const WatchError &) = default;
|
||||
WatchError& operator=(const WatchError &) = default;
|
||||
|
||||
WatchError(WatchError &&) = default;
|
||||
WatchError& operator=(WatchError &&) = default;
|
||||
|
||||
virtual ~WatchError() = default;
|
||||
};
|
||||
|
||||
|
||||
// MovedError and AskError are defined in shards.h
|
||||
class MovedError;
|
||||
|
||||
class AskError;
|
||||
|
||||
void throw_error(redisContext &context, const std::string &err_info);
|
||||
|
||||
void throw_error(const redisReply &reply);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_ERRORS_H
|
35
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.cpp
vendored
Normal file
35
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.cpp
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "pipeline.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
std::vector<ReplyUPtr> PipelineImpl::exec(Connection &connection, std::size_t cmd_num) {
|
||||
std::vector<ReplyUPtr> replies;
|
||||
while (cmd_num > 0) {
|
||||
replies.push_back(connection.recv());
|
||||
--cmd_num;
|
||||
}
|
||||
|
||||
return replies;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
49
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.h
vendored
Normal file
49
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.h
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_PIPELINE_H
|
||||
#define SEWENEW_REDISPLUSPLUS_PIPELINE_H
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include "connection.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class PipelineImpl {
|
||||
public:
|
||||
template <typename Cmd, typename ...Args>
|
||||
void command(Connection &connection, Cmd cmd, Args &&...args) {
|
||||
assert(!connection.broken());
|
||||
|
||||
cmd(connection, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
std::vector<ReplyUPtr> exec(Connection &connection, std::size_t cmd_num);
|
||||
|
||||
void discard(Connection &connection, std::size_t /*cmd_num*/) {
|
||||
// Reconnect to Redis to discard all commands.
|
||||
connection.reconnect();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_PIPELINE_H
|
1844
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.h
vendored
Normal file
1844
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
208
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.hpp
vendored
Normal file
208
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.hpp
vendored
Normal file
|
@ -0,0 +1,208 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
template <typename Impl>
|
||||
template <typename ...Args>
|
||||
QueuedRedis<Impl>::QueuedRedis(const ConnectionSPtr &connection, Args &&...args) :
|
||||
_connection(connection),
|
||||
_impl(std::forward<Args>(args)...) {
|
||||
assert(_connection);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
Redis QueuedRedis<Impl>::redis() {
|
||||
return Redis(_connection);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename Cmd, typename ...Args>
|
||||
auto QueuedRedis<Impl>::command(Cmd cmd, Args &&...args)
|
||||
-> typename std::enable_if<!std::is_convertible<Cmd, StringView>::value,
|
||||
QueuedRedis<Impl>&>::type {
|
||||
try {
|
||||
_sanity_check();
|
||||
|
||||
_impl.command(*_connection, cmd, std::forward<Args>(args)...);
|
||||
|
||||
++_cmd_num;
|
||||
} catch (const Error &e) {
|
||||
_invalidate();
|
||||
throw;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename ...Args>
|
||||
QueuedRedis<Impl>& QueuedRedis<Impl>::command(const StringView &cmd_name, Args &&...args) {
|
||||
auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) {
|
||||
CmdArgs cmd_args;
|
||||
cmd_args.append(cmd_name, std::forward<Args>(args)...);
|
||||
connection.send(cmd_args);
|
||||
};
|
||||
|
||||
return command(cmd, cmd_name, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename Input>
|
||||
auto QueuedRedis<Impl>::command(Input first, Input last)
|
||||
-> typename std::enable_if<IsIter<Input>::value, QueuedRedis<Impl>&>::type {
|
||||
if (first == last) {
|
||||
throw Error("command: empty range");
|
||||
}
|
||||
|
||||
auto cmd = [](Connection &connection, Input first, Input last) {
|
||||
CmdArgs cmd_args;
|
||||
while (first != last) {
|
||||
cmd_args.append(*first);
|
||||
++first;
|
||||
}
|
||||
connection.send(cmd_args);
|
||||
};
|
||||
|
||||
return command(cmd, first, last);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
QueuedReplies QueuedRedis<Impl>::exec() {
|
||||
try {
|
||||
_sanity_check();
|
||||
|
||||
auto replies = _impl.exec(*_connection, _cmd_num);
|
||||
|
||||
_rewrite_replies(replies);
|
||||
|
||||
_reset();
|
||||
|
||||
return QueuedReplies(std::move(replies));
|
||||
} catch (const Error &e) {
|
||||
_invalidate();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::discard() {
|
||||
try {
|
||||
_sanity_check();
|
||||
|
||||
_impl.discard(*_connection, _cmd_num);
|
||||
|
||||
_reset();
|
||||
} catch (const Error &e) {
|
||||
_invalidate();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::_sanity_check() const {
|
||||
if (!_valid) {
|
||||
throw Error("Not in valid state");
|
||||
}
|
||||
|
||||
if (_connection->broken()) {
|
||||
throw Error("Connection is broken");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
inline void QueuedRedis<Impl>::_reset() {
|
||||
_cmd_num = 0;
|
||||
|
||||
_set_cmd_indexes.clear();
|
||||
|
||||
_georadius_cmd_indexes.clear();
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::_invalidate() {
|
||||
_valid = false;
|
||||
|
||||
_reset();
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::_rewrite_replies(std::vector<ReplyUPtr> &replies) const {
|
||||
_rewrite_replies(_set_cmd_indexes, reply::rewrite_set_reply, replies);
|
||||
|
||||
_rewrite_replies(_georadius_cmd_indexes, reply::rewrite_georadius_reply, replies);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename Func>
|
||||
void QueuedRedis<Impl>::_rewrite_replies(const std::vector<std::size_t> &indexes,
|
||||
Func rewriter,
|
||||
std::vector<ReplyUPtr> &replies) const {
|
||||
for (auto idx : indexes) {
|
||||
assert(idx < replies.size());
|
||||
|
||||
auto &reply = replies[idx];
|
||||
|
||||
assert(reply);
|
||||
|
||||
rewriter(*reply);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::size_t QueuedReplies::size() const {
|
||||
return _replies.size();
|
||||
}
|
||||
|
||||
inline redisReply& QueuedReplies::get(std::size_t idx) {
|
||||
_index_check(idx);
|
||||
|
||||
auto &reply = _replies[idx];
|
||||
|
||||
assert(reply);
|
||||
|
||||
return *reply;
|
||||
}
|
||||
|
||||
template <typename Result>
|
||||
inline Result QueuedReplies::get(std::size_t idx) {
|
||||
auto &reply = get(idx);
|
||||
|
||||
return reply::parse<Result>(reply);
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
inline void QueuedReplies::get(std::size_t idx, Output output) {
|
||||
auto &reply = get(idx);
|
||||
|
||||
reply::to_array(reply, output);
|
||||
}
|
||||
|
||||
inline void QueuedReplies::_index_check(std::size_t idx) const {
|
||||
if (idx >= size()) {
|
||||
throw Error("Out of range");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP
|
25
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis++.h
vendored
Normal file
25
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis++.h
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H
|
||||
|
||||
#include "redis.h"
|
||||
#include "redis_cluster.h"
|
||||
#include "queued_redis.h"
|
||||
#include "sentinel.h"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H
|
882
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.cpp
vendored
Normal file
882
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.cpp
vendored
Normal file
|
@ -0,0 +1,882 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "redis.h"
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "command.h"
|
||||
#include "errors.h"
|
||||
#include "queued_redis.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
Redis::Redis(const std::string &uri) : Redis(ConnectionOptions(uri)) {}
|
||||
|
||||
Redis::Redis(const ConnectionSPtr &connection) : _connection(connection) {
|
||||
assert(_connection);
|
||||
}
|
||||
|
||||
Pipeline Redis::pipeline() {
|
||||
return Pipeline(std::make_shared<Connection>(_pool.create()));
|
||||
}
|
||||
|
||||
Transaction Redis::transaction(bool piped) {
|
||||
return Transaction(std::make_shared<Connection>(_pool.create()), piped);
|
||||
}
|
||||
|
||||
Subscriber Redis::subscriber() {
|
||||
return Subscriber(_pool.create());
|
||||
}
|
||||
|
||||
// CONNECTION commands.
|
||||
|
||||
void Redis::auth(const StringView &password) {
|
||||
auto reply = command(cmd::auth, password);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::echo(const StringView &msg) {
|
||||
auto reply = command(cmd::echo, msg);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::ping() {
|
||||
auto reply = command<void (*)(Connection &)>(cmd::ping);
|
||||
|
||||
return reply::to_status(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::ping(const StringView &msg) {
|
||||
auto reply = command<void (*)(Connection &, const StringView &)>(cmd::ping, msg);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
void Redis::swapdb(long long idx1, long long idx2) {
|
||||
auto reply = command(cmd::swapdb, idx1, idx2);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// SERVER commands.
|
||||
|
||||
void Redis::bgrewriteaof() {
|
||||
auto reply = command(cmd::bgrewriteaof);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::bgsave() {
|
||||
auto reply = command(cmd::bgsave);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::dbsize() {
|
||||
auto reply = command(cmd::dbsize);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::flushall(bool async) {
|
||||
auto reply = command(cmd::flushall, async);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::flushdb(bool async) {
|
||||
auto reply = command(cmd::flushdb, async);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::info() {
|
||||
auto reply = command<void (*)(Connection &)>(cmd::info);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::info(const StringView §ion) {
|
||||
auto reply = command<void (*)(Connection &, const StringView &)>(cmd::info, section);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lastsave() {
|
||||
auto reply = command(cmd::lastsave);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::save() {
|
||||
auto reply = command(cmd::save);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// KEY commands.
|
||||
|
||||
long long Redis::del(const StringView &key) {
|
||||
auto reply = command(cmd::del, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::dump(const StringView &key) {
|
||||
auto reply = command(cmd::dump, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::exists(const StringView &key) {
|
||||
auto reply = command(cmd::exists, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::expire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::expire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::expireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::expireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::move(const StringView &key, long long db) {
|
||||
auto reply = command(cmd::move, key, db);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::persist(const StringView &key) {
|
||||
auto reply = command(cmd::persist, key);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::pexpire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::pexpire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::pexpireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::pexpireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::pttl(const StringView &key) {
|
||||
auto reply = command(cmd::pttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::randomkey() {
|
||||
auto reply = command(cmd::randomkey);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
void Redis::rename(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::rename, key, newkey);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::renamenx(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::renamenx, key, newkey);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void Redis::restore(const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
bool replace) {
|
||||
auto reply = command(cmd::restore, key, val, ttl, replace);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::touch(const StringView &key) {
|
||||
auto reply = command(cmd::touch, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::ttl(const StringView &key) {
|
||||
auto reply = command(cmd::ttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::type(const StringView &key) {
|
||||
auto reply = command(cmd::type, key);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::unlink(const StringView &key) {
|
||||
auto reply = command(cmd::unlink, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::wait(long long numslaves, long long timeout) {
|
||||
auto reply = command(cmd::wait, numslaves, timeout);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// STRING commands.
|
||||
|
||||
long long Redis::append(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::append, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::bitcount(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::bitcount, key, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::bitop(BitOp op, const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::bitop, op, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::bitpos(const StringView &key,
|
||||
long long bit,
|
||||
long long start,
|
||||
long long end) {
|
||||
auto reply = command(cmd::bitpos, key, bit, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::decr(const StringView &key) {
|
||||
auto reply = command(cmd::decr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::decrby(const StringView &key, long long decrement) {
|
||||
auto reply = command(cmd::decrby, key, decrement);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::get(const StringView &key) {
|
||||
auto reply = command(cmd::get, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::getbit(const StringView &key, long long offset) {
|
||||
auto reply = command(cmd::getbit, key, offset);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::getrange(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::getrange, key, start, end);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::getset(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::getset, key, val);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::incr(const StringView &key) {
|
||||
auto reply = command(cmd::incr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::incrby(const StringView &key, long long increment) {
|
||||
auto reply = command(cmd::incrby, key, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double Redis::incrbyfloat(const StringView &key, double increment) {
|
||||
auto reply = command(cmd::incrbyfloat, key, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
void Redis::psetex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::psetex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::set(const StringView &key,
|
||||
const StringView &val,
|
||||
const std::chrono::milliseconds &ttl,
|
||||
UpdateType type) {
|
||||
auto reply = command(cmd::set, key, val, ttl.count(), type);
|
||||
|
||||
reply::rewrite_set_reply(*reply);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void Redis::setex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::setex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::setnx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::setnx, key, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::setrange(const StringView &key, long long offset, const StringView &val) {
|
||||
auto reply = command(cmd::setrange, key, offset, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::strlen(const StringView &key) {
|
||||
auto reply = command(cmd::strlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// LIST commands.
|
||||
|
||||
OptionalStringPair Redis::blpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::blpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair Redis::blpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return blpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalStringPair Redis::brpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::brpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair Redis::brpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return brpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalString Redis::brpoplpush(const StringView &source,
|
||||
const StringView &destination,
|
||||
long long timeout) {
|
||||
auto reply = command(cmd::brpoplpush, source, destination, timeout);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::lindex(const StringView &key, long long index) {
|
||||
auto reply = command(cmd::lindex, key, index);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::linsert(const StringView &key,
|
||||
InsertPosition position,
|
||||
const StringView &pivot,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::linsert, key, position, pivot, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::llen(const StringView &key) {
|
||||
auto reply = command(cmd::llen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::lpop(const StringView &key) {
|
||||
auto reply = command(cmd::lpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lrem(const StringView &key, long long count, const StringView &val) {
|
||||
auto reply = command(cmd::lrem, key, count, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::lset(const StringView &key, long long index, const StringView &val) {
|
||||
auto reply = command(cmd::lset, key, index, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::ltrim(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::ltrim, key, start, stop);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::rpop(const StringView &key) {
|
||||
auto reply = command(cmd::rpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::rpoplpush(const StringView &source, const StringView &destination) {
|
||||
auto reply = command(cmd::rpoplpush, source, destination);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::rpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::rpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::hdel(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hdel, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hexists(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hexists, key, field);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::hget(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hget, key, field);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::hincrby(const StringView &key, const StringView &field, long long increment) {
|
||||
auto reply = command(cmd::hincrby, key, field, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double Redis::hincrbyfloat(const StringView &key, const StringView &field, double increment) {
|
||||
auto reply = command(cmd::hincrbyfloat, key, field, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::hlen(const StringView &key) {
|
||||
auto reply = command(cmd::hlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hset(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hset, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hset(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hset(key, item.first, item.second);
|
||||
}
|
||||
|
||||
bool Redis::hsetnx(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hsetnx, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hsetnx(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hsetnx(key, item.first, item.second);
|
||||
}
|
||||
|
||||
long long Redis::hstrlen(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hstrlen, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SET commands.
|
||||
|
||||
long long Redis::sadd(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::scard(const StringView &key) {
|
||||
auto reply = command(cmd::scard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::sdiffstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sdiffstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::sinterstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sinterstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::sismember(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sismember, key, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::smove(const StringView &source,
|
||||
const StringView &destination,
|
||||
const StringView &member) {
|
||||
auto reply = command(cmd::smove, source, destination, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::spop(const StringView &key) {
|
||||
auto reply = command(cmd::spop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::srandmember(const StringView &key) {
|
||||
auto reply = command(cmd::srandmember, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::srem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::srem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::sunionstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sunionstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SORTED SET commands.
|
||||
|
||||
auto Redis::bzpopmax(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmax, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
auto Redis::bzpopmin(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmin, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zadd(const StringView &key,
|
||||
const StringView &member,
|
||||
double score,
|
||||
UpdateType type,
|
||||
bool changed) {
|
||||
auto reply = command(cmd::zadd, key, member, score, type, changed);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zcard(const StringView &key) {
|
||||
auto reply = command(cmd::zcard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double Redis::zincrby(const StringView &key, double increment, const StringView &member) {
|
||||
auto reply = command(cmd::zincrby, key, increment, member);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zinterstore(const StringView &destination, const StringView &key, double weight) {
|
||||
auto reply = command(cmd::zinterstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> Redis::zpopmax(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmax, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> Redis::zpopmin(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmin, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::zrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zrem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zremrangebyrank(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::zremrangebyrank, key, start, stop);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::zrevrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrevrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble Redis::zscore(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zscore, key, member);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zunionstore(const StringView &destination, const StringView &key, double weight) {
|
||||
auto reply = command(cmd::zunionstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// HYPERLOGLOG commands.
|
||||
|
||||
bool Redis::pfadd(const StringView &key, const StringView &element) {
|
||||
auto reply = command(cmd::pfadd, key, element);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::pfcount(const StringView &key) {
|
||||
auto reply = command(cmd::pfcount, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::pfmerge(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::pfmerge, destination, key);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// GEO commands.
|
||||
|
||||
long long Redis::geoadd(const StringView &key,
|
||||
const std::tuple<StringView, double, double> &member) {
|
||||
auto reply = command(cmd::geoadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble Redis::geodist(const StringView &key,
|
||||
const StringView &member1,
|
||||
const StringView &member2,
|
||||
GeoUnit unit) {
|
||||
auto reply = command(cmd::geodist, key, member1, member2, unit);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::georadius(const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadius_store,
|
||||
key,
|
||||
loc,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::georadiusbymember(const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadiusbymember_store,
|
||||
key,
|
||||
member,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
// SCRIPTING commands.
|
||||
|
||||
void Redis::script_flush() {
|
||||
auto reply = command(cmd::script_flush);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::script_kill() {
|
||||
auto reply = command(cmd::script_kill);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::script_load(const StringView &script) {
|
||||
auto reply = command(cmd::script_load, script);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
// PUBSUB commands.
|
||||
|
||||
long long Redis::publish(const StringView &channel, const StringView &message) {
|
||||
auto reply = command(cmd::publish, channel, message);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// Transaction commands.
|
||||
|
||||
void Redis::watch(const StringView &key) {
|
||||
auto reply = command(cmd::watch, key);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// Stream commands.
|
||||
|
||||
long long Redis::xack(const StringView &key, const StringView &group, const StringView &id) {
|
||||
auto reply = command(cmd::xack, key, group, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xdel(const StringView &key, const StringView &id) {
|
||||
auto reply = command(cmd::xdel, key, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::xgroup_create(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &id,
|
||||
bool mkstream) {
|
||||
auto reply = command(cmd::xgroup_create, key, group, id, mkstream);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::xgroup_setid(const StringView &key, const StringView &group, const StringView &id) {
|
||||
auto reply = command(cmd::xgroup_setid, key, group, id);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xgroup_destroy(const StringView &key, const StringView &group) {
|
||||
auto reply = command(cmd::xgroup_destroy, key, group);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xgroup_delconsumer(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &consumer) {
|
||||
auto reply = command(cmd::xgroup_delconsumer, key, group, consumer);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xlen(const StringView &key) {
|
||||
auto reply = command(cmd::xlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xtrim(const StringView &key, long long count, bool approx) {
|
||||
auto reply = command(cmd::xtrim, key, count, approx);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1523
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.h
vendored
Normal file
1523
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1365
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.hpp
vendored
Normal file
1365
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
769
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.cpp
vendored
Normal file
769
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.cpp
vendored
Normal file
|
@ -0,0 +1,769 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "redis_cluster.h"
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "command.h"
|
||||
#include "errors.h"
|
||||
#include "queued_redis.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
RedisCluster::RedisCluster(const std::string &uri) : RedisCluster(ConnectionOptions(uri)) {}
|
||||
|
||||
Redis RedisCluster::redis(const StringView &hash_tag) {
|
||||
auto opts = _pool.connection_options(hash_tag);
|
||||
return Redis(std::make_shared<Connection>(opts));
|
||||
}
|
||||
|
||||
Pipeline RedisCluster::pipeline(const StringView &hash_tag) {
|
||||
auto opts = _pool.connection_options(hash_tag);
|
||||
return Pipeline(std::make_shared<Connection>(opts));
|
||||
}
|
||||
|
||||
Transaction RedisCluster::transaction(const StringView &hash_tag, bool piped) {
|
||||
auto opts = _pool.connection_options(hash_tag);
|
||||
return Transaction(std::make_shared<Connection>(opts), piped);
|
||||
}
|
||||
|
||||
Subscriber RedisCluster::subscriber() {
|
||||
auto opts = _pool.connection_options();
|
||||
return Subscriber(Connection(opts));
|
||||
}
|
||||
|
||||
// KEY commands.
|
||||
|
||||
long long RedisCluster::del(const StringView &key) {
|
||||
auto reply = command(cmd::del, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::dump(const StringView &key) {
|
||||
auto reply = command(cmd::dump, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::exists(const StringView &key) {
|
||||
auto reply = command(cmd::exists, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::expire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::expire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::expireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::expireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::persist(const StringView &key) {
|
||||
auto reply = command(cmd::persist, key);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::pexpire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::pexpire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::pexpireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::pexpireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::pttl(const StringView &key) {
|
||||
auto reply = command(cmd::pttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::rename(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::rename, key, newkey);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::renamenx(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::renamenx, key, newkey);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::restore(const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
bool replace) {
|
||||
auto reply = command(cmd::restore, key, val, ttl, replace);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::touch(const StringView &key) {
|
||||
auto reply = command(cmd::touch, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::ttl(const StringView &key) {
|
||||
auto reply = command(cmd::ttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string RedisCluster::type(const StringView &key) {
|
||||
auto reply = command(cmd::type, key);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::unlink(const StringView &key) {
|
||||
auto reply = command(cmd::unlink, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// STRING commands.
|
||||
|
||||
long long RedisCluster::append(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::append, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::bitcount(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::bitcount, key, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::bitop(BitOp op, const StringView &destination, const StringView &key) {
|
||||
auto reply = _command(cmd::bitop, destination, op, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::bitpos(const StringView &key,
|
||||
long long bit,
|
||||
long long start,
|
||||
long long end) {
|
||||
auto reply = command(cmd::bitpos, key, bit, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::decr(const StringView &key) {
|
||||
auto reply = command(cmd::decr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::decrby(const StringView &key, long long decrement) {
|
||||
auto reply = command(cmd::decrby, key, decrement);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::get(const StringView &key) {
|
||||
auto reply = command(cmd::get, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::getbit(const StringView &key, long long offset) {
|
||||
auto reply = command(cmd::getbit, key, offset);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string RedisCluster::getrange(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::getrange, key, start, end);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::getset(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::getset, key, val);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::incr(const StringView &key) {
|
||||
auto reply = command(cmd::incr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::incrby(const StringView &key, long long increment) {
|
||||
auto reply = command(cmd::incrby, key, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double RedisCluster::incrbyfloat(const StringView &key, double increment) {
|
||||
auto reply = command(cmd::incrbyfloat, key, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::psetex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::psetex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::set(const StringView &key,
|
||||
const StringView &val,
|
||||
const std::chrono::milliseconds &ttl,
|
||||
UpdateType type) {
|
||||
auto reply = command(cmd::set, key, val, ttl.count(), type);
|
||||
|
||||
reply::rewrite_set_reply(*reply);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::setex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::setex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::setnx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::setnx, key, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::setrange(const StringView &key, long long offset, const StringView &val) {
|
||||
auto reply = command(cmd::setrange, key, offset, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::strlen(const StringView &key) {
|
||||
auto reply = command(cmd::strlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// LIST commands.
|
||||
|
||||
OptionalStringPair RedisCluster::blpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::blpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair RedisCluster::blpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return blpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalStringPair RedisCluster::brpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::brpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair RedisCluster::brpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return brpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::brpoplpush(const StringView &source,
|
||||
const StringView &destination,
|
||||
long long timeout) {
|
||||
auto reply = command(cmd::brpoplpush, source, destination, timeout);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::lindex(const StringView &key, long long index) {
|
||||
auto reply = command(cmd::lindex, key, index);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::linsert(const StringView &key,
|
||||
InsertPosition position,
|
||||
const StringView &pivot,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::linsert, key, position, pivot, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::llen(const StringView &key) {
|
||||
auto reply = command(cmd::llen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::lpop(const StringView &key) {
|
||||
auto reply = command(cmd::lpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::lpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::lpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::lrem(const StringView &key, long long count, const StringView &val) {
|
||||
auto reply = command(cmd::lrem, key, count, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::lset(const StringView &key, long long index, const StringView &val) {
|
||||
auto reply = command(cmd::lset, key, index, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::ltrim(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::ltrim, key, start, stop);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::rpop(const StringView &key) {
|
||||
auto reply = command(cmd::rpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::rpoplpush(const StringView &source, const StringView &destination) {
|
||||
auto reply = command(cmd::rpoplpush, source, destination);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::rpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::rpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::hdel(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hdel, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hexists(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hexists, key, field);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::hget(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hget, key, field);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::hincrby(const StringView &key, const StringView &field, long long increment) {
|
||||
auto reply = command(cmd::hincrby, key, field, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double RedisCluster::hincrbyfloat(const StringView &key, const StringView &field, double increment) {
|
||||
auto reply = command(cmd::hincrbyfloat, key, field, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::hlen(const StringView &key) {
|
||||
auto reply = command(cmd::hlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hset(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hset, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hset(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hset(key, item.first, item.second);
|
||||
}
|
||||
|
||||
bool RedisCluster::hsetnx(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hsetnx, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hsetnx(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hsetnx(key, item.first, item.second);
|
||||
}
|
||||
|
||||
long long RedisCluster::hstrlen(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hstrlen, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SET commands.
|
||||
|
||||
long long RedisCluster::sadd(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::scard(const StringView &key) {
|
||||
auto reply = command(cmd::scard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::sdiffstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sdiffstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::sinterstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sinterstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::sismember(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sismember, key, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::smove(const StringView &source,
|
||||
const StringView &destination,
|
||||
const StringView &member) {
|
||||
auto reply = command(cmd::smove, source, destination, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::spop(const StringView &key) {
|
||||
auto reply = command(cmd::spop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::srandmember(const StringView &key) {
|
||||
auto reply = command(cmd::srandmember, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::srem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::srem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::sunionstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sunionstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SORTED SET commands.
|
||||
|
||||
auto RedisCluster::bzpopmax(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmax, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
auto RedisCluster::bzpopmin(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmin, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zadd(const StringView &key,
|
||||
const StringView &member,
|
||||
double score,
|
||||
UpdateType type,
|
||||
bool changed) {
|
||||
auto reply = command(cmd::zadd, key, member, score, type, changed);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zcard(const StringView &key) {
|
||||
auto reply = command(cmd::zcard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double RedisCluster::zincrby(const StringView &key, double increment, const StringView &member) {
|
||||
auto reply = command(cmd::zincrby, key, increment, member);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zinterstore(const StringView &destination,
|
||||
const StringView &key,
|
||||
double weight) {
|
||||
auto reply = command(cmd::zinterstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> RedisCluster::zpopmax(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmax, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> RedisCluster::zpopmin(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmin, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::zrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zrem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zremrangebyrank(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::zremrangebyrank, key, start, stop);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::zrevrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrevrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble RedisCluster::zscore(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zscore, key, member);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zunionstore(const StringView &destination,
|
||||
const StringView &key,
|
||||
double weight) {
|
||||
auto reply = command(cmd::zunionstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// HYPERLOGLOG commands.
|
||||
|
||||
bool RedisCluster::pfadd(const StringView &key, const StringView &element) {
|
||||
auto reply = command(cmd::pfadd, key, element);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::pfcount(const StringView &key) {
|
||||
auto reply = command(cmd::pfcount, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::pfmerge(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::pfmerge, destination, key);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// GEO commands.
|
||||
|
||||
long long RedisCluster::geoadd(const StringView &key,
|
||||
const std::tuple<StringView, double, double> &member) {
|
||||
auto reply = command(cmd::geoadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble RedisCluster::geodist(const StringView &key,
|
||||
const StringView &member1,
|
||||
const StringView &member2,
|
||||
GeoUnit unit) {
|
||||
auto reply = command(cmd::geodist, key, member1, member2, unit);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::georadius(const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadius_store,
|
||||
key,
|
||||
loc,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::georadiusbymember(const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadiusbymember_store,
|
||||
key,
|
||||
member,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
// PUBSUB commands.
|
||||
|
||||
long long RedisCluster::publish(const StringView &channel, const StringView &message) {
|
||||
auto reply = command(cmd::publish, channel, message);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// Stream commands.
|
||||
|
||||
long long RedisCluster::xack(const StringView &key, const StringView &group, const StringView &id) {
|
||||
auto reply = command(cmd::xack, key, group, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xdel(const StringView &key, const StringView &id) {
|
||||
auto reply = command(cmd::xdel, key, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::xgroup_create(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &id,
|
||||
bool mkstream) {
|
||||
auto reply = command(cmd::xgroup_create, key, group, id, mkstream);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::xgroup_setid(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &id) {
|
||||
auto reply = command(cmd::xgroup_setid, key, group, id);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xgroup_destroy(const StringView &key, const StringView &group) {
|
||||
auto reply = command(cmd::xgroup_destroy, key, group);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xgroup_delconsumer(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &consumer) {
|
||||
auto reply = command(cmd::xgroup_delconsumer, key, group, consumer);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xlen(const StringView &key) {
|
||||
auto reply = command(cmd::xlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xtrim(const StringView &key, long long count, bool approx) {
|
||||
auto reply = command(cmd::xtrim, key, count, approx);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::_asking(Connection &connection) {
|
||||
// Send ASKING command.
|
||||
connection.send("ASKING");
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1395
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.h
vendored
Normal file
1395
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1415
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.hpp
vendored
Normal file
1415
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
150
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/reply.cpp
vendored
Normal file
150
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/reply.cpp
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "reply.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace reply {
|
||||
|
||||
std::string to_status(redisReply &reply) {
|
||||
if (!reply::is_status(reply)) {
|
||||
throw ProtoError("Expect STATUS reply");
|
||||
}
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw ProtoError("A null status reply");
|
||||
}
|
||||
|
||||
// Old version hiredis' *redisReply::len* is of type int.
|
||||
// So we CANNOT have something like: *return {reply.str, reply.len}*.
|
||||
return std::string(reply.str, reply.len);
|
||||
}
|
||||
|
||||
std::string parse(ParseTag<std::string>, redisReply &reply) {
|
||||
if (!reply::is_string(reply) && !reply::is_status(reply)) {
|
||||
throw ProtoError("Expect STRING reply");
|
||||
}
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw ProtoError("A null string reply");
|
||||
}
|
||||
|
||||
// Old version hiredis' *redisReply::len* is of type int.
|
||||
// So we CANNOT have something like: *return {reply.str, reply.len}*.
|
||||
return std::string(reply.str, reply.len);
|
||||
}
|
||||
|
||||
long long parse(ParseTag<long long>, redisReply &reply) {
|
||||
if (!reply::is_integer(reply)) {
|
||||
throw ProtoError("Expect INTEGER reply");
|
||||
}
|
||||
|
||||
return reply.integer;
|
||||
}
|
||||
|
||||
double parse(ParseTag<double>, redisReply &reply) {
|
||||
return std::stod(parse<std::string>(reply));
|
||||
}
|
||||
|
||||
bool parse(ParseTag<bool>, redisReply &reply) {
|
||||
auto ret = parse<long long>(reply);
|
||||
|
||||
if (ret == 1) {
|
||||
return true;
|
||||
} else if (ret == 0) {
|
||||
return false;
|
||||
} else {
|
||||
throw ProtoError("Invalid bool reply: " + std::to_string(ret));
|
||||
}
|
||||
}
|
||||
|
||||
void parse(ParseTag<void>, redisReply &reply) {
|
||||
if (!reply::is_status(reply)) {
|
||||
throw ProtoError("Expect STATUS reply");
|
||||
}
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw ProtoError("A null status reply");
|
||||
}
|
||||
|
||||
static const std::string OK = "OK";
|
||||
|
||||
// Old version hiredis' *redisReply::len* is of type int.
|
||||
// So we have to cast it to an unsigned int.
|
||||
if (static_cast<std::size_t>(reply.len) != OK.size()
|
||||
|| OK.compare(0, OK.size(), reply.str, reply.len) != 0) {
|
||||
throw ProtoError("NOT ok status reply: " + reply::to_status(reply));
|
||||
}
|
||||
}
|
||||
|
||||
void rewrite_set_reply(redisReply &reply) {
|
||||
if (is_nil(reply)) {
|
||||
// Failed to set, and make it a FALSE reply.
|
||||
reply.type = REDIS_REPLY_INTEGER;
|
||||
reply.integer = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a "OK" status reply.
|
||||
reply::parse<void>(reply);
|
||||
|
||||
assert(is_status(reply) && reply.str != nullptr);
|
||||
|
||||
free(reply.str);
|
||||
|
||||
// Make it a TRUE reply.
|
||||
reply.type = REDIS_REPLY_INTEGER;
|
||||
reply.integer = 1;
|
||||
}
|
||||
|
||||
void rewrite_georadius_reply(redisReply &reply) {
|
||||
if (is_array(reply) && reply.element == nullptr) {
|
||||
// Make it a nil reply.
|
||||
reply.type = REDIS_REPLY_NIL;
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
bool is_flat_array(redisReply &reply) {
|
||||
assert(reply::is_array(reply));
|
||||
|
||||
// Empty array reply.
|
||||
if (reply.element == nullptr || reply.elements == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *sub_reply = reply.element[0];
|
||||
|
||||
// Null element.
|
||||
if (sub_reply == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !reply::is_array(*sub_reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
363
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/reply.h
vendored
Normal file
363
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/reply.h
vendored
Normal file
|
@ -0,0 +1,363 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_REPLY_H
|
||||
#define SEWENEW_REDISPLUSPLUS_REPLY_H
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "errors.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
struct ReplyDeleter {
|
||||
void operator()(redisReply *reply) const {
|
||||
if (reply != nullptr) {
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using ReplyUPtr = std::unique_ptr<redisReply, ReplyDeleter>;
|
||||
|
||||
namespace reply {
|
||||
|
||||
template <typename T>
|
||||
struct ParseTag {};
|
||||
|
||||
template <typename T>
|
||||
inline T parse(redisReply &reply) {
|
||||
return parse(ParseTag<T>(), reply);
|
||||
}
|
||||
|
||||
void parse(ParseTag<void>, redisReply &reply);
|
||||
|
||||
std::string parse(ParseTag<std::string>, redisReply &reply);
|
||||
|
||||
long long parse(ParseTag<long long>, redisReply &reply);
|
||||
|
||||
double parse(ParseTag<double>, redisReply &reply);
|
||||
|
||||
bool parse(ParseTag<bool>, redisReply &reply);
|
||||
|
||||
template <typename T>
|
||||
Optional<T> parse(ParseTag<Optional<T>>, redisReply &reply);
|
||||
|
||||
template <typename T, typename U>
|
||||
std::pair<T, U> parse(ParseTag<std::pair<T, U>>, redisReply &reply);
|
||||
|
||||
template <typename ...Args>
|
||||
std::tuple<Args...> parse(ParseTag<std::tuple<Args...>>, redisReply &reply);
|
||||
|
||||
template <typename T, typename std::enable_if<IsSequenceContainer<T>::value, int>::type = 0>
|
||||
T parse(ParseTag<T>, redisReply &reply);
|
||||
|
||||
template <typename T, typename std::enable_if<IsAssociativeContainer<T>::value, int>::type = 0>
|
||||
T parse(ParseTag<T>, redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
long long parse_scan_reply(redisReply &reply, Output output);
|
||||
|
||||
inline bool is_error(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_ERROR;
|
||||
}
|
||||
|
||||
inline bool is_nil(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_NIL;
|
||||
}
|
||||
|
||||
inline bool is_string(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_STRING;
|
||||
}
|
||||
|
||||
inline bool is_status(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_STATUS;
|
||||
}
|
||||
|
||||
inline bool is_integer(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_INTEGER;
|
||||
}
|
||||
|
||||
inline bool is_array(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_ARRAY;
|
||||
}
|
||||
|
||||
std::string to_status(redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
void to_array(redisReply &reply, Output output);
|
||||
|
||||
// Rewrite set reply to bool type
|
||||
void rewrite_set_reply(redisReply &reply);
|
||||
|
||||
// Rewrite georadius reply to OptionalLongLong type
|
||||
void rewrite_georadius_reply(redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
auto parse_xpending_reply(redisReply &reply, Output output)
|
||||
-> std::tuple<long long, OptionalString, OptionalString>;
|
||||
|
||||
}
|
||||
|
||||
// Inline implementations.
|
||||
|
||||
namespace reply {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Output>
|
||||
void to_array(redisReply &reply, Output output) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply.element == nullptr) {
|
||||
// Empty array.
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0; idx != reply.elements; ++idx) {
|
||||
auto *sub_reply = reply.element[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null array element reply");
|
||||
}
|
||||
|
||||
*output = parse<typename IterType<Output>::type>(*sub_reply);
|
||||
|
||||
++output;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_flat_array(redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
void to_flat_array(redisReply &reply, Output output) {
|
||||
if (reply.element == nullptr) {
|
||||
// Empty array.
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements % 2 != 0) {
|
||||
throw ProtoError("Not string pair array reply");
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0; idx != reply.elements; idx += 2) {
|
||||
auto *key_reply = reply.element[idx];
|
||||
auto *val_reply = reply.element[idx + 1];
|
||||
if (key_reply == nullptr || val_reply == nullptr) {
|
||||
throw ProtoError("Null string array reply");
|
||||
}
|
||||
|
||||
using Pair = typename IterType<Output>::type;
|
||||
using FirstType = typename std::decay<typename Pair::first_type>::type;
|
||||
using SecondType = typename std::decay<typename Pair::second_type>::type;
|
||||
*output = std::make_pair(parse<FirstType>(*key_reply),
|
||||
parse<SecondType>(*val_reply));
|
||||
|
||||
++output;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
void to_array(std::true_type, redisReply &reply, Output output) {
|
||||
if (is_flat_array(reply)) {
|
||||
to_flat_array(reply, output);
|
||||
} else {
|
||||
to_array(reply, output);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
void to_array(std::false_type, redisReply &reply, Output output) {
|
||||
to_array(reply, output);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::tuple<T> parse_tuple(redisReply **reply, std::size_t idx) {
|
||||
assert(reply != nullptr);
|
||||
|
||||
auto *sub_reply = reply[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null reply");
|
||||
}
|
||||
|
||||
return std::make_tuple(parse<T>(*sub_reply));
|
||||
}
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
auto parse_tuple(redisReply **reply, std::size_t idx) ->
|
||||
typename std::enable_if<sizeof...(Args) != 0, std::tuple<T, Args...>>::type {
|
||||
assert(reply != nullptr);
|
||||
|
||||
return std::tuple_cat(parse_tuple<T>(reply, idx),
|
||||
parse_tuple<Args...>(reply, idx + 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Optional<T> parse(ParseTag<Optional<T>>, redisReply &reply) {
|
||||
if (reply::is_nil(reply)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return Optional<T>(parse<T>(reply));
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
std::pair<T, U> parse(ParseTag<std::pair<T, U>>, redisReply &reply) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply.elements != 2) {
|
||||
throw ProtoError("NOT key-value PAIR reply");
|
||||
}
|
||||
|
||||
if (reply.element == nullptr) {
|
||||
throw ProtoError("Null PAIR reply");
|
||||
}
|
||||
|
||||
auto *first = reply.element[0];
|
||||
auto *second = reply.element[1];
|
||||
if (first == nullptr || second == nullptr) {
|
||||
throw ProtoError("Null pair reply");
|
||||
}
|
||||
|
||||
return std::make_pair(parse<typename std::decay<T>::type>(*first),
|
||||
parse<typename std::decay<U>::type>(*second));
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
std::tuple<Args...> parse(ParseTag<std::tuple<Args...>>, redisReply &reply) {
|
||||
constexpr auto size = sizeof...(Args);
|
||||
|
||||
static_assert(size > 0, "DO NOT support parsing tuple with 0 element");
|
||||
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply.elements != size) {
|
||||
throw ProtoError("Expect tuple reply with " + std::to_string(size) + "elements");
|
||||
}
|
||||
|
||||
if (reply.element == nullptr) {
|
||||
throw ProtoError("Null TUPLE reply");
|
||||
}
|
||||
|
||||
return detail::parse_tuple<Args...>(reply.element, 0);
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<IsSequenceContainer<T>::value, int>::type>
|
||||
T parse(ParseTag<T>, redisReply &reply) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
T container;
|
||||
|
||||
to_array(reply, std::back_inserter(container));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<IsAssociativeContainer<T>::value, int>::type>
|
||||
T parse(ParseTag<T>, redisReply &reply) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
T container;
|
||||
|
||||
to_array(reply, std::inserter(container, container.end()));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
long long parse_scan_reply(redisReply &reply, Output output) {
|
||||
if (reply.elements != 2 || reply.element == nullptr) {
|
||||
throw ProtoError("Invalid scan reply");
|
||||
}
|
||||
|
||||
auto *cursor_reply = reply.element[0];
|
||||
auto *data_reply = reply.element[1];
|
||||
if (cursor_reply == nullptr || data_reply == nullptr) {
|
||||
throw ProtoError("Invalid cursor reply or data reply");
|
||||
}
|
||||
|
||||
auto cursor_str = reply::parse<std::string>(*cursor_reply);
|
||||
auto new_cursor = 0;
|
||||
try {
|
||||
new_cursor = std::stoll(cursor_str);
|
||||
} catch (const std::exception &e) {
|
||||
throw ProtoError("Invalid cursor reply: " + cursor_str);
|
||||
}
|
||||
|
||||
reply::to_array(*data_reply, output);
|
||||
|
||||
return new_cursor;
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
void to_array(redisReply &reply, Output output) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
detail::to_array(typename IsKvPairIter<Output>::type(), reply, output);
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
auto parse_xpending_reply(redisReply &reply, Output output)
|
||||
-> std::tuple<long long, OptionalString, OptionalString> {
|
||||
if (!is_array(reply) || reply.elements != 4) {
|
||||
throw ProtoError("expect array reply with 4 elements");
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0; idx != reply.elements; ++idx) {
|
||||
if (reply.element[idx] == nullptr) {
|
||||
throw ProtoError("null array reply");
|
||||
}
|
||||
}
|
||||
|
||||
auto num = parse<long long>(*(reply.element[0]));
|
||||
auto start = parse<OptionalString>(*(reply.element[1]));
|
||||
auto end = parse<OptionalString>(*(reply.element[2]));
|
||||
|
||||
auto &entry_reply = *(reply.element[3]);
|
||||
if (!is_nil(entry_reply)) {
|
||||
to_array(entry_reply, output);
|
||||
}
|
||||
|
||||
return std::make_tuple(num, std::move(start), std::move(end));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_REPLY_H
|
361
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.cpp
vendored
Normal file
361
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.cpp
vendored
Normal file
|
@ -0,0 +1,361 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "sentinel.h"
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include "redis.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class Sentinel::Iterator {
|
||||
public:
|
||||
Iterator(std::list<Connection> &healthy_sentinels,
|
||||
std::list<ConnectionOptions> &broken_sentinels) :
|
||||
_healthy_sentinels(healthy_sentinels),
|
||||
_broken_sentinels(broken_sentinels) {
|
||||
reset();
|
||||
}
|
||||
|
||||
Connection& next();
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::list<Connection> &_healthy_sentinels;
|
||||
|
||||
std::size_t _healthy_size = 0;
|
||||
|
||||
std::list<ConnectionOptions> &_broken_sentinels;
|
||||
|
||||
std::size_t _broken_size = 0;
|
||||
};
|
||||
|
||||
Connection& Sentinel::Iterator::next() {
|
||||
while (_healthy_size > 0) {
|
||||
assert(_healthy_sentinels.size() >= _healthy_size);
|
||||
|
||||
--_healthy_size;
|
||||
|
||||
auto &connection = _healthy_sentinels.front();
|
||||
if (connection.broken()) {
|
||||
_broken_sentinels.push_front(connection.options());
|
||||
++_broken_size;
|
||||
|
||||
_healthy_sentinels.pop_front();
|
||||
} else {
|
||||
_healthy_sentinels.splice(_healthy_sentinels.end(),
|
||||
_healthy_sentinels,
|
||||
_healthy_sentinels.begin());
|
||||
|
||||
return _healthy_sentinels.back();
|
||||
}
|
||||
}
|
||||
|
||||
while (_broken_size > 0) {
|
||||
assert(_broken_sentinels.size() >= _broken_size);
|
||||
|
||||
--_broken_size;
|
||||
|
||||
try {
|
||||
const auto &opt = _broken_sentinels.front();
|
||||
Connection connection(opt);
|
||||
_healthy_sentinels.push_back(std::move(connection));
|
||||
|
||||
_broken_sentinels.pop_front();
|
||||
|
||||
return _healthy_sentinels.back();
|
||||
} catch (const Error &e) {
|
||||
// Failed to connect to sentinel.
|
||||
_broken_sentinels.splice(_broken_sentinels.end(),
|
||||
_broken_sentinels,
|
||||
_broken_sentinels.begin());
|
||||
}
|
||||
}
|
||||
|
||||
throw StopIterError();
|
||||
}
|
||||
|
||||
void Sentinel::Iterator::reset() {
|
||||
_healthy_size = _healthy_sentinels.size();
|
||||
_broken_size = _broken_sentinels.size();
|
||||
}
|
||||
|
||||
Sentinel::Sentinel(const SentinelOptions &sentinel_opts) :
|
||||
_broken_sentinels(_parse_options(sentinel_opts)),
|
||||
_sentinel_opts(sentinel_opts) {
|
||||
if (_sentinel_opts.connect_timeout == std::chrono::milliseconds(0)
|
||||
|| _sentinel_opts.socket_timeout == std::chrono::milliseconds(0)) {
|
||||
throw Error("With sentinel, connection timeout and socket timeout cannot be 0");
|
||||
}
|
||||
}
|
||||
|
||||
Connection Sentinel::master(const std::string &master_name, const ConnectionOptions &opts) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
Iterator iter(_healthy_sentinels, _broken_sentinels);
|
||||
std::size_t retries = 0;
|
||||
while (true) {
|
||||
try {
|
||||
auto &sentinel = iter.next();
|
||||
|
||||
auto master = _get_master_addr_by_name(sentinel, master_name);
|
||||
if (!master) {
|
||||
// Try the next sentinel.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto connection = _connect_redis(*master, opts);
|
||||
if (_get_role(connection) != Role::MASTER) {
|
||||
// Retry the whole process at most SentinelOptions::max_retry times.
|
||||
++retries;
|
||||
if (retries > _sentinel_opts.max_retry) {
|
||||
throw Error("Failed to get master from sentinel");
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(_sentinel_opts.retry_interval);
|
||||
|
||||
// Restart the iteration.
|
||||
iter.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (const StopIterError &e) {
|
||||
throw;
|
||||
} catch (const Error &e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connection Sentinel::slave(const std::string &master_name, const ConnectionOptions &opts) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
Iterator iter(_healthy_sentinels, _broken_sentinels);
|
||||
std::size_t retries = 0;
|
||||
while (true) {
|
||||
try {
|
||||
auto &sentinel = iter.next();
|
||||
|
||||
auto slaves = _get_slave_addr_by_name(sentinel, master_name);
|
||||
if (slaves.empty()) {
|
||||
// Try the next sentinel.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normally slaves list is NOT very large, so there won't be a performance problem.
|
||||
auto slave_iter = std::find(slaves.begin(),
|
||||
slaves.end(),
|
||||
Node{opts.host, opts.port});
|
||||
if (slave_iter != slaves.end() && slave_iter != slaves.begin()) {
|
||||
// The given node is still a valid slave. Try it first.
|
||||
std::swap(*(slaves.begin()), *slave_iter);
|
||||
}
|
||||
|
||||
for (const auto &slave : slaves) {
|
||||
try {
|
||||
auto connection = _connect_redis(slave, opts);
|
||||
if (_get_role(connection) != Role::SLAVE) {
|
||||
// Retry the whole process at most SentinelOptions::max_retry times.
|
||||
++retries;
|
||||
if (retries > _sentinel_opts.max_retry) {
|
||||
throw Error("Failed to get slave from sentinel");
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(_sentinel_opts.retry_interval);
|
||||
|
||||
// Restart the iteration.
|
||||
iter.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (const Error &e) {
|
||||
// Try the next slave.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (const StopIterError &e) {
|
||||
throw;
|
||||
} catch (const Error &e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Optional<Node> Sentinel::_get_master_addr_by_name(Connection &connection, const StringView &name) {
|
||||
connection.send("SENTINEL GET-MASTER-ADDR-BY-NAME %b", name.data(), name.size());
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
|
||||
auto master = reply::parse<Optional<std::pair<std::string, std::string>>>(*reply);
|
||||
if (!master) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
try {
|
||||
port = std::stoi(master->second);
|
||||
} catch (const std::exception &) {
|
||||
throw ProtoError("Master port is invalid: " + master->second);
|
||||
}
|
||||
|
||||
return Optional<Node>{Node{master->first, port}};
|
||||
}
|
||||
|
||||
std::vector<Node> Sentinel::_get_slave_addr_by_name(Connection &connection,
|
||||
const StringView &name) {
|
||||
try {
|
||||
connection.send("SENTINEL SLAVES %b", name.data(), name.size());
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
|
||||
auto slaves = _parse_slave_info(*reply);
|
||||
|
||||
// Make slave list random.
|
||||
std::mt19937 gen(std::random_device{}());
|
||||
std::shuffle(slaves.begin(), slaves.end(), gen);
|
||||
|
||||
return slaves;
|
||||
} catch (const ReplyError &e) {
|
||||
// Unknown master name.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Node> Sentinel::_parse_slave_info(redisReply &reply) const {
|
||||
using SlaveInfo = std::unordered_map<std::string, std::string>;
|
||||
|
||||
auto slaves = reply::parse<std::vector<SlaveInfo>>(reply);
|
||||
|
||||
std::vector<Node> nodes;
|
||||
for (const auto &slave : slaves) {
|
||||
auto flags_iter = slave.find("flags");
|
||||
auto ip_iter = slave.find("ip");
|
||||
auto port_iter = slave.find("port");
|
||||
if (flags_iter == slave.end() || ip_iter == slave.end() || port_iter == slave.end()) {
|
||||
throw ProtoError("Invalid slave info");
|
||||
}
|
||||
|
||||
// This slave is down, e.g. 's_down,slave,disconnected'
|
||||
if (flags_iter->second != "slave") {
|
||||
continue;
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
try {
|
||||
port = std::stoi(port_iter->second);
|
||||
} catch (const std::exception &) {
|
||||
throw ProtoError("Slave port is invalid: " + port_iter->second);
|
||||
}
|
||||
|
||||
nodes.push_back(Node{ip_iter->second, port});
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Connection Sentinel::_connect_redis(const Node &node, ConnectionOptions opts) {
|
||||
opts.host = node.host;
|
||||
opts.port = node.port;
|
||||
|
||||
return Connection(opts);
|
||||
}
|
||||
|
||||
Role Sentinel::_get_role(Connection &connection) {
|
||||
connection.send("INFO REPLICATION");
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
auto info = reply::parse<std::string>(*reply);
|
||||
|
||||
auto start = info.find("role:");
|
||||
if (start == std::string::npos) {
|
||||
throw ProtoError("Invalid INFO REPLICATION reply");
|
||||
}
|
||||
start += 5;
|
||||
auto stop = info.find("\r\n", start);
|
||||
if (stop == std::string::npos) {
|
||||
throw ProtoError("Invalid INFO REPLICATION reply");
|
||||
}
|
||||
|
||||
auto role = info.substr(start, stop - start);
|
||||
if (role == "master") {
|
||||
return Role::MASTER;
|
||||
} else if (role == "slave") {
|
||||
return Role::SLAVE;
|
||||
} else {
|
||||
throw Error("Invalid role: " + role);
|
||||
}
|
||||
}
|
||||
|
||||
std::list<ConnectionOptions> Sentinel::_parse_options(const SentinelOptions &opts) const {
|
||||
std::list<ConnectionOptions> options;
|
||||
for (const auto &node : opts.nodes) {
|
||||
ConnectionOptions opt;
|
||||
opt.host = node.first;
|
||||
opt.port = node.second;
|
||||
opt.password = opts.password;
|
||||
opt.keep_alive = opts.keep_alive;
|
||||
opt.connect_timeout = opts.connect_timeout;
|
||||
opt.socket_timeout = opts.socket_timeout;
|
||||
|
||||
options.push_back(opt);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
SimpleSentinel::SimpleSentinel(const std::shared_ptr<Sentinel> &sentinel,
|
||||
const std::string &master_name,
|
||||
Role role) :
|
||||
_sentinel(sentinel),
|
||||
_master_name(master_name),
|
||||
_role(role) {
|
||||
if (!_sentinel) {
|
||||
throw Error("Sentinel cannot be null");
|
||||
}
|
||||
|
||||
if (_role != Role::MASTER && _role != Role::SLAVE) {
|
||||
throw Error("Role must be Role::MASTER or Role::SLAVE");
|
||||
}
|
||||
}
|
||||
|
||||
Connection SimpleSentinel::create(const ConnectionOptions &opts) {
|
||||
assert(_sentinel);
|
||||
|
||||
if (_role == Role::MASTER) {
|
||||
return _sentinel->master(_master_name, opts);
|
||||
}
|
||||
|
||||
assert(_role == Role::SLAVE);
|
||||
|
||||
return _sentinel->slave(_master_name, opts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
138
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.h
vendored
Normal file
138
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.h
vendored
Normal file
|
@ -0,0 +1,138 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_SENTINEL_H
|
||||
#define SEWENEW_REDISPLUSPLUS_SENTINEL_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include "connection.h"
|
||||
#include "shards.h"
|
||||
#include "reply.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
struct SentinelOptions {
|
||||
std::vector<std::pair<std::string, int>> nodes;
|
||||
|
||||
std::string password;
|
||||
|
||||
bool keep_alive = true;
|
||||
|
||||
std::chrono::milliseconds connect_timeout{100};
|
||||
|
||||
std::chrono::milliseconds socket_timeout{100};
|
||||
|
||||
std::chrono::milliseconds retry_interval{100};
|
||||
|
||||
std::size_t max_retry = 2;
|
||||
};
|
||||
|
||||
class Sentinel {
|
||||
public:
|
||||
explicit Sentinel(const SentinelOptions &sentinel_opts);
|
||||
|
||||
Sentinel(const Sentinel &) = delete;
|
||||
Sentinel& operator=(const Sentinel &) = delete;
|
||||
|
||||
Sentinel(Sentinel &&) = delete;
|
||||
Sentinel& operator=(Sentinel &&) = delete;
|
||||
|
||||
~Sentinel() = default;
|
||||
|
||||
private:
|
||||
Connection master(const std::string &master_name, const ConnectionOptions &opts);
|
||||
|
||||
Connection slave(const std::string &master_name, const ConnectionOptions &opts);
|
||||
|
||||
class Iterator;
|
||||
|
||||
friend class SimpleSentinel;
|
||||
|
||||
std::list<ConnectionOptions> _parse_options(const SentinelOptions &opts) const;
|
||||
|
||||
Optional<Node> _get_master_addr_by_name(Connection &connection, const StringView &name);
|
||||
|
||||
std::vector<Node> _get_slave_addr_by_name(Connection &connection, const StringView &name);
|
||||
|
||||
Connection _connect_redis(const Node &node, ConnectionOptions opts);
|
||||
|
||||
Role _get_role(Connection &connection);
|
||||
|
||||
std::vector<Node> _parse_slave_info(redisReply &reply) const;
|
||||
|
||||
std::list<Connection> _healthy_sentinels;
|
||||
|
||||
std::list<ConnectionOptions> _broken_sentinels;
|
||||
|
||||
SentinelOptions _sentinel_opts;
|
||||
|
||||
std::mutex _mutex;
|
||||
};
|
||||
|
||||
class SimpleSentinel {
|
||||
public:
|
||||
SimpleSentinel(const std::shared_ptr<Sentinel> &sentinel,
|
||||
const std::string &master_name,
|
||||
Role role);
|
||||
|
||||
SimpleSentinel() = default;
|
||||
|
||||
SimpleSentinel(const SimpleSentinel &) = default;
|
||||
SimpleSentinel& operator=(const SimpleSentinel &) = default;
|
||||
|
||||
SimpleSentinel(SimpleSentinel &&) = default;
|
||||
SimpleSentinel& operator=(SimpleSentinel &&) = default;
|
||||
|
||||
~SimpleSentinel() = default;
|
||||
|
||||
explicit operator bool() const {
|
||||
return bool(_sentinel);
|
||||
}
|
||||
|
||||
Connection create(const ConnectionOptions &opts);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Sentinel> _sentinel;
|
||||
|
||||
std::string _master_name;
|
||||
|
||||
Role _role = Role::MASTER;
|
||||
};
|
||||
|
||||
class StopIterError : public Error {
|
||||
public:
|
||||
StopIterError() : Error("StopIterError") {}
|
||||
|
||||
StopIterError(const StopIterError &) = default;
|
||||
StopIterError& operator=(const StopIterError &) = default;
|
||||
|
||||
StopIterError(StopIterError &&) = default;
|
||||
StopIterError& operator=(StopIterError &&) = default;
|
||||
|
||||
virtual ~StopIterError() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SENTINEL_H
|
50
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards.cpp
vendored
Normal file
50
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards.cpp
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "shards.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
RedirectionError::RedirectionError(const std::string &msg): ReplyError(msg) {
|
||||
std::tie(_slot, _node) = _parse_error(msg);
|
||||
}
|
||||
|
||||
std::pair<Slot, Node> RedirectionError::_parse_error(const std::string &msg) const {
|
||||
// "slot ip:port"
|
||||
auto space_pos = msg.find(" ");
|
||||
auto colon_pos = msg.find(":");
|
||||
if (space_pos == std::string::npos
|
||||
|| colon_pos == std::string::npos
|
||||
|| colon_pos < space_pos) {
|
||||
throw ProtoError("Invalid ASK error message: " + msg);
|
||||
}
|
||||
|
||||
try {
|
||||
auto slot = std::stoull(msg.substr(0, space_pos));
|
||||
auto host = msg.substr(space_pos + 1, colon_pos - space_pos - 1);
|
||||
auto port = std::stoi(msg.substr(colon_pos + 1));
|
||||
|
||||
return {slot, {host, port}};
|
||||
} catch (const std::exception &e) {
|
||||
throw ProtoError("Invalid ASK error message: " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
115
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards.h
vendored
Normal file
115
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards.h
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_SHARDS_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
using Slot = std::size_t;
|
||||
|
||||
struct SlotRange {
|
||||
Slot min;
|
||||
Slot max;
|
||||
};
|
||||
|
||||
inline bool operator<(const SlotRange &lhs, const SlotRange &rhs) {
|
||||
return lhs.max < rhs.max;
|
||||
}
|
||||
|
||||
struct Node {
|
||||
std::string host;
|
||||
int port;
|
||||
};
|
||||
|
||||
inline bool operator==(const Node &lhs, const Node &rhs) {
|
||||
return lhs.host == rhs.host && lhs.port == rhs.port;
|
||||
}
|
||||
|
||||
struct NodeHash {
|
||||
std::size_t operator()(const Node &node) const noexcept {
|
||||
auto host_hash = std::hash<std::string>{}(node.host);
|
||||
auto port_hash = std::hash<int>{}(node.port);
|
||||
return host_hash ^ (port_hash << 1);
|
||||
}
|
||||
};
|
||||
|
||||
using Shards = std::map<SlotRange, Node>;
|
||||
|
||||
class RedirectionError : public ReplyError {
|
||||
public:
|
||||
RedirectionError(const std::string &msg);
|
||||
|
||||
RedirectionError(const RedirectionError &) = default;
|
||||
RedirectionError& operator=(const RedirectionError &) = default;
|
||||
|
||||
RedirectionError(RedirectionError &&) = default;
|
||||
RedirectionError& operator=(RedirectionError &&) = default;
|
||||
|
||||
virtual ~RedirectionError() = default;
|
||||
|
||||
Slot slot() const {
|
||||
return _slot;
|
||||
}
|
||||
|
||||
const Node& node() const {
|
||||
return _node;
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<Slot, Node> _parse_error(const std::string &msg) const;
|
||||
|
||||
Slot _slot = 0;
|
||||
Node _node;
|
||||
};
|
||||
|
||||
class MovedError : public RedirectionError {
|
||||
public:
|
||||
explicit MovedError(const std::string &msg) : RedirectionError(msg) {}
|
||||
|
||||
MovedError(const MovedError &) = default;
|
||||
MovedError& operator=(const MovedError &) = default;
|
||||
|
||||
MovedError(MovedError &&) = default;
|
||||
MovedError& operator=(MovedError &&) = default;
|
||||
|
||||
virtual ~MovedError() = default;
|
||||
};
|
||||
|
||||
class AskError : public RedirectionError {
|
||||
public:
|
||||
explicit AskError(const std::string &msg) : RedirectionError(msg) {}
|
||||
|
||||
AskError(const AskError &) = default;
|
||||
AskError& operator=(const AskError &) = default;
|
||||
|
||||
AskError(AskError &&) = default;
|
||||
AskError& operator=(AskError &&) = default;
|
||||
|
||||
virtual ~AskError() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_H
|
319
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.cpp
vendored
Normal file
319
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.cpp
vendored
Normal file
|
@ -0,0 +1,319 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "shards_pool.h"
|
||||
#include <unordered_set>
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
const std::size_t ShardsPool::SHARDS;
|
||||
|
||||
ShardsPool::ShardsPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts) :
|
||||
_pool_opts(pool_opts),
|
||||
_connection_opts(connection_opts) {
|
||||
if (_connection_opts.type != ConnectionType::TCP) {
|
||||
throw Error("Only support TCP connection for Redis Cluster");
|
||||
}
|
||||
|
||||
Connection connection(_connection_opts);
|
||||
|
||||
_shards = _cluster_slots(connection);
|
||||
|
||||
_init_pool(_shards);
|
||||
}
|
||||
|
||||
ShardsPool::ShardsPool(ShardsPool &&that) {
|
||||
std::lock_guard<std::mutex> lock(that._mutex);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
ShardsPool& ShardsPool::operator=(ShardsPool &&that) {
|
||||
if (this != &that) {
|
||||
std::lock(_mutex, that._mutex);
|
||||
std::lock_guard<std::mutex> lock_this(_mutex, std::adopt_lock);
|
||||
std::lock_guard<std::mutex> lock_that(that._mutex, std::adopt_lock);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::fetch(const StringView &key) {
|
||||
auto slot = _slot(key);
|
||||
|
||||
return _fetch(slot);
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::fetch() {
|
||||
auto slot = _slot();
|
||||
|
||||
return _fetch(slot);
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::fetch(const Node &node) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
auto iter = _pools.find(node);
|
||||
if (iter == _pools.end()) {
|
||||
// Node doesn't exist, and it should be a newly created node.
|
||||
// So add a new connection pool.
|
||||
iter = _add_node(node);
|
||||
}
|
||||
|
||||
assert(iter != _pools.end());
|
||||
|
||||
return GuardedConnection(iter->second);
|
||||
}
|
||||
|
||||
void ShardsPool::update() {
|
||||
// My might send command to a removed node.
|
||||
// Try at most 3 times.
|
||||
for (auto idx = 0; idx < 3; ++idx) {
|
||||
try {
|
||||
// Randomly pick a connection.
|
||||
auto guarded_connection = fetch();
|
||||
auto shards = _cluster_slots(guarded_connection.connection());
|
||||
|
||||
std::unordered_set<Node, NodeHash> nodes;
|
||||
for (const auto &shard : shards) {
|
||||
nodes.insert(shard.second);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
// TODO: If shards is unchanged, no need to update, and return immediately.
|
||||
|
||||
_shards = std::move(shards);
|
||||
|
||||
// Remove non-existent nodes.
|
||||
for (auto iter = _pools.begin(); iter != _pools.end(); ) {
|
||||
if (nodes.find(iter->first) == nodes.end()) {
|
||||
// Node has been removed.
|
||||
_pools.erase(iter++);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
// Add connection pool for new nodes.
|
||||
// In fact, connections will be created lazily.
|
||||
for (const auto &node : nodes) {
|
||||
if (_pools.find(node) == _pools.end()) {
|
||||
_add_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Update successfully.
|
||||
return;
|
||||
} catch (const Error &) {
|
||||
// continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw Error("Failed to update shards info");
|
||||
}
|
||||
|
||||
ConnectionOptions ShardsPool::connection_options(const StringView &key) {
|
||||
auto slot = _slot(key);
|
||||
|
||||
return _connection_options(slot);
|
||||
}
|
||||
|
||||
ConnectionOptions ShardsPool::connection_options() {
|
||||
auto slot = _slot();
|
||||
|
||||
return _connection_options(slot);
|
||||
}
|
||||
void ShardsPool::_move(ShardsPool &&that) {
|
||||
_pool_opts = that._pool_opts;
|
||||
_connection_opts = that._connection_opts;
|
||||
_shards = std::move(that._shards);
|
||||
_pools = std::move(that._pools);
|
||||
}
|
||||
|
||||
void ShardsPool::_init_pool(const Shards &shards) {
|
||||
for (const auto &shard : shards) {
|
||||
_add_node(shard.second);
|
||||
}
|
||||
}
|
||||
|
||||
Shards ShardsPool::_cluster_slots(Connection &connection) const {
|
||||
auto reply = _cluster_slots_command(connection);
|
||||
|
||||
assert(reply);
|
||||
|
||||
return _parse_reply(*reply);
|
||||
}
|
||||
|
||||
ReplyUPtr ShardsPool::_cluster_slots_command(Connection &connection) const {
|
||||
connection.send("CLUSTER SLOTS");
|
||||
|
||||
return connection.recv();
|
||||
}
|
||||
|
||||
Shards ShardsPool::_parse_reply(redisReply &reply) const {
|
||||
if (!reply::is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply.element == nullptr || reply.elements == 0) {
|
||||
throw Error("Empty slots");
|
||||
}
|
||||
|
||||
Shards shards;
|
||||
for (std::size_t idx = 0; idx != reply.elements; ++idx) {
|
||||
auto *sub_reply = reply.element[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null slot info");
|
||||
}
|
||||
|
||||
shards.emplace(_parse_slot_info(*sub_reply));
|
||||
}
|
||||
|
||||
return shards;
|
||||
}
|
||||
|
||||
std::pair<SlotRange, Node> ShardsPool::_parse_slot_info(redisReply &reply) const {
|
||||
if (reply.elements < 3 || reply.element == nullptr) {
|
||||
throw ProtoError("Invalid slot info");
|
||||
}
|
||||
|
||||
// Min slot id
|
||||
auto *min_slot_reply = reply.element[0];
|
||||
if (min_slot_reply == nullptr) {
|
||||
throw ProtoError("Invalid min slot");
|
||||
}
|
||||
std::size_t min_slot = reply::parse<long long>(*min_slot_reply);
|
||||
|
||||
// Max slot id
|
||||
auto *max_slot_reply = reply.element[1];
|
||||
if (max_slot_reply == nullptr) {
|
||||
throw ProtoError("Invalid max slot");
|
||||
}
|
||||
std::size_t max_slot = reply::parse<long long>(*max_slot_reply);
|
||||
|
||||
if (min_slot > max_slot) {
|
||||
throw ProtoError("Invalid slot range");
|
||||
}
|
||||
|
||||
// Master node info
|
||||
auto *node_reply = reply.element[2];
|
||||
if (node_reply == nullptr
|
||||
|| !reply::is_array(*node_reply)
|
||||
|| node_reply->element == nullptr
|
||||
|| node_reply->elements < 2) {
|
||||
throw ProtoError("Invalid node info");
|
||||
}
|
||||
|
||||
auto master_host = reply::parse<std::string>(*(node_reply->element[0]));
|
||||
int master_port = reply::parse<long long>(*(node_reply->element[1]));
|
||||
|
||||
// By now, we ignore node id and other replicas' info.
|
||||
|
||||
return {SlotRange{min_slot, max_slot}, Node{master_host, master_port}};
|
||||
}
|
||||
|
||||
Slot ShardsPool::_slot(const StringView &key) const {
|
||||
// The following code is copied from: https://redis.io/topics/cluster-spec
|
||||
// And I did some minor changes.
|
||||
|
||||
const auto *k = key.data();
|
||||
auto keylen = key.size();
|
||||
|
||||
// start-end indexes of { and }.
|
||||
std::size_t s = 0;
|
||||
std::size_t e = 0;
|
||||
|
||||
// Search the first occurrence of '{'.
|
||||
for (s = 0; s < keylen; s++)
|
||||
if (k[s] == '{') break;
|
||||
|
||||
// No '{' ? Hash the whole key. This is the base case.
|
||||
if (s == keylen) return crc16(k, keylen) & SHARDS;
|
||||
|
||||
// '{' found? Check if we have the corresponding '}'.
|
||||
for (e = s + 1; e < keylen; e++)
|
||||
if (k[e] == '}') break;
|
||||
|
||||
// No '}' or nothing between {} ? Hash the whole key.
|
||||
if (e == keylen || e == s + 1) return crc16(k, keylen) & SHARDS;
|
||||
|
||||
// If we are here there is both a { and a } on its right. Hash
|
||||
// what is in the middle between { and }.
|
||||
return crc16(k + s + 1, e - s - 1) & SHARDS;
|
||||
}
|
||||
|
||||
Slot ShardsPool::_slot() const {
|
||||
static thread_local std::default_random_engine engine;
|
||||
|
||||
std::uniform_int_distribution<std::size_t> uniform_dist(0, SHARDS);
|
||||
|
||||
return uniform_dist(engine);
|
||||
}
|
||||
|
||||
ConnectionPoolSPtr& ShardsPool::_get_pool(Slot slot) {
|
||||
auto shards_iter = _shards.lower_bound(SlotRange{slot, slot});
|
||||
if (shards_iter == _shards.end() || slot < shards_iter->first.min) {
|
||||
throw Error("Slot is out of range: " + std::to_string(slot));
|
||||
}
|
||||
|
||||
const auto &node = shards_iter->second;
|
||||
|
||||
auto node_iter = _pools.find(node);
|
||||
if (node_iter == _pools.end()) {
|
||||
throw Error("Slot is NOT covered: " + std::to_string(slot));
|
||||
}
|
||||
|
||||
return node_iter->second;
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::_fetch(Slot slot) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
auto &pool = _get_pool(slot);
|
||||
|
||||
assert(pool);
|
||||
|
||||
return GuardedConnection(pool);
|
||||
}
|
||||
|
||||
ConnectionOptions ShardsPool::_connection_options(Slot slot) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
auto &pool = _get_pool(slot);
|
||||
|
||||
assert(pool);
|
||||
|
||||
return pool->connection_options();
|
||||
}
|
||||
|
||||
auto ShardsPool::_add_node(const Node &node) -> NodeMap::iterator {
|
||||
auto opts = _connection_opts;
|
||||
opts.host = node.host;
|
||||
opts.port = node.port;
|
||||
|
||||
return _pools.emplace(node, std::make_shared<ConnectionPool>(_pool_opts, opts)).first;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
137
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.h
vendored
Normal file
137
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.h
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H
|
||||
#define SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <memory>
|
||||
#include "reply.h"
|
||||
#include "connection_pool.h"
|
||||
#include "shards.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
using ConnectionPoolSPtr = std::shared_ptr<ConnectionPool>;
|
||||
|
||||
class GuardedConnection {
|
||||
public:
|
||||
GuardedConnection(const ConnectionPoolSPtr &pool) : _pool(pool),
|
||||
_connection(_pool->fetch()) {
|
||||
assert(!_connection.broken());
|
||||
}
|
||||
|
||||
GuardedConnection(const GuardedConnection &) = delete;
|
||||
GuardedConnection& operator=(const GuardedConnection &) = delete;
|
||||
|
||||
GuardedConnection(GuardedConnection &&) = default;
|
||||
GuardedConnection& operator=(GuardedConnection &&) = default;
|
||||
|
||||
~GuardedConnection() {
|
||||
_pool->release(std::move(_connection));
|
||||
}
|
||||
|
||||
Connection& connection() {
|
||||
return _connection;
|
||||
}
|
||||
|
||||
private:
|
||||
ConnectionPoolSPtr _pool;
|
||||
Connection _connection;
|
||||
};
|
||||
|
||||
class ShardsPool {
|
||||
public:
|
||||
ShardsPool() = default;
|
||||
|
||||
ShardsPool(const ShardsPool &that) = delete;
|
||||
ShardsPool& operator=(const ShardsPool &that) = delete;
|
||||
|
||||
ShardsPool(ShardsPool &&that);
|
||||
ShardsPool& operator=(ShardsPool &&that);
|
||||
|
||||
~ShardsPool() = default;
|
||||
|
||||
ShardsPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts);
|
||||
|
||||
// Fetch a connection by key.
|
||||
GuardedConnection fetch(const StringView &key);
|
||||
|
||||
// Randomly pick a connection.
|
||||
GuardedConnection fetch();
|
||||
|
||||
// Fetch a connection by node.
|
||||
GuardedConnection fetch(const Node &node);
|
||||
|
||||
void update();
|
||||
|
||||
ConnectionOptions connection_options(const StringView &key);
|
||||
|
||||
ConnectionOptions connection_options();
|
||||
|
||||
private:
|
||||
void _move(ShardsPool &&that);
|
||||
|
||||
void _init_pool(const Shards &shards);
|
||||
|
||||
Shards _cluster_slots(Connection &connection) const;
|
||||
|
||||
ReplyUPtr _cluster_slots_command(Connection &connection) const;
|
||||
|
||||
Shards _parse_reply(redisReply &reply) const;
|
||||
|
||||
std::pair<SlotRange, Node> _parse_slot_info(redisReply &reply) const;
|
||||
|
||||
// Get slot by key.
|
||||
std::size_t _slot(const StringView &key) const;
|
||||
|
||||
// Randomly pick a slot.
|
||||
std::size_t _slot() const;
|
||||
|
||||
ConnectionPoolSPtr& _get_pool(Slot slot);
|
||||
|
||||
GuardedConnection _fetch(Slot slot);
|
||||
|
||||
ConnectionOptions _connection_options(Slot slot);
|
||||
|
||||
using NodeMap = std::unordered_map<Node, ConnectionPoolSPtr, NodeHash>;
|
||||
|
||||
NodeMap::iterator _add_node(const Node &node);
|
||||
|
||||
ConnectionPoolOptions _pool_opts;
|
||||
|
||||
ConnectionOptions _connection_opts;
|
||||
|
||||
Shards _shards;
|
||||
|
||||
NodeMap _pools;
|
||||
|
||||
std::mutex _mutex;
|
||||
|
||||
static const std::size_t SHARDS = 16383;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H
|
222
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.cpp
vendored
Normal file
222
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.cpp
vendored
Normal file
|
@ -0,0 +1,222 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "subscriber.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
const Subscriber::TypeIndex Subscriber::_msg_type_index = {
|
||||
{"message", MsgType::MESSAGE},
|
||||
{"pmessage", MsgType::PMESSAGE},
|
||||
{"subscribe", MsgType::SUBSCRIBE},
|
||||
{"unsubscribe", MsgType::UNSUBSCRIBE},
|
||||
{"psubscribe", MsgType::PSUBSCRIBE},
|
||||
{"punsubscribe", MsgType::PUNSUBSCRIBE}
|
||||
};
|
||||
|
||||
Subscriber::Subscriber(Connection connection) : _connection(std::move(connection)) {}
|
||||
|
||||
void Subscriber::subscribe(const StringView &channel) {
|
||||
_check_connection();
|
||||
|
||||
// TODO: cmd::subscribe DOES NOT send the subscribe message to Redis.
|
||||
// In fact, it puts the command to network buffer.
|
||||
// So we need a queue to record these sub or unsub commands, and
|
||||
// ensure that before stopping the subscriber, all these commands
|
||||
// have really been sent to Redis.
|
||||
cmd::subscribe(_connection, channel);
|
||||
}
|
||||
|
||||
void Subscriber::unsubscribe() {
|
||||
_check_connection();
|
||||
|
||||
cmd::unsubscribe(_connection);
|
||||
}
|
||||
|
||||
void Subscriber::unsubscribe(const StringView &channel) {
|
||||
_check_connection();
|
||||
|
||||
cmd::unsubscribe(_connection, channel);
|
||||
}
|
||||
|
||||
void Subscriber::psubscribe(const StringView &pattern) {
|
||||
_check_connection();
|
||||
|
||||
cmd::psubscribe(_connection, pattern);
|
||||
}
|
||||
|
||||
void Subscriber::punsubscribe() {
|
||||
_check_connection();
|
||||
|
||||
cmd::punsubscribe(_connection);
|
||||
}
|
||||
|
||||
void Subscriber::punsubscribe(const StringView &pattern) {
|
||||
_check_connection();
|
||||
|
||||
cmd::punsubscribe(_connection, pattern);
|
||||
}
|
||||
|
||||
void Subscriber::consume() {
|
||||
_check_connection();
|
||||
|
||||
ReplyUPtr reply;
|
||||
try {
|
||||
reply = _connection.recv();
|
||||
} catch (const TimeoutError &) {
|
||||
_connection.reset();
|
||||
throw;
|
||||
}
|
||||
|
||||
assert(reply);
|
||||
|
||||
if (!reply::is_array(*reply) || reply->elements < 1 || reply->element == nullptr) {
|
||||
throw ProtoError("Invalid subscribe message");
|
||||
}
|
||||
|
||||
auto type = _msg_type(reply->element[0]);
|
||||
switch (type) {
|
||||
case MsgType::MESSAGE:
|
||||
_handle_message(*reply);
|
||||
break;
|
||||
|
||||
case MsgType::PMESSAGE:
|
||||
_handle_pmessage(*reply);
|
||||
break;
|
||||
|
||||
case MsgType::SUBSCRIBE:
|
||||
case MsgType::UNSUBSCRIBE:
|
||||
case MsgType::PSUBSCRIBE:
|
||||
case MsgType::PUNSUBSCRIBE:
|
||||
_handle_meta(type, *reply);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
Subscriber::MsgType Subscriber::_msg_type(redisReply *reply) const {
|
||||
if (reply == nullptr) {
|
||||
throw ProtoError("Null type reply.");
|
||||
}
|
||||
|
||||
auto type = reply::parse<std::string>(*reply);
|
||||
|
||||
auto iter = _msg_type_index.find(type);
|
||||
if (iter == _msg_type_index.end()) {
|
||||
throw ProtoError("Invalid message type.");
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void Subscriber::_check_connection() {
|
||||
if (_connection.broken()) {
|
||||
throw Error("Connection is broken");
|
||||
}
|
||||
}
|
||||
|
||||
void Subscriber::_handle_message(redisReply &reply) {
|
||||
if (_msg_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements != 3) {
|
||||
throw ProtoError("Expect 3 sub replies");
|
||||
}
|
||||
|
||||
assert(reply.element != nullptr);
|
||||
|
||||
auto *channel_reply = reply.element[1];
|
||||
if (channel_reply == nullptr) {
|
||||
throw ProtoError("Null channel reply");
|
||||
}
|
||||
auto channel = reply::parse<std::string>(*channel_reply);
|
||||
|
||||
auto *msg_reply = reply.element[2];
|
||||
if (msg_reply == nullptr) {
|
||||
throw ProtoError("Null message reply");
|
||||
}
|
||||
auto msg = reply::parse<std::string>(*msg_reply);
|
||||
|
||||
_msg_callback(std::move(channel), std::move(msg));
|
||||
}
|
||||
|
||||
void Subscriber::_handle_pmessage(redisReply &reply) {
|
||||
if (_pmsg_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements != 4) {
|
||||
throw ProtoError("Expect 4 sub replies");
|
||||
}
|
||||
|
||||
assert(reply.element != nullptr);
|
||||
|
||||
auto *pattern_reply = reply.element[1];
|
||||
if (pattern_reply == nullptr) {
|
||||
throw ProtoError("Null pattern reply");
|
||||
}
|
||||
auto pattern = reply::parse<std::string>(*pattern_reply);
|
||||
|
||||
auto *channel_reply = reply.element[2];
|
||||
if (channel_reply == nullptr) {
|
||||
throw ProtoError("Null channel reply");
|
||||
}
|
||||
auto channel = reply::parse<std::string>(*channel_reply);
|
||||
|
||||
auto *msg_reply = reply.element[3];
|
||||
if (msg_reply == nullptr) {
|
||||
throw ProtoError("Null message reply");
|
||||
}
|
||||
auto msg = reply::parse<std::string>(*msg_reply);
|
||||
|
||||
_pmsg_callback(std::move(pattern), std::move(channel), std::move(msg));
|
||||
}
|
||||
|
||||
void Subscriber::_handle_meta(MsgType type, redisReply &reply) {
|
||||
if (_meta_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements != 3) {
|
||||
throw ProtoError("Expect 3 sub replies");
|
||||
}
|
||||
|
||||
assert(reply.element != nullptr);
|
||||
|
||||
auto *channel_reply = reply.element[1];
|
||||
if (channel_reply == nullptr) {
|
||||
throw ProtoError("Null channel reply");
|
||||
}
|
||||
auto channel = reply::parse<OptionalString>(*channel_reply);
|
||||
|
||||
auto *num_reply = reply.element[2];
|
||||
if (num_reply == nullptr) {
|
||||
throw ProtoError("Null num reply");
|
||||
}
|
||||
auto num = reply::parse<long long>(*num_reply);
|
||||
|
||||
_meta_callback(type, std::move(channel), num);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
231
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.h
vendored
Normal file
231
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.h
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H
|
||||
#define SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include "connection.h"
|
||||
#include "reply.h"
|
||||
#include "command.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
// @NOTE: Subscriber is NOT thread-safe.
|
||||
// Subscriber uses callbacks to handle messages. There are 6 kinds of messages:
|
||||
// 1) MESSAGE: message sent to a channel.
|
||||
// 2) PMESSAGE: message sent to channels of a given pattern.
|
||||
// 3) SUBSCRIBE: meta message sent when we successfully subscribe to a channel.
|
||||
// 4) UNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel.
|
||||
// 5) PSUBSCRIBE: meta message sent when we successfully subscribe to a channel pattern.
|
||||
// 6) PUNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel pattern.
|
||||
//
|
||||
// Use Subscriber::on_message(MsgCallback) to set the callback function for message of
|
||||
// *MESSAGE* type, and the callback interface is:
|
||||
// void (std::string channel, std::string msg)
|
||||
//
|
||||
// Use Subscriber::on_pmessage(PatternMsgCallback) to set the callback function for message of
|
||||
// *PMESSAGE* type, and the callback interface is:
|
||||
// void (std::string pattern, std::string channel, std::string msg)
|
||||
//
|
||||
// Messages of other types are called *META MESSAGE*, they have the same callback interface.
|
||||
// Use Subscriber::on_meta(MetaCallback) to set the callback function:
|
||||
// void (Subscriber::MsgType type, OptionalString channel, long long num)
|
||||
//
|
||||
// NOTE: If we haven't subscribe/psubscribe to any channel/pattern, and try to
|
||||
// unsubscribe/punsubscribe without any parameter, i.e. unsubscribe/punsubscribe all
|
||||
// channels/patterns, *channel* will be null. So the second parameter of meta callback
|
||||
// is of type *OptionalString*.
|
||||
//
|
||||
// All these callback interfaces pass std::string by value, and you can take their ownership
|
||||
// (i.e. std::move) safely.
|
||||
//
|
||||
// If you don't set callback for a specific kind of message, Subscriber::consume() will
|
||||
// receive the message, and ignore it, i.e. no callback will be called.
|
||||
class Subscriber {
|
||||
public:
|
||||
Subscriber(const Subscriber &) = delete;
|
||||
Subscriber& operator=(const Subscriber &) = delete;
|
||||
|
||||
Subscriber(Subscriber &&) = default;
|
||||
Subscriber& operator=(Subscriber &&) = default;
|
||||
|
||||
~Subscriber() = default;
|
||||
|
||||
enum class MsgType {
|
||||
SUBSCRIBE,
|
||||
UNSUBSCRIBE,
|
||||
PSUBSCRIBE,
|
||||
PUNSUBSCRIBE,
|
||||
MESSAGE,
|
||||
PMESSAGE
|
||||
};
|
||||
|
||||
template <typename MsgCb>
|
||||
void on_message(MsgCb msg_callback);
|
||||
|
||||
template <typename PMsgCb>
|
||||
void on_pmessage(PMsgCb pmsg_callback);
|
||||
|
||||
template <typename MetaCb>
|
||||
void on_meta(MetaCb meta_callback);
|
||||
|
||||
void subscribe(const StringView &channel);
|
||||
|
||||
template <typename Input>
|
||||
void subscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void subscribe(std::initializer_list<T> channels) {
|
||||
subscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void unsubscribe();
|
||||
|
||||
void unsubscribe(const StringView &channel);
|
||||
|
||||
template <typename Input>
|
||||
void unsubscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void unsubscribe(std::initializer_list<T> channels) {
|
||||
unsubscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void psubscribe(const StringView &pattern);
|
||||
|
||||
template <typename Input>
|
||||
void psubscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void psubscribe(std::initializer_list<T> channels) {
|
||||
psubscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void punsubscribe();
|
||||
|
||||
void punsubscribe(const StringView &channel);
|
||||
|
||||
template <typename Input>
|
||||
void punsubscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void punsubscribe(std::initializer_list<T> channels) {
|
||||
punsubscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void consume();
|
||||
|
||||
private:
|
||||
friend class Redis;
|
||||
|
||||
friend class RedisCluster;
|
||||
|
||||
explicit Subscriber(Connection connection);
|
||||
|
||||
MsgType _msg_type(redisReply *reply) const;
|
||||
|
||||
void _check_connection();
|
||||
|
||||
void _handle_message(redisReply &reply);
|
||||
|
||||
void _handle_pmessage(redisReply &reply);
|
||||
|
||||
void _handle_meta(MsgType type, redisReply &reply);
|
||||
|
||||
using MsgCallback = std::function<void (std::string channel, std::string msg)>;
|
||||
|
||||
using PatternMsgCallback = std::function<void (std::string pattern,
|
||||
std::string channel,
|
||||
std::string msg)>;
|
||||
|
||||
using MetaCallback = std::function<void (MsgType type,
|
||||
OptionalString channel,
|
||||
long long num)>;
|
||||
|
||||
using TypeIndex = std::unordered_map<std::string, MsgType>;
|
||||
static const TypeIndex _msg_type_index;
|
||||
|
||||
Connection _connection;
|
||||
|
||||
MsgCallback _msg_callback = nullptr;
|
||||
|
||||
PatternMsgCallback _pmsg_callback = nullptr;
|
||||
|
||||
MetaCallback _meta_callback = nullptr;
|
||||
};
|
||||
|
||||
template <typename MsgCb>
|
||||
void Subscriber::on_message(MsgCb msg_callback) {
|
||||
_msg_callback = msg_callback;
|
||||
}
|
||||
|
||||
template <typename PMsgCb>
|
||||
void Subscriber::on_pmessage(PMsgCb pmsg_callback) {
|
||||
_pmsg_callback = pmsg_callback;
|
||||
}
|
||||
|
||||
template <typename MetaCb>
|
||||
void Subscriber::on_meta(MetaCb meta_callback) {
|
||||
_meta_callback = meta_callback;
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::subscribe(Input first, Input last) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
_check_connection();
|
||||
|
||||
cmd::subscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::unsubscribe(Input first, Input last) {
|
||||
_check_connection();
|
||||
|
||||
cmd::unsubscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::psubscribe(Input first, Input last) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
_check_connection();
|
||||
|
||||
cmd::psubscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::punsubscribe(Input first, Input last) {
|
||||
_check_connection();
|
||||
|
||||
cmd::punsubscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H
|
123
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/transaction.cpp
vendored
Normal file
123
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/transaction.cpp
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "transaction.h"
|
||||
#include "command.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
std::vector<ReplyUPtr> TransactionImpl::exec(Connection &connection, std::size_t cmd_num) {
|
||||
_close_transaction();
|
||||
|
||||
_get_queued_replies(connection, cmd_num);
|
||||
|
||||
return _exec(connection);
|
||||
}
|
||||
|
||||
void TransactionImpl::discard(Connection &connection, std::size_t cmd_num) {
|
||||
_close_transaction();
|
||||
|
||||
_get_queued_replies(connection, cmd_num);
|
||||
|
||||
_discard(connection);
|
||||
}
|
||||
|
||||
void TransactionImpl::_open_transaction(Connection &connection) {
|
||||
assert(!_in_transaction);
|
||||
|
||||
cmd::multi(connection);
|
||||
auto reply = connection.recv();
|
||||
auto status = reply::to_status(*reply);
|
||||
if (status != "OK") {
|
||||
throw Error("Failed to open transaction: " + status);
|
||||
}
|
||||
|
||||
_in_transaction = true;
|
||||
}
|
||||
|
||||
void TransactionImpl::_close_transaction() {
|
||||
if (!_in_transaction) {
|
||||
throw Error("No command in transaction");
|
||||
}
|
||||
|
||||
_in_transaction = false;
|
||||
}
|
||||
|
||||
void TransactionImpl::_get_queued_reply(Connection &connection) {
|
||||
auto reply = connection.recv();
|
||||
auto status = reply::to_status(*reply);
|
||||
if (status != "QUEUED") {
|
||||
throw Error("Invalid QUEUED reply: " + status);
|
||||
}
|
||||
}
|
||||
|
||||
void TransactionImpl::_get_queued_replies(Connection &connection, std::size_t cmd_num) {
|
||||
if (_piped) {
|
||||
// Get all QUEUED reply
|
||||
while (cmd_num > 0) {
|
||||
_get_queued_reply(connection);
|
||||
|
||||
--cmd_num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ReplyUPtr> TransactionImpl::_exec(Connection &connection) {
|
||||
cmd::exec(connection);
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
if (reply::is_nil(*reply)) {
|
||||
// Execution has been aborted, i.e. watched key has been modified.
|
||||
throw WatchError();
|
||||
}
|
||||
|
||||
if (!reply::is_array(*reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply->element == nullptr || reply->elements == 0) {
|
||||
// Since we don't allow EXEC without any command, this ARRAY reply
|
||||
// should NOT be null or empty.
|
||||
throw ProtoError("Null ARRAY reply");
|
||||
}
|
||||
|
||||
std::vector<ReplyUPtr> replies;
|
||||
for (std::size_t idx = 0; idx != reply->elements; ++idx) {
|
||||
auto *sub_reply = reply->element[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null sub reply");
|
||||
}
|
||||
|
||||
auto r = ReplyUPtr(sub_reply);
|
||||
reply->element[idx] = nullptr;
|
||||
replies.push_back(std::move(r));
|
||||
}
|
||||
|
||||
return replies;
|
||||
}
|
||||
|
||||
void TransactionImpl::_discard(Connection &connection) {
|
||||
cmd::discard(connection);
|
||||
auto reply = connection.recv();
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
77
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/transaction.h
vendored
Normal file
77
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/transaction.h
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TRANSACTION_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TRANSACTION_H
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include "connection.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class TransactionImpl {
|
||||
public:
|
||||
explicit TransactionImpl(bool piped) : _piped(piped) {}
|
||||
|
||||
template <typename Cmd, typename ...Args>
|
||||
void command(Connection &connection, Cmd cmd, Args &&...args);
|
||||
|
||||
std::vector<ReplyUPtr> exec(Connection &connection, std::size_t cmd_num);
|
||||
|
||||
void discard(Connection &connection, std::size_t cmd_num);
|
||||
|
||||
private:
|
||||
void _open_transaction(Connection &connection);
|
||||
|
||||
void _close_transaction();
|
||||
|
||||
void _get_queued_reply(Connection &connection);
|
||||
|
||||
void _get_queued_replies(Connection &connection, std::size_t cmd_num);
|
||||
|
||||
std::vector<ReplyUPtr> _exec(Connection &connection);
|
||||
|
||||
void _discard(Connection &connection);
|
||||
|
||||
bool _in_transaction = false;
|
||||
|
||||
bool _piped;
|
||||
};
|
||||
|
||||
template <typename Cmd, typename ...Args>
|
||||
void TransactionImpl::command(Connection &connection, Cmd cmd, Args &&...args) {
|
||||
assert(!connection.broken());
|
||||
|
||||
if (!_in_transaction) {
|
||||
_open_transaction(connection);
|
||||
}
|
||||
|
||||
cmd(connection, std::forward<Args>(args)...);
|
||||
|
||||
if (!_piped) {
|
||||
_get_queued_reply(connection);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TRANSACTION_H
|
269
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/utils.h
vendored
Normal file
269
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/utils.h
vendored
Normal file
|
@ -0,0 +1,269 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_UTILS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_UTILS_H
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
// By now, not all compilers support std::string_view,
|
||||
// so we make our own implementation.
|
||||
class StringView {
|
||||
public:
|
||||
constexpr StringView() noexcept = default;
|
||||
|
||||
constexpr StringView(const char *data, std::size_t size) : _data(data), _size(size) {}
|
||||
|
||||
StringView(const char *data) : _data(data), _size(std::strlen(data)) {}
|
||||
|
||||
StringView(const std::string &str) : _data(str.data()), _size(str.size()) {}
|
||||
|
||||
constexpr StringView(const StringView &) noexcept = default;
|
||||
|
||||
StringView& operator=(const StringView &) noexcept = default;
|
||||
|
||||
constexpr const char* data() const noexcept {
|
||||
return _data;
|
||||
}
|
||||
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return _size;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *_data = nullptr;
|
||||
std::size_t _size = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Optional {
|
||||
public:
|
||||
Optional() = default;
|
||||
|
||||
Optional(const Optional &) = default;
|
||||
Optional& operator=(const Optional &) = default;
|
||||
|
||||
Optional(Optional &&) = default;
|
||||
Optional& operator=(Optional &&) = default;
|
||||
|
||||
~Optional() = default;
|
||||
|
||||
template <typename ...Args>
|
||||
explicit Optional(Args &&...args) : _value(true, T(std::forward<Args>(args)...)) {}
|
||||
|
||||
explicit operator bool() const {
|
||||
return _value.first;
|
||||
}
|
||||
|
||||
T& value() {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
const T& value() const {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return &(_value.second);
|
||||
}
|
||||
|
||||
const T* operator->() const {
|
||||
return &(_value.second);
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
const T& operator*() const {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<bool, T> _value;
|
||||
};
|
||||
|
||||
using OptionalString = Optional<std::string>;
|
||||
|
||||
using OptionalLongLong = Optional<long long>;
|
||||
|
||||
using OptionalDouble = Optional<double>;
|
||||
|
||||
using OptionalStringPair = Optional<std::pair<std::string, std::string>>;
|
||||
|
||||
template <typename ...>
|
||||
struct IsKvPair : std::false_type {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct IsKvPair<std::pair<T, U>> : std::true_type {};
|
||||
|
||||
template <typename ...>
|
||||
using Void = void;
|
||||
|
||||
template <typename T, typename U = Void<>>
|
||||
struct IsInserter : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
//struct IsInserter<T, Void<typename T::container_type>> : std::true_type {};
|
||||
struct IsInserter<T,
|
||||
typename std::enable_if<!std::is_void<typename T::container_type>::value>::type>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename Iter, typename T = Void<>>
|
||||
struct IterType {
|
||||
using type = typename std::iterator_traits<Iter>::value_type;
|
||||
};
|
||||
|
||||
template <typename Iter>
|
||||
//struct IterType<Iter, Void<typename Iter::container_type>> {
|
||||
struct IterType<Iter,
|
||||
//typename std::enable_if<std::is_void<typename Iter::value_type>::value>::type> {
|
||||
typename std::enable_if<IsInserter<Iter>::value>::type> {
|
||||
using type = typename std::decay<typename Iter::container_type::value_type>::type;
|
||||
};
|
||||
|
||||
template <typename Iter, typename T = Void<>>
|
||||
struct IsIter : std::false_type {};
|
||||
|
||||
template <typename Iter>
|
||||
struct IsIter<Iter, typename std::enable_if<IsInserter<Iter>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename Iter>
|
||||
//struct IsIter<Iter, Void<typename std::iterator_traits<Iter>::iterator_category>>
|
||||
struct IsIter<Iter,
|
||||
typename std::enable_if<!std::is_void<
|
||||
typename std::iterator_traits<Iter>::value_type>::value>::type>
|
||||
: std::integral_constant<bool, !std::is_convertible<Iter, StringView>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsKvPairIter : IsKvPair<typename IterType<T>::type> {};
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
struct TupleWithType : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct TupleWithType<T, std::tuple<>> : std::false_type {};
|
||||
|
||||
template <typename T, typename U, typename ...Args>
|
||||
struct TupleWithType<T, std::tuple<U, Args...>> : TupleWithType<T, std::tuple<Args...>> {};
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
struct TupleWithType<T, std::tuple<T, Args...>> : std::true_type {};
|
||||
|
||||
template <std::size_t ...Is>
|
||||
struct IndexSequence {};
|
||||
|
||||
template <std::size_t I, std::size_t ...Is>
|
||||
struct MakeIndexSequence : MakeIndexSequence<I - 1, I - 1, Is...> {};
|
||||
|
||||
template <std::size_t ...Is>
|
||||
struct MakeIndexSequence<0, Is...> : IndexSequence<Is...> {};
|
||||
|
||||
// NthType and NthValue are taken from
|
||||
// https://stackoverflow.com/questions/14261183
|
||||
template <std::size_t I, typename ...Args>
|
||||
struct NthType {};
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
struct NthType<0, Arg, Args...> {
|
||||
using type = Arg;
|
||||
};
|
||||
|
||||
template <std::size_t I, typename Arg, typename ...Args>
|
||||
struct NthType<I, Arg, Args...> {
|
||||
using type = typename NthType<I - 1, Args...>::type;
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
struct LastType {
|
||||
using type = typename NthType<sizeof...(Args) - 1, Args...>::type;
|
||||
};
|
||||
|
||||
struct InvalidLastType {};
|
||||
|
||||
template <>
|
||||
struct LastType<> {
|
||||
using type = InvalidLastType;
|
||||
};
|
||||
|
||||
template <std::size_t I, typename Arg, typename ...Args>
|
||||
auto NthValue(Arg &&arg, Args &&...)
|
||||
-> typename std::enable_if<(I == 0), decltype(std::forward<Arg>(arg))>::type {
|
||||
return std::forward<Arg>(arg);
|
||||
}
|
||||
|
||||
template <std::size_t I, typename Arg, typename ...Args>
|
||||
auto NthValue(Arg &&, Args &&...args)
|
||||
-> typename std::enable_if<(I > 0),
|
||||
decltype(std::forward<typename NthType<I, Arg, Args...>::type>(
|
||||
std::declval<typename NthType<I, Arg, Args...>::type>()))>::type {
|
||||
return std::forward<typename NthType<I, Arg, Args...>::type>(
|
||||
NthValue<I - 1>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
auto LastValue(Args &&...args)
|
||||
-> decltype(std::forward<typename LastType<Args...>::type>(
|
||||
std::declval<typename LastType<Args...>::type>())) {
|
||||
return std::forward<typename LastType<Args...>::type>(
|
||||
NthValue<sizeof...(Args) - 1>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename T, typename = Void<>>
|
||||
struct HasPushBack : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct HasPushBack<T,
|
||||
typename std::enable_if<
|
||||
std::is_void<decltype(
|
||||
std::declval<T>().push_back(std::declval<typename T::value_type>())
|
||||
)>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename T, typename = Void<>>
|
||||
struct HasInsert : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct HasInsert<T,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
decltype(std::declval<T>().insert(std::declval<typename T::const_iterator>(),
|
||||
std::declval<typename T::value_type>())),
|
||||
typename T::iterator>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsSequenceContainer
|
||||
: std::integral_constant<bool,
|
||||
HasPushBack<T>::value
|
||||
&& !std::is_same<typename std::decay<T>::type, std::string>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsAssociativeContainer
|
||||
: std::integral_constant<bool,
|
||||
HasInsert<T>::value && !HasPushBack<T>::value> {};
|
||||
|
||||
uint16_t crc16(const char *buf, int len);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_UTILS_H
|
33
controller/thirdparty/redis-plus-plus-1.1.1/test/CMakeLists.txt
vendored
Normal file
33
controller/thirdparty/redis-plus-plus-1.1.1/test/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
project(test_redis++)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
cmake_minimum_required(VERSION 3.0.0)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 2.8.0)
|
||||
endif()
|
||||
|
||||
set(PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src/sw/redis++)
|
||||
|
||||
file(GLOB PROJECT_SOURCE_FILES "${PROJECT_SOURCE_DIR}/*.cpp")
|
||||
|
||||
add_executable(${PROJECT_NAME} ${PROJECT_SOURCE_FILES})
|
||||
|
||||
# hiredis dependency
|
||||
find_path(HIREDIS_HEADER hiredis)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${HIREDIS_HEADER})
|
||||
|
||||
find_library(HIREDIS_STATIC_LIB libhiredis.a)
|
||||
target_link_libraries(${PROJECT_NAME} ${HIREDIS_STATIC_LIB})
|
||||
|
||||
# redis++ dependency
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ../src)
|
||||
set(REDIS_PLUS_PLUS_LIB ${CMAKE_CURRENT_BINARY_DIR}/../lib/libredis++.a)
|
||||
|
||||
## solaris socket dependency
|
||||
IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)" )
|
||||
target_link_libraries(${PROJECT_NAME} -lsocket)
|
||||
ENDIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)" )
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} ${REDIS_PLUS_PLUS_LIB} ${CMAKE_THREAD_LIBS_INIT})
|
83
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/benchmark_test.h
vendored
Normal file
83
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/benchmark_test.h
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
struct BenchmarkOptions {
|
||||
std::size_t pool_size = 5;
|
||||
std::size_t thread_num = 10;
|
||||
std::size_t total_request_num = 100000;
|
||||
std::size_t key_len = 10;
|
||||
std::size_t val_len = 10;
|
||||
};
|
||||
|
||||
template <typename RedisInstance>
|
||||
class BenchmarkTest {
|
||||
public:
|
||||
BenchmarkTest(const BenchmarkOptions &opts, RedisInstance &instance);
|
||||
|
||||
~BenchmarkTest() {
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
template <typename Func>
|
||||
void _run(const std::string &title, Func &&func);
|
||||
|
||||
template <typename Func>
|
||||
std::size_t _run(Func &&func, std::size_t request_num);
|
||||
|
||||
void _test_get();
|
||||
|
||||
std::vector<std::string> _gen_keys() const;
|
||||
|
||||
std::string _gen_value() const;
|
||||
|
||||
void _cleanup();
|
||||
|
||||
const std::string& _key(std::size_t idx) const {
|
||||
return _keys[idx % _keys.size()];
|
||||
}
|
||||
|
||||
BenchmarkOptions _opts;
|
||||
|
||||
RedisInstance &_redis;
|
||||
|
||||
std::vector<std::string> _keys;
|
||||
|
||||
std::string _value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "benchmark_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H
|
178
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/benchmark_test.hpp
vendored
Normal file
178
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/benchmark_test.hpp
vendored
Normal file
|
@ -0,0 +1,178 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <future>
|
||||
#include <algorithm>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
BenchmarkTest<RedisInstance>::BenchmarkTest(const BenchmarkOptions &opts,
|
||||
RedisInstance &instance) : _opts(opts), _redis(instance) {
|
||||
REDIS_ASSERT(_opts.pool_size > 0
|
||||
&& _opts.thread_num > 0
|
||||
&& _opts.total_request_num > 0
|
||||
&& _opts.key_len > 0
|
||||
&& _opts.val_len > 0,
|
||||
"Invalid benchmark test options.");
|
||||
|
||||
_keys = _gen_keys();
|
||||
_value = _gen_value();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void BenchmarkTest<RedisInstance>::run() {
|
||||
_cleanup();
|
||||
|
||||
_run("SET key value", [this](std::size_t idx) { this->_redis.set(this->_key(idx), _value); });
|
||||
|
||||
_run("GET key", [this](std::size_t idx) {
|
||||
auto res = this->_redis.get(this->_key(idx));
|
||||
(void)res;
|
||||
});
|
||||
|
||||
_cleanup();
|
||||
|
||||
_run("LPUSH key value", [this](std::size_t idx) {
|
||||
this->_redis.lpush(this->_key(idx), _value);
|
||||
});
|
||||
|
||||
_run("LRANGE key 0 10", [this](std::size_t idx) {
|
||||
std::vector<std::string> res;
|
||||
res.reserve(10);
|
||||
this->_redis.lrange(this->_key(idx), 0, 10, std::back_inserter(res));
|
||||
});
|
||||
|
||||
_run("LPOP key", [this](std::size_t idx) {
|
||||
auto res = this->_redis.lpop(this->_key(idx));
|
||||
(void)res;
|
||||
});
|
||||
|
||||
_cleanup();
|
||||
|
||||
_run("INCR key", [this](std::size_t idx) {
|
||||
auto num = this->_redis.incr(this->_key(idx));
|
||||
(void)num;
|
||||
});
|
||||
|
||||
_cleanup();
|
||||
|
||||
_run("SADD key member", [this](std::size_t idx) {
|
||||
auto num = this->_redis.sadd(this->_key(idx), _value);
|
||||
(void)num;
|
||||
});
|
||||
|
||||
_run("SPOP key", [this](std::size_t idx) {
|
||||
auto res = this->_redis.spop(this->_key(idx));
|
||||
(void)res;
|
||||
});
|
||||
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
template <typename Func>
|
||||
void BenchmarkTest<RedisInstance>::_run(const std::string &title, Func &&func) {
|
||||
auto thread_num = _opts.thread_num;
|
||||
auto requests_per_thread = _opts.total_request_num / thread_num;
|
||||
auto total_request_num = requests_per_thread * thread_num;
|
||||
std::vector<std::future<std::size_t>> res;
|
||||
res.reserve(thread_num);
|
||||
res.push_back(std::async(std::launch::async,
|
||||
[this](Func &&func, std::size_t request_num) {
|
||||
return this->_run(std::forward<Func>(func), request_num);
|
||||
},
|
||||
std::forward<Func>(func),
|
||||
requests_per_thread));
|
||||
|
||||
auto total_in_msec = 0;
|
||||
for (auto &fut : res) {
|
||||
total_in_msec += fut.get();
|
||||
}
|
||||
|
||||
auto total_in_sec = total_in_msec * 1.0 / 1000;
|
||||
|
||||
auto avg = total_in_msec * 1.0 / total_request_num;
|
||||
|
||||
auto ops = static_cast<std::size_t>(1000 / avg);
|
||||
|
||||
std::cout << "-----" << title << "-----" << std::endl;
|
||||
std::cout << total_request_num << " requests cost " << total_in_sec << " seconds" << std::endl;
|
||||
std::cout << ops << " requests per second" << std::endl;
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
template <typename Func>
|
||||
std::size_t BenchmarkTest<RedisInstance>::_run(Func &&func, std::size_t request_num) {
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
for (auto idx = 0U; idx != request_num; ++idx) {
|
||||
func(idx);
|
||||
}
|
||||
|
||||
auto stop = std::chrono::steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
std::vector<std::string> BenchmarkTest<RedisInstance>::_gen_keys() const {
|
||||
const auto KEY_NUM = 100;
|
||||
std::vector<std::string> res;
|
||||
res.reserve(KEY_NUM);
|
||||
std::default_random_engine engine(std::random_device{}());
|
||||
std::uniform_int_distribution<int> uniform_dist(0, 255);
|
||||
for (auto i = 0; i != KEY_NUM; ++i) {
|
||||
std::string str;
|
||||
str.reserve(_opts.key_len);
|
||||
for (std::size_t j = 0; j != _opts.key_len; ++j) {
|
||||
str.push_back(static_cast<char>(uniform_dist(engine)));
|
||||
}
|
||||
res.push_back(str);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
std::string BenchmarkTest<RedisInstance>::_gen_value() const {
|
||||
return std::string(_opts.val_len, 'x');
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void BenchmarkTest<RedisInstance>::_cleanup() {
|
||||
for (const auto &key : _keys) {
|
||||
_redis.del(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
|
49
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/connection_cmds_test.h
vendored
Normal file
49
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/connection_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class ConnectionCmdTest {
|
||||
public:
|
||||
explicit ConnectionCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _run(Redis &redis);
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "connection_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H
|
50
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/connection_cmds_test.hpp
vendored
Normal file
50
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/connection_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ConnectionCmdTest<RedisInstance>::run() {
|
||||
cluster_specializing_test(*this, &ConnectionCmdTest<RedisInstance>::_run, _redis);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ConnectionCmdTest<RedisInstance>::_run(Redis &instance) {
|
||||
auto message = std::string("hello");
|
||||
|
||||
REDIS_ASSERT(instance.echo(message) == message, "failed to test echo");
|
||||
|
||||
REDIS_ASSERT(instance.ping() == "PONG", "failed to test ping");
|
||||
|
||||
REDIS_ASSERT(instance.ping(message) == message, "failed to test ping");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP
|
47
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/geo_cmds_test.h
vendored
Normal file
47
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/geo_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class GeoCmdTest {
|
||||
public:
|
||||
explicit GeoCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "geo_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H
|
149
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/geo_cmds_test.hpp
vendored
Normal file
149
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/geo_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void GeoCmdTest<RedisInstance>::run() {
|
||||
auto key = test_key("geo");
|
||||
auto dest = test_key("dest");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {key, dest});
|
||||
|
||||
auto members = {
|
||||
std::make_tuple("m1", 10.0, 11.0),
|
||||
std::make_tuple("m2", 10.1, 11.1),
|
||||
std::make_tuple("m3", 10.2, 11.2)
|
||||
};
|
||||
|
||||
REDIS_ASSERT(_redis.geoadd(key, std::make_tuple("m1", 10.0, 11.0)) == 1,
|
||||
"failed to test geoadd");
|
||||
REDIS_ASSERT(_redis.geoadd(key, members) == 2, "failed to test geoadd");
|
||||
|
||||
auto dist = _redis.geodist(key, "m1", "m4", GeoUnit::KM);
|
||||
REDIS_ASSERT(!dist, "failed to test geodist with nonexistent member");
|
||||
|
||||
std::vector<OptionalString> hashes;
|
||||
_redis.geohash(key, {"m1", "m4"}, std::back_inserter(hashes));
|
||||
REDIS_ASSERT(hashes.size() == 2, "failed to test geohash");
|
||||
REDIS_ASSERT(bool(hashes[0]) && *(hashes[0]) == "s1zned3z8u0" && !(hashes[1]),
|
||||
"failed to test geohash");
|
||||
hashes.clear();
|
||||
_redis.geohash(key, {"m4"}, std::back_inserter(hashes));
|
||||
REDIS_ASSERT(hashes.size() == 1 && !(hashes[0]), "failed to test geohash");
|
||||
|
||||
std::vector<Optional<std::pair<double, double>>> pos;
|
||||
_redis.geopos(key, {"m4"}, std::back_inserter(pos));
|
||||
REDIS_ASSERT(pos.size() == 1 && !(pos[0]), "failed to test geopos");
|
||||
|
||||
auto num = _redis.georadius(key,
|
||||
std::make_pair(10.1, 11.1),
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
dest,
|
||||
false,
|
||||
10);
|
||||
REDIS_ASSERT(bool(num) && *num == 3, "failed to test georadius with store option");
|
||||
|
||||
std::vector<std::string> mems;
|
||||
_redis.georadius(key,
|
||||
std::make_pair(10.1, 11.1),
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(mems));
|
||||
REDIS_ASSERT(mems.size() == 3, "failed to test georadius with no option");
|
||||
|
||||
std::vector<std::tuple<std::string, double>> with_dist;
|
||||
_redis.georadius(key,
|
||||
std::make_pair(10.1, 11.1),
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(with_dist));
|
||||
REDIS_ASSERT(with_dist.size() == 3, "failed to test georadius with dist");
|
||||
|
||||
std::vector<std::tuple<std::string, double, std::pair<double, double>>> with_dist_coord;
|
||||
_redis.georadius(key,
|
||||
std::make_pair(10.1, 11.1),
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(with_dist_coord));
|
||||
REDIS_ASSERT(with_dist_coord.size() == 3, "failed to test georadius with dist and coord");
|
||||
|
||||
num = _redis.georadiusbymember(key,
|
||||
"m1",
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
dest,
|
||||
false,
|
||||
10);
|
||||
REDIS_ASSERT(bool(num) && *num == 3, "failed to test georadiusbymember with store option");
|
||||
|
||||
mems.clear();
|
||||
_redis.georadiusbymember(key,
|
||||
"m1",
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(mems));
|
||||
REDIS_ASSERT(mems.size() == 3, "failed to test georadiusbymember with no option");
|
||||
|
||||
with_dist.clear();
|
||||
_redis.georadiusbymember(key,
|
||||
"m1",
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(with_dist));
|
||||
REDIS_ASSERT(with_dist.size() == 3, "failed to test georadiusbymember with dist");
|
||||
|
||||
with_dist_coord.clear();
|
||||
_redis.georadiusbymember(key,
|
||||
"m1",
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(with_dist_coord));
|
||||
REDIS_ASSERT(with_dist_coord.size() == 3,
|
||||
"failed to test georadiusbymember with dist and coord");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP
|
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hash_cmds_test.h
vendored
Normal file
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hash_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class HashCmdTest {
|
||||
public:
|
||||
explicit HashCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_hash();
|
||||
|
||||
void _test_hash_batch();
|
||||
|
||||
void _test_numeric();
|
||||
|
||||
void _test_hscan();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "hash_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H
|
177
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hash_cmds_test.hpp
vendored
Normal file
177
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hash_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP
|
||||
|
||||
#include <unordered_map>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::run() {
|
||||
_test_hash();
|
||||
|
||||
_test_hash_batch();
|
||||
|
||||
_test_numeric();
|
||||
|
||||
_test_hscan();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::_test_hash() {
|
||||
auto key = test_key("hash");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto f1 = std::string("f1");
|
||||
auto v1 = std::string("v1");
|
||||
auto f2 = std::string("f2");
|
||||
auto v2 = std::string("v2");
|
||||
auto f3 = std::string("f3");
|
||||
auto v3 = std::string("v3");
|
||||
|
||||
REDIS_ASSERT(_redis.hset(key, f1, v1), "failed to test hset");
|
||||
REDIS_ASSERT(!_redis.hset(key, f1, v2), "failed to test hset with exist field");
|
||||
|
||||
auto res = _redis.hget(key, f1);
|
||||
REDIS_ASSERT(res && *res == v2, "failed to test hget");
|
||||
|
||||
REDIS_ASSERT(_redis.hsetnx(key, f2, v1), "failed to test hsetnx");
|
||||
REDIS_ASSERT(!_redis.hsetnx(key, f2, v2), "failed to test hsetnx with exist field");
|
||||
|
||||
res = _redis.hget(key, f2);
|
||||
REDIS_ASSERT(res && *res == v1, "failed to test hget");
|
||||
|
||||
REDIS_ASSERT(!_redis.hexists(key, f3), "failed to test hexists");
|
||||
REDIS_ASSERT(_redis.hset(key, std::make_pair(f3, v3)), "failed to test hset");
|
||||
REDIS_ASSERT(_redis.hexists(key, f3), "failed to test hexists");
|
||||
|
||||
REDIS_ASSERT(_redis.hlen(key) == 3, "failed to test hlen");
|
||||
REDIS_ASSERT(_redis.hstrlen(key, f1) == static_cast<long long>(v1.size()),
|
||||
"failed to test hstrlen");
|
||||
|
||||
REDIS_ASSERT(_redis.hdel(key, f1) == 1, "failed to test hdel");
|
||||
REDIS_ASSERT(_redis.hdel(key, {f1, f2, f3}) == 2, "failed to test hdel range");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::_test_hash_batch() {
|
||||
auto key = test_key("hash");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto f1 = std::string("f1");
|
||||
auto v1 = std::string("v1");
|
||||
auto f2 = std::string("f2");
|
||||
auto v2 = std::string("v2");
|
||||
auto f3 = std::string("f3");
|
||||
|
||||
_redis.hmset(key, {std::make_pair(f1, v1),
|
||||
std::make_pair(f2, v2)});
|
||||
|
||||
std::vector<std::string> fields;
|
||||
_redis.hkeys(key, std::back_inserter(fields));
|
||||
REDIS_ASSERT(fields.size() == 2, "failed to test hkeys");
|
||||
|
||||
std::vector<std::string> vals;
|
||||
_redis.hvals(key, std::back_inserter(vals));
|
||||
REDIS_ASSERT(vals.size() == 2, "failed to test hvals");
|
||||
|
||||
std::unordered_map<std::string, std::string> items;
|
||||
_redis.hgetall(key, std::inserter(items, items.end()));
|
||||
REDIS_ASSERT(items.size() == 2 && items[f1] == v1 && items[f2] == v2,
|
||||
"failed to test hgetall");
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> item_vec;
|
||||
_redis.hgetall(key, std::back_inserter(item_vec));
|
||||
REDIS_ASSERT(item_vec.size() == 2, "failed to test hgetall");
|
||||
|
||||
std::vector<OptionalString> res;
|
||||
_redis.hmget(key, {f1, f2, f3}, std::back_inserter(res));
|
||||
REDIS_ASSERT(res.size() == 3
|
||||
&& bool(res[0]) && *(res[0]) == v1
|
||||
&& bool(res[1]) && *(res[1]) == v2
|
||||
&& !res[2],
|
||||
"failed to test hmget");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::_test_numeric() {
|
||||
auto key = test_key("numeric");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto field = "field";
|
||||
|
||||
REDIS_ASSERT(_redis.hincrby(key, field, 1) == 1, "failed to test hincrby");
|
||||
REDIS_ASSERT(_redis.hincrby(key, field, -1) == 0, "failed to test hincrby");
|
||||
REDIS_ASSERT(_redis.hincrbyfloat(key, field, 1.5) == 1.5, "failed to test hincrbyfloat");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::_test_hscan() {
|
||||
auto key = test_key("hscan");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto items = std::unordered_map<std::string, std::string>{
|
||||
std::make_pair("f1", "v1"),
|
||||
std::make_pair("f2", "v2"),
|
||||
std::make_pair("f3", "v3"),
|
||||
};
|
||||
|
||||
_redis.hmset(key, items.begin(), items.end());
|
||||
|
||||
std::unordered_map<std::string, std::string> item_map;
|
||||
auto cursor = 0;
|
||||
while (true) {
|
||||
cursor = _redis.hscan(key, cursor, "f*", 2, std::inserter(item_map, item_map.end()));
|
||||
if (cursor == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
REDIS_ASSERT(item_map == items, "failed to test hscan with pattern and count");
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> item_vec;
|
||||
cursor = 0;
|
||||
while (true) {
|
||||
cursor = _redis.hscan(key, cursor, std::back_inserter(item_vec));
|
||||
if (cursor == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
REDIS_ASSERT(item_vec.size() == items.size(), "failed to test hscan");
|
||||
for (const auto &ele : item_vec) {
|
||||
REDIS_ASSERT(items.find(ele.first) != items.end(), "failed to test hscan");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP
|
47
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hyperloglog_cmds_test.h
vendored
Normal file
47
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hyperloglog_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class HyperloglogCmdTest {
|
||||
public:
|
||||
explicit HyperloglogCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "hyperloglog_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H
|
67
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hyperloglog_cmds_test.hpp
vendored
Normal file
67
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hyperloglog_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HyperloglogCmdTest<RedisInstance>::run() {
|
||||
auto k1 = test_key("k1");
|
||||
auto k2 = test_key("k2");
|
||||
auto k3 = test_key("k3");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {k1, k2, k3});
|
||||
|
||||
_redis.pfadd(k1, "a");
|
||||
auto members1 = {"b", "c", "d", "e", "f", "g"};
|
||||
_redis.pfadd(k1, members1);
|
||||
|
||||
auto cnt = _redis.pfcount(k1);
|
||||
auto err = cnt * 1.0 / (1 + members1.size());
|
||||
REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfadd and pfcount");
|
||||
|
||||
auto members2 = {"a", "b", "c", "h", "i", "j", "k"};
|
||||
_redis.pfadd(k2, members2);
|
||||
auto total = 1 + members1.size() + members2.size() - 3;
|
||||
|
||||
cnt = _redis.pfcount({k1, k2});
|
||||
err = cnt * 1.0 / total;
|
||||
REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfcount");
|
||||
|
||||
_redis.pfmerge(k3, {k1, k2});
|
||||
cnt = _redis.pfcount(k3);
|
||||
err = cnt * 1.0 / total;
|
||||
REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfcount");
|
||||
|
||||
_redis.pfmerge(k3, k1);
|
||||
REDIS_ASSERT(cnt == _redis.pfcount(k3), "failed to test pfmerge");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP
|
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/keys_cmds_test.h
vendored
Normal file
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/keys_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class KeysCmdTest {
|
||||
public:
|
||||
explicit KeysCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_key();
|
||||
|
||||
void _test_randomkey(Redis &instance);
|
||||
|
||||
void _test_ttl();
|
||||
|
||||
void _test_scan(Redis &instance);
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "keys_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H
|
166
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/keys_cmds_test.hpp
vendored
Normal file
166
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/keys_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::run() {
|
||||
_test_key();
|
||||
|
||||
cluster_specializing_test(*this, &KeysCmdTest<RedisInstance>::_test_randomkey, _redis);
|
||||
|
||||
_test_ttl();
|
||||
|
||||
cluster_specializing_test(*this, &KeysCmdTest<RedisInstance>::_test_scan, _redis);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::_test_key() {
|
||||
auto key = test_key("key");
|
||||
auto dest = test_key("dest");
|
||||
auto new_key_name = test_key("new-key");
|
||||
auto not_exist_key = test_key("not-exist");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {key, dest, new_key_name});
|
||||
|
||||
REDIS_ASSERT(_redis.exists(key) == 0, "failed to test exists");
|
||||
|
||||
auto val = std::string("val");
|
||||
_redis.set(key, val);
|
||||
|
||||
REDIS_ASSERT(_redis.exists({key, not_exist_key}) == 1, "failed to test exists");
|
||||
|
||||
auto new_val = _redis.dump(key);
|
||||
REDIS_ASSERT(bool(new_val), "failed to test dump");
|
||||
|
||||
_redis.restore(dest, *new_val, std::chrono::seconds(1000));
|
||||
|
||||
new_val = _redis.get(dest);
|
||||
REDIS_ASSERT(bool(new_val) && *new_val == val, "failed to test dump and restore");
|
||||
|
||||
_redis.rename(dest, new_key_name);
|
||||
|
||||
bool not_exist = false;
|
||||
try {
|
||||
_redis.rename(not_exist_key, new_key_name);
|
||||
} catch (const Error &e) {
|
||||
not_exist = true;
|
||||
}
|
||||
REDIS_ASSERT(not_exist, "failed to test rename with nonexistent key");
|
||||
|
||||
REDIS_ASSERT(_redis.renamenx(new_key_name, dest), "failed to test renamenx");
|
||||
|
||||
REDIS_ASSERT(_redis.touch(not_exist_key) == 0, "failed to test touch");
|
||||
REDIS_ASSERT(_redis.touch({key, dest, new_key_name}) == 2, "failed to test touch");
|
||||
|
||||
REDIS_ASSERT(_redis.type(key) == "string", "failed to test type");
|
||||
|
||||
REDIS_ASSERT(_redis.del({new_key_name, dest}) == 1, "failed to test del");
|
||||
REDIS_ASSERT(_redis.unlink({new_key_name, key}) == 1, "failed to test unlink");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::_test_randomkey(Redis &instance) {
|
||||
auto key = test_key("randomkey");
|
||||
|
||||
KeyDeleter<Redis> deleter(instance, key);
|
||||
|
||||
instance.set(key, "value");
|
||||
|
||||
auto rand_key = instance.randomkey();
|
||||
REDIS_ASSERT(bool(rand_key), "failed to test randomkey");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::_test_ttl() {
|
||||
using namespace std::chrono;
|
||||
|
||||
auto key = test_key("ttl");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
_redis.set(key, "val", seconds(100));
|
||||
auto ttl = _redis.ttl(key);
|
||||
REDIS_ASSERT(ttl > 0 && ttl <= 100, "failed to test ttl");
|
||||
|
||||
REDIS_ASSERT(_redis.persist(key), "failed to test persist");
|
||||
ttl = _redis.ttl(key);
|
||||
REDIS_ASSERT(ttl == -1, "failed to test ttl");
|
||||
|
||||
REDIS_ASSERT(_redis.expire(key, seconds(100)),
|
||||
"failed to test expire");
|
||||
|
||||
auto tp = time_point_cast<seconds>(system_clock::now() + seconds(100));
|
||||
REDIS_ASSERT(_redis.expireat(key, tp), "failed to test expireat");
|
||||
ttl = _redis.ttl(key);
|
||||
REDIS_ASSERT(ttl > 0, "failed to test expireat");
|
||||
|
||||
REDIS_ASSERT(_redis.pexpire(key, milliseconds(100000)), "failed to test expire");
|
||||
|
||||
auto pttl = _redis.pttl(key);
|
||||
REDIS_ASSERT(pttl > 0 && pttl <= 100000, "failed to test pttl");
|
||||
|
||||
auto tp_milli = time_point_cast<milliseconds>(system_clock::now() + milliseconds(100000));
|
||||
REDIS_ASSERT(_redis.pexpireat(key, tp_milli), "failed to test pexpireat");
|
||||
pttl = _redis.pttl(key);
|
||||
REDIS_ASSERT(pttl > 0, "failed to test pexpireat");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::_test_scan(Redis &instance) {
|
||||
std::string key_pattern = "!@#$%^&()_+alseufoawhnlkszd";
|
||||
auto k1 = test_key(key_pattern + "k1");
|
||||
auto k2 = test_key(key_pattern + "k2");
|
||||
auto k3 = test_key(key_pattern + "k3");
|
||||
|
||||
auto keys = {k1, k2, k3};
|
||||
|
||||
KeyDeleter<Redis> deleter(instance, keys);
|
||||
|
||||
instance.set(k1, "v");
|
||||
instance.set(k2, "v");
|
||||
instance.set(k3, "v");
|
||||
|
||||
auto cursor = 0;
|
||||
std::unordered_set<std::string> res;
|
||||
while (true) {
|
||||
cursor = instance.scan(cursor, "*" + key_pattern + "*", 2, std::inserter(res, res.end()));
|
||||
if (cursor == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
REDIS_ASSERT(res == std::unordered_set<std::string>(keys),
|
||||
"failed to test scan");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP
|
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/list_cmds_test.h
vendored
Normal file
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/list_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class ListCmdTest {
|
||||
public:
|
||||
explicit ListCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_lpoppush();
|
||||
|
||||
void _test_rpoppush();
|
||||
|
||||
void _test_list();
|
||||
|
||||
void _test_blocking();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "list_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H
|
154
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/list_cmds_test.hpp
vendored
Normal file
154
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/list_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,154 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::run() {
|
||||
_test_lpoppush();
|
||||
|
||||
_test_rpoppush();
|
||||
|
||||
_test_list();
|
||||
|
||||
_test_blocking();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::_test_lpoppush() {
|
||||
auto key = test_key("lpoppush");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto item = _redis.lpop(key);
|
||||
REDIS_ASSERT(!item, "failed to test lpop");
|
||||
|
||||
REDIS_ASSERT(_redis.lpushx(key, "1") == 0, "failed to test lpushx");
|
||||
REDIS_ASSERT(_redis.lpush(key, "1") == 1, "failed to test lpush");
|
||||
REDIS_ASSERT(_redis.lpushx(key, "2") == 2, "failed to test lpushx");
|
||||
REDIS_ASSERT(_redis.lpush(key, {"3", "4", "5"}) == 5, "failed to test lpush");
|
||||
|
||||
item = _redis.lpop(key);
|
||||
REDIS_ASSERT(item && *item == "5", "failed to test lpop");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::_test_rpoppush() {
|
||||
auto key = test_key("rpoppush");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto item = _redis.rpop(key);
|
||||
REDIS_ASSERT(!item, "failed to test rpop");
|
||||
|
||||
REDIS_ASSERT(_redis.rpushx(key, "1") == 0, "failed to test rpushx");
|
||||
REDIS_ASSERT(_redis.rpush(key, "1") == 1, "failed to test rpush");
|
||||
REDIS_ASSERT(_redis.rpushx(key, "2") == 2, "failed to test rpushx");
|
||||
REDIS_ASSERT(_redis.rpush(key, {"3", "4", "5"}) == 5, "failed to test rpush");
|
||||
|
||||
item = _redis.rpop(key);
|
||||
REDIS_ASSERT(item && *item == "5", "failed to test rpop");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::_test_list() {
|
||||
auto key = test_key("list");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto item = _redis.lindex(key, 0);
|
||||
REDIS_ASSERT(!item, "failed to test lindex");
|
||||
|
||||
_redis.lpush(key, {"1", "2", "3", "4", "5"});
|
||||
|
||||
REDIS_ASSERT(_redis.lrem(key, 0, "3") == 1, "failed to test lrem");
|
||||
|
||||
REDIS_ASSERT(_redis.linsert(key, InsertPosition::BEFORE, "2", "3") == 5,
|
||||
"failed to test lindex");
|
||||
|
||||
REDIS_ASSERT(_redis.llen(key) == 5, "failed to test llen");
|
||||
|
||||
_redis.lset(key, 0, "6");
|
||||
item = _redis.lindex(key, 0);
|
||||
REDIS_ASSERT(item && *item == "6", "failed to test lindex");
|
||||
|
||||
_redis.ltrim(key, 0, 2);
|
||||
|
||||
std::vector<std::string> res;
|
||||
_redis.lrange(key, 0, -1, std::back_inserter(res));
|
||||
REDIS_ASSERT(res == std::vector<std::string>({"6", "4", "3"}), "failed to test ltrim");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::_test_blocking() {
|
||||
auto k1 = test_key("k1");
|
||||
auto k2 = test_key("k2");
|
||||
auto k3 = test_key("k3");
|
||||
|
||||
auto keys = {k1, k2, k3};
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, keys);
|
||||
|
||||
std::string val("value");
|
||||
_redis.lpush(k1, val);
|
||||
|
||||
auto res = _redis.blpop(keys.begin(), keys.end());
|
||||
REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test blpop");
|
||||
|
||||
res = _redis.brpop(keys, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(!res, "failed to test brpop with timeout");
|
||||
|
||||
_redis.lpush(k1, val);
|
||||
res = _redis.blpop(k1);
|
||||
REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test blpop");
|
||||
|
||||
res = _redis.blpop(k1, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(!res, "failed to test blpop with timeout");
|
||||
|
||||
_redis.lpush(k1, val);
|
||||
res = _redis.brpop(k1);
|
||||
REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test brpop");
|
||||
|
||||
res = _redis.brpop(k1, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(!res, "failed to test brpop with timeout");
|
||||
|
||||
auto str = _redis.brpoplpush(k2, k3, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(!str, "failed to test brpoplpush with timeout");
|
||||
|
||||
_redis.lpush(k2, val);
|
||||
str = _redis.brpoplpush(k2, k3);
|
||||
REDIS_ASSERT(str && *str == val, "failed to test brpoplpush");
|
||||
|
||||
str = _redis.rpoplpush(k3, k2);
|
||||
REDIS_ASSERT(str && *str == val, "failed to test rpoplpush");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP
|
57
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pipeline_transaction_test.h
vendored
Normal file
57
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pipeline_transaction_test.h
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class PipelineTransactionTest {
|
||||
public:
|
||||
explicit PipelineTransactionTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
Pipeline _pipeline(const StringView &key);
|
||||
|
||||
Transaction _transaction(const StringView &key, bool piped);
|
||||
|
||||
void _test_pipeline(const StringView &key, Pipeline &pipe);
|
||||
|
||||
void _test_transaction(const StringView &key, Transaction &tx);
|
||||
|
||||
void _test_watch();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "pipeline_transaction_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H
|
184
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pipeline_transaction_test.hpp
vendored
Normal file
184
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pipeline_transaction_test.hpp
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP
|
||||
|
||||
#include <string>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PipelineTransactionTest<RedisInstance>::run() {
|
||||
{
|
||||
auto key = test_key("pipeline");
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
auto pipe = _pipeline(key);
|
||||
_test_pipeline(key, pipe);
|
||||
}
|
||||
|
||||
{
|
||||
auto key = test_key("transaction");
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
auto tx = _transaction(key, true);
|
||||
_test_transaction(key, tx);
|
||||
}
|
||||
|
||||
{
|
||||
auto key = test_key("transaction");
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
auto tx = _transaction(key, false);
|
||||
_test_transaction(key, tx);
|
||||
}
|
||||
|
||||
_test_watch();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
Pipeline PipelineTransactionTest<RedisInstance>::_pipeline(const StringView &) {
|
||||
return _redis.pipeline();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Pipeline PipelineTransactionTest<RedisCluster>::_pipeline(const StringView &key) {
|
||||
return _redis.pipeline(key);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
Transaction PipelineTransactionTest<RedisInstance>::_transaction(const StringView &, bool piped) {
|
||||
return _redis.transaction(piped);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Transaction PipelineTransactionTest<RedisCluster>::_transaction(const StringView &key,
|
||||
bool piped) {
|
||||
return _redis.transaction(key, piped);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PipelineTransactionTest<RedisInstance>::_test_pipeline(const StringView &key,
|
||||
Pipeline &pipe) {
|
||||
std::string val("value");
|
||||
auto replies = pipe.set(key, val)
|
||||
.get(key)
|
||||
.strlen(key)
|
||||
.exec();
|
||||
|
||||
REDIS_ASSERT(replies.get<bool>(0), "failed to test pipeline with set operation");
|
||||
|
||||
auto new_val = replies.get<OptionalString>(1);
|
||||
std::size_t len = replies.get<long long>(2);
|
||||
REDIS_ASSERT(bool(new_val) && *new_val == val && len == val.size(),
|
||||
"failed to test pipeline with string operations");
|
||||
|
||||
REDIS_ASSERT(reply::parse<bool>(replies.get(0)), "failed to test pipeline with set operation");
|
||||
|
||||
new_val = reply::parse<OptionalString>(replies.get(1));
|
||||
len = reply::parse<long long>(replies.get(2));
|
||||
REDIS_ASSERT(bool(new_val) && *new_val == val && len == val.size(),
|
||||
"failed to test pipeline with string operations");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PipelineTransactionTest<RedisInstance>::_test_transaction(const StringView &key,
|
||||
Transaction &tx) {
|
||||
std::unordered_map<std::string, std::string> m = {
|
||||
std::make_pair("f1", "v1"),
|
||||
std::make_pair("f2", "v2"),
|
||||
std::make_pair("f3", "v3")
|
||||
};
|
||||
auto replies = tx.hmset(key, m.begin(), m.end())
|
||||
.hgetall(key)
|
||||
.hdel(key, "f1")
|
||||
.exec();
|
||||
|
||||
replies.get<void>(0);
|
||||
|
||||
decltype(m) mm;
|
||||
replies.get(1, std::inserter(mm, mm.end()));
|
||||
REDIS_ASSERT(mm == m, "failed to test transaction");
|
||||
|
||||
REDIS_ASSERT(replies.get<long long>(2) == 1, "failed to test transaction");
|
||||
|
||||
tx.set(key, "value")
|
||||
.get(key)
|
||||
.incr(key);
|
||||
|
||||
tx.discard();
|
||||
|
||||
replies = tx.del(key)
|
||||
.set(key, "value")
|
||||
.exec();
|
||||
|
||||
REDIS_ASSERT(replies.get<long long>(0) == 1, "failed to test transaction");
|
||||
|
||||
REDIS_ASSERT(replies.get<bool>(1), "failed to test transaction");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PipelineTransactionTest<RedisInstance>::_test_watch() {
|
||||
auto key = test_key("watch");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
{
|
||||
auto tx = _transaction(key, false);
|
||||
|
||||
auto redis = tx.redis();
|
||||
|
||||
redis.watch(key);
|
||||
|
||||
auto replies = tx.set(key, "1").get(key).exec();
|
||||
|
||||
REDIS_ASSERT(replies.size() == 2
|
||||
&& replies.template get<bool>(0) == true, "failed to test watch");
|
||||
|
||||
auto val = replies.template get<sw::redis::OptionalString>(1);
|
||||
|
||||
REDIS_ASSERT(val && *val == "1", "failed to test watch");
|
||||
}
|
||||
|
||||
try {
|
||||
auto tx = _transaction(key, false);
|
||||
|
||||
auto redis = tx.redis();
|
||||
|
||||
redis.watch(key);
|
||||
|
||||
// Key has been modified by other client.
|
||||
_redis.set(key, "val");
|
||||
|
||||
// Transaction should fail, and throw WatchError
|
||||
tx.set(key, "1").exec();
|
||||
|
||||
REDIS_ASSERT(false, "failed to test watch");
|
||||
} catch (const sw::redis::WatchError &err) {
|
||||
// Catch the error.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue