├── .Rbuildignore ├── .editorconfig ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── ChangeLog ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── R ├── pubsub.R └── redis.R ├── README.md ├── cleanup ├── configure ├── configure.ac ├── demo └── 00Index ├── inst ├── NEWS.Rd ├── demos │ ├── queueDemo.R │ ├── quickTest.R │ ├── simDemo.R │ ├── simpleBenchmark.R │ ├── simpleBenchmark2.R │ ├── simpleRedisClient.R │ └── spDemo.R ├── examples │ ├── readC++.cpp │ ├── readPython.py │ ├── readR.R │ ├── readShell.sh │ ├── runAll.sh │ ├── writeC++.cpp │ ├── writePython.py │ ├── writeR.R │ └── writeShell.sh ├── hiredis.COPYING ├── pubsub │ ├── README.md │ ├── example_screenshot.png │ ├── intraday-ES-from-Redis.r │ ├── intraday-ES-to-Redis.r │ ├── intraday-GLOBEX-from-Redis.r │ ├── intraday-GLOBEX-to-Redis.r │ └── pruneRedis.r └── tinytest │ ├── test_basics.R │ ├── test_exec.R │ ├── test_execv.R │ ├── test_exists.R │ ├── test_expire.R │ ├── test_hash.R │ ├── test_list.R │ ├── test_server_issues.R │ └── test_set.R ├── man ├── redisMonitorChannels.Rd └── rhiredis.Rd ├── src ├── Makevars.in ├── Makevars.ucrt ├── Makevars.win ├── Redis.cpp ├── hiredis │ ├── COPYING │ ├── GNUmakefile │ ├── alloc.c │ ├── alloc.h │ ├── async.c │ ├── async.h │ ├── async_private.h │ ├── dict.c │ ├── dict.h │ ├── fmacros.h │ ├── hiredis.c │ ├── hiredis.h │ ├── net.c │ ├── net.h │ ├── read.c │ ├── read.h │ ├── sds.c │ ├── sds.h │ ├── sdsalloc.h │ ├── sockcompat.c │ ├── sockcompat.h │ └── win32.h └── init.c ├── tests ├── pubsub.R └── tinytest.R ├── tools └── winlibs.R └── vignettes ├── market-monitoring.Rnw ├── redis-intro.pdf ├── redis-introduction.Rnw ├── redis-monitoring.pdf └── rmd ├── four-symbols.png ├── gspc_2022-02.png ├── redis-intro.Rmd ├── redis-monitoring.Rmd └── redis.bib /.Rbuildignore: -------------------------------------------------------------------------------- 1 | LICENSE 2 | ^\.travis\.yml$ 3 | ^.*\.Rproj$ 4 | ^\.Rproj\.user$ 5 | ^local 6 | .*\.tar\.gz$ 7 | ^\.github 8 | ^\.editorconfig$ 9 | ^vignettes/rmd 10 | demo/00Index 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | # Matches multiple files with brace expansion notation 13 | # 4 space indentation 14 | [*.{c,cpp,h,hpp,R,r}] 15 | indent_style = space 16 | indent_size = 4 17 | 18 | # Tab indentation (no size specified) 19 | [Makefile] 20 | indent_style = tab 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | # Run CI for R using https://eddelbuettel.github.io/r-ci/ 2 | 3 | name: ci 4 | 5 | on: 6 | push: 7 | pull_request: 8 | 9 | env: 10 | _R_CHECK_FORCE_SUGGESTS_: "false" 11 | RunRcppRedisTests: "no" 12 | 13 | jobs: 14 | ci: 15 | strategy: 16 | matrix: 17 | include: 18 | #- {os: macOS-latest} 19 | - {os: ubuntu-latest} 20 | 21 | runs-on: ${{ matrix.os }} 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | 27 | - name: Setup 28 | uses: eddelbuettel/github-actions/r-ci-setup@master 29 | 30 | - name: Bootstrap 31 | run: ./run.sh bootstrap 32 | 33 | - name: Dependencies 34 | run: | 35 | ./run.sh install_aptget libhiredis-dev 36 | ./run.sh install_deps 37 | 38 | - name: Test 39 | run: ./run.sh run_tests 40 | 41 | #- name: Coverage 42 | # if: ${{ matrix.os == 'ubuntu-latest' }} 43 | # run: ./run.sh coverage 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | src/*.o 5 | src/*.so 6 | src/*.dll 7 | ^\.Rproj\.user$ 8 | vignettes/rmd/redis-intro.pdf 9 | vignettes/rmd/redis-monitoring.pdf 10 | local/ 11 | vignettes/rmd/intro-arxiv/ 12 | vignettes/rmd/monitoring-arxiv/ 13 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: RcppRedis 2 | Type: Package 3 | Title: 'Rcpp' Bindings for 'Redis' using the 'hiredis' Library 4 | Version: 0.2.5.1 5 | Date: 2025-03-26 6 | Authors@R: c(person("Dirk", "Eddelbuettel", role = c("aut", "cre"), email = "edd@debian.org", 7 | comment = c(ORCID = "0000-0001-6419-907X")), 8 | person("Bryan", "Lewis", role = "aut", comment = "pub/sub code from 'rredis'")) 9 | Description: Connection to the 'Redis' key/value store using the 10 | C-language client library 'hiredis' (included as a fallback) with 11 | 'MsgPack' encoding provided via 'RcppMsgPack' headers. It now also 12 | includes the pub/sub functions from the 'rredis' package. 13 | SystemRequirements: An available hiredis library (eg via package 14 | libhiredis-dev on Debian/Ubuntu, hiredis-devel on Fedora/RedHat, or 15 | directly from source from ) can be 16 | used but version 1.0.2 is also included and built on demand if needed. 17 | The optional 'rredis' package can be installed from the 'ghrr' 'drat' 18 | repo for additional illustrations and tests. 19 | URL: https://github.com/eddelbuettel/rcppredis, https://dirk.eddelbuettel.com/code/rcpp.redis.html 20 | BugReports: https://github.com/eddelbuettel/rcppredis/issues 21 | License: GPL (>= 2) 22 | Imports: methods, Rcpp (>= 0.11.0), RApiSerialize (>= 0.1.4) 23 | LinkingTo: Rcpp, RApiSerialize 24 | Suggests: RcppMsgPack, tinytest 25 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | useDynLib(RcppRedis, .registration=TRUE) # shared library from this package 2 | import(methods,Rcpp,RApiSerialize) # packages we require 3 | exportPattern("^[[:alpha:]]+") # export all identifiers starting with letters 4 | -------------------------------------------------------------------------------- /R/pubsub.R: -------------------------------------------------------------------------------- 1 | # Callback handler for convenience 2 | redisMonitorChannels <- function(context, type=c("rdata", "raw", "string")) { 3 | type <- match.arg(type) 4 | x <- context$listen(type) 5 | if (length(x) != 3 || x[[1]] != "message") return(x) 6 | if (exists(x[[2]], mode="function")) { 7 | return(do.call(x[[2]],as.list(x[[3]]))) 8 | } 9 | x 10 | } 11 | -------------------------------------------------------------------------------- /R/redis.R: -------------------------------------------------------------------------------- 1 | ## minimal R interface to redis using the hiredis library 2 | 3 | ## ensure module gets loaded 4 | loadModule("Redis", TRUE) 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## RcppRedis: RcppRedis is a Rcpp and hiredis-based Redis client for R 2 | 3 | [![CI](https://github.com/eddelbuettel/rcppredis/workflows/ci/badge.svg)](https://github.com/eddelbuettel/rcppredis/actions?query=workflow%3Aci) 4 | [![License](https://img.shields.io/badge/license-GPL%20%28%3E=%202%29-brightgreen.svg?style=flat)](https://www.gnu.org/licenses/gpl-2.0.html) 5 | [![CRAN](https://www.r-pkg.org/badges/version/RcppRedis)](https://cran.r-project.org/package=RcppRedis) 6 | [![r-universe](https://eddelbuettel.r-universe.dev/badges/RcppRedis)](https://eddelbuettel.r-universe.dev/RcppRedis) 7 | [![Dependencies](https://tinyverse.netlify.app/badge/RcppRedis)](https://cran.r-project.org/package=RcppRedis) 8 | [![Downloads](https://cranlogs.r-pkg.org/badges/RcppRedis?color=brightgreen)](https://www.r-pkg.org:443/pkg/RcppRedis) 9 | [![Last Commit](https://img.shields.io/github/last-commit/eddelbuettel/rcppredis)](https://github.com/eddelbuettel/rcppredis) 10 | 11 | ### Dependencies 12 | 13 | The package has three dependencies that should be easily resolvable: 14 | 15 | - [hiredis](https://github.com/redis/hiredis), the main C library for redis, eg via 16 | [libhiredis-dev](https://packages.debian.org/sid/libhiredis-dev) on Debian or Ubuntu; as 17 | a fallback [hiredis](https://github.com/redis/hiredis) is also included 18 | - [Rcpp](https://github.com/RcppCore/Rcpp) for seamless R and C++ integration (on 19 | [CRAN](https://cran.r-project.org/package=Rcpp)) 20 | - [RApiSerialize](https://github.com/eddelbuettel/rapiserialize) for C-level serialization 21 | from the R API (on [CRAN](https://cran.r-project.org/package=RApiSerialize)) , and if 22 | so, of sufficient vintage80 23 | 24 | The package should install from source like any other R package. If the a 25 | [hiredis](https://github.com/redis/hiredis) library is found, it will be used. The 26 | `pkg-config` script is used to find the hiredis headers and library. Otherwise the 27 | embedded [hiredis](https://github.com/redis/hiredis) is used. All of Rcpp, RApiSerialized 28 | and RcppRedis can be installed directly from [CRAN](https://cran.r-project.org) (which is 29 | the recommended approach) or GitHub. 30 | 31 | [MessagePack](http://msgpack.org/index.html) support is optional, and provided by 32 | [RcppMsgPack](https://github.com/eddelbuettel/rcppmsgpack) package on 33 | [CRAN](https://cran.r-project.org/package=RcppMsgPack) which, if installed, is used to 34 | provide [MessagePack](http://msgpack.org/index.html) headers for 35 | [MessagePack](http://msgpack.org/index.html) serialization. 36 | 37 | A backend is needed to run the code. The package will work with any protocol-compatible backend and 38 | for example [valkey](https://valkey.io/) (which is increasingly replacing redis) works just fine. 39 | 40 | ### Getting Started 41 | 42 | Run some of the scripts from the `demos/` or `examples/` directories. 43 | 44 | ### Status 45 | 46 | The package works well, is used in production, and has been on 47 | [CRAN](https://cran.r-project.org) for some time. 48 | 49 | It is however only providing a subset of the Redis API. 50 | 51 | ### History 52 | 53 | This package was derived from an initial fork of an earlier attempt named 54 | 'rhiredis' by Wush Wu, and has since been extended in a number of 55 | ways. William Pleasant provided some early patches. Whit Armstrong and 56 | Russell Pierce contributed extensions. We also offered a new home for the 57 | pub/sub mechanism first implemented in the now archived package `rredis` by 58 | Bryan W. Lewis. 59 | 60 | ### Authors 61 | 62 | Dirk Eddelbuettel and Bryan W. Lewis, based on earlier work by Wush Wu and 63 | with contributions by William Pleasant, Russell Pierce and Whit Armstrong. 64 | 65 | ### License 66 | 67 | GPL (>= 2) 68 | -------------------------------------------------------------------------------- /cleanup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf autom4te.cache/ config.* src/Makedeps src/Makevars \ 4 | src/*.o src/*.d src/*.a src/*.dll src/*.so src/*.rc src/symbols.rds \ 5 | src/hiredis/*.o src/hiredis/*.a \ 6 | */*~ *~ \ 7 | inst/examples/readC++ \ 8 | vignettes/rmd/auto/ \ 9 | vignettes/rmd/*.log \ 10 | vignettes/rmd/*.aux \ 11 | vignettes/rmd/*.out \ 12 | vignettes/rmd/*.tex \ 13 | vignettes/rmd/*.blg \ 14 | vignettes/rmd/*.bbl \ 15 | vignettes/rmd/*.xwm \ 16 | vignettes/rmd/pinp.cls \ 17 | vignettes/rmd/jss.bst 18 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | ## 2 | ## RcppRedis configure.ac 3 | ## 4 | ## RcppRedis -- R interface to Redis via Rcpp and hiredis 5 | ## 6 | ## Copyright (C) 2014 - 2025 Dirk Eddelbuettel 7 | ## 8 | ## RcppRedis is free software: you can redistribute it and/or modify 9 | ## it under the terms of the GNU General Public License as published by 10 | ## the Free Software Foundation, either version 2 of the License, or 11 | ## (at your option) any later version. 12 | ## 13 | ## RcppRedis is distributed in the hope that it will be useful, 14 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ## GNU General Public License for more details. 17 | ## 18 | ## You should have received a copy of the GNU General Public License 19 | ## along with RcppRedis. If not, see . 20 | 21 | # require at least autoconf 2.61 22 | AC_PREREQ(2.61) 23 | 24 | # Process this file with autoconf to produce a configure script. 25 | AC_INIT([RcppRedis],[0.2.4],[edd@debian.org]) 26 | 27 | # Ensure C++ is set up as R expects 28 | : ${R_HOME=`R RHOME`} 29 | if test -z "${R_HOME}"; then 30 | AC_MSG_ERROR([Could not determine R_HOME.]) 31 | fi 32 | CXX=`"${R_HOME}/bin/R" CMD config CXX` 33 | CXXFLAGS=`"${R_HOME}/bin/R" CMD config CXXFLAGS` 34 | AC_LANG(C++) 35 | AC_REQUIRE_CPP 36 | AC_PROG_CXX 37 | 38 | ## default to building from embedded sources ... unless hiredis found 39 | hiredis_libs=hiredis/libhiredis.a 40 | hiredis_tgt=libhiredis.a 41 | hiredis_cxxflags="-I." 42 | 43 | ## ... but check for pkg-config 44 | AC_DEFUN([AC_PROG_PKGCONFIG], [AC_CHECK_PROG(PKGCONFIG,pkg-config,yes)]) 45 | AC_PROG_PKGCONFIG 46 | 47 | ## use pkg-config for hiredis 48 | ## 49 | if test x"${PKGCONFIG}" = x"yes"; then 50 | ## check via pkg-config for hiredis 51 | if pkg-config --exists hiredis; then 52 | ## obtain cflags and obtain libs 53 | hiredis_cxxflags=`pkg-config --cflags hiredis` 54 | hiredis_libs=`pkg-config --libs hiredis` 55 | ## no need to build libhiredis.a 56 | hiredis_tgt="" 57 | else 58 | AC_MSG_WARN([pkg-config exists but hiredis is not available.]) 59 | fi 60 | fi 61 | 62 | ## And make sure these flags are used for the tests below. 63 | CPPFLAGS="${hiredis_cxxflags} ${CPPFLAGS}" 64 | CXXFLAGS="${hiredis_cxxflags} ${CXXFLAGS}" 65 | 66 | ## check for headers Debian has in libhiredis-dev 67 | hiredis_header=hiredis/hiredis.h 68 | hiredis_header_cache_var=AS_TR_SH([ac_cv_header_$hiredis_header]) 69 | AC_CHECK_HEADER([$hiredis_header],,[ 70 | # If it didn't work, try adding /usr/local directly then trying again 71 | AC_MSG_WARN([Hiredis headers not found with via default CXXFLAGS and CPPFLAGS, trying /usr/local/include]) 72 | CPPFLAGS="${hiredis_cxxflags} ${CPPFLAGS} -I/usr/local/include" 73 | CXXFLAGS="${hiredis_cxxflags} ${CXXFLAGS} -I/usr/local/include -L/usr/local/lib" 74 | # unset the cache variable for this particular header check 75 | # so we can check again with different defaults specified. 76 | AC_MSG_WARN([Unsetting $hiredis_header_cache_var]) 77 | AS_UNSET([$hiredis_header_cache_var]) 78 | AC_CHECK_HEADER([$hiredis_header],, 79 | [AC_MSG_WARN([hiredis headers not found.])]) 80 | ]) 81 | 82 | ## could check for minimal suitable version here 83 | 84 | ## First, set R_HOME, respecting an environment variable if set (now above) 85 | #: ${R_HOME=$(R RHOME)} 86 | #if test -z "${R_HOME}"; then 87 | # AC_MSG_ERROR([Could not determine R_HOME.]) 88 | #fi 89 | 90 | ## look for (optional !!) MsgPack headers 91 | ## RcppMsgPack on CRAN fits the bill -- but is a soft dependency 92 | AC_MSG_CHECKING([for RcppMsgPack]) 93 | ## Check if R has RcppMsgPack 94 | $("${R_HOME}/bin/Rscript" --vanilla -e 'hasPkg <- "RcppMsgPack" %in% rownames(installed.packages()); q(save="no", status=if (hasPkg) packageVersion("RcppMsgPack") >= "0.2.0" else FALSE)') 95 | if test x"$?" = x"1"; then 96 | AC_MSG_RESULT([yes]) 97 | msgpackincdir=$("${R_HOME}/bin/Rscript" --vanilla -e 'cat(system.file("include", package="RcppMsgPack"))') 98 | msgpack_cxxflags="-I${msgpackincdir} -DHAVE_MSGPACK" 99 | AC_MSG_NOTICE([Found RcppMsgPack, using '${msgpack_cxxflags}']) 100 | else 101 | AC_MSG_RESULT([no]) 102 | AC_MSG_NOTICE([Install (optional) RcppMsgPack (>= 0.2.0) from CRAN via 'install.packages("RcppMsgPack")']) 103 | fi 104 | 105 | ## now use all these 106 | AC_SUBST([PKG_CXXFLAGS],["${PKG_CXXFLAGS} $hiredis_cxxflags $msgpack_cxxflags"]) 107 | AC_SUBST([PKG_LIBS],["${PKG_LIBS} $hiredis_libs"]) 108 | AC_SUBST([PKG_TARGET], [" $hiredis_tgt"]) 109 | AC_CONFIG_FILES([src/Makevars]) 110 | AC_OUTPUT 111 | 112 | echo " 113 | ${PACKAGE_NAME} ${PACKAGE_VERSION} 114 | ================ 115 | 116 | compiler flags: ${PKG_CXXFLAGS} 117 | link argument: ${PKG_LIBS} 118 | library target: ${PKG_TARGET} 119 | " 120 | -------------------------------------------------------------------------------- /demo/00Index: -------------------------------------------------------------------------------- 1 | simpleRedisClient simple Redis demo 2 | simpleBenchmark simple benchmark versus rredis 3 | simpleBenchmark2 another simple benchmark versus rredis 4 | quickTest a simple comparison to rredis 5 | spDemo simple timing for several decades of (daily) SP500 data 6 | simDemo simple timing demo with simulated 'financial' data 7 | -------------------------------------------------------------------------------- /inst/NEWS.Rd: -------------------------------------------------------------------------------- 1 | \name{NEWS} 2 | \title{News for Package \pkg{RcppRedis}} 3 | \newcommand{\ghpr}{\href{https://github.com/eddelbuettel/rcppredis/pull/#1}{##1}} 4 | \newcommand{\ghit}{\href{https://github.com/eddelbuettel/rcppredis/issues/#1}{##1}} 5 | 6 | \section{Changes in version 0.2.5 (2025-03-26)}{ 7 | \itemize{ 8 | \item The continuous integration setup was updated several times 9 | \item Badges and URLs in README.md have been updated 10 | \item An updated interface from \pkg{RApiSerialize} is now used, and a 11 | versioned dependency on version 0.1.4 or later has been added 12 | \item The DESCRIPTION file now uses Authors@R 13 | \item Two possible bashisms have been converted in \code{configure.ac} 14 | \item The (fallback if needed) build of \code{libhiredis.a} now sets 15 | \code{-DNDEBUG}, four uses of \code{sprintf} converted to \code{snprintf} 16 | } 17 | } 18 | 19 | \section{Changes in version 0.2.4 (2023-08-19)}{ 20 | \itemize{ 21 | \item Add missing alias for \sQuote{RcppRedis-package} to \code{rhiredis.Rd}. 22 | \item Remove Suggests: \pkg{rredis} which triggers a NOTE nag as it is 23 | only on an \sQuote{Additional_repositories}. 24 | } 25 | } 26 | 27 | \section{Changes in version 0.2.3 (2023-03-08)}{ 28 | \itemize{ 29 | \item No longer set a C++ compilation standard as the default choices by 30 | R are sufficient for the package. 31 | \item Switch include to Rcpp/Rcpp which signals use of all Rcpp features 32 | including Modules. 33 | } 34 | } 35 | 36 | \section{Changes in version 0.2.2 (2022-10-31)}{ 37 | \itemize{ 38 | \item Thanks to a suggestion by Paul Murrell, the real-time chart 39 | demo now uses dev.hold() and.flush() for flicker-free updates. 40 | \item One function prototype was updated for \code{clang-15}. 41 | \item GitHub Actions were updated to checkout version 3. 42 | } 43 | } 44 | 45 | \section{Changes in version 0.2.1 (2022-04-09)}{ 46 | \itemize{ 47 | \item The \code{rredis} package can be installed via the repo listed in 48 | \code{Additional_repositories}; the \code{pubsub.R} test file makes 49 | \code{rredis} optional and conditional; all demos now note that the 50 | optional \code{rredis} package is installable via the \code{drat} listed in 51 | \code{Additional_repositories}. 52 | \item The fallback-compilation of \code{hiredis} has been forced to 53 | override compilation flags because CRAN knows better than upstream. 54 | \item The GLOBEX pub/sub example has small updates. 55 | } 56 | } 57 | 58 | \section{Changes in version 0.2.0 (2022-03-08)}{ 59 | \itemize{ 60 | \item Two simple C++11 features remove needs for \pkg{BH} and 61 | \code{lexical_cast()} (Dirk in \ghpr{45} addressing \ghit{44}). 62 | \item Redis pub/sub is now supported (Dirk in \ghpr{43}, Bryan in \ghpr{46}). 63 | \item Bryan Lewis is now a coauthor. 64 | \item Added pub/sub examples for single and multiple Globex symbols. 65 | \item The included hiredis sources have been updated to release 1.0.2. 66 | \item Two vignettes have been added to introduce Redis and to described a live 67 | market-monitoring application included in directory \code{pubsub/}. 68 | \item The UCRT build was updated per a suggestion by Tomas. 69 | } 70 | } 71 | 72 | \section{Changes in version 0.1.11 (2021-06-26)}{ 73 | \itemize{ 74 | \item The CI setup was updated to use \code{run.sh} from 75 | \href{https://eddelbuettel.github.io/r-ci/}{r-ci} (Dirk). 76 | \item A new function \code{quit} can be used to close a connection 77 | (Dirk). 78 | \item The windows build was updated to libhiredis 1.0.0, and UCRT 79 | support was added (Jeroen in \ghpr{42}). 80 | } 81 | } 82 | 83 | \section{Changes in version 0.1.10 (2020-01-16)}{ 84 | \itemize{ 85 | \item The package now uses \pkg{tinytest} for unit tests (Dirk in 86 | \ghpr{41}). 87 | } 88 | } 89 | 90 | \section{Changes in version 0.1.9 (2018-10-27)}{ 91 | \itemize{ 92 | \item The \code{configure} setup is more robust with respect to the 93 | C++ setup [CRAN request]. 94 | \item The Travis builds was updated to R 3.5 along with all others 95 | (\ghpr{34}). 96 | \item A new Redis function \code{hexists} was added (Whit Armstrong 97 | in \ghpr{35}). 98 | \item The \pkg{hiredis} library source is now included, and built on 99 | all systems unwilling or unable to provide it (\ghpr{36}). 100 | \item Added hash functions HDEL, HLEN, HKEYS, and HGETALL (Whit 101 | Armstrong in \ghpr{38}). 102 | } 103 | } 104 | 105 | \section{Changes in version 0.1.8 (2017-09-09)}{ 106 | \itemize{ 107 | \item A new file \code{init.c} was added with calls to 108 | \code{R_registerRoutines()} and \code{R_useDynamicSymbols()} 109 | \item Symbol registration is enabled in \code{useDynLib} 110 | \item Travis CI was updated to using \code{run.sh} 111 | \item The (optional MessagePack) code was updated for MsgPack 2.* 112 | } 113 | } 114 | 115 | \section{Changes in version 0.1.7 (2016-04-27)}{ 116 | \itemize{ 117 | \item Added support for \code{timeout} constructor argument (PR \ghpr{14} by 118 | Russell Pierce) 119 | \item Added new commands \code{exists}, \code{ltrim}, \code{expire} and 120 | \code{pexpire} along with unit tests (PR \ghpr{16} by Russell 121 | Pierce) 122 | \item Return \code{NULL} for empty keys in serialized \code{get} for 123 | consistency with \code{lpop} and \code{rpop} (also PR \ghpr{16} by Russell 124 | Pierce) 125 | \item Minor corrections to \code{get} code and \code{hget} and 126 | \code{hset} documentation (also PR \ghpr{16} by Russell Pierce) 127 | \item Error conditions are now properly forwarded as R errors (PR 128 | \ghpr{22} by Russell Pierce) 129 | \item Results from Redis commands are now checked for \code{NULL} (PR 130 | \ghpr{23} by Russell Pierce) 131 | \item MessagePack encoding can now be used which requires 132 | MessagePackage headers of version 1.0 or later; the (optional) 133 | RcppMsgPack package can be used. 134 | } 135 | } 136 | 137 | \section{Changes in version 0.1.6 (2015-10-05)}{ 138 | \itemize{ 139 | \item Added support (including new unit tests) for \code{lpop}, 140 | \code{rpop}, \code{lpush}, \code{rpush} as well as \code{auth} via 141 | augmented constructor (all thanks to PRs \ghpr{11} and \ghpr{13} by 142 | Russell Pierce) 143 | \item Added \code{ping} command and unit test 144 | } 145 | } 146 | 147 | \section{Changes in version 0.1.5 (2015-07-17)}{ 148 | \itemize{ 149 | \item Another minor fix to unit test setup for \CRANpkg{rredis}. 150 | } 151 | } 152 | 153 | \section{Changes in version 0.1.4 (2015-07-04)}{ 154 | \itemize{ 155 | \item Minor update to unit test setup for \CRANpkg{rredis}. 156 | \item No longer list URLs to Redis as automated CRAN tests for URL 157 | validity choke on redirects. 158 | } 159 | } 160 | 161 | \section{Changes in version 0.1.3 (2014-12-10)}{ 162 | \itemize{ 163 | \item Bug fix setting correct return type of \code{zcount} 164 | } 165 | } 166 | 167 | \section{Changes in version 0.1.2 (2014-11-06)}{ 168 | \itemize{ 169 | \item New commands \code{execv}, \code{hset}, \code{hget}, 170 | \code{sadd}, \code{srem}, and \code{smembers} contributed by John 171 | Laing and Whit Armstrong over pull requests \ghpr{3} and \ghpr{4}. 172 | } 173 | } 174 | 175 | \section{Changes in version 0.1.1 (2014-06-09)}{ 176 | \itemize{ 177 | \item Now with Windows support thanks to the installation of builds 178 | of the hiredis library (created by John Buonagurio) at CRAN / 179 | win-builder (thanks to Uwe Ligges) 180 | \item Added support for new command \code{zcount} 181 | } 182 | } 183 | 184 | \section{Changes in version 0.1.0 (2014-05-10)}{ 185 | \itemize{ 186 | \item Initial CRAN upload 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /inst/demos/queueDemo.R: -------------------------------------------------------------------------------- 1 | ## Initial version by Iñaki Ucar using Redux 2 | ## Adapted to RcppRedis by Dirk Eddelbuettel 3 | 4 | useRcppRedis <- function() { 5 | 6 | ensure_queue <- function(name) { 7 | ##list(con = redux::hiredis(), name = name, temp = paste0(name, "_temp")) 8 | redis <- new(RcppRedis::Redis) 9 | list(con = redis, name = name, temp = paste0(name, "_temp")) 10 | } 11 | 12 | publish <- function(queue, message) { 13 | ##invisible(queue$con$LPUSH(queue$name, message)) 14 | invisible(queue$con$lpush(queue$name, message)) 15 | } 16 | 17 | list_messages <- function(queue) { 18 | ##list(READY = queue$con$LRANGE(queue$name, 0, -1), 19 | ## PROCESSING = queue$con$LRANGE(queue$temp, 0, -1)) 20 | list(READY = queue$con$lrange(queue$name, 0, -1), 21 | PROCESSING = queue$con$lrange(queue$temp, 0, -1)) 22 | } 23 | 24 | try_consume <- function(queue) { 25 | ##message <- queue$con$RPOPLPUSH(queue$name, queue$temp) 26 | message <- queue$con$lmove(queue$name, queue$temp, 'RIGHT', 'LEFT') 27 | if (is.null(message)) return(message) 28 | list(queue = queue, message = message) 29 | } 30 | 31 | ack <- function(message) { 32 | ##invisible(message$queue$con$LREM(message$queue$temp, 1, message$message)) 33 | invisible(message$queue$con$lrem(message$queue$temp, 1, message$message)) 34 | } 35 | 36 | ############################################################################# 37 | 38 | ##system("docker run -d --rm --name valkey -p 6379:6379 valkey/valkey") 39 | ## assume Redis running 40 | 41 | q <- ensure_queue("jobs") 42 | ##q 43 | 44 | publish(q, message = "Hello world!") 45 | publish(q, message = "Hello again!") 46 | cat("\n--Messages after two enqueus\n") 47 | str(list_messages(q)) 48 | 49 | msg <- try_consume(q) 50 | cat("\n--Consumed message 1\n") 51 | msg$message 52 | cat("\n--Messages after consume\n") 53 | str(list_messages(q)) 54 | 55 | print(ack(msg)) 56 | cat("\n--Messages after ack 1\n") 57 | str(list_messages(q)) 58 | 59 | msg2 <- try_consume(q) 60 | cat("\n--Consumed message 2\n") 61 | msg2$message 62 | print(ack(msg2)) 63 | cat("\n--Messages after ack 2\n") 64 | str(list_messages(q)) 65 | 66 | cat("\n--Final try consume\n") 67 | str(try_consume(q)) 68 | 69 | ##system("docker stop valkey") 70 | } 71 | 72 | useRedux <- function() { 73 | ensure_queue <- function(name) { 74 | list(con = redux::hiredis(), name = name, temp = paste0(name, "_temp")) 75 | } 76 | 77 | publish <- function(queue, message) { 78 | invisible(queue$con$LPUSH(queue$name, message)) 79 | } 80 | 81 | list_messages <- function(queue) { 82 | list(READY = queue$con$LRANGE(queue$name, 0, -1), 83 | PROCESSING = queue$con$LRANGE(queue$temp, 0, -1)) 84 | } 85 | 86 | try_consume <- function(queue) { 87 | message <- queue$con$RPOPLPUSH(queue$name, queue$temp) 88 | if (is.null(message)) return(message) 89 | list(queue = queue, message = message) 90 | } 91 | 92 | ack <- function(message) { 93 | invisible(message$queue$con$LREM(message$queue$temp, 1, message$message)) 94 | } 95 | 96 | ############################################################################# 97 | 98 | ## #system("docker run -d --rm --name valkey -p 6379:6379 valkey/valkey") 99 | 100 | q <- ensure_queue("jobs") 101 | ##q 102 | 103 | publish(q, message = "Hello world!") 104 | publish(q, message = "Hello again!") 105 | cat("\n--Messages after two enqueus\n") 106 | str(list_messages(q)) 107 | 108 | msg <- try_consume(q) 109 | cat("\n--Consumed message 1\n") 110 | msg$message 111 | cat("\n--Messages after consume\n") 112 | str(list_messages(q)) 113 | 114 | print(ack(msg)) 115 | cat("\n--Messages after ack 1\n") 116 | str(list_messages(q)) 117 | 118 | msg2 <- try_consume(q) 119 | cat("\n--Consumed message 2\n") 120 | msg2$message 121 | print(ack(msg2)) 122 | cat("\n--Messages after ack 2\n") 123 | str(list_messages(q)) 124 | 125 | cat("\n--Final try consume\n") 126 | str(try_consume(q)) 127 | 128 | ##system("docker stop valkey") 129 | 130 | } 131 | 132 | quickCheck <- function() { 133 | redis <- new(RcppRedis::Redis) 134 | redis$lpush("foo", "banana") 135 | redis$lpush("foo", "banana") 136 | #print(str(redis$lrange("foo", 0, -1))) 137 | print(redis$llen("foo")) 138 | redis$lrem("foo", 1, "banana") 139 | #print(str(redis$lrange("foo", 0, -1))) 140 | print(redis$llen("foo")) 141 | redis$lpop("foo") 142 | #print(str(redis$lrange("foo", 0, -1))) 143 | print(redis$llen("foo")) 144 | invisible(NULL) 145 | } 146 | 147 | useRcppRedis() 148 | #useRedux() 149 | #quickCheck() 150 | -------------------------------------------------------------------------------- /inst/demos/quickTest.R: -------------------------------------------------------------------------------- 1 | 2 | suppressMessages({ 3 | library(RcppRedis) 4 | ## use install.packages("rredis", repos=c("https://ghrr.github.io/drat", getOption("repos"))) 5 | library(rredis) 6 | }) 7 | 8 | data(trees) 9 | fit <- lm(log(Volume) ~ log(Girth) + log(Height), data=trees) 10 | 11 | redis <- new(Redis) 12 | rredis::redisConnect(nodelay=TRUE) # new rredis option to mimich networking behavior of hiredis 13 | 14 | ## set a serialized object 15 | key <- "foo" 16 | redis$set(key, serialize(fit,NULL,ascii=TRUE)) 17 | 18 | 19 | ## retrieve with rredis 20 | fit2 <- rredis::redisGet(key) 21 | 22 | ## check 23 | all.equal(fit, fit2) 24 | 25 | 26 | ## or serialize an object internally 27 | key <- "foo2" 28 | redis$set(key, fit) 29 | 30 | ## retrieve with rredis 31 | fit3 <- rredis::redisGet(key) 32 | 33 | ## check 34 | all.equal(fit, fit3) 35 | 36 | ## retrieve with rredis 37 | fit4 <- redis$get(key) 38 | 39 | ## check 40 | all.equal(fit, fit4) 41 | -------------------------------------------------------------------------------- /inst/demos/simDemo.R: -------------------------------------------------------------------------------- 1 | 2 | library(xts) 3 | library(rredis) # use install.packages("rredis", repos=c("https://ghrr.github.io/drat", getOption("repos"))) 4 | library(RcppRedis) 5 | library(rbenchmark) 6 | 7 | set.seed(123) 8 | 9 | N <- 2500 10 | 11 | x <- xts(100*cumprod(1+rnorm(N)*0.005 + (runif(N)>0.95)*rnorm(N)*0.025), 12 | order.by=Sys.time()+cumsum(exp(3*runif(N)))) 13 | plot(x, main="Simulated Series", type='l') 14 | 15 | redisConnect() 16 | redis <- new(Redis) 17 | 18 | dat <- data.frame(key=as.numeric(index(x)), val=coredata(x)) 19 | 20 | setAsAscii <- function(dat) { 21 | N <- nrow(dat) 22 | ## insertion is row by row 23 | for (i in 1:N) { 24 | redisZAdd("ex:ascii:series", dat[i,1], dat[i,]) 25 | } 26 | } 27 | 28 | ## retrieval is by list 29 | getFromAscii <- function() { 30 | xx <- do.call(rbind, redisZRange("ex:ascii:series", 0, -1)) 31 | xt <- xts(xx[,-1], order.by=as.POSIXct(xx[,1], origin="1970-01-01")) 32 | } 33 | 34 | setAsBinary <- function(dat) { 35 | redis$zadd("ex:bin:series", as.matrix(dat)) 36 | } 37 | 38 | getFromBinary <- function() { 39 | zz <- redis$zrange("ex:bin:series", 0, -1) 40 | zt <- xts(zz[,-1], order.by=as.POSIXct(zz[,1], origin="1970-01-01")) 41 | } 42 | 43 | redis$zremrangebyscore("ex:ascii:series", 0, Inf) 44 | redis$zremrangebyscore("ex:bin:series", 0, Inf) 45 | 46 | cat("Writing ascii ... patience\n") 47 | setAsAscii(dat) 48 | setAsBinary(dat) 49 | 50 | xt <- getFromAscii() 51 | zt <- getFromBinary() 52 | identical(xt,zt) 53 | 54 | redis$zremrangebyscore("ex:ascii:series", 0, Inf) 55 | redis$zremrangebyscore("ex:bin:series", 0, Inf) 56 | 57 | benchmark(setAsAscii(dat), setAsBinary(dat), 58 | replications=1, order="relative")[,1:4] 59 | 60 | 61 | benchmark(getFromAscii(), getFromBinary(), replications=10, order="relative")[,1:4] 62 | -------------------------------------------------------------------------------- /inst/demos/simpleBenchmark.R: -------------------------------------------------------------------------------- 1 | 2 | library(RcppRedis) 3 | library(rredis) # use install.packages("rredis", repos=c("https://ghrr.github.io/drat", getOption("repos"))) 4 | library(rbenchmark) 5 | 6 | data(trees) 7 | fit <- lm(log(Volume) ~ log(Girth) + log(Height), data=trees) 8 | 9 | rawfit <- rawToChar(serialize(fit,NULL,ascii=TRUE)) 10 | 11 | redis <- new(Redis) 12 | rredis::redisConnect() 13 | 14 | hiredis <- function() redis$exec(paste("SET fits1 ", 15 | rawToChar(serialize(fit,NULL,ascii=TRUE)), 16 | sep="")) 17 | rredis <- function() rredis::redisSet("fits2", fit) 18 | 19 | res <- benchmark(hiredis(), rredis(), replications=100)[,1:4] 20 | print(res) 21 | 22 | all.equal(unserialize(charToRaw(redis$exec("GET fits1"))), fit) 23 | all.equal(rredis::redisGet("fits2"), fit) 24 | 25 | if (redisExists("abc1")) redisDelete("abc1") 26 | if (redisExists("abc2")) redisDelete("abc2") 27 | 28 | abc <- paste0(rep(letters,10),collapse="") 29 | 30 | hiredisP<- function() redis$exec(paste0("RPUSH abc1 ",rawToChar(serialize(abc,NULL,ascii=TRUE)))) 31 | rredisP <- function() rredis::redisRPush("abc2", abc) 32 | 33 | resP <- benchmark(hiredisP(), rredisP(), replications=100)[,1:4] 34 | print(resP) 35 | 36 | 37 | hiredisLR <- function() lapply(redis$exec("LRANGE abc1 0 -1"),function(x)unserialize(charToRaw(x))) 38 | rredisLR <- function() redisLRange("abc2",0,-1) 39 | 40 | resLR <- benchmark(hiredisLR(), rredisLR(), replications=100)[,1:4] 41 | print(resLR) 42 | 43 | abc1 <- hiredisLR() 44 | abc2 <- rredisLR() 45 | abcList <- lapply(1:101,function(x) abc) 46 | 47 | all.equal(abc1, abcList) 48 | all.equal(abc1, abc2) 49 | -------------------------------------------------------------------------------- /inst/demos/simpleBenchmark2.R: -------------------------------------------------------------------------------- 1 | 2 | suppressMessages({ 3 | library(RcppRedis) 4 | ## use install.packages("rredis", repos=c("https://ghrr.github.io/drat", getOption("repos"))) 5 | library(rredis) 6 | library(rbenchmark) 7 | }) 8 | 9 | data(trees) 10 | fit <- lm(log(Volume) ~ log(Girth) + log(Height), data=trees) 11 | 12 | 13 | redis <- new(Redis) 14 | rredis::redisConnect(nodelay=TRUE) # new rredis option to mimich networking behavior of hiredis 15 | 16 | ## writes as ascii 17 | hiredis <- function() redis$exec(paste0("SET fits1 ", 18 | rawToChar(serialize(fit,NULL,ascii=TRUE)))) 19 | 20 | ## writes as binary 21 | rredis <- function() rredis::redisSet("fits2", fit) 22 | 23 | ## writes as binary 24 | hiredisInt <- function() redis$set("fits3", fit) 25 | 26 | 27 | res <- benchmark(hiredis(), rredis(), hiredisInt(), replications=500, order="relative")[,1:4] 28 | cat("\nResults for SET\n") 29 | print(res) 30 | 31 | stopifnot(all.equal(unserialize(charToRaw(redis$exec("GET fits1"))), fit)) 32 | stopifnot(all.equal(rredis::redisGet("fits2"), fit)) 33 | stopifnot(all.equal(rredis::redisGet("fits3"), fit)) 34 | 35 | 36 | 37 | hiredis <- function() unserialize(charToRaw(redis$exec("GET fits1"))) 38 | 39 | rredis <- function() rredis::redisGet("fits2") 40 | 41 | hiredisInt <- function() redis$get("fits3") 42 | 43 | 44 | res <- benchmark(hiredis(), rredis(), hiredisInt(), replications=500, order="relative")[,1:4] 45 | cat("\nResults for GET\n") 46 | print(res) 47 | -------------------------------------------------------------------------------- /inst/demos/simpleRedisClient.R: -------------------------------------------------------------------------------- 1 | #!/usr/bin/Rscript 2 | 3 | suppressMessages(library(RcppRedis)) 4 | print(Redis) 5 | 6 | ## simple ping / pong test 7 | redis <- new(Redis) 8 | redis$exec("PING") 9 | redis$exec("PING") 10 | 11 | ## set and retrieve a number 12 | redis2 <- new(Redis, "127.0.0.1", 6379) 13 | redis2$exec("SET testchannel 42") 14 | redis2$exec("GET testchannel") 15 | 16 | ## set and retrieve a complete R object 17 | data(trees) 18 | fit <- lm(log(Volume) ~ log(Girth) + log(Height), data=trees) 19 | redis$exec(paste("SET fits ", rawToChar(serialize(fit,NULL,ascii=TRUE)), sep="")) 20 | all.equal(unserialize(charToRaw(redis$exec("GET fits"))), fit) 21 | -------------------------------------------------------------------------------- /inst/demos/spDemo.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | library(rredis) # use install.packages("rredis", repos=c("https://ghrr.github.io/drat", getOption("repos"))) 4 | suppressMessages(library(RcppRedis)) 5 | suppressMessages(library(xts)) 6 | library(rbenchmark) 7 | 8 | redisConnect() # default localhost 9 | redis <- new(Redis) 10 | 11 | if (!redisExists("sp500")) { 12 | suppressMessages(library(quantmod)) 13 | options("getSymbols.warning4.0"=FALSE) ## suppress some line noise 14 | sp <- getSymbols("^GSPC", auto.assign=FALSE, from="1950-01-01", to=Sys.Date()) 15 | redisSet("sp500", sp) 16 | cat("Downloaded SP500 and stored in redis\n") 17 | } else { 18 | cat("Retrieving SP500 from redis\n") 19 | sp <- redisGet("sp500") 20 | } 21 | #print(summary(sp)) 22 | 23 | rInsert <- function(x) { 24 | n <- nrow(x) 25 | key <- "sp500_R" 26 | if (redisExists(key)) redisDelete(key) 27 | M <- cbind(as.numeric(index(x)), coredata(x)) 28 | for (i in 1:n) { 29 | redisRPush(key, M[i,,drop=TRUE]) 30 | } 31 | invisible(NULL) 32 | } 33 | 34 | ## This is atrociously slow: 35 | ## 36 | ## R> system.time(rInsert(sp)) 37 | ## user system elapsed 38 | ## 16.392 0.292 645.643 39 | ## R> 40 | 41 | system.time(m1 <- do.call(rbind, redisLRange("sp500_R", 0, -1))) 42 | system.time(m2 <- do.call(rbind, redis$lrange("sp500_R", 0, -1))) 43 | identical(m1,m2) 44 | system.time(m3 <- redis$listToMatrix(redis$lrange("sp500_R", 0, -1))) 45 | m4 <- m1 46 | dimnames(m4) <- list() 47 | identical(m4,m3) 48 | 49 | ## approx factor 20 50 | res <- benchmark(do.call(rbind, redisLRange("sp500_R", 0, -1)), 51 | do.call(rbind, redis$lrange("sp500_R", 0, -1)), 52 | redis$listToMatrix(redis$lrange("sp500_R", 0, -1)), 53 | order="relative", replications=25)[,1:4] 54 | print(res) 55 | 56 | 57 | cInsert <- function(x) { 58 | n <- nrow(x) 59 | key <- "sp500_C" 60 | if (redisExists(key)) redisDelete(key) 61 | M <- cbind(as.numeric(index(x)), coredata(x)) 62 | for (i in 1:n) { 63 | redis$listRPush(key, M[i,]) 64 | } 65 | invisible(NULL) 66 | } 67 | 68 | 69 | ## approx factor 20 70 | res <- benchmark(do.call(rbind, redisLRange("sp500_R", 0, -1)), 71 | do.call(rbind, redis$lrange("sp500_R", 0, -1)), 72 | redis$listToMatrix(redis$lrange("sp500_R", 0, -1)), 73 | redis$listToMatrix(redis$listRange("sp500_C", 0, -1)), 74 | order="relative", replications=25)[,1:4] 75 | print(res) 76 | 77 | 78 | ## R> print(res) 79 | ## test replications elapsed relative 80 | ## 4 redis$listToMatrix(redis$listRange("sp500_C", 0, -1)) 25 0.296 1.000 81 | ## 3 redis$listToMatrix(redis$lrange("sp500_R", 0, -1)) 25 2.300 7.770 82 | ## 2 do.call(rbind, redis$lrange("sp500_R", 0, -1)) 25 2.629 8.882 83 | ## 1 do.call(rbind, redisLRange("sp500_R", 0, -1)) 25 48.028 162.257 84 | ## R> 85 | 86 | ## redo after Bryan's socket/nagle update to rredis: 87 | ## test replications elapsed relative 88 | ## 4 redis$listToMatrix(redis$listRange("sp500_C", 0, -1)) 25 0.407 1.000 89 | ## 3 redis$listToMatrix(redis$lrange("sp500_R", 0, -1)) 25 2.112 5.189 90 | ## 2 do.call(rbind, redis$lrange("sp500_R", 0, -1)) 25 2.458 6.039 91 | ## 1 do.call(rbind, redisLRange("sp500_R", 0, -1)) 25 48.367 118.838 92 | ## edd@max:~/git/rhiredis/demo$ 93 | -------------------------------------------------------------------------------- /inst/examples/readC++.cpp: -------------------------------------------------------------------------------- 1 | // -*- compile-command: "g++ -o readC++ readC++.cpp -lhiredis"; -*-* 2 | 3 | #include // we check in configure for this 4 | #include 5 | #include 6 | 7 | int main(void) { 8 | 9 | redisContext *prc; // pointer to redis context 10 | 11 | std::string host="127.0.0.1"; 12 | int port=6379; 13 | 14 | prc = redisConnect(host.c_str(), port); 15 | if (prc->err) { 16 | std::cerr << "Redis connection error: " << prc->errstr << std::endl; 17 | exit(-1); 18 | } 19 | 20 | std::string cmd = "get theanswer"; 21 | 22 | redisReply *reply = static_cast(redisCommand(prc, cmd.c_str())); 23 | // we simplify greatly here and accomodate only one type 24 | if (reply->type == REDIS_REPLY_STRING) { 25 | std::cout << "REPLY: " << reply->str << std::endl; 26 | } 27 | freeReplyObject(reply); 28 | 29 | exit(0); 30 | } 31 | -------------------------------------------------------------------------------- /inst/examples/readPython.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import redis 4 | 5 | redishost = "localhost" 6 | redisserver = redis.StrictRedis(redishost) 7 | 8 | key1 = "ex:ascii:simpleString" 9 | val1 = redisserver.get(key1) 10 | print("Got", val1, "from", key1) 11 | 12 | key2 = "ex:ascii:scalarVal" 13 | val2 = redisserver.lpop(key2) 14 | print("LPop'ed", val2, "from", key2) 15 | 16 | key3 = "ex:ascii:vectorVal" 17 | val3 = redisserver.lpop(key3) 18 | print("LPop'ed", val3, "from", key3) 19 | -------------------------------------------------------------------------------- /inst/examples/readR.R: -------------------------------------------------------------------------------- 1 | #!/usr/bin/Rscript 2 | 3 | library(methods) 4 | library(RcppRedis) 5 | 6 | redis <- new(Redis) 7 | 8 | key1 <- "ex:ascii:simpleString" 9 | val1 <- redis$getString(key1) 10 | cat("Got", val1, "from", key1, "\n") 11 | 12 | key2 <- "ex:ascii:scalarVal" 13 | val2 <- redis$listLPop(key2) 14 | cat("Got", val2, "from", key2, "\n") 15 | 16 | key3 <- "ex:ascii:vectorVal" 17 | val3 <- redis$listLPop(key3) 18 | cat("Got from", key3, "\n") 19 | print(str(val3)) 20 | 21 | key4 <- "ex:ascii:vectorVal" 22 | val4 <- redis$listRangeAsStrings(key4, 0, -1) 23 | cat("Got from", key4, "\n") 24 | print(val4) 25 | 26 | 27 | -------------------------------------------------------------------------------- /inst/examples/readShell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | key1="ex:ascii:simpleString" 4 | val1=$(redis-cli get ${key1}) 5 | echo "Got'ed '${val1}' from '${key1}'" 6 | 7 | key2="ex:ascii:scalarVal" 8 | val2=$(redis-cli lpop ${key2}) 9 | echo "LPop'ed '${val2}' from '${key2}'" 10 | 11 | key3="ex:ascii:vectorVal" 12 | val3=$(redis-cli lpop ${key3}) 13 | echo "LPop'ed '${val3}' from '${key3}'" 14 | -------------------------------------------------------------------------------- /inst/examples/runAll.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function runShell { 4 | ./writeShell.sh 5 | 6 | ./readShell.sh 7 | ./readShell.sh 8 | 9 | } 10 | 11 | function runPython { 12 | ./writePython.py 13 | 14 | ./readPython.py 15 | ./readPython.py 16 | 17 | } 18 | 19 | function cleanup { 20 | # cleanup 21 | redis-cli del 'ex:ascii:simpleString' 22 | redis-cli del 'ex:ascii:scalarVal' 23 | redis-cli del 'ex:ascii:vectorVal' 24 | } 25 | 26 | 27 | runShell 28 | runPython 29 | cleanup 30 | -------------------------------------------------------------------------------- /inst/examples/writeC++.cpp: -------------------------------------------------------------------------------- 1 | // -*- compile-command: "g++ -o writeC++ writeC++.cpp -lhiredis"; -*-* 2 | 3 | #include // we check in configure for this 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | 10 | redisContext *prc; // pointer to redis context 11 | 12 | std::string host="127.0.0.1"; 13 | int port=6379; 14 | 15 | prc = redisConnect(host.c_str(), port); 16 | if (prc->err) { 17 | std::cerr << "Redis connection error: " << prc->errstr << std::endl; 18 | exit(-1); 19 | } 20 | 21 | double score = 1.0; // initial 'score' 22 | std::vector vec(2); // as an example: pos 0 will be 'score', pos 1 'value' 23 | vec[0] = score; 24 | vec[1] = 100.0; 25 | 26 | int n = 10; 27 | for (int i=0; itype == REDIS_REPLY_STRING) { 37 | std::cout << " REPLY STRING: " << reply->str << std::endl; 38 | } 39 | if (reply->type == REDIS_REPLY_STATUS) { 40 | std::cout << " REPLY STATUS: " << reply->str << std::endl; 41 | } 42 | if (reply->type == REDIS_REPLY_ERROR) { 43 | std::cout << " REPLY ERROR: " << reply->str << std::endl; 44 | } 45 | if (reply->type == REDIS_REPLY_INTEGER) { 46 | std::cout << " REPLY INT: " << reply->integer << std::endl; 47 | } 48 | if (reply->type == REDIS_REPLY_NIL) { 49 | std::cout << " REPLY NIL" << std::endl; 50 | } 51 | freeReplyObject(reply); 52 | 53 | score += 1.0; 54 | vec[0] = score; 55 | vec[1] *= 1.001; 56 | } 57 | exit(0); 58 | } 59 | -------------------------------------------------------------------------------- /inst/examples/writePython.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import redis 4 | 5 | redishost = "localhost" 6 | redisserver = redis.StrictRedis(redishost) 7 | 8 | key1 = "ex:ascii:simpleString" 9 | val1 = "abracadabra" 10 | res = redisserver.set(key1, val1) 11 | print("Set", val1, "under", key1, "with result", res) 12 | 13 | key2 = "ex:ascii:scalarVal" 14 | val2 = 42 15 | res = redisserver.rpush(key2, val2) 16 | print("Pushed", val2, "under", key2, "with result", res) 17 | 18 | key3 = "ex:ascii:vectorVal" 19 | #val3 = [40, 41, 42] 20 | val3 = "40, 41, 42" 21 | res = redisserver.rpush(key3, val3) 22 | print("Pushed", val3, "under", key3, "with result", res) 23 | 24 | print("Done.") 25 | -------------------------------------------------------------------------------- /inst/examples/writeR.R: -------------------------------------------------------------------------------- 1 | #!/usr/bin/Rscript 2 | 3 | library(methods) 4 | library(RcppRedis) 5 | 6 | redis <- new(Redis) 7 | 8 | key1 <- "ex:ascii:simpleString" 9 | val1 <- "abracadrabra" 10 | res <- redis$setString(key1, val1) 11 | cat("Wrote", val1, "for", key1, "with result", res, "\n") 12 | 13 | #key2 <- "scalarVal" 14 | #val2 <- redis$listRange(key2, 0, -1) 15 | #cat("Got", val2, "from", key2, "\n") 16 | #print(str(val2)) 17 | 18 | #key3 <- "vectorVal" 19 | #val3 <- redis$listRange(key3, 0, -1) 20 | #cat("Got", val2, "from", key2, "\n") 21 | #print(str(val3)) 22 | 23 | #key4 <- "vectorVal" 24 | #val4 <- redis$listRangeAsStrings(key4, 0, -1) 25 | #print(val4) 26 | -------------------------------------------------------------------------------- /inst/examples/writeShell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | key1="ex:ascii:simpleString" 4 | val1="abracadrabra" 5 | res=$(redis-cli set ${key1} ${val1}) 6 | echo "Set '${val1}' under '${key1}' with result ${res}" 7 | 8 | key2="ex:ascii:scalarVal" 9 | val2=42 10 | res=$(redis-cli rpush ${key2} ${val2}) 11 | echo "Pushed '${val2}' under '${key2}' with result ${res}" 12 | 13 | key3="ex:ascii:vectorVal" 14 | val3="40,41,42" 15 | res=$(redis-cli rpush ${key3} ${val3}) 16 | echo "Pushed '${val3}' under '${key3}' with result ${res}" 17 | -------------------------------------------------------------------------------- /inst/hiredis.COPYING: -------------------------------------------------------------------------------- 1 | The following license applies to code from the hiredis library which might be 2 | linked into the installed package on platforms such as Mac OS X and/or Windows: 3 | 4 | Copyright (c) 2009-2011, Salvatore Sanfilippo 5 | Copyright (c) 2010-2011, Pieter Noordhuis 6 | 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, 13 | this list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | * Neither the name of Redis nor the names of its contributors may be used 20 | to endorse or promote products derived from this software without specific 21 | prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 27 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 30 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | -------------------------------------------------------------------------------- /inst/pubsub/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Pub/Sub Example with 'Live' Intra-Daily Market Data 3 | 4 | ### What Is This? 5 | 6 | This directory contains a pair of files to gather and display "live" (_i.e._ 7 | delayed by the usual amount as is common for non-subscribers to generally 8 | expensive live market data) futures data. 9 | 10 | ### What Does It Look Like? 11 | 12 | Here is a screenshot from a few months ago. The plotting logic and details have 13 | not changed. Displayed are two days worth of price snapshots every ten seconds. 14 | 15 | ![](example_screenshot.png) 16 | 17 | ### Give Me More Details 18 | 19 | One file uses package [quantmod](https://cloud.r-project.org/package=quantmod) 20 | to gather the data. It collects the data points in [Redis](https://redis.io) to 21 | build a history, but also publishes the on a channel by the name of the symbol. 22 | 23 | The second file retrieves the recent history, and subscribes to the channel to 24 | receive updates which it displays as it gets them. Because it uses an R 25 | graphics device to plot (also via package 26 | [quantmod](https://cloud.r-project.org/package=quantmod)) it is easiest to 27 | `source()` the file in an R session. (`Rscript` defaults to setting 28 | `interactive` to FALSE; otherwise R can be used with `--interactive`, or `r` 29 | from [littler](https://cloud.r-project.org/package=quantmod) can be used with 30 | its `-i` switch). 31 | 32 | The third file should be invoked weekly or daily from `crontab` and prunes the 33 | history down to just the last months. All three files could be generalized to 34 | read more than one symbol, or host, or ... from config files. For now, and for 35 | simplicity, just the front ES contract is monitored. 36 | 37 | ### Going Further 38 | 39 | The second set of files generalizes the approach to subscribe to a set of 40 | symbols---where we use a set of symbols from CME Globex, an electronic trading 41 | system with extended hours. 42 | Data for each symbols is cached, and published, in the 'to-Redis' file. 43 | The corresponding 'from-Redis' file consumes and plots the data, again using a 44 | single process to cover multiple symbols and subscriptions. 45 | 46 | ### Acknowledgements 47 | 48 | These files owes their basic structure to a [gist by Josh Ulrich](https://gist.github.com/joshuaulrich/ee11ef67b1461df399b84efd3c8f9f67#file-intraday-sp500-r) 49 | which also contained the basic subscription loop (and which is also the basis of 50 | this [extended and documented version in package 51 | dang](https://github.com/eddelbuettel/dang/blob/master/R/intradayMarketMonitor.R). This 52 | was then generalized to use a symbol such as `ES=F` (for the front month SP500 53 | futures contract) for which no history is available by both caching in [Redis](https://redis.io) 54 | and using pub/sub to distribute. The `rredis` package (installable from the 55 | [ghrr drat](https://ghrr.github.io/drat) repo) was used for the initial 56 | pub/sub approach. Bryan W. Lewis then ported and adapted the pub/sub model to 57 | this RcppRedis package. 58 | 59 | ### Authors 60 | 61 | Dirk Eddelbuettel, Joshua Ulrich, Bryan W. Lewis 62 | 63 | ### License 64 | 65 | GPL (>= 2) 66 | -------------------------------------------------------------------------------- /inst/pubsub/example_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddelbuettel/rcppredis/47f4458026b2a5537618164f1e7b395328a01524/inst/pubsub/example_screenshot.png -------------------------------------------------------------------------------- /inst/pubsub/intraday-ES-from-Redis.r: -------------------------------------------------------------------------------- 1 | 2 | suppressMessages({ 3 | library(quantmod) 4 | library(anytime) 5 | library(RcppRedis) 6 | }) 7 | 8 | defaultTZ <- "America/Chicago" 9 | symbol <- "ES=F" 10 | host <- "localhost" 11 | ndays <- 2 12 | 13 | most_recent_n_days <- function(x, n=2, minobs=1500) { 14 | tt <- table(as.Date(index(x))) 15 | if (length(tt) < n) return(x) 16 | cutoff <- paste(format(as.Date(names(head(tail(tt[tt>minobs], n), 1)))), "00:00:00") 17 | newx <- x[ index(x) >= as.POSIXct(cutoff) ] 18 | msg(Sys.time(), "most recent data starting at", format(head(index(newx),1))) 19 | newx 20 | } 21 | 22 | get_all_data <- function(symbol, host) { 23 | m <- redis$zrange(symbol, 0, -1) 24 | colnames(m) <- c("Time", "Close", "Change", "PctChange", "Volume") 25 | y <- xts(m[,-1], order.by=anytime(as.numeric(m[,1]))) 26 | y 27 | } 28 | 29 | show_plot <- function(symbol, x) { 30 | lastx <- tail(coredata(x),1) 31 | cname <- paste(symbol, 32 | format(lastx[,"Close"]), 33 | round(lastx[,"Change"], 2), 34 | round(lastx[,"PctChange"], 5), 35 | format(Sys.time(), "%H:%M:%S"), 36 | sep = " ") 37 | cs <- quantmod::chart_Series(x[,"Close"], name = cname) 38 | ## cf issue 270 39 | ## chart_Series(IBM, name=NULL) 40 | ## title('IBM', font.main=3, col.main="#ff0000", line=2.5) 41 | plot(cs) 42 | } 43 | 44 | msg <- function(ts, ...) { 45 | op <- options(digits.secs=3) 46 | cat(format(ts), ..., "\n") 47 | options(op) 48 | } 49 | 50 | redis <- new(Redis, host) 51 | if (redis$ping() != "PONG") stop("No Redis server?", call. = FALSE) 52 | x <- get_all_data(symbol, host) 53 | x <- most_recent_n_days(x,ndays) 54 | show_plot(symbol, x) 55 | 56 | ## This is the callback function assigned to a symbol 57 | .data2xts <- function(x) { 58 | m <- read.csv(text=x, sep=";", header=FALSE, col.names = c("Time", "Close", "Change", "PctChange", "Volume")) 59 | y <- xts(m[,-1,drop=FALSE], anytime(as.numeric(m[,1,drop=FALSE]))) 60 | y 61 | } 62 | assign(symbol, .data2xts) # programmatic version of callback `ES=F` <- function(x) .... 63 | redis$subscribe(symbol) 64 | 65 | market_closed <- TRUE 66 | repeat { 67 | curr_t <- Sys.time() 68 | now_t <- xts(, curr_t) 69 | now <- .indexhour(now_t)*100 + .indexmin(now_t) 70 | if (now >= 1525 && now < 1710) { # this may not get hit on futures ... 71 | x <- most_recent_n_days(x,ndays) 72 | tgt <- as.POSIXct(paste(format(as.Date(curr_t)), "17:09:59.999")) 73 | dt <- max(1,round(as.numeric(difftime(tgt, curr_t, units="secs")),0)) 74 | msg(index(now_t), "after close; sleeping", dt, "seconds") 75 | Sys.sleep(dt) 76 | market_closed <- TRUE 77 | next 78 | } else if ((now >= 1710 || now < 1526) && market_closed) { 79 | x <- most_recent_n_days(x,ndays) 80 | msg(index(now_t), "market open") 81 | market_closed <- FALSE 82 | prevVol <- 0 83 | } 84 | y <- redisMonitorChannels(redis) 85 | if (!is.null(y)) { 86 | x <- rbind(x,y) 87 | x <- x[!duplicated(index(x))] 88 | ll <- tail(y,1) 89 | msg(Sys.time(), "data", format(index(ll)), "close", ll[1,"Close"], "change", ll[1, "Change"]) 90 | } else { 91 | msg(index(now_t), "null data in y") 92 | } 93 | show_plot(symbol, x) 94 | } 95 | # never reached but could do 'redis$unsubscribe(symbol)' here 96 | -------------------------------------------------------------------------------- /inst/pubsub/intraday-ES-to-Redis.r: -------------------------------------------------------------------------------- 1 | ## cf https://gist.github.com/joshuaulrich/ee11ef67b1461df399b84efd3c8f9f67#file-intraday-sp500-r 2 | 3 | suppressMessages({ 4 | library(quantmod) 5 | library(RcppRedis) 6 | }) 7 | 8 | defaultTZ <- "America/Chicago" 9 | symbol <- "ES=F" 10 | 11 | get_data <- function(symbol) { 12 | quote <- getQuote(symbol) 13 | vec <- c(Time = as.numeric(quote$`Trade Time`), 14 | Close = quote$Last, 15 | Change = quote$Change, 16 | PctChange = quote$`% Change`, 17 | Volume = quote$Volume) 18 | vec 19 | } 20 | 21 | store_data <- function(vec, symbol) { 22 | redis$zadd(symbol, matrix(vec, 1)) 23 | redis$publish(symbol, paste(vec,collapse=";")) 24 | } 25 | 26 | msg <- function(ts, ...) { 27 | op <- options(digits.secs=3) 28 | cat(format(ts), ..., "\n") 29 | options(op) 30 | } 31 | 32 | x <- NULL 33 | redis <- new(Redis, "localhost") 34 | if (redis$ping() != "PONG") stop("No Redis server?", call. = FALSE) 35 | 36 | market_closed <- TRUE 37 | errored <- FALSE 38 | prevVol <- 0 39 | repeat { 40 | curr_t <- Sys.time() 41 | now_t <- xts(, curr_t) 42 | now <- .indexhour(now_t)*100 + .indexmin(now_t) 43 | if (now >= 1525 && now < 1710) { 44 | ## we need one NA observations to plot a gap 45 | vec <- c(Time = as.numeric(curr_t), 46 | Close = NA_real_, 47 | Change = NA_real_, 48 | PctChange = NA_real_, 49 | Volume = NA_real_) 50 | store_data(vec, symbol) 51 | tgt <- as.POSIXct(paste(format(as.Date(curr_t)), "17:09:59.999")) 52 | dt <- max(1L,round(as.numeric(difftime(tgt, curr_t, units="secs")),0)) 53 | msg(index(now_t), "after close; setting NA, sleeping", dt, "seconds") 54 | Sys.sleep(dt) 55 | market_closed <- TRUE 56 | next 57 | } else if ((now >= 1710 || now < 1526) && market_closed) { 58 | msg(index(now_t), "market open") 59 | market_closed <- FALSE 60 | prevVol <- 0 61 | } 62 | y <- try(get_data(symbol), silent = TRUE) 63 | if (inherits(y, "try-error")) { 64 | msg(curr_t, "Error:", attr(y, "condition")[["message"]]) 65 | errored <- TRUE 66 | Sys.sleep(15) 67 | next 68 | } else if (errored) { 69 | errored <- FALSE 70 | msg(curr_t, "...recovered") 71 | } 72 | v <- y["Volume"] 73 | if (v != prevVol) { 74 | store_data(y, symbol) 75 | msg(curr_t, "Storing data from", format(anytime::anytime(y["Time"])), "close", y["Close"], "change", y["Change"]) 76 | } 77 | prevVol <- v 78 | Sys.sleep(10) 79 | } 80 | -------------------------------------------------------------------------------- /inst/pubsub/intraday-GLOBEX-from-Redis.r: -------------------------------------------------------------------------------- 1 | 2 | suppressMessages({ 3 | library(quantmod) # plotting 4 | library(xts) # data container 5 | library(anytime) # convenient time parsing 6 | library(RcppRedis) # data cache and pub/sub 7 | #library(ggplot2) # for show_plot_gg 8 | #library(patchwork) # for show_plot_gg 9 | }) 10 | 11 | ## Parameters 12 | defaultTZ <- "America/Chicago" 13 | symbols <- c("BTC=F", "CL=F", "ES=F", "GC=F") 14 | host <- "localhost" 15 | ndays <- 2 16 | 17 | ## Callback handler for convenience 18 | multiSymbolRedisMonitorChannels <- function(context, type="rdata", env=.GlobalEnv) { 19 | res <- context$listen(type) 20 | if (length(res) != 3 || res[[1]] != "message") return(res) 21 | if (exists(res[[2]], mode="function", envir=env)) { 22 | data <- do.call(res[[2]], as.list(res[[3]]), envir=env) 23 | val <- list(symbol=res[[2]], data=data) 24 | return(val) 25 | } 26 | res 27 | } 28 | 29 | most_recent_n_days <- function(rl, n=2, minobs=1500) { 30 | rl <- lapply(rl, function(x) { 31 | tt <- table(as.Date(index(x))) 32 | if (length(tt) < n || nrow(x) < minobs) return(x) 33 | cutoff <- paste(format(as.Date(names(head(tail(tt[tt>minobs], n), 1)))), "00:00:00") 34 | newx <- x[ index(x) >= as.POSIXct(cutoff) ] 35 | #msg(Sys.time(), "most recent data starting at", format(head(index(newx),1))) 36 | newx 37 | }) 38 | } 39 | 40 | get_all_data <- function(symbols, host) { 41 | rl <- sapply(symbols, simplify=FALSE, FUN=function(symbol) { 42 | m <- redis$zrange(symbol, 0, -1) 43 | colnames(m) <- c("Time", "Close", "Change", "PctChange", "Volume") 44 | y <- xts(m[,-1,drop=FALSE], order.by=anytime(as.numeric(m[,1,drop=FALSE]))) 45 | y 46 | }) 47 | } 48 | 49 | show_plot_base <- function(symbols, rl) { 50 | dev.hold(1L) # thanks to Paul Murrell: hold the plot device ... 51 | op <- par(no.readonly=TRUE) 52 | par(mfrow=c(length(symbols), 1)) 53 | res <- lapply(symbols, function(symbol) { 54 | x <- rl[[symbol]] 55 | lastx <- tail(coredata(x),1) 56 | cname <- paste(symbol, 57 | format(lastx[,"Close"]), 58 | round(lastx[,"Change"], 2), 59 | round(lastx[,"PctChange"], 5), 60 | format(Sys.time(), "%H:%M:%S"), 61 | sep = " ") 62 | cs <- quantmod::chart_Series(x[,"Close"], name = cname) 63 | ## cf issue 270 64 | ## chart_Series(IBM, name=NULL) 65 | ## title('IBM', font.main=3, col.main="#ff0000", line=2.5) 66 | plot(cs) 67 | }) 68 | par(op) 69 | dev.flush(1L) # ... and flush all at once without flicker 70 | } 71 | 72 | ## alternate using ggplot and patchwork 73 | show_plot_gg <- function(symbols, rl) { 74 | res <- lapply(symbols, function(symbol) { 75 | x <- rl[[symbol]] 76 | lastx <- tail(coredata(x),1) 77 | cname <- paste(symbol, 78 | format(lastx[,"Close"]), 79 | round(lastx[,"Change"], 2), 80 | round(lastx[,"PctChange"], 5), 81 | format(Sys.time(), "%H:%M:%S"), 82 | sep = " ") 83 | xx <- data.frame(Date=index(x), coredata(x)) 84 | ggplot(data=xx) + aes(x=Date, y=Close) + geom_line() + 85 | labs(title = cname, y = "", x = "") + 86 | theme(plot.title = element_text(size = 9), 87 | plot.margin = margin(0, 0, 0, 0, "cm")) 88 | }) 89 | print(res[[1]] / res[[2]] / res[[3]]) 90 | } 91 | 92 | show_plot <- show_plot_base 93 | #show_plot <- show_plot_gg 94 | 95 | msg <- function(ts, ...) { 96 | op <- options(digits.secs=3) 97 | cat(format(ts), ..., "\n") 98 | options(op) 99 | } 100 | 101 | redis <- new(Redis, host) 102 | if (redis$ping() != "PONG") stop("No Redis server?", call. = FALSE) 103 | x <- get_all_data(symbols, host) 104 | x <- most_recent_n_days(x, ndays) 105 | show_plot(symbols, x) 106 | 107 | env <- new.env() # local environment for callbacks 108 | 109 | ## This is the callback function assigned to a symbol 110 | .data2xts <- function(x) { 111 | m <- read.csv(text=x, sep=";", header=FALSE, col.names = c("Time", "Close", "Change", "PctChange", "Volume")) 112 | y <- xts(m[,-1,drop=FALSE], anytime(as.numeric(m[,1,drop=FALSE]))) 113 | y 114 | } 115 | ## With environment 'env', assign callback function for each symbol 116 | res <- sapply(symbols, function(symbol) { 117 | assign(symbol, .data2xts, envir=env) # programmatic version `ES=F` <- function(x) .... 118 | redis$subscribe(symbol) 119 | }) 120 | 121 | 122 | market_closed <- TRUE 123 | repeat { 124 | curr_t <- Sys.time() 125 | now_t <- xts(, curr_t) 126 | now <- .indexhour(now_t)*100 + .indexmin(now_t) 127 | if (now >= 1525 && now < 1710) { # this may not get hit on futures ... 128 | x <- most_recent_n_days(x,ndays) 129 | tgt <- as.POSIXct(paste(format(as.Date(curr_t)), "17:09:59.999")) 130 | dt <- max(1,round(as.numeric(difftime(tgt, curr_t, units="secs")),0)) 131 | msg(index(now_t), "after close; sleeping", dt, "seconds") 132 | Sys.sleep(dt) 133 | market_closed <- TRUE 134 | next 135 | } else if ((now >= 1710 || now < 1526) && market_closed) { 136 | x <- most_recent_n_days(x,ndays) 137 | msg(index(now_t), "market open") 138 | market_closed <- FALSE 139 | } 140 | rl <- multiSymbolRedisMonitorChannels(redis, env=env) # monitor channels in context of 'env' 141 | if (is.list(rl)) { 142 | sym <- rl[["symbol"]] 143 | x[[sym]] <- rbind(x[[sym]], rl[["data"]]) 144 | z <- tail(x[[sym]],1) 145 | if (sym == symbols[3]) msg(Sys.time(), "data", format(index(z)), "close", z[1,"Close"], "change", z[1, "PctChange"]) 146 | } else { 147 | msg(index(now_t), "null data in y") 148 | } 149 | show_plot(symbols, x) 150 | } 151 | # never reached but could do 'redis$unsubscribe(symbol)' here 152 | -------------------------------------------------------------------------------- /inst/pubsub/intraday-GLOBEX-to-Redis.r: -------------------------------------------------------------------------------- 1 | ## cf https://gist.github.com/joshuaulrich/ee11ef67b1461df399b84efd3c8f9f67#file-intraday-sp500-r 2 | 3 | suppressMessages({ 4 | library(quantmod) 5 | library(RcppRedis) 6 | }) 7 | 8 | defaultTZ <- "America/Chicago" 9 | symbols <- c("BTC=F", "CL=F", "ES=F", "GC=F") 10 | 11 | get_data <- function(symbols) { 12 | quotes <- getQuote(symbols) 13 | quotes$Open <- quotes$High <- quotes$Low <- NULL 14 | colnames(quotes) <- c("Time", "Close", "Change", "PctChange", "Volume") 15 | quotes$Time <- as.numeric(quotes$Time) 16 | quotes 17 | } 18 | 19 | store_data <- function(curr_t, res) { 20 | symbols <- rownames(res) 21 | res <- as.matrix(res) 22 | for (symbol in symbols) { 23 | vec <- res[symbol,,drop=FALSE] 24 | #print(vec) 25 | if (redis$ping() == "PONG") { 26 | redis$zadd(symbol, vec) 27 | redis$publish(symbol, paste(vec,collapse=";")) 28 | } else { 29 | msg(curr_t, "skipping update, no redis ?") 30 | } 31 | } 32 | } 33 | 34 | msg <- function(ts, ...) { 35 | op <- options(digits.secs=3) 36 | cat(format(ts), ..., "\n") 37 | options(op) 38 | } 39 | 40 | x <- NULL 41 | redis <- new(Redis, "localhost") 42 | if (redis$ping() != "PONG") stop("No Redis server?", call. = FALSE) 43 | 44 | market_closed <- TRUE 45 | errored <- FALSE 46 | prevVol <- 0 47 | res <- get_data(symbols) 48 | repeat { 49 | curr_t <- Sys.time() 50 | now_t <- xts(, curr_t) 51 | now <- .indexhour(now_t)*100 + .indexmin(now_t) 52 | if (now >= 1525 && now < 1710) { 53 | ## we need one NA observations to plot a gap 54 | vec <- c(Time = as.numeric(curr_t), 55 | Close = NA_real_, 56 | Change = NA_real_, 57 | PctChange = NA_real_, 58 | Volume = NA_real_) 59 | nres <- res 60 | for (symbol in symbols) 61 | nres[symbol,] <- vec 62 | store_data(curr_t, nres) 63 | tgt <- as.POSIXct(paste(format(as.Date(curr_t)), "17:09:59.999")) 64 | dt <- max(1L,round(as.numeric(difftime(tgt, curr_t, units="secs")),0)) 65 | msg(index(now_t), "after close; setting NA, sleeping", dt, "seconds") 66 | Sys.sleep(dt) 67 | market_closed <- TRUE 68 | next 69 | } else if ((now >= 1710 || now < 1526) && market_closed) { 70 | msg(curr_t, "market open") 71 | market_closed <- FALSE 72 | #prevVol <- 0 73 | } 74 | res <- try(get_data(symbols), silent = TRUE) 75 | if (inherits(res, "try-error")) { 76 | msg(curr_t, "Error:", attr(res, "condition")[["message"]]) 77 | errored <- TRUE 78 | Sys.sleep(15) 79 | next 80 | } else if (errored) { 81 | errored <- FALSE 82 | msg(curr_t, "...recovered") 83 | } 84 | v <- res[3, "Volume"] 85 | if (v != prevVol) { 86 | store_data(curr_t, res) 87 | msg(curr_t, "Storing data from", format(anytime::anytime(res[3,"Time"]))) #, "for", paste(rownames(res)[1], res[1, "Change"], rownames(res)[2], res[2, "Change"], collapse=",")) 88 | } 89 | prevVol <- v 90 | Sys.sleep(10) 91 | } 92 | -------------------------------------------------------------------------------- /inst/pubsub/pruneRedis.r: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env r 2 | 3 | suppressMessages({ 4 | library(anytime) 5 | library(RcppRedis) 6 | library(xts) 7 | }) 8 | 9 | defaultTZ <- "America/Chicago" 10 | host <- "localhost" 11 | symbols <- c("BTC=F", "ES=F", "GC=F", "CL=F") 12 | 13 | redis <- new(Redis, host) 14 | if (redis$ping() != "PONG") stop("No Redis server?", call. = FALSE) 15 | 16 | for (symbol in symbols) { 17 | m <- redis$zrange(symbol, 0, -1) 18 | colnames(m) <- c("Time", "Close", "Change", "PctChange", "Volume") 19 | y <- xts(m[,-1], order.by=anytime(as.numeric(m[,1]))) 20 | if (as.numeric(index(y)[1], Sys.time(), units="days") > 30) { 21 | onemonthago <- Sys.time() - 60*60*24*31 22 | cutoff <- as.numeric(onemonthago) 23 | lastold <- max(which(m[, 1] < cutoff )) 24 | redis$zremrangebyscore(symbol, 0, m[lastold,1]) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /inst/tinytest/test_basics.R: -------------------------------------------------------------------------------- 1 | 2 | if (!requireNamespace("rredis", quietly=TRUE)) exit_file("Skip this test") 3 | 4 | #test_01_setup <- function() { 5 | suppressMessages({ 6 | library(datasets) 7 | library(rredis) 8 | library(RcppRedis) 9 | }) 10 | 11 | rredis::redisConnect(nodelay=FALSE) # rredis connection object, but no tcpdelay 12 | # as our use of hiredis seesm to interfere 13 | 14 | redis <- new(Redis) # RcppRedis object 15 | 16 | data(trees) 17 | fit <- lm(log(Volume) ~ log(Girth) + log(Height), data=trees) 18 | 19 | 20 | #test_02_treesExternalSerialize <- function() { 21 | 22 | ## set a externally serialized object 23 | key <- "RcppRedis:test:foo" 24 | redis$set(key, serialize(fit, NULL, ascii=TRUE)) 25 | 26 | ## retrieve with rredis 27 | fit2 <- rredis::redisGet(key) 28 | 29 | ## check 30 | expect_equal(fit, fit2) 31 | 32 | 33 | #test_03_treesInternalSerialize <- function() { 34 | ## or serialize an object internally 35 | key <- "RcppRedis:test:foo2" 36 | redis$set(key, fit) 37 | 38 | ## retrieve with rredis 39 | fit3 <- rredis::redisGet(key) 40 | 41 | ## check 42 | expect_equal(fit, fit3) 43 | 44 | 45 | #test_04_treesRRedis <- function() { 46 | ## retrieve with rredis 47 | key <- "RcppRedis:test:foo2" 48 | fit4 <- redis$get(key) 49 | 50 | ## check 51 | expect_equal(fit, fit4) 52 | 53 | 54 | #test_05_ping <- function() { 55 | res <- redis$ping() 56 | expect_equal(res, "PONG") 57 | 58 | #test_06_cleanup <- function() { 59 | expect_equal(redis$del(c("RcppRedis:test:foo","RcppRedis:test:foo2")), 2) 60 | -------------------------------------------------------------------------------- /inst/tinytest/test_exec.R: -------------------------------------------------------------------------------- 1 | #test_01_setup <- function() { 2 | suppressMessages(library(RcppRedis)) 3 | redis <- new(Redis) 4 | 5 | key <- "RcppRedis:exec:test:foo" 6 | val <- "qux" 7 | #} 8 | 9 | #test_02_exec <- function() { 10 | ## set and get 11 | redis$exec(paste("SET", key, val)) 12 | res <- redis$exec(paste("GET", key)) 13 | expect_equal(res, val) 14 | 15 | 16 | #test_03_execError <- function() { 17 | expect_error(redis$exec("LRANGE mylist 0 elephant")) 18 | 19 | 20 | #test_04_cleanup <- function() { 21 | ## delete set 22 | expect_equal(redis$del(key), 1) 23 | -------------------------------------------------------------------------------- /inst/tinytest/test_execv.R: -------------------------------------------------------------------------------- 1 | #test_01_setup <- function() { 2 | suppressMessages(library(RcppRedis)) 3 | redis <- new(Redis) 4 | 5 | key <- "RcppRedis:test:foo bar" 6 | val <- "qux baz" 7 | 8 | 9 | #test_02_execv <- function() { 10 | ## set and get 11 | redis$execv(c("SET", key, val)) 12 | res <- redis$execv(c("GET", key)) 13 | expect_equal(res, val) 14 | 15 | 16 | #test_03_execvError <- function() { 17 | expect_error(redis$execv(c("LRANGE mylist 0 elephant"))) 18 | 19 | 20 | #test_04_cleanup <- function() { 21 | ## delete set 22 | n <- redis$execv(c("del", key)) 23 | expect_equal(n, 1) 24 | -------------------------------------------------------------------------------- /inst/tinytest/test_exists.R: -------------------------------------------------------------------------------- 1 | #test_01_setup <- function() { 2 | suppressMessages(library(RcppRedis)) 3 | redis <- new(Redis) 4 | key <- "RcppRedis:test:myexists" 5 | 6 | 7 | #test_02_exists <- function() { 8 | redis$set(key, 10) 9 | n <- redis$exists(key) 10 | expect_equal(n, 1) 11 | 12 | 13 | #test_02_doesNotExist <- function() { 14 | n <- redis$exists("thisKeyIsNotSet-test") 15 | expect_equal(n, 0) 16 | 17 | 18 | #test_03_cleanup <- function() { 19 | ## delete key 20 | expect_equal(redis$del(key), 1) 21 | -------------------------------------------------------------------------------- /inst/tinytest/test_expire.R: -------------------------------------------------------------------------------- 1 | #test_01_setup <- function() { 2 | suppressMessages(library(RcppRedis)) 3 | redis <- new(Redis) 4 | key <- "RcppRedis:test:myexpires" 5 | 6 | 7 | #test_02_keyexpiresAfter1s <- function() { 8 | redis$set(key, 10) 9 | redis$expire(key, 1) 10 | Sys.sleep(1.5) 11 | n <- redis$exists(key) 12 | expect_equal(n, 0) 13 | 14 | 15 | #test_02_keyexpiresAfter2s <- function() { 16 | redis$set(key, 10) 17 | redis$expire(key, 2) 18 | Sys.sleep(1) 19 | n <- redis$exists(key) 20 | expect_equal(n, 1) 21 | Sys.sleep(1.5) 22 | n <- redis$exists(key) 23 | expect_equal(n, 0) 24 | 25 | 26 | #test_02_keyexpiresAfter1500ms <- function() { 27 | redis$set(key, 10) 28 | redis$expire(key, .9) 29 | Sys.sleep(.3) 30 | n <- redis$exists(key) 31 | expect_equal(n, 1) 32 | Sys.sleep(1.6) 33 | n <- redis$exists(key) 34 | expect_equal(n, 0) 35 | 36 | 37 | #test_03_cleanup <- function() { 38 | ## delete key 39 | expect_equal(redis$del(key), 0) 40 | expect_equal(redis$exists(key), 0) 41 | -------------------------------------------------------------------------------- /inst/tinytest/test_hash.R: -------------------------------------------------------------------------------- 1 | #test_01_setup <- function() { 2 | suppressMessages(library(RcppRedis)) 3 | redis <- new(Redis) 4 | 5 | hashname <- "myhash" 6 | 7 | xname <- "hat" 8 | xdata <- rnorm(10) 9 | 10 | yname <- "cat" 11 | ydata <- rnorm(10) 12 | 13 | 14 | #test_02_hset <- function() { 15 | ## create a hash 16 | n <- redis$hset(hashname, xname, xdata) 17 | expect_equal(n, 1) 18 | 19 | 20 | #test_03_hset_2 <- function() { 21 | ## add an already-present element to a hash 22 | n <- redis$hset(hashname, xname, xdata) 23 | expect_equal(n, 0) 24 | 25 | 26 | #test_04_hget <- function() { 27 | ## pull back the set element 28 | hashdata <- redis$hget(hashname, xname) 29 | expect_identical(xdata, hashdata) 30 | 31 | 32 | #test_05_setup <- function() { 33 | ## add more ele to hash 34 | n <- redis$hset(hashname, yname, ydata) 35 | expect_equal(n, 1) 36 | 37 | 38 | #test_06_hlen <- function() { 39 | ## check for two keys 40 | n <- redis$hlen(hashname) 41 | expect_equal(n, 2) 42 | 43 | 44 | #test_07_hkeys <- function() { 45 | ## check for values hash keys: sort order not given 46 | res <- redis$hkeys(hashname) 47 | expect_equal(sort(res), sort(c(xname,yname))) 48 | 49 | 50 | #test_08_hgetall <- function() { 51 | ## check for all data 52 | res <- redis$hgetall(hashname) 53 | expect_equal(sort(names(res)), sort(c(xname,yname))) 54 | expect_equal(res[[xname]], xdata) 55 | expect_equal(res[[yname]], ydata) 56 | 57 | 58 | #test_09_hdel <- function() { 59 | ## check 60 | n <- redis$hdel(hashname,yname) 61 | expect_equal(n, 1) 62 | 63 | 64 | #test_09_hdel_1 <- function() { 65 | ## check already deleted 66 | n <- redis$hdel(hashname,yname) 67 | expect_equal(n, 0) 68 | 69 | 70 | #test_09_hdel_2 <- function() { 71 | ## check 72 | n <- redis$hdel(hashname,xname) 73 | expect_equal(n, 1) 74 | 75 | 76 | #test_09_hdel_3 <- function() { 77 | ## check hash should now be removed 78 | n <- redis$hexists(hashname, xname) 79 | expect_equal(n, 0) 80 | -------------------------------------------------------------------------------- /inst/tinytest/test_list.R: -------------------------------------------------------------------------------- 1 | #test_01_setup <- function() { 2 | suppressMessages(library(RcppRedis)) 3 | redis <- new(Redis) 4 | 5 | exampleNumericElement <- 1.0 6 | exampleCharElement <- "a" 7 | key <- "RcppRedis:test:mylist" 8 | expect_equal(redis$del(key), 0) 9 | 10 | 11 | #test_02_lpush <- function() { 12 | ## add example elements to the list 13 | n1 <- redis$lpush(key, exampleNumericElement) 14 | n2 <- redis$lpush(key, exampleCharElement) 15 | expect_equal(n1, 1) 16 | expect_equal(n2, 2) 17 | 18 | 19 | #test_03_lpop <- function() { 20 | ## remove example elements from the list 21 | res1 <- redis$lpop(key) 22 | res2 <- redis$lpop(key) 23 | expect_equal(res1, exampleCharElement) 24 | expect_equal(res2, exampleNumericElement) 25 | 26 | 27 | #test_04_lpopnull <- function() { 28 | ## try to lpop beyond list extent 29 | res <- redis$rpop(key) 30 | expect_equal(res, NULL) 31 | 32 | 33 | #test_05_rpush <- function() { 34 | ## add example elements to the list 35 | n1 <- redis$rpush(key, exampleNumericElement) 36 | n2 <- redis$rpush(key, exampleCharElement) 37 | expect_equal(n1, 1) 38 | expect_equal(n2, 2) 39 | 40 | 41 | #test_06_rpop <- function() { 42 | ## remove example elements from the list 43 | res1 <- redis$rpop(key) 44 | res2 <- redis$rpop(key) 45 | expect_equal(res1, exampleCharElement) 46 | expect_equal(res2, exampleNumericElement) 47 | 48 | 49 | #test_07_rpopnull <- function() { 50 | ## try to rpop beyond list extent 51 | res <- redis$rpop(key) 52 | expect_equal(res, NULL) 53 | 54 | 55 | #test_08_ltrim <- function() { 56 | ##try to trim a list 57 | redis$lpush(key, 1) 58 | redis$lpush(key, 2) 59 | redis$lpush(key, 3) 60 | redis$ltrim(key, 0, 1) 61 | res <- redis$lrange(key, 0, 100) 62 | expect_equal(res,list(3,2)) 63 | expect_equal(redis$del(key), 1) 64 | 65 | 66 | #test_09_cleanup <- function() { 67 | ## delete key 68 | expect_equal(redis$del(key), 0) 69 | 70 | 71 | ## check lrem 72 | redis$del(key) 73 | elem <- "abc" 74 | redis$lpush(key, elem) 75 | redis$lpush(key, elem) 76 | redis$lpush(key, elem) 77 | redis$lpush(key, elem) 78 | expect_equal(redis$llen(key), 4) 79 | redis$lrem(key, 1, elem) 80 | expect_equal(redis$llen(key), 3) 81 | redis$lrem(key, -1, elem) 82 | expect_equal(redis$llen(key), 2) 83 | expect_equal(redis$lpop(key), elem) 84 | expect_equal(redis$lpop(key), elem) 85 | expect_equal(redis$keys(key), character()) 86 | 87 | ## check lmove 88 | altkey <- "RcppRedis:test:myotherlist" 89 | redis$del(altkey) 90 | redis$rpush(key, 1) 91 | redis$rpush(key, 2) 92 | redis$rpush(key, 3) 93 | redis$lmove(key, altkey, "LEFT", "RIGHT") 94 | expect_equal(redis$llen(key), 2) 95 | expect_equal(redis$llen(altkey), 1) 96 | redis$lmove(key, altkey, "LEFT", "LEFT") 97 | expect_equal(redis$llen(key), 1) 98 | expect_equal(redis$llen(altkey), 2) 99 | expect_equal(redis$lpop(key), 3) # as 1 and 2 have been moved (both from left) 100 | expect_equal(redis$lpop(altkey), 2) # as 2 was inserted to the left 101 | expect_equal(redis$lpop(altkey), 1) # as 1 remains 102 | -------------------------------------------------------------------------------- /inst/tinytest/test_server_issues.R: -------------------------------------------------------------------------------- 1 | 2 | exit_file("Skip this test") 3 | 4 | #test_01_setup <- function() { 5 | suppressMessages(library(RcppRedis)) 6 | ## we start the Redis server for this test as a slave so we don't clobber the main running version of redis for the rest of the tests 7 | tmpfile <- tempfile() 8 | writeLines("requirepass badPassword", tmpfile) 9 | system(paste("redis-server", tmpfile, "--port 7777 --slaveof localhost 6379"), wait=FALSE) 10 | ## Wait for server to come up 11 | Sys.sleep(5) 12 | 13 | 14 | #test_02_testUnauth <- function () { 15 | redis <- new(RcppRedis::Redis, "localhost", 7777, auth = "", 10) 16 | ## we expect an exception because we haven't send the password 17 | expect_error(redis$ping()) 18 | 19 | 20 | #test_03_testAuth <- function () { 21 | redis <- new(RcppRedis::Redis, "localhost", 7777, auth = "badPassword", 10) 22 | expect_equal(redis$ping(), "PONG") 23 | 24 | 25 | #test_04_killServer <- function() { 26 | ## confirm server is up 27 | expect_equal(redis$ping(),"PONG") 28 | ## kill server 29 | expect_error(redis$exec("SHUTDOWN")) 30 | 31 | 32 | #test_05_cleanup <- function() { 33 | #NULL 34 | -------------------------------------------------------------------------------- /inst/tinytest/test_set.R: -------------------------------------------------------------------------------- 1 | #test_01_setup <- function() { 2 | suppressMessages(library(RcppRedis)) 3 | redis <- new(Redis) 4 | 5 | x <- data.frame(x=letters[1:5], y=rnorm(5)) 6 | y <- list(foo=list(bar="qux")) 7 | key <- "RcppRedis:test:myset" 8 | 9 | 10 | #test_02_sadd <- function() { 11 | ## add one element into a set 12 | n <- redis$sadd(key, x) 13 | expect_equal(n, 1) 14 | 15 | 16 | #test_03_sadd_2 <- function() { 17 | ## add an already-present element to a set 18 | n <- redis$sadd(key, x) 19 | expect_equal(n, 0) 20 | 21 | 22 | #test_04_smembers <- function() { 23 | ## pull back the set element 24 | mem <- redis$smembers(key) 25 | expect_identical(x, mem[[1]]) 26 | 27 | 28 | #test_05_sadd_3 <- function() { 29 | ## add a new element to a set 30 | n <- redis$sadd(key, y) 31 | expect_equal(n, 1) 32 | 33 | 34 | #test_06_smembers_2 <- function() { 35 | ## pull back the set elements 36 | ## order not guaranteed 37 | mem <- redis$smembers(key) 38 | checkTrue(identical(mem, list(x, y)) | 39 | identical(mem, list(y, x))) 40 | 41 | 42 | #test_07_srem <- function() { 43 | ## drop an element from set 44 | n <- redis$srem(key, x) 45 | expect_equal(n, 1) 46 | 47 | 48 | #test_08_smembers_3 <- function() { 49 | ## pull back the remaining element 50 | ## order not guaranteed 51 | mem <- redis$smembers(key) 52 | expect_identical(y, mem[[1]]) 53 | 54 | 55 | #test_09_cleanup <- function() { 56 | ## delete set 57 | expect_equal(redis$del(key), 1) 58 | -------------------------------------------------------------------------------- /man/redisMonitorChannels.Rd: -------------------------------------------------------------------------------- 1 | \name{redisMonitorChannels} 2 | \alias{redisMonitorChannels} 3 | \title{redisMonitorChannels} 4 | \description{Listen for messages on subscribed Redis message channels.} 5 | \usage{ 6 | redisMonitorChannels(context, type=c("rdata", "raw", "string")) 7 | } 8 | \arguments{ 9 | \item{context}{ A valid Redis context (see example). } 10 | \item{type}{ The expected message value type. } 11 | } 12 | \details{(From the Redis.io documentation): 13 | implement the Publish/Subscribe messaging paradigm where (citing Wikipedia) 14 | senders (publishers) are not programmed to send their messages to specific 15 | receivers (subscribers). Rather, published messages are characterized into 16 | channels, without knowledge of what (if any) subscribers there may be. 17 | Subscribers express interest in one or more channels, and only receive messages 18 | that are of interest, without knowledge of what (if any) publishers there are. 19 | 20 | The \code{redisMonitorChannels} function may be called repeatedly in an 21 | event loop to service messages on all subscribed channels. When a message 22 | is received, the \code{redisMonitorChannels} function will attempt to 23 | evaluate a callback function with same name as the channel, with the message 24 | as its single argument. If no such function can be found, the message is 25 | returned. See the help page for \code{redisGetResponse} for a description 26 | of the message format. 27 | 28 | WARNING: The \code{redisMonitorChannels} function blocks indefinitely until a 29 | message is received. 30 | 31 | Use the lower-level \code{listen} context method to simply poll channels 32 | for messages without evaluating function callbacks. 33 | } 34 | \value{The result of an evaluated function callback message, or if 35 | no matching callback exists, the message.} 36 | \references{ 37 | http://redis.io/commands 38 | } 39 | \author{ 40 | B. W. Lewis 41 | } 42 | \examples{ 43 | \dontrun{ 44 | x <- new(Redis) 45 | y <- new(Redis) 46 | 47 | # Define a callback function to process messages from channel 1: 48 | channel1 <- function(x) { 49 | cat("Message received from channel 1: ",x,"\n") 50 | } 51 | # Define a callback function to process messages from channel 2: 52 | channel2 <- function(x) { 53 | cat("Message received from channel 2: ",x,"\n") 54 | } 55 | 56 | # Subscribe to the channels... 57 | x$subscribe(c("channel1", "channel2")) 58 | y$publish("channel2", pi) 59 | 60 | redisMonitorChannels(x) 61 | 62 | # Unsubscribe 63 | x$unsubscribe(c("channel1", "channel2")) 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /man/rhiredis.Rd: -------------------------------------------------------------------------------- 1 | \name{RcppRedis} 2 | \alias{Redis} 3 | \alias{Rcpp_Redis} 4 | \alias{Rcpp_Redis-class} 5 | \alias{serializeToChar} 6 | \alias{unserializeFromChar} 7 | \alias{serializeToRaw} 8 | \alias{unserializeFromRaw} 9 | \alias{RcppRedis-package} 10 | \docType{package} 11 | \title{Rcpp module using hiredis library to connect R to Redis} 12 | \description{ 13 | The \verb{Redis} module is created using Rcpp modules and wraps a 14 | minimal class \verb{Redis} around an Redis connection context object 15 | which permits bi-directional communication with a Redis in-memory database. 16 | 17 | New instances can be created using either a default constructor 18 | (using localhost and the default port) and either host and port, or 19 | just the host. 20 | 21 | Currently, the module has just one worker command which sends a string 22 | to the Redis instance and returns a string. 23 | 24 | The helper functions \code{serializeToChar()} and 25 | \code{unserializeFromChar} convert R objects to/from a character 26 | representation (and internalize the conversion from \code{raw} 27 | to \code{char} representation at the compiled level). 28 | The helper functions \code{serializeToRaw()} and 29 | \code{unserializeFromRaw} convert R objects to/from raw vectors. 30 | 31 | } 32 | \details{ 33 | Please consult the Redis documentation for details on the available 34 | commands. See the Rcpp-modules vignette for details on Rcpp modules. 35 | } 36 | \author{Dirk Eddelbuettel \email{edd@debian.org}} 37 | \keyword{package} 38 | -------------------------------------------------------------------------------- /src/Makevars.in: -------------------------------------------------------------------------------- 1 | ## -*- mode: makefile; -*- 2 | 3 | ## Configure tells us about locations for 4 | ## hiredis headers and library via the variables 5 | ## if MsgPack is seen we add it as well 6 | PKG_CXXFLAGS = @PKG_CXXFLAGS@ 7 | #-DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT 8 | PKG_LIBS = @PKG_LIBS@ 9 | 10 | .PHONY: all @PKG_TARGET@ 11 | 12 | all: $(SHLIB) 13 | $(SHLIB): @PKG_TARGET@ 14 | 15 | libhiredis.a: 16 | $(MAKE) CC="$(CC)" CFLAGS="-DNDEBUG $(CFLAGS) $(CPICFLAGS)" AR="$(AR)" RANLIB="$(RANLIB)" --directory=hiredis --makefile=GNUmakefile libhiredis.a 17 | -------------------------------------------------------------------------------- /src/Makevars.ucrt: -------------------------------------------------------------------------------- 1 | ## -*- mode: Makefile; -*- 2 | ## 3 | PKG_CPPFLAGS = -I${R_TOOLS_SOFT}/include/hiredis -DSTRICT_R_HEADERS 4 | PKG_LIBS = -lhiredis -lws2_32 5 | -------------------------------------------------------------------------------- /src/Makevars.win: -------------------------------------------------------------------------------- 1 | ## -*- mode: Makefile; -*- 2 | ## 3 | RWINLIB=../windows/hiredis-1.0.0 4 | PKG_CPPFLAGS = -I${RWINLIB}/include -DSTRICT_R_HEADERS 5 | PKG_LIBS = -L${RWINLIB}/lib${R_ARCH}${CRT} -lhiredis -lws2_32 6 | 7 | all: winlibs 8 | 9 | winlibs: 10 | "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" "../tools/winlibs.R" 11 | -------------------------------------------------------------------------------- /src/hiredis/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2011, Salvatore Sanfilippo 2 | Copyright (c) 2010-2011, Pieter Noordhuis 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of Redis nor the names of its contributors may be used 17 | to endorse or promote products derived from this software without specific 18 | prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/hiredis/GNUmakefile: -------------------------------------------------------------------------------- 1 | # Hiredis Makefile 2 | # Copyright (C) 2010-2011 Salvatore Sanfilippo 3 | # Copyright (C) 2010-2011 Pieter Noordhuis 4 | # This file is released under the BSD license, see the COPYING file 5 | 6 | OBJ=alloc.o net.o hiredis.o sds.o async.o read.o sockcompat.o 7 | SSL_OBJ=ssl.o 8 | EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib hiredis-example-push 9 | ifeq ($(USE_SSL),1) 10 | EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl 11 | endif 12 | TESTS=hiredis-test 13 | LIBNAME=libhiredis 14 | PKGCONFNAME=hiredis.pc 15 | SSL_LIBNAME=libhiredis_ssl 16 | SSL_PKGCONFNAME=hiredis_ssl.pc 17 | CMAKECONFNAME=HiredisConfig.cmake 18 | CMAKEVERSNAME=HiredisConfigVersion.cmake 19 | 20 | HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}') 21 | HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}') 22 | HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}') 23 | HIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}') 24 | 25 | # Installation related variables and target 26 | PREFIX?=$(DESTDIR)/usr 27 | INCLUDE_PATH?=include/hiredis 28 | LIBRARY_PATH?=lib 29 | PKGCONF_PATH?=pkgconfig 30 | CMAKE_PATH?=cmake/Hiredis 31 | INSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH) 32 | INSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH) 33 | INSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH) 34 | INSTALL_CMAKE_PATH= $(INSTALL_LIBRARY_PATH)/$(CMAKE_PATH) 35 | 36 | # redis-server configuration used for testing 37 | REDIS_PORT=56379 38 | REDIS_SERVER=redis-server 39 | define REDIS_TEST_CONFIG 40 | daemonize yes 41 | pidfile /tmp/hiredis-test-redis.pid 42 | port $(REDIS_PORT) 43 | bind 127.0.0.1 44 | unixsocket /tmp/hiredis-test-redis.sock 45 | endef 46 | export REDIS_TEST_CONFIG 47 | 48 | # Fallback to gcc when $CC is not in $PATH. 49 | CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc') 50 | CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++') 51 | OPTIMIZATION?=-O3 52 | #WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers 53 | WARNINGS=-Wall 54 | DEBUG_FLAGS?= -g -ggdb 55 | REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) 56 | REAL_LDFLAGS=$(LDFLAGS) 57 | 58 | DYLIBSUFFIX=so 59 | STLIBSUFFIX=a 60 | DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) 61 | DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) 62 | DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX) 63 | 64 | DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) 65 | STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) 66 | STLIB_MAKE_CMD=$(AR) rcs 67 | 68 | SSL_DYLIB_MINOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) 69 | SSL_DYLIB_MAJOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) 70 | SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX) 71 | SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX) 72 | SSL_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(SSL_DYLIB_MINOR_NAME) 73 | 74 | # Platform-specific overrides 75 | uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 76 | 77 | USE_SSL?=0 78 | 79 | # This is required for test.c only 80 | ifeq ($(USE_SSL),1) 81 | CFLAGS+=-DHIREDIS_TEST_SSL 82 | endif 83 | 84 | ifeq ($(uname_S),Linux) 85 | SSL_LDFLAGS=-lssl -lcrypto 86 | else 87 | OPENSSL_PREFIX?=/usr/local/opt/openssl 88 | CFLAGS+=-I$(OPENSSL_PREFIX)/include 89 | SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto 90 | endif 91 | 92 | ifeq ($(uname_S),SunOS) 93 | IS_SUN_CC=$(shell sh -c '$(CC) -V 2>&1 |egrep -i -c "sun|studio"') 94 | ifeq ($(IS_SUN_CC),1) 95 | SUN_SHARED_FLAG=-G 96 | else 97 | SUN_SHARED_FLAG=-shared 98 | endif 99 | REAL_LDFLAGS+= -ldl -lnsl -lsocket 100 | DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS) 101 | SSL_DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(SSL_DYLIBNAME) -h $(SSL_DYLIB_MINOR_NAME) $(LDFLAGS) $(SSL_LDFLAGS) 102 | endif 103 | ifeq ($(uname_S),Darwin) 104 | DYLIBSUFFIX=dylib 105 | DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX) 106 | DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) 107 | SSL_DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME) -o $(SSL_DYLIBNAME) $(LDFLAGS) $(SSL_LDFLAGS) 108 | DYLIB_PLUGIN=-Wl,-undefined -Wl,dynamic_lookup 109 | endif 110 | 111 | all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME) 112 | ifeq ($(USE_SSL),1) 113 | all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) 114 | endif 115 | 116 | # Deps (use make dep to generate this) 117 | alloc.o: alloc.c fmacros.h alloc.h 118 | async.o: async.c fmacros.h alloc.h async.h hiredis.h read.h sds.h net.h dict.c dict.h win32.h async_private.h 119 | dict.o: dict.c fmacros.h alloc.h dict.h 120 | hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h alloc.h net.h async.h win32.h 121 | net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h sockcompat.h win32.h 122 | read.o: read.c fmacros.h alloc.h read.h sds.h win32.h 123 | sds.o: sds.c sds.h sdsalloc.h alloc.h 124 | sockcompat.o: sockcompat.c sockcompat.h 125 | ssl.o: ssl.c hiredis.h read.h sds.h alloc.h async.h win32.h async_private.h 126 | test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h sockcompat.h win32.h 127 | 128 | $(DYLIBNAME): $(OBJ) 129 | $(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS) 130 | 131 | $(STLIBNAME): $(OBJ) 132 | $(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ) 133 | 134 | $(SSL_DYLIBNAME): $(SSL_OBJ) 135 | $(SSL_DYLIB_MAKE_CMD) $(DYLIB_PLUGIN) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(LDFLAGS) $(SSL_LDFLAGS) 136 | 137 | $(SSL_STLIBNAME): $(SSL_OBJ) 138 | $(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ) 139 | 140 | dynamic: $(DYLIBNAME) 141 | static: $(STLIBNAME) 142 | ifeq ($(USE_SSL),1) 143 | dynamic: $(SSL_DYLIBNAME) 144 | static: $(SSL_STLIBNAME) 145 | endif 146 | 147 | # Binaries: 148 | hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME) 149 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(REAL_LDFLAGS) 150 | 151 | hiredis-example-libevent-ssl: examples/example-libevent-ssl.c adapters/libevent.h $(STLIBNAME) $(SSL_STLIBNAME) 152 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS) 153 | 154 | hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME) 155 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lev $(STLIBNAME) $(REAL_LDFLAGS) 156 | 157 | hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME) 158 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) $(REAL_LDFLAGS) 159 | 160 | hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME) 161 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -livykis $(STLIBNAME) $(REAL_LDFLAGS) 162 | 163 | hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME) 164 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) $(REAL_LDFLAGS) 165 | 166 | hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME) 167 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS) 168 | 169 | 170 | ifndef AE_DIR 171 | hiredis-example-ae: 172 | @echo "Please specify AE_DIR (e.g. /src)" 173 | @false 174 | else 175 | hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME) 176 | $(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) 177 | endif 178 | 179 | ifndef LIBUV_DIR 180 | hiredis-example-libuv: 181 | @echo "Please specify LIBUV_DIR (e.g. ../libuv/)" 182 | @false 183 | else 184 | hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME) 185 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS) 186 | endif 187 | 188 | ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),) 189 | hiredis-example-qt: 190 | @echo "Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR" 191 | @false 192 | else 193 | hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME) 194 | $(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ 195 | $(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore 196 | $(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ 197 | $(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore 198 | $(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 199 | endif 200 | 201 | hiredis-example: examples/example.c $(STLIBNAME) 202 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS) 203 | 204 | hiredis-example-push: examples/example-push.c $(STLIBNAME) 205 | $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS) 206 | 207 | examples: $(EXAMPLES) 208 | 209 | TEST_LIBS = $(STLIBNAME) 210 | ifeq ($(USE_SSL),1) 211 | TEST_LIBS += $(SSL_STLIBNAME) 212 | TEST_LDFLAGS = $(SSL_LDFLAGS) -lssl -lcrypto -lpthread 213 | endif 214 | 215 | hiredis-test: test.o $(TEST_LIBS) 216 | $(CC) -o $@ $(REAL_CFLAGS) -I. $^ $(REAL_LDFLAGS) $(TEST_LDFLAGS) 217 | 218 | hiredis-%: %.o $(STLIBNAME) 219 | $(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS) 220 | 221 | test: hiredis-test 222 | ./hiredis-test 223 | 224 | check: hiredis-test 225 | TEST_SSL=$(USE_SSL) ./test.sh 226 | 227 | .c.o: 228 | $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $< 229 | 230 | clean: 231 | rm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov 232 | rm -f $(CMAKECONFNAME) $(CMAKEVERSNAME) 233 | 234 | dep: 235 | $(CC) $(CPPFLAGS) $(CFLAGS) -MM *.c 236 | 237 | INSTALL?= cp -pPR 238 | 239 | $(PKGCONFNAME): hiredis.h 240 | @echo "Generating $@ for pkgconfig..." 241 | @echo prefix=$(PREFIX) > $@ 242 | @echo exec_prefix=\$${prefix} >> $@ 243 | @echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@ 244 | @echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@ 245 | @echo >> $@ 246 | @echo Name: hiredis >> $@ 247 | @echo Description: Minimalistic C client library for Redis. >> $@ 248 | @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@ 249 | @echo Libs: -L\$${libdir} -lhiredis >> $@ 250 | @echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@ 251 | 252 | $(SSL_PKGCONFNAME): hiredis_ssl.h 253 | @echo "Generating $@ for pkgconfig..." 254 | @echo prefix=$(PREFIX) > $@ 255 | @echo exec_prefix=\$${prefix} >> $@ 256 | @echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@ 257 | @echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@ 258 | @echo >> $@ 259 | @echo Name: hiredis_ssl >> $@ 260 | @echo Description: SSL Support for hiredis. >> $@ 261 | @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@ 262 | @echo Requires: hiredis >> $@ 263 | @echo Libs: -L\$${libdir} -lhiredis_ssl >> $@ 264 | @echo Libs.private: -lssl -lcrypto >> $@ 265 | 266 | $(CMAKECONFNAME): $(CMAKECONFNAME).in 267 | cp $(CMAKECONFNAME).in $(CMAKECONFNAME) 268 | 269 | $(CMAKEVERSNAME): $(CMAKEVERSNAME).in 270 | sed -e "s,@HIREDIS_VERSION@,$(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH),g" \ 271 | $(CMAKEVERSNAME).in >$(CMAKEVERSNAME) 272 | 273 | install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) $(CMAKECONFNAME) $(CMAKEVERSNAME) 274 | mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH) 275 | $(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH) 276 | $(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters 277 | $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME) 278 | cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME) 279 | $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH) 280 | mkdir -p $(INSTALL_PKGCONF_PATH) 281 | $(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH) 282 | mkdir -p $(INSTALL_CMAKE_PATH) 283 | $(INSTALL) $(CMAKECONFNAME) $(INSTALL_CMAKE_PATH) 284 | $(INSTALL) $(CMAKEVERSNAME) $(INSTALL_CMAKE_PATH) 285 | 286 | ifeq ($(USE_SSL),1) 287 | install: install-ssl 288 | 289 | install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) 290 | mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH) 291 | $(INSTALL) hiredis_ssl.h $(INSTALL_INCLUDE_PATH) 292 | $(INSTALL) $(SSL_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME) 293 | cd $(INSTALL_LIBRARY_PATH) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIBNAME) 294 | $(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH) 295 | mkdir -p $(INSTALL_PKGCONF_PATH) 296 | $(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH) 297 | endif 298 | 299 | 32bit: 300 | @echo "" 301 | @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386" 302 | @echo "" 303 | $(MAKE) CFLAGS="-m32" LDFLAGS="-m32" 304 | 305 | 32bit-vars: 306 | $(eval CFLAGS=-m32) 307 | $(eval LDFLAGS=-m32) 308 | 309 | gprof: 310 | $(MAKE) CFLAGS="-pg" LDFLAGS="-pg" 311 | 312 | gcov: 313 | $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs" 314 | 315 | coverage: gcov 316 | make check 317 | mkdir -p tmp/lcov 318 | lcov -d . -c -o tmp/lcov/hiredis.info 319 | genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info 320 | 321 | noopt: 322 | $(MAKE) OPTIMIZATION="" 323 | 324 | .PHONY: all test check clean dep install 32bit 32bit-vars gprof gcov noopt 325 | -------------------------------------------------------------------------------- /src/hiredis/alloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Michael Grunder 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include "fmacros.h" 32 | #include "alloc.h" 33 | #include 34 | #include 35 | 36 | hiredisAllocFuncs hiredisAllocFns = { 37 | .mallocFn = malloc, 38 | .callocFn = calloc, 39 | .reallocFn = realloc, 40 | .strdupFn = strdup, 41 | .freeFn = free, 42 | }; 43 | 44 | /* Override hiredis' allocators with ones supplied by the user */ 45 | hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) { 46 | hiredisAllocFuncs orig = hiredisAllocFns; 47 | 48 | hiredisAllocFns = *override; 49 | 50 | return orig; 51 | } 52 | 53 | /* Reset allocators to use libc defaults */ 54 | void hiredisResetAllocators(void) { 55 | hiredisAllocFns = (hiredisAllocFuncs) { 56 | .mallocFn = malloc, 57 | .callocFn = calloc, 58 | .reallocFn = realloc, 59 | .strdupFn = strdup, 60 | .freeFn = free, 61 | }; 62 | } 63 | 64 | #ifdef _WIN32 65 | 66 | void *hi_malloc(size_t size) { 67 | return hiredisAllocFns.mallocFn(size); 68 | } 69 | 70 | void *hi_calloc(size_t nmemb, size_t size) { 71 | return hiredisAllocFns.callocFn(nmemb, size); 72 | } 73 | 74 | void *hi_realloc(void *ptr, size_t size) { 75 | return hiredisAllocFns.reallocFn(ptr, size); 76 | } 77 | 78 | char *hi_strdup(const char *str) { 79 | return hiredisAllocFns.strdupFn(str); 80 | } 81 | 82 | void hi_free(void *ptr) { 83 | hiredisAllocFns.freeFn(ptr); 84 | } 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/hiredis/alloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Michael Grunder 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef HIREDIS_ALLOC_H 32 | #define HIREDIS_ALLOC_H 33 | 34 | #include /* for size_t */ 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /* Structure pointing to our actually configured allocators */ 41 | typedef struct hiredisAllocFuncs { 42 | void *(*mallocFn)(size_t); 43 | void *(*callocFn)(size_t,size_t); 44 | void *(*reallocFn)(void*,size_t); 45 | char *(*strdupFn)(const char*); 46 | void (*freeFn)(void*); 47 | } hiredisAllocFuncs; 48 | 49 | hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha); 50 | void hiredisResetAllocators(void); 51 | 52 | #ifndef _WIN32 53 | 54 | /* Hiredis' configured allocator function pointer struct */ 55 | extern hiredisAllocFuncs hiredisAllocFns; 56 | 57 | static inline void *hi_malloc(size_t size) { 58 | return hiredisAllocFns.mallocFn(size); 59 | } 60 | 61 | static inline void *hi_calloc(size_t nmemb, size_t size) { 62 | return hiredisAllocFns.callocFn(nmemb, size); 63 | } 64 | 65 | static inline void *hi_realloc(void *ptr, size_t size) { 66 | return hiredisAllocFns.reallocFn(ptr, size); 67 | } 68 | 69 | static inline char *hi_strdup(const char *str) { 70 | return hiredisAllocFns.strdupFn(str); 71 | } 72 | 73 | static inline void hi_free(void *ptr) { 74 | hiredisAllocFns.freeFn(ptr); 75 | } 76 | 77 | #else 78 | 79 | void *hi_malloc(size_t size); 80 | void *hi_calloc(size_t nmemb, size_t size); 81 | void *hi_realloc(void *ptr, size_t size); 82 | char *hi_strdup(const char *str); 83 | void hi_free(void *ptr); 84 | 85 | #endif 86 | 87 | #ifdef __cplusplus 88 | } 89 | #endif 90 | 91 | #endif /* HIREDIS_ALLOC_H */ 92 | -------------------------------------------------------------------------------- /src/hiredis/async.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2011, Salvatore Sanfilippo 3 | * Copyright (c) 2010-2011, Pieter Noordhuis 4 | * 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef __HIREDIS_ASYNC_H 33 | #define __HIREDIS_ASYNC_H 34 | #include "hiredis.h" 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ 41 | struct dict; /* dictionary header is included in async.c */ 42 | 43 | /* Reply callback prototype and container */ 44 | typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); 45 | typedef struct redisCallback { 46 | struct redisCallback *next; /* simple singly linked list */ 47 | redisCallbackFn *fn; 48 | int pending_subs; 49 | void *privdata; 50 | } redisCallback; 51 | 52 | /* List of callbacks for either regular replies or pub/sub */ 53 | typedef struct redisCallbackList { 54 | redisCallback *head, *tail; 55 | } redisCallbackList; 56 | 57 | /* Connection callback prototypes */ 58 | typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); 59 | typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); 60 | typedef void(redisTimerCallback)(void *timer, void *privdata); 61 | 62 | /* Context for an async connection to Redis */ 63 | typedef struct redisAsyncContext { 64 | /* Hold the regular context, so it can be realloc'ed. */ 65 | redisContext c; 66 | 67 | /* Setup error flags so they can be used directly. */ 68 | int err; 69 | char *errstr; 70 | 71 | /* Not used by hiredis */ 72 | void *data; 73 | void (*dataCleanup)(void *privdata); 74 | 75 | /* Event library data and hooks */ 76 | struct { 77 | void *data; 78 | 79 | /* Hooks that are called when the library expects to start 80 | * reading/writing. These functions should be idempotent. */ 81 | void (*addRead)(void *privdata); 82 | void (*delRead)(void *privdata); 83 | void (*addWrite)(void *privdata); 84 | void (*delWrite)(void *privdata); 85 | void (*cleanup)(void *privdata); 86 | void (*scheduleTimer)(void *privdata, struct timeval tv); 87 | } ev; 88 | 89 | /* Called when either the connection is terminated due to an error or per 90 | * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ 91 | redisDisconnectCallback *onDisconnect; 92 | 93 | /* Called when the first write event was received. */ 94 | redisConnectCallback *onConnect; 95 | 96 | /* Regular command callbacks */ 97 | redisCallbackList replies; 98 | 99 | /* Address used for connect() */ 100 | struct sockaddr *saddr; 101 | size_t addrlen; 102 | 103 | /* Subscription callbacks */ 104 | struct { 105 | redisCallbackList invalid; 106 | struct dict *channels; 107 | struct dict *patterns; 108 | } sub; 109 | 110 | /* Any configured RESP3 PUSH handler */ 111 | redisAsyncPushFn *push_cb; 112 | } redisAsyncContext; 113 | 114 | /* Functions that proxy to hiredis */ 115 | redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options); 116 | redisAsyncContext *redisAsyncConnect(const char *ip, int port); 117 | redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); 118 | redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, 119 | const char *source_addr); 120 | redisAsyncContext *redisAsyncConnectUnix(const char *path); 121 | int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); 122 | int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); 123 | 124 | redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn); 125 | int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv); 126 | void redisAsyncDisconnect(redisAsyncContext *ac); 127 | void redisAsyncFree(redisAsyncContext *ac); 128 | 129 | /* Handle read/write events */ 130 | void redisAsyncHandleRead(redisAsyncContext *ac); 131 | void redisAsyncHandleWrite(redisAsyncContext *ac); 132 | void redisAsyncHandleTimeout(redisAsyncContext *ac); 133 | void redisAsyncRead(redisAsyncContext *ac); 134 | void redisAsyncWrite(redisAsyncContext *ac); 135 | 136 | /* Command functions for an async context. Write the command to the 137 | * output buffer and register the provided callback. */ 138 | int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); 139 | int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); 140 | int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); 141 | int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); 142 | 143 | #ifdef __cplusplus 144 | } 145 | #endif 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /src/hiredis/async_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2011, Salvatore Sanfilippo 3 | * Copyright (c) 2010-2011, Pieter Noordhuis 4 | * 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef __HIREDIS_ASYNC_PRIVATE_H 33 | #define __HIREDIS_ASYNC_PRIVATE_H 34 | 35 | #define _EL_ADD_READ(ctx) \ 36 | do { \ 37 | refreshTimeout(ctx); \ 38 | if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ 39 | } while (0) 40 | #define _EL_DEL_READ(ctx) do { \ 41 | if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ 42 | } while(0) 43 | #define _EL_ADD_WRITE(ctx) \ 44 | do { \ 45 | refreshTimeout(ctx); \ 46 | if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ 47 | } while (0) 48 | #define _EL_DEL_WRITE(ctx) do { \ 49 | if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ 50 | } while(0) 51 | #define _EL_CLEANUP(ctx) do { \ 52 | if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ 53 | ctx->ev.cleanup = NULL; \ 54 | } while(0); 55 | 56 | static inline void refreshTimeout(redisAsyncContext *ctx) { 57 | #define REDIS_TIMER_ISSET(tvp) \ 58 | (tvp && ((tvp)->tv_sec || (tvp)->tv_usec)) 59 | 60 | #define REDIS_EL_TIMER(ac, tvp) \ 61 | if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \ 62 | (ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \ 63 | } 64 | 65 | if (ctx->c.flags & REDIS_CONNECTED) { 66 | REDIS_EL_TIMER(ctx, ctx->c.command_timeout); 67 | } else { 68 | REDIS_EL_TIMER(ctx, ctx->c.connect_timeout); 69 | } 70 | } 71 | 72 | void __redisAsyncDisconnect(redisAsyncContext *ac); 73 | void redisProcessCallbacks(redisAsyncContext *ac); 74 | 75 | #endif /* __HIREDIS_ASYNC_PRIVATE_H */ 76 | -------------------------------------------------------------------------------- /src/hiredis/dict.c: -------------------------------------------------------------------------------- 1 | /* Hash table implementation. 2 | * 3 | * This file implements in memory hash tables with insert/del/replace/find/ 4 | * get-random-element operations. Hash tables will auto resize if needed 5 | * tables of power of two in size are used, collisions are handled by 6 | * chaining. See the source code for more information... :) 7 | * 8 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * * Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * * Neither the name of Redis nor the names of its contributors may be used 20 | * to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | #include "fmacros.h" 37 | #include "alloc.h" 38 | #include 39 | #include 40 | #include 41 | #include "dict.h" 42 | 43 | /* -------------------------- private prototypes ---------------------------- */ 44 | 45 | static int _dictExpandIfNeeded(dict *ht); 46 | static unsigned long _dictNextPower(unsigned long size); 47 | static int _dictKeyIndex(dict *ht, const void *key); 48 | static int _dictInit(dict *ht, dictType *type, void *privDataPtr); 49 | 50 | /* -------------------------- hash functions -------------------------------- */ 51 | 52 | /* Generic hash function (a popular one from Bernstein). 53 | * I tested a few and this was the best. */ 54 | static unsigned int dictGenHashFunction(const unsigned char *buf, int len) { 55 | unsigned int hash = 5381; 56 | 57 | while (len--) 58 | hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */ 59 | return hash; 60 | } 61 | 62 | /* ----------------------------- API implementation ------------------------- */ 63 | 64 | /* Reset an hashtable already initialized with ht_init(). 65 | * NOTE: This function should only called by ht_destroy(). */ 66 | static void _dictReset(dict *ht) { 67 | ht->table = NULL; 68 | ht->size = 0; 69 | ht->sizemask = 0; 70 | ht->used = 0; 71 | } 72 | 73 | /* Create a new hash table */ 74 | static dict *dictCreate(dictType *type, void *privDataPtr) { 75 | dict *ht = hi_malloc(sizeof(*ht)); 76 | if (ht == NULL) 77 | return NULL; 78 | 79 | _dictInit(ht,type,privDataPtr); 80 | return ht; 81 | } 82 | 83 | /* Initialize the hash table */ 84 | static int _dictInit(dict *ht, dictType *type, void *privDataPtr) { 85 | _dictReset(ht); 86 | ht->type = type; 87 | ht->privdata = privDataPtr; 88 | return DICT_OK; 89 | } 90 | 91 | /* Expand or create the hashtable */ 92 | static int dictExpand(dict *ht, unsigned long size) { 93 | dict n; /* the new hashtable */ 94 | unsigned long realsize = _dictNextPower(size), i; 95 | 96 | /* the size is invalid if it is smaller than the number of 97 | * elements already inside the hashtable */ 98 | if (ht->used > size) 99 | return DICT_ERR; 100 | 101 | _dictInit(&n, ht->type, ht->privdata); 102 | n.size = realsize; 103 | n.sizemask = realsize-1; 104 | n.table = hi_calloc(realsize,sizeof(dictEntry*)); 105 | if (n.table == NULL) 106 | return DICT_ERR; 107 | 108 | /* Copy all the elements from the old to the new table: 109 | * note that if the old hash table is empty ht->size is zero, 110 | * so dictExpand just creates an hash table. */ 111 | n.used = ht->used; 112 | for (i = 0; i < ht->size && ht->used > 0; i++) { 113 | dictEntry *he, *nextHe; 114 | 115 | if (ht->table[i] == NULL) continue; 116 | 117 | /* For each hash entry on this slot... */ 118 | he = ht->table[i]; 119 | while(he) { 120 | unsigned int h; 121 | 122 | nextHe = he->next; 123 | /* Get the new element index */ 124 | h = dictHashKey(ht, he->key) & n.sizemask; 125 | he->next = n.table[h]; 126 | n.table[h] = he; 127 | ht->used--; 128 | /* Pass to the next element */ 129 | he = nextHe; 130 | } 131 | } 132 | assert(ht->used == 0); 133 | hi_free(ht->table); 134 | 135 | /* Remap the new hashtable in the old */ 136 | *ht = n; 137 | return DICT_OK; 138 | } 139 | 140 | /* Add an element to the target hash table */ 141 | static int dictAdd(dict *ht, void *key, void *val) { 142 | int index; 143 | dictEntry *entry; 144 | 145 | /* Get the index of the new element, or -1 if 146 | * the element already exists. */ 147 | if ((index = _dictKeyIndex(ht, key)) == -1) 148 | return DICT_ERR; 149 | 150 | /* Allocates the memory and stores key */ 151 | entry = hi_malloc(sizeof(*entry)); 152 | if (entry == NULL) 153 | return DICT_ERR; 154 | 155 | entry->next = ht->table[index]; 156 | ht->table[index] = entry; 157 | 158 | /* Set the hash entry fields. */ 159 | dictSetHashKey(ht, entry, key); 160 | dictSetHashVal(ht, entry, val); 161 | ht->used++; 162 | return DICT_OK; 163 | } 164 | 165 | /* Add an element, discarding the old if the key already exists. 166 | * Return 1 if the key was added from scratch, 0 if there was already an 167 | * element with such key and dictReplace() just performed a value update 168 | * operation. */ 169 | static int dictReplace(dict *ht, void *key, void *val) { 170 | dictEntry *entry, auxentry; 171 | 172 | /* Try to add the element. If the key 173 | * does not exists dictAdd will succeed. */ 174 | if (dictAdd(ht, key, val) == DICT_OK) 175 | return 1; 176 | /* It already exists, get the entry */ 177 | entry = dictFind(ht, key); 178 | if (entry == NULL) 179 | return 0; 180 | 181 | /* Free the old value and set the new one */ 182 | /* Set the new value and free the old one. Note that it is important 183 | * to do that in this order, as the value may just be exactly the same 184 | * as the previous one. In this context, think to reference counting, 185 | * you want to increment (set), and then decrement (free), and not the 186 | * reverse. */ 187 | auxentry = *entry; 188 | dictSetHashVal(ht, entry, val); 189 | dictFreeEntryVal(ht, &auxentry); 190 | return 0; 191 | } 192 | 193 | /* Search and remove an element */ 194 | static int dictDelete(dict *ht, const void *key) { 195 | unsigned int h; 196 | dictEntry *de, *prevde; 197 | 198 | if (ht->size == 0) 199 | return DICT_ERR; 200 | h = dictHashKey(ht, key) & ht->sizemask; 201 | de = ht->table[h]; 202 | 203 | prevde = NULL; 204 | while(de) { 205 | if (dictCompareHashKeys(ht,key,de->key)) { 206 | /* Unlink the element from the list */ 207 | if (prevde) 208 | prevde->next = de->next; 209 | else 210 | ht->table[h] = de->next; 211 | 212 | dictFreeEntryKey(ht,de); 213 | dictFreeEntryVal(ht,de); 214 | hi_free(de); 215 | ht->used--; 216 | return DICT_OK; 217 | } 218 | prevde = de; 219 | de = de->next; 220 | } 221 | return DICT_ERR; /* not found */ 222 | } 223 | 224 | /* Destroy an entire hash table */ 225 | static int _dictClear(dict *ht) { 226 | unsigned long i; 227 | 228 | /* Free all the elements */ 229 | for (i = 0; i < ht->size && ht->used > 0; i++) { 230 | dictEntry *he, *nextHe; 231 | 232 | if ((he = ht->table[i]) == NULL) continue; 233 | while(he) { 234 | nextHe = he->next; 235 | dictFreeEntryKey(ht, he); 236 | dictFreeEntryVal(ht, he); 237 | hi_free(he); 238 | ht->used--; 239 | he = nextHe; 240 | } 241 | } 242 | /* Free the table and the allocated cache structure */ 243 | hi_free(ht->table); 244 | /* Re-initialize the table */ 245 | _dictReset(ht); 246 | return DICT_OK; /* never fails */ 247 | } 248 | 249 | /* Clear & Release the hash table */ 250 | static void dictRelease(dict *ht) { 251 | _dictClear(ht); 252 | hi_free(ht); 253 | } 254 | 255 | static dictEntry *dictFind(dict *ht, const void *key) { 256 | dictEntry *he; 257 | unsigned int h; 258 | 259 | if (ht->size == 0) return NULL; 260 | h = dictHashKey(ht, key) & ht->sizemask; 261 | he = ht->table[h]; 262 | while(he) { 263 | if (dictCompareHashKeys(ht, key, he->key)) 264 | return he; 265 | he = he->next; 266 | } 267 | return NULL; 268 | } 269 | 270 | static dictIterator *dictGetIterator(dict *ht) { 271 | dictIterator *iter = hi_malloc(sizeof(*iter)); 272 | if (iter == NULL) 273 | return NULL; 274 | 275 | iter->ht = ht; 276 | iter->index = -1; 277 | iter->entry = NULL; 278 | iter->nextEntry = NULL; 279 | return iter; 280 | } 281 | 282 | static dictEntry *dictNext(dictIterator *iter) { 283 | while (1) { 284 | if (iter->entry == NULL) { 285 | iter->index++; 286 | if (iter->index >= 287 | (signed)iter->ht->size) break; 288 | iter->entry = iter->ht->table[iter->index]; 289 | } else { 290 | iter->entry = iter->nextEntry; 291 | } 292 | if (iter->entry) { 293 | /* We need to save the 'next' here, the iterator user 294 | * may delete the entry we are returning. */ 295 | iter->nextEntry = iter->entry->next; 296 | return iter->entry; 297 | } 298 | } 299 | return NULL; 300 | } 301 | 302 | static void dictReleaseIterator(dictIterator *iter) { 303 | hi_free(iter); 304 | } 305 | 306 | /* ------------------------- private functions ------------------------------ */ 307 | 308 | /* Expand the hash table if needed */ 309 | static int _dictExpandIfNeeded(dict *ht) { 310 | /* If the hash table is empty expand it to the initial size, 311 | * if the table is "full" double its size. */ 312 | if (ht->size == 0) 313 | return dictExpand(ht, DICT_HT_INITIAL_SIZE); 314 | if (ht->used == ht->size) 315 | return dictExpand(ht, ht->size*2); 316 | return DICT_OK; 317 | } 318 | 319 | /* Our hash table capability is a power of two */ 320 | static unsigned long _dictNextPower(unsigned long size) { 321 | unsigned long i = DICT_HT_INITIAL_SIZE; 322 | 323 | if (size >= LONG_MAX) return LONG_MAX; 324 | while(1) { 325 | if (i >= size) 326 | return i; 327 | i *= 2; 328 | } 329 | } 330 | 331 | /* Returns the index of a free slot that can be populated with 332 | * an hash entry for the given 'key'. 333 | * If the key already exists, -1 is returned. */ 334 | static int _dictKeyIndex(dict *ht, const void *key) { 335 | unsigned int h; 336 | dictEntry *he; 337 | 338 | /* Expand the hashtable if needed */ 339 | if (_dictExpandIfNeeded(ht) == DICT_ERR) 340 | return -1; 341 | /* Compute the key hash value */ 342 | h = dictHashKey(ht, key) & ht->sizemask; 343 | /* Search if this slot does not already contain the given key */ 344 | he = ht->table[h]; 345 | while(he) { 346 | if (dictCompareHashKeys(ht, key, he->key)) 347 | return -1; 348 | he = he->next; 349 | } 350 | return h; 351 | } 352 | 353 | -------------------------------------------------------------------------------- /src/hiredis/dict.h: -------------------------------------------------------------------------------- 1 | /* Hash table implementation. 2 | * 3 | * This file implements in memory hash tables with insert/del/replace/find/ 4 | * get-random-element operations. Hash tables will auto resize if needed 5 | * tables of power of two in size are used, collisions are handled by 6 | * chaining. See the source code for more information... :) 7 | * 8 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * * Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * * Neither the name of Redis nor the names of its contributors may be used 20 | * to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | #ifndef __DICT_H 37 | #define __DICT_H 38 | 39 | #define DICT_OK 0 40 | #define DICT_ERR 1 41 | 42 | /* Unused arguments generate annoying warnings... */ 43 | #define DICT_NOTUSED(V) ((void) V) 44 | 45 | typedef struct dictEntry { 46 | void *key; 47 | void *val; 48 | struct dictEntry *next; 49 | } dictEntry; 50 | 51 | typedef struct dictType { 52 | unsigned int (*hashFunction)(const void *key); 53 | void *(*keyDup)(void *privdata, const void *key); 54 | void *(*valDup)(void *privdata, const void *obj); 55 | int (*keyCompare)(void *privdata, const void *key1, const void *key2); 56 | void (*keyDestructor)(void *privdata, void *key); 57 | void (*valDestructor)(void *privdata, void *obj); 58 | } dictType; 59 | 60 | typedef struct dict { 61 | dictEntry **table; 62 | dictType *type; 63 | unsigned long size; 64 | unsigned long sizemask; 65 | unsigned long used; 66 | void *privdata; 67 | } dict; 68 | 69 | typedef struct dictIterator { 70 | dict *ht; 71 | int index; 72 | dictEntry *entry, *nextEntry; 73 | } dictIterator; 74 | 75 | /* This is the initial size of every hash table */ 76 | #define DICT_HT_INITIAL_SIZE 4 77 | 78 | /* ------------------------------- Macros ------------------------------------*/ 79 | #define dictFreeEntryVal(ht, entry) \ 80 | if ((ht)->type->valDestructor) \ 81 | (ht)->type->valDestructor((ht)->privdata, (entry)->val) 82 | 83 | #define dictSetHashVal(ht, entry, _val_) do { \ 84 | if ((ht)->type->valDup) \ 85 | entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ 86 | else \ 87 | entry->val = (_val_); \ 88 | } while(0) 89 | 90 | #define dictFreeEntryKey(ht, entry) \ 91 | if ((ht)->type->keyDestructor) \ 92 | (ht)->type->keyDestructor((ht)->privdata, (entry)->key) 93 | 94 | #define dictSetHashKey(ht, entry, _key_) do { \ 95 | if ((ht)->type->keyDup) \ 96 | entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ 97 | else \ 98 | entry->key = (_key_); \ 99 | } while(0) 100 | 101 | #define dictCompareHashKeys(ht, key1, key2) \ 102 | (((ht)->type->keyCompare) ? \ 103 | (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ 104 | (key1) == (key2)) 105 | 106 | #define dictHashKey(ht, key) (ht)->type->hashFunction(key) 107 | 108 | #define dictGetEntryKey(he) ((he)->key) 109 | #define dictGetEntryVal(he) ((he)->val) 110 | #define dictSlots(ht) ((ht)->size) 111 | #define dictSize(ht) ((ht)->used) 112 | 113 | /* API */ 114 | static unsigned int dictGenHashFunction(const unsigned char *buf, int len); 115 | static dict *dictCreate(dictType *type, void *privDataPtr); 116 | static int dictExpand(dict *ht, unsigned long size); 117 | static int dictAdd(dict *ht, void *key, void *val); 118 | static int dictReplace(dict *ht, void *key, void *val); 119 | static int dictDelete(dict *ht, const void *key); 120 | static void dictRelease(dict *ht); 121 | static dictEntry * dictFind(dict *ht, const void *key); 122 | static dictIterator *dictGetIterator(dict *ht); 123 | static dictEntry *dictNext(dictIterator *iter); 124 | static void dictReleaseIterator(dictIterator *iter); 125 | 126 | #endif /* __DICT_H */ 127 | -------------------------------------------------------------------------------- /src/hiredis/fmacros.h: -------------------------------------------------------------------------------- 1 | #ifndef __HIREDIS_FMACRO_H 2 | #define __HIREDIS_FMACRO_H 3 | 4 | #define _XOPEN_SOURCE 600 5 | #define _POSIX_C_SOURCE 200112L 6 | 7 | #if defined(__APPLE__) && defined(__MACH__) 8 | /* Enable TCP_KEEPALIVE */ 9 | #define _DARWIN_C_SOURCE 10 | #endif 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/hiredis/hiredis.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2011, Salvatore Sanfilippo 3 | * Copyright (c) 2010-2014, Pieter Noordhuis 4 | * Copyright (c) 2015, Matt Stancliff , 5 | * Jan-Erik Rediger 6 | * 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * * Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * * Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * * Neither the name of Redis nor the names of its contributors may be used 18 | * to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef __HIREDIS_H 35 | #define __HIREDIS_H 36 | #include "read.h" 37 | #include /* for va_list */ 38 | #ifndef _MSC_VER 39 | #include /* for struct timeval */ 40 | #else 41 | struct timeval; /* forward declaration */ 42 | typedef long long ssize_t; 43 | #endif 44 | #include /* uintXX_t, etc */ 45 | #include "sds.h" /* for sds */ 46 | #include "alloc.h" /* for allocation wrappers */ 47 | 48 | #define HIREDIS_MAJOR 1 49 | #define HIREDIS_MINOR 0 50 | #define HIREDIS_PATCH 2 51 | #define HIREDIS_SONAME 1.0.0 52 | 53 | /* Connection type can be blocking or non-blocking and is set in the 54 | * least significant bit of the flags field in redisContext. */ 55 | #define REDIS_BLOCK 0x1 56 | 57 | /* Connection may be disconnected before being free'd. The second bit 58 | * in the flags field is set when the context is connected. */ 59 | #define REDIS_CONNECTED 0x2 60 | 61 | /* The async API might try to disconnect cleanly and flush the output 62 | * buffer and read all subsequent replies before disconnecting. 63 | * This flag means no new commands can come in and the connection 64 | * should be terminated once all replies have been read. */ 65 | #define REDIS_DISCONNECTING 0x4 66 | 67 | /* Flag specific to the async API which means that the context should be clean 68 | * up as soon as possible. */ 69 | #define REDIS_FREEING 0x8 70 | 71 | /* Flag that is set when an async callback is executed. */ 72 | #define REDIS_IN_CALLBACK 0x10 73 | 74 | /* Flag that is set when the async context has one or more subscriptions. */ 75 | #define REDIS_SUBSCRIBED 0x20 76 | 77 | /* Flag that is set when monitor mode is active */ 78 | #define REDIS_MONITORING 0x40 79 | 80 | /* Flag that is set when we should set SO_REUSEADDR before calling bind() */ 81 | #define REDIS_REUSEADDR 0x80 82 | 83 | /** 84 | * Flag that indicates the user does not want the context to 85 | * be automatically freed upon error 86 | */ 87 | #define REDIS_NO_AUTO_FREE 0x200 88 | 89 | #define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ 90 | 91 | /* number of times we retry to connect in the case of EADDRNOTAVAIL and 92 | * SO_REUSEADDR is being used. */ 93 | #define REDIS_CONNECT_RETRIES 10 94 | 95 | /* Forward declarations for structs defined elsewhere */ 96 | struct redisAsyncContext; 97 | struct redisContext; 98 | 99 | /* RESP3 push helpers and callback prototypes */ 100 | #define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH) 101 | typedef void (redisPushFn)(void *, void *); 102 | typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *); 103 | 104 | #ifdef __cplusplus 105 | extern "C" { 106 | #endif 107 | 108 | /* This is the reply object returned by redisCommand() */ 109 | typedef struct redisReply { 110 | int type; /* REDIS_REPLY_* */ 111 | long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ 112 | double dval; /* The double when type is REDIS_REPLY_DOUBLE */ 113 | size_t len; /* Length of string */ 114 | char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING 115 | REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */ 116 | char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null 117 | terminated 3 character content type, such as "txt". */ 118 | size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ 119 | struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ 120 | } redisReply; 121 | 122 | redisReader *redisReaderCreate(void); 123 | 124 | /* Function to free the reply objects hiredis returns by default. */ 125 | void freeReplyObject(void *reply); 126 | 127 | /* Functions to format a command according to the protocol. */ 128 | int redisvFormatCommand(char **target, const char *format, va_list ap); 129 | int redisFormatCommand(char **target, const char *format, ...); 130 | int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); 131 | int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen); 132 | void redisFreeCommand(char *cmd); 133 | void redisFreeSdsCommand(sds cmd); 134 | 135 | enum redisConnectionType { 136 | REDIS_CONN_TCP, 137 | REDIS_CONN_UNIX, 138 | REDIS_CONN_USERFD 139 | }; 140 | 141 | struct redisSsl; 142 | 143 | #define REDIS_OPT_NONBLOCK 0x01 144 | #define REDIS_OPT_REUSEADDR 0x02 145 | 146 | /** 147 | * Don't automatically free the async object on a connection failure, 148 | * or other implicit conditions. Only free on an explicit call to disconnect() or free() 149 | */ 150 | #define REDIS_OPT_NOAUTOFREE 0x04 151 | 152 | /* Don't automatically intercept and free RESP3 PUSH replies. */ 153 | #define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 154 | 155 | /* In Unix systems a file descriptor is a regular signed int, with -1 156 | * representing an invalid descriptor. In Windows it is a SOCKET 157 | * (32- or 64-bit unsigned integer depending on the architecture), where 158 | * all bits set (~0) is INVALID_SOCKET. */ 159 | #ifndef _WIN32 160 | typedef int redisFD; 161 | #define REDIS_INVALID_FD -1 162 | #else 163 | #ifdef _WIN64 164 | typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */ 165 | #else 166 | typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */ 167 | #endif 168 | #define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */ 169 | #endif 170 | 171 | typedef struct { 172 | /* 173 | * the type of connection to use. This also indicates which 174 | * `endpoint` member field to use 175 | */ 176 | int type; 177 | /* bit field of REDIS_OPT_xxx */ 178 | int options; 179 | /* timeout value for connect operation. If NULL, no timeout is used */ 180 | const struct timeval *connect_timeout; 181 | /* timeout value for commands. If NULL, no timeout is used. This can be 182 | * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */ 183 | const struct timeval *command_timeout; 184 | union { 185 | /** use this field for tcp/ip connections */ 186 | struct { 187 | const char *source_addr; 188 | const char *ip; 189 | int port; 190 | } tcp; 191 | /** use this field for unix domain sockets */ 192 | const char *unix_socket; 193 | /** 194 | * use this field to have hiredis operate an already-open 195 | * file descriptor */ 196 | redisFD fd; 197 | } endpoint; 198 | 199 | /* Optional user defined data/destructor */ 200 | void *privdata; 201 | void (*free_privdata)(void *); 202 | 203 | /* A user defined PUSH message callback */ 204 | redisPushFn *push_cb; 205 | redisAsyncPushFn *async_push_cb; 206 | } redisOptions; 207 | 208 | /** 209 | * Helper macros to initialize options to their specified fields. 210 | */ 211 | #define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \ 212 | (opts)->type = REDIS_CONN_TCP; \ 213 | (opts)->endpoint.tcp.ip = ip_; \ 214 | (opts)->endpoint.tcp.port = port_; 215 | 216 | #define REDIS_OPTIONS_SET_UNIX(opts, path) \ 217 | (opts)->type = REDIS_CONN_UNIX; \ 218 | (opts)->endpoint.unix_socket = path; 219 | 220 | #define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \ 221 | (opts)->privdata = data; \ 222 | (opts)->free_privdata = dtor; \ 223 | 224 | typedef struct redisContextFuncs { 225 | void (*free_privctx)(void *); 226 | void (*async_read)(struct redisAsyncContext *); 227 | void (*async_write)(struct redisAsyncContext *); 228 | ssize_t (*read)(struct redisContext *, char *, size_t); 229 | ssize_t (*write)(struct redisContext *); 230 | } redisContextFuncs; 231 | 232 | /* Context for a connection to Redis */ 233 | typedef struct redisContext { 234 | const redisContextFuncs *funcs; /* Function table */ 235 | 236 | int err; /* Error flags, 0 when there is no error */ 237 | char errstr[128]; /* String representation of error when applicable */ 238 | redisFD fd; 239 | int flags; 240 | char *obuf; /* Write buffer */ 241 | redisReader *reader; /* Protocol reader */ 242 | 243 | enum redisConnectionType connection_type; 244 | struct timeval *connect_timeout; 245 | struct timeval *command_timeout; 246 | 247 | struct { 248 | char *host; 249 | char *source_addr; 250 | int port; 251 | } tcp; 252 | 253 | struct { 254 | char *path; 255 | } unix_sock; 256 | 257 | /* For non-blocking connect */ 258 | struct sockadr *saddr; 259 | size_t addrlen; 260 | 261 | /* Optional data and corresponding destructor users can use to provide 262 | * context to a given redisContext. Not used by hiredis. */ 263 | void *privdata; 264 | void (*free_privdata)(void *); 265 | 266 | /* Internal context pointer presently used by hiredis to manage 267 | * SSL connections. */ 268 | void *privctx; 269 | 270 | /* An optional RESP3 PUSH handler */ 271 | redisPushFn *push_cb; 272 | } redisContext; 273 | 274 | redisContext *redisConnectWithOptions(const redisOptions *options); 275 | redisContext *redisConnect(const char *ip, int port); 276 | redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); 277 | redisContext *redisConnectNonBlock(const char *ip, int port); 278 | redisContext *redisConnectBindNonBlock(const char *ip, int port, 279 | const char *source_addr); 280 | redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, 281 | const char *source_addr); 282 | redisContext *redisConnectUnix(const char *path); 283 | redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); 284 | redisContext *redisConnectUnixNonBlock(const char *path); 285 | redisContext *redisConnectFd(redisFD fd); 286 | 287 | /** 288 | * Reconnect the given context using the saved information. 289 | * 290 | * This re-uses the exact same connect options as in the initial connection. 291 | * host, ip (or path), timeout and bind address are reused, 292 | * flags are used unmodified from the existing context. 293 | * 294 | * Returns REDIS_OK on successful connect or REDIS_ERR otherwise. 295 | */ 296 | int redisReconnect(redisContext *c); 297 | 298 | redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn); 299 | int redisSetTimeout(redisContext *c, const struct timeval tv); 300 | int redisEnableKeepAlive(redisContext *c); 301 | void redisFree(redisContext *c); 302 | redisFD redisFreeKeepFd(redisContext *c); 303 | int redisBufferRead(redisContext *c); 304 | int redisBufferWrite(redisContext *c, int *done); 305 | 306 | /* In a blocking context, this function first checks if there are unconsumed 307 | * replies to return and returns one if so. Otherwise, it flushes the output 308 | * buffer to the socket and reads until it has a reply. In a non-blocking 309 | * context, it will return unconsumed replies until there are no more. */ 310 | int redisGetReply(redisContext *c, void **reply); 311 | int redisGetReplyFromReader(redisContext *c, void **reply); 312 | 313 | /* Write a formatted command to the output buffer. Use these functions in blocking mode 314 | * to get a pipeline of commands. */ 315 | int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len); 316 | 317 | /* Write a command to the output buffer. Use these functions in blocking mode 318 | * to get a pipeline of commands. */ 319 | int redisvAppendCommand(redisContext *c, const char *format, va_list ap); 320 | int redisAppendCommand(redisContext *c, const char *format, ...); 321 | int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); 322 | 323 | /* Issue a command to Redis. In a blocking context, it is identical to calling 324 | * redisAppendCommand, followed by redisGetReply. The function will return 325 | * NULL if there was an error in performing the request, otherwise it will 326 | * return the reply. In a non-blocking context, it is identical to calling 327 | * only redisAppendCommand and will always return NULL. */ 328 | void *redisvCommand(redisContext *c, const char *format, va_list ap); 329 | void *redisCommand(redisContext *c, const char *format, ...); 330 | void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); 331 | 332 | #ifdef __cplusplus 333 | } 334 | #endif 335 | 336 | #endif 337 | -------------------------------------------------------------------------------- /src/hiredis/net.h: -------------------------------------------------------------------------------- 1 | /* Extracted from anet.c to work properly with Hiredis error reporting. 2 | * 3 | * Copyright (c) 2009-2011, Salvatore Sanfilippo 4 | * Copyright (c) 2010-2014, Pieter Noordhuis 5 | * Copyright (c) 2015, Matt Stancliff , 6 | * Jan-Erik Rediger 7 | * 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * * Redistributions of source code must retain the above copyright notice, 14 | * this list of conditions and the following disclaimer. 15 | * * Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * * Neither the name of Redis nor the names of its contributors may be used 19 | * to endorse or promote products derived from this software without 20 | * specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | * POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #ifndef __NET_H 36 | #define __NET_H 37 | 38 | #include "hiredis.h" 39 | 40 | void redisNetClose(redisContext *c); 41 | ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap); 42 | ssize_t redisNetWrite(redisContext *c); 43 | 44 | int redisCheckSocketError(redisContext *c); 45 | int redisContextSetTimeout(redisContext *c, const struct timeval tv); 46 | int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); 47 | int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, 48 | const struct timeval *timeout, 49 | const char *source_addr); 50 | int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); 51 | int redisKeepAlive(redisContext *c, int interval); 52 | int redisCheckConnectDone(redisContext *c, int *completed); 53 | 54 | int redisSetTcpNoDelay(redisContext *c); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/hiredis/read.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2011, Salvatore Sanfilippo 3 | * Copyright (c) 2010-2011, Pieter Noordhuis 4 | * 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | #ifndef __HIREDIS_READ_H 34 | #define __HIREDIS_READ_H 35 | #include /* for size_t */ 36 | 37 | #define REDIS_ERR -1 38 | #define REDIS_OK 0 39 | 40 | /* When an error occurs, the err flag in a context is set to hold the type of 41 | * error that occurred. REDIS_ERR_IO means there was an I/O error and you 42 | * should use the "errno" variable to find out what is wrong. 43 | * For other values, the "errstr" field will hold a description. */ 44 | #define REDIS_ERR_IO 1 /* Error in read or write */ 45 | #define REDIS_ERR_EOF 3 /* End of file */ 46 | #define REDIS_ERR_PROTOCOL 4 /* Protocol error */ 47 | #define REDIS_ERR_OOM 5 /* Out of memory */ 48 | #define REDIS_ERR_TIMEOUT 6 /* Timed out */ 49 | #define REDIS_ERR_OTHER 2 /* Everything else... */ 50 | 51 | #define REDIS_REPLY_STRING 1 52 | #define REDIS_REPLY_ARRAY 2 53 | #define REDIS_REPLY_INTEGER 3 54 | #define REDIS_REPLY_NIL 4 55 | #define REDIS_REPLY_STATUS 5 56 | #define REDIS_REPLY_ERROR 6 57 | #define REDIS_REPLY_DOUBLE 7 58 | #define REDIS_REPLY_BOOL 8 59 | #define REDIS_REPLY_MAP 9 60 | #define REDIS_REPLY_SET 10 61 | #define REDIS_REPLY_ATTR 11 62 | #define REDIS_REPLY_PUSH 12 63 | #define REDIS_REPLY_BIGNUM 13 64 | #define REDIS_REPLY_VERB 14 65 | 66 | /* Default max unused reader buffer. */ 67 | #define REDIS_READER_MAX_BUF (1024*16) 68 | 69 | /* Default multi-bulk element limit */ 70 | #define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1) 71 | 72 | #ifdef __cplusplus 73 | extern "C" { 74 | #endif 75 | 76 | typedef struct redisReadTask { 77 | int type; 78 | long long elements; /* number of elements in multibulk container */ 79 | int idx; /* index in parent (array) object */ 80 | void *obj; /* holds user-generated value for a read task */ 81 | struct redisReadTask *parent; /* parent task */ 82 | void *privdata; /* user-settable arbitrary field */ 83 | } redisReadTask; 84 | 85 | typedef struct redisReplyObjectFunctions { 86 | void *(*createString)(const redisReadTask*, char*, size_t); 87 | void *(*createArray)(const redisReadTask*, size_t); 88 | void *(*createInteger)(const redisReadTask*, long long); 89 | void *(*createDouble)(const redisReadTask*, double, char*, size_t); 90 | void *(*createNil)(const redisReadTask*); 91 | void *(*createBool)(const redisReadTask*, int); 92 | void (*freeObject)(void*); 93 | } redisReplyObjectFunctions; 94 | 95 | typedef struct redisReader { 96 | int err; /* Error flags, 0 when there is no error */ 97 | char errstr[128]; /* String representation of error when applicable */ 98 | 99 | char *buf; /* Read buffer */ 100 | size_t pos; /* Buffer cursor */ 101 | size_t len; /* Buffer length */ 102 | size_t maxbuf; /* Max length of unused buffer */ 103 | long long maxelements; /* Max multi-bulk elements */ 104 | 105 | redisReadTask **task; 106 | int tasks; 107 | 108 | int ridx; /* Index of current read task */ 109 | void *reply; /* Temporary reply pointer */ 110 | 111 | redisReplyObjectFunctions *fn; 112 | void *privdata; 113 | } redisReader; 114 | 115 | /* Public API for the protocol parser. */ 116 | redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn); 117 | void redisReaderFree(redisReader *r); 118 | int redisReaderFeed(redisReader *r, const char *buf, size_t len); 119 | int redisReaderGetReply(redisReader *r, void **reply); 120 | 121 | #define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) 122 | #define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) 123 | #define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) 124 | 125 | #ifdef __cplusplus 126 | } 127 | #endif 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /src/hiredis/sds.h: -------------------------------------------------------------------------------- 1 | /* SDSLib 2.0 -- A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2015, Salvatore Sanfilippo 4 | * Copyright (c) 2015, Oran Agra 5 | * Copyright (c) 2015, Redis Labs, Inc 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __SDS_H 34 | #define __SDS_H 35 | 36 | #define SDS_MAX_PREALLOC (1024*1024) 37 | #ifdef _MSC_VER 38 | #define __attribute__(x) 39 | typedef long long ssize_t; 40 | #define SSIZE_MAX (LLONG_MAX >> 1) 41 | #endif 42 | 43 | #include 44 | #include 45 | #include 46 | 47 | typedef char *sds; 48 | 49 | /* Note: sdshdr5 is never used, we just access the flags byte directly. 50 | * However is here to document the layout of type 5 SDS strings. */ 51 | struct __attribute__ ((__packed__)) sdshdr5 { 52 | unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ 53 | char buf[]; 54 | }; 55 | struct __attribute__ ((__packed__)) sdshdr8 { 56 | uint8_t len; /* used */ 57 | uint8_t alloc; /* excluding the header and null terminator */ 58 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 59 | char buf[]; 60 | }; 61 | struct __attribute__ ((__packed__)) sdshdr16 { 62 | uint16_t len; /* used */ 63 | uint16_t alloc; /* excluding the header and null terminator */ 64 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 65 | char buf[]; 66 | }; 67 | struct __attribute__ ((__packed__)) sdshdr32 { 68 | uint32_t len; /* used */ 69 | uint32_t alloc; /* excluding the header and null terminator */ 70 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 71 | char buf[]; 72 | }; 73 | struct __attribute__ ((__packed__)) sdshdr64 { 74 | uint64_t len; /* used */ 75 | uint64_t alloc; /* excluding the header and null terminator */ 76 | unsigned char flags; /* 3 lsb of type, 5 unused bits */ 77 | char buf[]; 78 | }; 79 | 80 | #define SDS_TYPE_5 0 81 | #define SDS_TYPE_8 1 82 | #define SDS_TYPE_16 2 83 | #define SDS_TYPE_32 3 84 | #define SDS_TYPE_64 4 85 | #define SDS_TYPE_MASK 7 86 | #define SDS_TYPE_BITS 3 87 | #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); 88 | #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) 89 | #define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) 90 | 91 | static inline size_t sdslen(const sds s) { 92 | unsigned char flags = s[-1]; 93 | switch(flags&SDS_TYPE_MASK) { 94 | case SDS_TYPE_5: 95 | return SDS_TYPE_5_LEN(flags); 96 | case SDS_TYPE_8: 97 | return SDS_HDR(8,s)->len; 98 | case SDS_TYPE_16: 99 | return SDS_HDR(16,s)->len; 100 | case SDS_TYPE_32: 101 | return SDS_HDR(32,s)->len; 102 | case SDS_TYPE_64: 103 | return SDS_HDR(64,s)->len; 104 | } 105 | return 0; 106 | } 107 | 108 | static inline size_t sdsavail(const sds s) { 109 | unsigned char flags = s[-1]; 110 | switch(flags&SDS_TYPE_MASK) { 111 | case SDS_TYPE_5: { 112 | return 0; 113 | } 114 | case SDS_TYPE_8: { 115 | SDS_HDR_VAR(8,s); 116 | return sh->alloc - sh->len; 117 | } 118 | case SDS_TYPE_16: { 119 | SDS_HDR_VAR(16,s); 120 | return sh->alloc - sh->len; 121 | } 122 | case SDS_TYPE_32: { 123 | SDS_HDR_VAR(32,s); 124 | return sh->alloc - sh->len; 125 | } 126 | case SDS_TYPE_64: { 127 | SDS_HDR_VAR(64,s); 128 | return sh->alloc - sh->len; 129 | } 130 | } 131 | return 0; 132 | } 133 | 134 | static inline void sdssetlen(sds s, size_t newlen) { 135 | unsigned char flags = s[-1]; 136 | switch(flags&SDS_TYPE_MASK) { 137 | case SDS_TYPE_5: 138 | { 139 | unsigned char *fp = ((unsigned char*)s)-1; 140 | *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS)); 141 | } 142 | break; 143 | case SDS_TYPE_8: 144 | SDS_HDR(8,s)->len = (uint8_t)newlen; 145 | break; 146 | case SDS_TYPE_16: 147 | SDS_HDR(16,s)->len = (uint16_t)newlen; 148 | break; 149 | case SDS_TYPE_32: 150 | SDS_HDR(32,s)->len = (uint32_t)newlen; 151 | break; 152 | case SDS_TYPE_64: 153 | SDS_HDR(64,s)->len = (uint64_t)newlen; 154 | break; 155 | } 156 | } 157 | 158 | static inline void sdsinclen(sds s, size_t inc) { 159 | unsigned char flags = s[-1]; 160 | switch(flags&SDS_TYPE_MASK) { 161 | case SDS_TYPE_5: 162 | { 163 | unsigned char *fp = ((unsigned char*)s)-1; 164 | unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc; 165 | *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); 166 | } 167 | break; 168 | case SDS_TYPE_8: 169 | SDS_HDR(8,s)->len += (uint8_t)inc; 170 | break; 171 | case SDS_TYPE_16: 172 | SDS_HDR(16,s)->len += (uint16_t)inc; 173 | break; 174 | case SDS_TYPE_32: 175 | SDS_HDR(32,s)->len += (uint32_t)inc; 176 | break; 177 | case SDS_TYPE_64: 178 | SDS_HDR(64,s)->len += (uint64_t)inc; 179 | break; 180 | } 181 | } 182 | 183 | /* sdsalloc() = sdsavail() + sdslen() */ 184 | static inline size_t sdsalloc(const sds s) { 185 | unsigned char flags = s[-1]; 186 | switch(flags&SDS_TYPE_MASK) { 187 | case SDS_TYPE_5: 188 | return SDS_TYPE_5_LEN(flags); 189 | case SDS_TYPE_8: 190 | return SDS_HDR(8,s)->alloc; 191 | case SDS_TYPE_16: 192 | return SDS_HDR(16,s)->alloc; 193 | case SDS_TYPE_32: 194 | return SDS_HDR(32,s)->alloc; 195 | case SDS_TYPE_64: 196 | return SDS_HDR(64,s)->alloc; 197 | } 198 | return 0; 199 | } 200 | 201 | static inline void sdssetalloc(sds s, size_t newlen) { 202 | unsigned char flags = s[-1]; 203 | switch(flags&SDS_TYPE_MASK) { 204 | case SDS_TYPE_5: 205 | /* Nothing to do, this type has no total allocation info. */ 206 | break; 207 | case SDS_TYPE_8: 208 | SDS_HDR(8,s)->alloc = (uint8_t)newlen; 209 | break; 210 | case SDS_TYPE_16: 211 | SDS_HDR(16,s)->alloc = (uint16_t)newlen; 212 | break; 213 | case SDS_TYPE_32: 214 | SDS_HDR(32,s)->alloc = (uint32_t)newlen; 215 | break; 216 | case SDS_TYPE_64: 217 | SDS_HDR(64,s)->alloc = (uint64_t)newlen; 218 | break; 219 | } 220 | } 221 | 222 | sds sdsnewlen(const void *init, size_t initlen); 223 | sds sdsnew(const char *init); 224 | sds sdsempty(void); 225 | sds sdsdup(const sds s); 226 | void sdsfree(sds s); 227 | sds sdsgrowzero(sds s, size_t len); 228 | sds sdscatlen(sds s, const void *t, size_t len); 229 | sds sdscat(sds s, const char *t); 230 | sds sdscatsds(sds s, const sds t); 231 | sds sdscpylen(sds s, const char *t, size_t len); 232 | sds sdscpy(sds s, const char *t); 233 | 234 | sds sdscatvprintf(sds s, const char *fmt, va_list ap); 235 | #ifdef __GNUC__ 236 | sds sdscatprintf(sds s, const char *fmt, ...) 237 | __attribute__((format(printf, 2, 3))); 238 | #else 239 | sds sdscatprintf(sds s, const char *fmt, ...); 240 | #endif 241 | 242 | sds sdscatfmt(sds s, char const *fmt, ...); 243 | sds sdstrim(sds s, const char *cset); 244 | int sdsrange(sds s, ssize_t start, ssize_t end); 245 | void sdsupdatelen(sds s); 246 | void sdsclear(sds s); 247 | int sdscmp(const sds s1, const sds s2); 248 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); 249 | void sdsfreesplitres(sds *tokens, int count); 250 | void sdstolower(sds s); 251 | void sdstoupper(sds s); 252 | sds sdsfromlonglong(long long value); 253 | sds sdscatrepr(sds s, const char *p, size_t len); 254 | sds *sdssplitargs(const char *line, int *argc); 255 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); 256 | sds sdsjoin(char **argv, int argc, char *sep); 257 | sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); 258 | 259 | /* Low level functions exposed to the user API */ 260 | sds sdsMakeRoomFor(sds s, size_t addlen); 261 | void sdsIncrLen(sds s, int incr); 262 | sds sdsRemoveFreeSpace(sds s); 263 | size_t sdsAllocSize(sds s); 264 | void *sdsAllocPtr(sds s); 265 | 266 | /* Export the allocator used by SDS to the program using SDS. 267 | * Sometimes the program SDS is linked to, may use a different set of 268 | * allocators, but may want to allocate or free things that SDS will 269 | * respectively free or allocate. */ 270 | void *sds_malloc(size_t size); 271 | void *sds_realloc(void *ptr, size_t size); 272 | void sds_free(void *ptr); 273 | 274 | #ifdef REDIS_TEST 275 | int sdsTest(int argc, char *argv[]); 276 | #endif 277 | 278 | #endif 279 | -------------------------------------------------------------------------------- /src/hiredis/sdsalloc.h: -------------------------------------------------------------------------------- 1 | /* SDSLib 2.0 -- A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2015, Salvatore Sanfilippo 4 | * Copyright (c) 2015, Oran Agra 5 | * Copyright (c) 2015, Redis Labs, Inc 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | /* SDS allocator selection. 34 | * 35 | * This file is used in order to change the SDS allocator at compile time. 36 | * Just define the following defines to what you want to use. Also add 37 | * the include of your alternate allocator if needed (not needed in order 38 | * to use the default libc allocator). */ 39 | 40 | #include "alloc.h" 41 | 42 | #define s_malloc hi_malloc 43 | #define s_realloc hi_realloc 44 | #define s_free hi_free 45 | -------------------------------------------------------------------------------- /src/hiredis/sockcompat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Marcus Geelnard 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #define REDIS_SOCKCOMPAT_IMPLEMENTATION 32 | #include "sockcompat.h" 33 | 34 | #ifdef _WIN32 35 | static int _wsaErrorToErrno(int err) { 36 | switch (err) { 37 | case WSAEWOULDBLOCK: 38 | return EWOULDBLOCK; 39 | case WSAEINPROGRESS: 40 | return EINPROGRESS; 41 | case WSAEALREADY: 42 | return EALREADY; 43 | case WSAENOTSOCK: 44 | return ENOTSOCK; 45 | case WSAEDESTADDRREQ: 46 | return EDESTADDRREQ; 47 | case WSAEMSGSIZE: 48 | return EMSGSIZE; 49 | case WSAEPROTOTYPE: 50 | return EPROTOTYPE; 51 | case WSAENOPROTOOPT: 52 | return ENOPROTOOPT; 53 | case WSAEPROTONOSUPPORT: 54 | return EPROTONOSUPPORT; 55 | case WSAEOPNOTSUPP: 56 | return EOPNOTSUPP; 57 | case WSAEAFNOSUPPORT: 58 | return EAFNOSUPPORT; 59 | case WSAEADDRINUSE: 60 | return EADDRINUSE; 61 | case WSAEADDRNOTAVAIL: 62 | return EADDRNOTAVAIL; 63 | case WSAENETDOWN: 64 | return ENETDOWN; 65 | case WSAENETUNREACH: 66 | return ENETUNREACH; 67 | case WSAENETRESET: 68 | return ENETRESET; 69 | case WSAECONNABORTED: 70 | return ECONNABORTED; 71 | case WSAECONNRESET: 72 | return ECONNRESET; 73 | case WSAENOBUFS: 74 | return ENOBUFS; 75 | case WSAEISCONN: 76 | return EISCONN; 77 | case WSAENOTCONN: 78 | return ENOTCONN; 79 | case WSAETIMEDOUT: 80 | return ETIMEDOUT; 81 | case WSAECONNREFUSED: 82 | return ECONNREFUSED; 83 | case WSAELOOP: 84 | return ELOOP; 85 | case WSAENAMETOOLONG: 86 | return ENAMETOOLONG; 87 | case WSAEHOSTUNREACH: 88 | return EHOSTUNREACH; 89 | case WSAENOTEMPTY: 90 | return ENOTEMPTY; 91 | default: 92 | /* We just return a generic I/O error if we could not find a relevant error. */ 93 | return EIO; 94 | } 95 | } 96 | 97 | static void _updateErrno(int success) { 98 | errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError()); 99 | } 100 | 101 | static int _initWinsock() { 102 | static int s_initialized = 0; 103 | if (!s_initialized) { 104 | static WSADATA wsadata; 105 | int err = WSAStartup(MAKEWORD(2,2), &wsadata); 106 | if (err != 0) { 107 | errno = _wsaErrorToErrno(err); 108 | return 0; 109 | } 110 | s_initialized = 1; 111 | } 112 | return 1; 113 | } 114 | 115 | int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { 116 | /* Note: This function is likely to be called before other functions, so run init here. */ 117 | if (!_initWinsock()) { 118 | return EAI_FAIL; 119 | } 120 | 121 | switch (getaddrinfo(node, service, hints, res)) { 122 | case 0: return 0; 123 | case WSATRY_AGAIN: return EAI_AGAIN; 124 | case WSAEINVAL: return EAI_BADFLAGS; 125 | case WSAEAFNOSUPPORT: return EAI_FAMILY; 126 | case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY; 127 | case WSAHOST_NOT_FOUND: return EAI_NONAME; 128 | case WSATYPE_NOT_FOUND: return EAI_SERVICE; 129 | case WSAESOCKTNOSUPPORT: return EAI_SOCKTYPE; 130 | default: return EAI_FAIL; /* Including WSANO_RECOVERY */ 131 | } 132 | } 133 | 134 | const char *win32_gai_strerror(int errcode) { 135 | switch (errcode) { 136 | case 0: errcode = 0; break; 137 | case EAI_AGAIN: errcode = WSATRY_AGAIN; break; 138 | case EAI_BADFLAGS: errcode = WSAEINVAL; break; 139 | case EAI_FAMILY: errcode = WSAEAFNOSUPPORT; break; 140 | case EAI_MEMORY: errcode = WSA_NOT_ENOUGH_MEMORY; break; 141 | case EAI_NONAME: errcode = WSAHOST_NOT_FOUND; break; 142 | case EAI_SERVICE: errcode = WSATYPE_NOT_FOUND; break; 143 | case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT; break; 144 | default: errcode = WSANO_RECOVERY; break; /* Including EAI_FAIL */ 145 | } 146 | return gai_strerror(errcode); 147 | } 148 | 149 | void win32_freeaddrinfo(struct addrinfo *res) { 150 | freeaddrinfo(res); 151 | } 152 | 153 | SOCKET win32_socket(int domain, int type, int protocol) { 154 | SOCKET s; 155 | 156 | /* Note: This function is likely to be called before other functions, so run init here. */ 157 | if (!_initWinsock()) { 158 | return INVALID_SOCKET; 159 | } 160 | 161 | _updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET); 162 | return s; 163 | } 164 | 165 | int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) { 166 | int ret = ioctlsocket(fd, (long)request, argp); 167 | _updateErrno(ret != SOCKET_ERROR); 168 | return ret != SOCKET_ERROR ? ret : -1; 169 | } 170 | 171 | int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) { 172 | int ret = bind(sockfd, addr, addrlen); 173 | _updateErrno(ret != SOCKET_ERROR); 174 | return ret != SOCKET_ERROR ? ret : -1; 175 | } 176 | 177 | int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) { 178 | int ret = connect(sockfd, addr, addrlen); 179 | _updateErrno(ret != SOCKET_ERROR); 180 | 181 | /* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as 182 | * EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX 183 | * logic consistent. */ 184 | if (errno == EWOULDBLOCK) { 185 | errno = EINPROGRESS; 186 | } 187 | 188 | return ret != SOCKET_ERROR ? ret : -1; 189 | } 190 | 191 | int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) { 192 | int ret = 0; 193 | if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) { 194 | if (*optlen >= sizeof (struct timeval)) { 195 | struct timeval *tv = optval; 196 | DWORD timeout = 0; 197 | socklen_t dwlen = 0; 198 | ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen); 199 | tv->tv_sec = timeout / 1000; 200 | tv->tv_usec = (timeout * 1000) % 1000000; 201 | } else { 202 | ret = WSAEFAULT; 203 | } 204 | *optlen = sizeof (struct timeval); 205 | } else { 206 | ret = getsockopt(sockfd, level, optname, (char*)optval, optlen); 207 | } 208 | _updateErrno(ret != SOCKET_ERROR); 209 | return ret != SOCKET_ERROR ? ret : -1; 210 | } 211 | 212 | int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) { 213 | int ret = 0; 214 | if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) { 215 | const struct timeval *tv = optval; 216 | DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000; 217 | ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD)); 218 | } else { 219 | ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen); 220 | } 221 | _updateErrno(ret != SOCKET_ERROR); 222 | return ret != SOCKET_ERROR ? ret : -1; 223 | } 224 | 225 | int win32_close(SOCKET fd) { 226 | int ret = closesocket(fd); 227 | _updateErrno(ret != SOCKET_ERROR); 228 | return ret != SOCKET_ERROR ? ret : -1; 229 | } 230 | 231 | ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) { 232 | int ret = recv(sockfd, (char*)buf, (int)len, flags); 233 | _updateErrno(ret != SOCKET_ERROR); 234 | return ret != SOCKET_ERROR ? ret : -1; 235 | } 236 | 237 | ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) { 238 | int ret = send(sockfd, (const char*)buf, (int)len, flags); 239 | _updateErrno(ret != SOCKET_ERROR); 240 | return ret != SOCKET_ERROR ? ret : -1; 241 | } 242 | 243 | int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) { 244 | int ret = WSAPoll(fds, nfds, timeout); 245 | _updateErrno(ret != SOCKET_ERROR); 246 | return ret != SOCKET_ERROR ? ret : -1; 247 | } 248 | #endif /* _WIN32 */ 249 | -------------------------------------------------------------------------------- /src/hiredis/sockcompat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Marcus Geelnard 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __SOCKCOMPAT_H 32 | #define __SOCKCOMPAT_H 33 | 34 | #ifndef _WIN32 35 | /* For POSIX systems we use the standard BSD socket API. */ 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #else 46 | /* For Windows we use winsock. */ 47 | #undef _WIN32_WINNT 48 | #define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */ 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #ifdef _MSC_VER 55 | typedef long long ssize_t; 56 | #endif 57 | 58 | /* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */ 59 | int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); 60 | const char *win32_gai_strerror(int errcode); 61 | void win32_freeaddrinfo(struct addrinfo *res); 62 | SOCKET win32_socket(int domain, int type, int protocol); 63 | int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp); 64 | int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); 65 | int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); 66 | int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen); 67 | int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen); 68 | int win32_close(SOCKET fd); 69 | ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags); 70 | ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags); 71 | typedef ULONG nfds_t; 72 | int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout); 73 | 74 | #ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION 75 | #define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res) 76 | #undef gai_strerror 77 | #define gai_strerror(errcode) win32_gai_strerror(errcode) 78 | #define freeaddrinfo(res) win32_freeaddrinfo(res) 79 | #define socket(domain, type, protocol) win32_socket(domain, type, protocol) 80 | #define ioctl(fd, request, argp) win32_ioctl(fd, request, argp) 81 | #define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen) 82 | #define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen) 83 | #define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen) 84 | #define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen) 85 | #define close(fd) win32_close(fd) 86 | #define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags) 87 | #define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags) 88 | #define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout) 89 | #endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */ 90 | #endif /* _WIN32 */ 91 | 92 | #endif /* __SOCKCOMPAT_H */ 93 | -------------------------------------------------------------------------------- /src/hiredis/win32.h: -------------------------------------------------------------------------------- 1 | #ifndef _WIN32_HELPER_INCLUDE 2 | #define _WIN32_HELPER_INCLUDE 3 | #ifdef _MSC_VER 4 | 5 | #include /* for struct timeval */ 6 | 7 | #ifndef inline 8 | #define inline __inline 9 | #endif 10 | 11 | #ifndef strcasecmp 12 | #define strcasecmp stricmp 13 | #endif 14 | 15 | #ifndef strncasecmp 16 | #define strncasecmp strnicmp 17 | #endif 18 | 19 | #ifndef va_copy 20 | #define va_copy(d,s) ((d) = (s)) 21 | #endif 22 | 23 | #ifndef snprintf 24 | #define snprintf c99_snprintf 25 | 26 | __inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) 27 | { 28 | int count = -1; 29 | 30 | if (size != 0) 31 | count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); 32 | if (count == -1) 33 | count = _vscprintf(format, ap); 34 | 35 | return count; 36 | } 37 | 38 | __inline int c99_snprintf(char* str, size_t size, const char* format, ...) 39 | { 40 | int count; 41 | va_list ap; 42 | 43 | va_start(ap, format); 44 | count = c99_vsnprintf(str, size, format, ap); 45 | va_end(ap); 46 | 47 | return count; 48 | } 49 | #endif 50 | #endif /* _MSC_VER */ 51 | 52 | #ifdef _WIN32 53 | #define strerror_r(errno,buf,len) strerror_s(buf,len,errno) 54 | #endif /* _WIN32 */ 55 | 56 | #endif /* _WIN32_HELPER_INCLUDE */ 57 | -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // for NULL 4 | #include 5 | 6 | /* FIXME: 7 | Check these declarations against the C/Fortran source code. 8 | */ 9 | 10 | /* .Call calls */ 11 | extern SEXP _rcpp_module_boot_Redis(void); 12 | 13 | static const R_CallMethodDef CallEntries[] = { 14 | {"_rcpp_module_boot_Redis", (DL_FUNC) &_rcpp_module_boot_Redis, 0}, 15 | {NULL, NULL, 0} 16 | }; 17 | 18 | void R_init_RcppRedis(DllInfo *dll) { 19 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 20 | R_useDynamicSymbols(dll, FALSE); 21 | } 22 | -------------------------------------------------------------------------------- /tests/pubsub.R: -------------------------------------------------------------------------------- 1 | 2 | ## Version 1 using rredis 3 | if (Sys.getenv("RunRcppRedisTests") == "yes" && requireNamespace("rredis", quietly=TRUE)) { 4 | suppressMessages(require(rredis)) 5 | ## redusMonitorChannels blocks forever until a message is received. We 6 | ## use a background R process to send us some test messages. 7 | publisher_rredis <- function() { 8 | Rbin <- paste(R.home(component="bin"), "R", sep="/") 9 | cmd <- "suppressMessages(require(rredis));redisConnect();Sys.sleep(1);redisPublish('channel1', charToRaw('1'));redisPublish('channel2', charToRaw('2'))" 10 | args <- c("--slave", "-e", paste("\"", cmd, "\"", sep="")) 11 | system(paste(c(Rbin, args), collapse=" "), intern=FALSE, wait=FALSE) 12 | } 13 | checkEquals <- function(x, y) if (!isTRUE(all.equal(x, y, check.attributes=FALSE))) stop() 14 | 15 | redisConnect() 16 | redisPublish("channel1", charToRaw("A raw charachter data message example")) 17 | redisPublish("channel2", charToRaw("A raw charachter data message example")) 18 | 19 | ## Define a callback function to process messages from channel 1: 20 | channel1 <- function(x) { 21 | cat("Message received from channel 1: ",x,"\n") 22 | checkEquals("1", x) 23 | } 24 | ## Define a callback function to process messages from channel 2: 25 | channel2 <- function(x) { 26 | cat("Message received from channel 2: ",x,"\n") 27 | checkEquals("2", x) 28 | } 29 | redisSubscribe(c('channel1','channel2')) 30 | ## Monitor channels for a few seconds until the background R process sends us some messages... 31 | publisher_rredis() 32 | redisMonitorChannels() 33 | redisMonitorChannels() 34 | redisUnsubscribe(c('channel1','channel2')) 35 | 36 | cat("Done rredis\n") 37 | } 38 | 39 | ## Version 2 using RcppRedis 40 | if (Sys.getenv("RunRcppRedisTests") == "yes") { 41 | suppressMessages(library(RcppRedis)) 42 | publisher_rcppredis <- function() { 43 | Rscript <- paste(R.home(component="bin"), "Rscript", sep="/") 44 | cmd <- "suppressMessages(library(RcppRedis)); r <- new(Redis); Sys.sleep(1); r\\$publish('channel1', '1'); r\\$publish('channel2', '2')" 45 | args <- c("-e", paste("\"", cmd, "\"", sep="")) 46 | system(paste(c(Rscript, args), collapse=" "), intern=FALSE, wait=FALSE) 47 | } 48 | 49 | checkEquals <- function(x, y) if (!isTRUE(all.equal(x, y, check.attributes=FALSE))) stop() 50 | 51 | redis <- new(Redis) 52 | redis$publish("channel1", charToRaw("A raw charachter data message example")) 53 | redis$publish("channel2", charToRaw("A raw charachter data message example")) 54 | 55 | ## Define a callback function to process messages from channel 1: 56 | channel1 <- function(x) { 57 | cat("Message received from channel 1: ",x,"\n") 58 | checkEquals("1", x) 59 | } 60 | ## Define a callback function to process messages from channel 2: 61 | channel2 <- function(x) { 62 | cat("Message received from channel 2: ",x,"\n") 63 | checkEquals("2", x) 64 | } 65 | redis$subscribe(c('channel1','channel2')) 66 | ## Monitor channels for a few seconds until the background R process sends us some messages... 67 | publisher_rcppredis() 68 | redisMonitorChannels(redis) 69 | redisMonitorChannels(redis) 70 | redis$unsubscribe(c('channel1','channel2')) 71 | 72 | cat("Done rcppredis\n") 73 | } 74 | -------------------------------------------------------------------------------- /tests/tinytest.R: -------------------------------------------------------------------------------- 1 | 2 | if (requireNamespace("tinytest", quietly=TRUE) && 3 | utils::packageVersion("tinytest") >= "1.0.0") { 4 | 5 | ## Set a seed to make the test deterministic 6 | set.seed(42) 7 | 8 | ## R makes us to this 9 | Sys.setenv("R_TESTS"="") 10 | 11 | 12 | ## RcppRedis tests require a Redis server 13 | ## we cannot always assume one, so default to FALSE 14 | runTests <- FALSE 15 | 16 | ## if we know a redis server is set up, we can signal this -- cf .travis.yml 17 | if (Sys.getenv("RunRcppRedisTests") == "yes") runTests <- TRUE 18 | 19 | ## here is a shortcut: if the user is 'edd' we also run tests 20 | ## ought to be wrong on CRAN, win-builder, ... 21 | if (Sys.getenv("USER") == "edd") runTests <- TRUE 22 | 23 | if (runTests) { 24 | ## there are several more granular ways to test files in a tinytest directory, 25 | ## see its package vignette; tests can also run once the package is installed 26 | ## using the same command `test_package(pkgName)`, or by director or file 27 | tinytest::test_package("RcppRedis", ncpu=getOption("Ncpus", 1)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tools/winlibs.R: -------------------------------------------------------------------------------- 1 | if (!file.exists("../windows/hiredis-1.0.0/include/hiredis/hiredis.h")) { 2 | if (getRversion() < "3.3.0") setInternet2() 3 | download.file("https://github.com/rwinlib/hiredis/archive/v1.0.0.zip", "lib.zip", quiet = TRUE) 4 | dir.create("../windows", showWarnings = FALSE) 5 | unzip("lib.zip", exdir = "../windows") 6 | unlink("lib.zip") 7 | } 8 | -------------------------------------------------------------------------------- /vignettes/market-monitoring.Rnw: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage{pdfpages} 3 | %\VignetteIndexEntry{Redis for Market Monitoring} 4 | %\VignettePackage{RcppRedis} 5 | %\VignetteEncoding{UTF-8} 6 | 7 | \begin{document} 8 | \includepdf[pages=-, fitpaper=true]{redis-monitoring.pdf} 9 | \end{document} 10 | -------------------------------------------------------------------------------- /vignettes/redis-intro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddelbuettel/rcppredis/47f4458026b2a5537618164f1e7b395328a01524/vignettes/redis-intro.pdf -------------------------------------------------------------------------------- /vignettes/redis-introduction.Rnw: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage{pdfpages} 3 | %\VignetteIndexEntry{Introduction to Redis} 4 | %\VignettePackage{RcppRedis} 5 | %\VignetteEncoding{UTF-8} 6 | 7 | \begin{document} 8 | \includepdf[pages=-, fitpaper=true]{redis-intro.pdf} 9 | \end{document} 10 | -------------------------------------------------------------------------------- /vignettes/redis-monitoring.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddelbuettel/rcppredis/47f4458026b2a5537618164f1e7b395328a01524/vignettes/redis-monitoring.pdf -------------------------------------------------------------------------------- /vignettes/rmd/four-symbols.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddelbuettel/rcppredis/47f4458026b2a5537618164f1e7b395328a01524/vignettes/rmd/four-symbols.png -------------------------------------------------------------------------------- /vignettes/rmd/gspc_2022-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddelbuettel/rcppredis/47f4458026b2a5537618164f1e7b395328a01524/vignettes/rmd/gspc_2022-02.png -------------------------------------------------------------------------------- /vignettes/rmd/redis-intro.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Brief Introduction to Redis 3 | 4 | # Use letters for affiliations 5 | author: 6 | - name: Dirk Eddelbuettel 7 | affiliation: 1 8 | address: 9 | - code: 1 10 | address: Department of Statistics, University of Illinois, Urbana-Champaign, IL, USA 11 | 12 | # For footer text TODO(fold into template, allow free form two-authors) 13 | lead_author_surname: Eddelbuettel 14 | 15 | # Place DOI URL or CRAN Package URL here 16 | doi: "https://cran.r-project.org/package=RcppRedis" 17 | 18 | # Abstract 19 | abstract: | 20 | This note provides a brief introduction to Redis highlighting its 21 | usefulness in multi-lingual statistical computing. 22 | 23 | # Font size of the document, values of 9pt (default), 10pt, 11pt and 12pt 24 | fontsize: 9pt 25 | 26 | # Optional: Force one-column layout, default is two-column 27 | two_column: true 28 | 29 | # Optional: Enables lineno mode, but only if one_column mode is also true 30 | #lineno: true 31 | 32 | # Optional: Enable one-sided layout, default is two-sided 33 | #one_sided: true 34 | 35 | # Optional: Enable section numbering, default is unnumbered 36 | #numbersections: true 37 | 38 | # Optional: Specify the depth of section number, default is 5 39 | #secnumdepth: 5 40 | 41 | # Optional: Skip inserting final break between acknowledgements, default is false 42 | skip_final_break: true 43 | 44 | # Optional: Bibliography 45 | bibliography: redis 46 | 47 | # Optional: Enable a 'Draft' watermark on the document 48 | watermark: false 49 | 50 | # Customize footer, eg by referencing the vignette 51 | footer_contents: "Redis Intro" 52 | 53 | # Produce a pinp document 54 | output: 55 | pinp::pinp: 56 | collapse: true 57 | keep_tex: false 58 | 59 | header-includes: > 60 | \newcommand{\proglang}[1]{\textsf{#1}} 61 | \newcommand{\pkg}[1]{\textbf{#1}} 62 | \newcommand{\R}{\proglang{R}} 63 | \newcommand{\Py}{\proglang{Python}} 64 | \newcommand{\Redis}{\proglang{Redis}} 65 | 66 | # Required: Vignette metadata for inclusion in a package. 67 | vignette: > 68 | %\VignetteIndexEntry{Brief Redis Introduction} 69 | %\VignettePackage{RcppRedis} 70 | %\VignetteEngine{knitr::rmarkdown} 71 | %\VignetteEncoding{UTF-8} 72 | %\usepackage[utf8]{inputenc} 73 | --- 74 | 75 | ```{r initialsetup, include=FALSE} 76 | knitr::opts_chunk$set(cache=FALSE) 77 | ``` 78 | 79 | 80 | # Overview and Introduction 81 | 82 | [Redis][redis] \citep{Redis} is a very popular, very powerful, and very widely-used 'in-memory database-structure 83 | store'. It runs as a background process (a "daemon" in Unix lingo) and can be accessed locally or 84 | across a network making it very popular choice for 'data caches'. There is more to say about 85 | [Redis][redis] than we possibly could in a _short_ vignette introducing it, and other places already 86 | do so. The [Little Redis Book](https://www.openmymind.net/2012/1/23/The-Little-Redis-Book/) \citep{Seguin:2021:Redis}, while a 87 | decade old (!!) is a fabulous _short_ start. The [official site][redis] is very good as well (but 88 | by now a little overwhelming as so many features have been added). 89 | 90 | This vignette aims highlight two aspects: how easy it is to use [Redis][redis] on simple examples, 91 | and also to stress how [Redis][redis] enables easy _multi-lingual_ computing as it can act as a 92 | 'middle-man' between any set of languages capable of speaking the [Redis][redis] protocol -- which 93 | may cover most if not all common languages one may want to use! 94 | 95 | # Data Structure Example One: Key-Value Setter and Getter 96 | 97 | We will show several simple examples for the 98 | 99 | - `redis-cli` command used directly or via shell scripts 100 | - \Py via the standard \Py package for [Redis][redis] 101 | - \proglang{C} / \proglang{C++} via the [hiredis][hiredis] library 102 | - \R via \pkg{RcppRedis} \citep{CRAN:RcppRedis} utilising the [hiredis][hiredis] library 103 | 104 | to demonstrate how different languages all can write to and read from [Redis][redis]. Our first example will 105 | use the simplest possibly data structure, a simple `SET` and `GET` of a key-value pair. 106 | 107 | ## Command-Line 108 | 109 | `redis-cli` is command-line client. Use is straightforward as shown an simply key-value getter and 110 | setter. We show use in 'shell script' form here, but the same commands also work interactively. 111 | 112 | ```{bash cliGetSet} 113 | ## note that document processing will show all 114 | ## three results at once as opposed to one at time 115 | redis-cli SET ice-cream chocolate 116 | redis-cli GET ice-cream 117 | redis-cli GET ice-cream 118 | ``` 119 | 120 | Here, as in general, we will omit hostname and authentication arguments: on the same machine, 121 | `redis-cli` and the background `redis` process should work 'as is'. For access across a (local or 122 | remote) network, the configuration will have to be altered to permit access at given network 123 | interfaces and IP address ranges. 124 | 125 | We show the [redis][redis] commands used in uppercase notation, this is in line with the 126 | documentation. Note, however, that [Redis][redis] itself is case-insensitive here so `set` is equivalent to 127 | `SET`. 128 | 129 | ## Python 130 | 131 | [Redis][redis] does have bindings for most, if not all, languages to 132 | computing with data. Here is a simple \proglang{Python} example. 133 | 134 | ```{python pyGetSet} 135 | import redis 136 | 137 | redishost = "localhost" 138 | redisserver = redis.StrictRedis(redishost) 139 | 140 | key = "ice-cream" 141 | val = "strawberry" 142 | res = redisserver.set(key, val) 143 | print("Set", val, "under", key, "with result", res) 144 | 145 | key = "ice-cream" 146 | val = redisserver.get(key) 147 | print("Got", val, "from", key) 148 | ``` 149 | 150 | For \Py, the redis commands are generally mapped to (lower-case named) member functions of the 151 | instantiated redis connection object, here `redisserver`. 152 | 153 | ## C / C++ 154 | 155 | Compiled languages work similarly. For \proglang{C} and \proglang{C++}, the [hiredis][hiredis] 'minimalistic' library from 156 | the [Redis][redis] project can be used---as it is by \pkg{RcppRedis}. Here we only show the code without executing 157 | it. This example is included in the package are as the preceding ones. \proglang{C} and \proglang{C++} work similarly to 158 | the interactive or \Py commands. A simplified (and incomplete, see the `examples/` 159 | directory of the package for more) example of writing to [Redis][redis] would be 160 | 161 | ```c++ 162 | redisContext *prc; // pointer to redis context 163 | 164 | std::string host = "127.0.0.1"; 165 | int port = 6379; 166 | 167 | prc = redisConnect(host.c_str(), port); 168 | // should check error here 169 | redisReply *reply = (redisReply*) 170 | redisCommand(prc, "SET ice-cream %s", value); 171 | // should check reply here 172 | ``` 173 | 174 | Reading is done by submitting for example a `GET` command for a key after which the `redisReply` 175 | contains the reply string. 176 | 177 | ## R 178 | 179 | The \pkg{RcppRedis} packages uses \pkg{Rcpp} Modules along with \pkg{Rcpp} 180 | \citep{CRAN:Rcpp,TAS:Rcpp} to connect the [hiredis][hiredis] library to \R. A simple \R example 181 | follows. 182 | 183 | ```{r RexSetGet} 184 | library(RcppRedis) 185 | redis <- new(Redis, "localhost") 186 | redis$set("ice-cream", "hazelnut") 187 | redis$get("ice-cream") 188 | ``` 189 | 190 | # Data Structure Example Two: Hashes 191 | 192 | [Redis][redis] has support for a number of standard data structures including hashes. 193 | The official documentation list [sixteen commands](https://redis.io/commands#hash) in the 194 | corresponding group covering writing (`hset`), reading (`hget`), checking for key (`hexists`), 195 | deleting a key (`hdel`) and more. 196 | 197 | ```{bash cliHash} 198 | redis-cli HSET myhash abc 42 199 | redis-cli HSET myhash def "some text" 200 | ``` 201 | 202 | We can illustrate reading and checking from Python: 203 | 204 | ```{python pyHash} 205 | print(redisserver.hget("myhash", "abc")) 206 | print(redisserver.hget("myhash", "def")) 207 | print(redisserver.hexists("myhash", "xyz")) 208 | ``` 209 | 210 | For historical reasons, \pkg{RcppRedis} converts to/from \proglang{R} internal serialization on 211 | hash operations so it cannot _directly_ interoperate with these examples as they not deploy 212 | \R-internal representation. The package has however a 'catch-all' command `exec` (which 213 | excutes a given \Redis command string) which can be used here: 214 | 215 | ```{r rHash} 216 | redis$exec("HGET myhash abc") 217 | redis$exec("HGET myhash def") 218 | redis$exec("HEXISTS myhash xyz") 219 | ``` 220 | 221 | # Data Structure Example Three: Sets 222 | 223 | Sets are another basic data structure that is well-understood. In sets, elements can be added (once), 224 | removed (if present), and sets can be joined, intersected and differenced. 225 | 226 | ```{bash cliSet} 227 | redis-cli SADD myset puppy 228 | redis-cli SADD myset kitten 229 | redis-cli SADD otherset birdie 230 | redis-cli SADD otherset kitten 231 | redis-cli SINTER myset otherset 232 | ``` 233 | 234 | We can do the same in \Py. Here we showi only the final intersect command---the set-addition 235 | commands are equivalent across implementations as are most other [Redis] command. 236 | 237 | ```{python, pySetPre, include=FALSE} 238 | import redis 239 | redisserver = redis.StrictRedis("localhost") 240 | ``` 241 | ```{python, pySet} 242 | print(redisserver.sinter("myset", "otherset")) 243 | ``` 244 | 245 | And similarly in \R where `exec` returns a list: 246 | 247 | ```{r rSet} 248 | redis$exec("SINTER myset otherset") 249 | ``` 250 | 251 | # Data Structure Example Four: Lists 252 | 253 | So far we have looked at setters and getters for values, hashes, and sets. All of these 254 | covered only one value per key. But \Redis has support for many more types. 255 | 256 | ```{bash cliList} 257 | redis-cli LPUSH mylist chocolate 258 | redis-cli LPUSH mylist strawberry vanilla 259 | redis-cli LLEN mylist 260 | ``` 261 | 262 | We can access the list in \Py. Here we show access by index. Note that the index is zero-based, so 263 | 'one' accesses the middle element in a list of length three. 264 | 265 | 266 | ```{python, pyList} 267 | print(redisserver.lindex("mylist", 1)) 268 | ``` 269 | 270 | In \R, using the 'list range' command for elements 0 to 1: 271 | 272 | ```{r rList} 273 | redis$exec("LRANGE mylist 0 1") 274 | ``` 275 | 276 | The \pkg{RcppRedis} list commands (under the 'standard' names) work on serialized R objects so we 277 | once again utilize the `exec` command to execute this using the 'standard' name. As access to 278 | unserialized data is useful, the package also two alternates for numeric and string return data: 279 | 280 | ```{r rList2} 281 | redis$listRangeAsStrings("mylist", 0, 1) 282 | ``` 283 | 284 | # Data Structure Example Five: Sorted Sets 285 | 286 | [Redis][redis] offers another data structure that can be of interest to us for use in time series. 287 | Recall how packages \pkg{zoo} \citep{CRAN:zoo} and \pkg{xts} \citep{CRAN:xts} are, essentially, 288 | indexed containers around (numeric) matrices with a sorting index. This is commonly the `Date` type 289 | in \R for daily data, or a `POSIXct` datimetime type for intra-daily data at approximately a 290 | microsecond resolution. One can then index by day or datetime, subset, merge, ... We can store such 291 | data in \Redis using sorted sets using the index as the first column. A quick \R example 292 | illustrates. 293 | 294 | ```{r sortedSet1} 295 | m1 <- matrix(c(100, 1, 2, 3), 1) 296 | redis$zadd("myz", m1) # add m1 indexed at 100 297 | m2 <- matrix(c(105, 2, 2, 4), 1) 298 | redis$zadd("myz", m2) # add m1 indexed at 105 299 | ``` 300 | 301 | In this first example we insert two matrices (with three values each) index at 100 and 105, 302 | respectively, to the sorted set under key `myz`. We will then ask for a range of data over the range 303 | from 90 to 120 which will include both sets of observations. 304 | 305 | ```{r sortedSet2} 306 | res <- redis$zrangebyscore("myz", 90, 120) 307 | res 308 | ``` 309 | 310 | 311 | # Communication Example: Publish/Subscribe 312 | 313 | We have seen above that writen a _value_ to a particular _key_ into a list, set, or sorted set is 314 | straightforward. So is publishing into a _channel_. \Redis keeps track of the current subscribers 315 | to a channel and dispatches the published content. 316 | 317 | Subscribers can join, and leave, anytime. Data is accessible via the publish/subscribe (or 318 | "pub/sub") mechanism while being subscribe. There is no mechanism _within pub/sub_ to obtain 'older' 319 | values, or to re-request values. Such services can however be provided by \Redis is its capacity of 320 | a data store. 321 | 322 | As subscription is typically blocking, we cannnot show a simple example in the vignette. But an 323 | illustration (without running code) follows. 324 | 325 | ```r 326 | ch1 <- function(x) { cat("Got", x, "in ch1\n") } 327 | redis$subscribe("ch1") 328 | ``` 329 | 330 | Here we declare a callback function which by our convention uses the same name as the channel. So in 331 | the next when the subscription is activated, the callback function is registered with the current 332 | \Redis object. Once another process or entity publishes to the channel, the callback function will 333 | be invoked along with the value published on the channel. 334 | 335 | # Application Example: Hash R Objects 336 | 337 | The ability to serialize R object makes it particularly to store R objects directly. This can be 338 | useful for data sets, and well as generated data 339 | 340 | ```{r treesEx} 341 | fit <- lm(Volume ~ . - 1, data=trees) 342 | redis$hset("myhash", "data", trees) 343 | redis$hset("myhash", "fit", fit) 344 | fit2 <- redis$hget("myhash", "fit") 345 | all.equal(fit, fit2) 346 | ``` 347 | The retrieved model fit is equal to the one we stored in [Redis]. We can also re-fit on the 348 | retrieved data and obtain the same coefficient. (The fit object stores information about the data 349 | set which differs here for technical reason internal to \R; the values are the same.) 350 | 351 | ```{r treesEx2} 352 | data2 <- redis$hget("myhash", "data") 353 | fit2 <- redis$hget("myhash", "fit") 354 | fit3 <- lm(Volume ~ . - 1, data=data2) 355 | all.equal(coef(fit2), coef(fit3)) 356 | ``` 357 | 358 | # Summary 359 | 360 | This vignettet introduces the [Redis][redis] data structure engine, and demonstrates how reading 361 | and writing different data types from different programming languages including \R, \Py and shell 362 | is concise and effective. A final example of storing an \R dataset and model fit further 363 | illustrates the versatility of \Redis. 364 | 365 | 366 | ```{bash cleanup, include=FALSE} 367 | redis-cli del ice-cream 368 | redis-cli hdel myhash abc 369 | redis-cli hdel myhash def 370 | redis-cli del mylist 371 | redis-cli del myset 372 | redis-cli del otherset 373 | redis-cli del myz 374 | ``` 375 | 376 | [redis]: https://redis.io 377 | [hiredis]: https://github.com/redis/hiredis 378 | -------------------------------------------------------------------------------- /vignettes/rmd/redis.bib: -------------------------------------------------------------------------------- 1 | 2 | @Manual{CRAN:dang, 3 | title = {dang: 'Dang' Associated New Goodies}, 4 | author = {Dirk Eddelbuettel}, 5 | year = {2021}, 6 | note = {R package version 0.0.15}, 7 | url = {https://CRAN.R-project.org/package=dang}, 8 | } 9 | 10 | @Manual{CRAN:quantmod, 11 | title = {quantmod: Quantitative Financial Modelling 12 | Framework}, 13 | author = {Jeffrey A. Ryan and Joshua M. Ulrich}, 14 | year = {2020}, 15 | note = {R package version 0.4.18}, 16 | url = {https://CRAN.R-project.org/package=quantmod}, 17 | } 18 | 19 | @Manual{CRAN:Rcpp, 20 | title = {{Rcpp}: Seamless {R} and {C++} Integration}, 21 | author = {Dirk Eddelbuettel and Romain Fran\c{c}ois and JJ 22 | Allaire and Kevin Ushey and Qiang Kou and 23 | Nathan Russel and John Chambers and Douglas Bates}, 24 | year = 2022, 25 | note = {R package version 1.0.8}, 26 | url = {https://CRAN.R-Project.org/package=Rcpp} 27 | } 28 | 29 | @Manual{CRAN:RcppRedis, 30 | title = {RcppRedis: 'Rcpp' Bindings for 'Redis' using the 'hiredis' Library}, 31 | author = {Dirk Eddelbuettel and Bryan W. Lewis}, 32 | year = 2022, 33 | note = {R package version 0.2.0}, 34 | url = {https://CRAN.R-Project.org/package=RcppRedis} 35 | } 36 | 37 | @Manual{CRAN:xts, 38 | title = {xts: eXtensible Time Series}, 39 | author = {Jeffrey A. Ryan and Joshua M. Ulrich}, 40 | year = {2020}, 41 | note = {R package version 0.12.1}, 42 | url = {https://CRAN.R-project.org/package=xts}, 43 | } 44 | 45 | @Manual{CRAN:zoo, 46 | title = {zoo: S3 Infrastructure for Regular and Irregular 47 | Time Series (Z's Ordered Observations)}, 48 | author = {Achim Zeileis and Gabor Grothendieck and Jeffrey 49 | A. Ryan}, 50 | year = {2021}, 51 | note = {R package version 1.8-9}, 52 | url = {https://CRAN.R-project.org/package=zoo}, 53 | } 54 | 55 | @Misc{Eddelbuettel:2022:Redis, 56 | title = {{A Brief Introduction to Redis}}, 57 | author = {Dirk Eddelbuettel}, 58 | year = {2022}, 59 | month = {March}, 60 | doi = {10.48550/arXiv.2203.06559}, 61 | } 62 | 63 | @Article{JSS:zoo, 64 | title = {zoo: S3 Infrastructure for Regular and Irregular 65 | Time Series}, 66 | author = {Achim Zeileis and Gabor Grothendieck}, 67 | journal = {Journal of Statistical Software}, 68 | year = {2005}, 69 | volume = {14}, 70 | number = {6}, 71 | pages = {1--27}, 72 | url = {https://CRAN.R-Project.org/package=zoo}, 73 | doi = {10.18637/jss.v014.i06}, 74 | } 75 | 76 | @Article{JSS:zoo, 77 | title = {zoo: S3 Infrastructure for Regular and Irregular 78 | Time Series}, 79 | author = {Achim Zeileis and Gabor Grothendieck}, 80 | journal = {Journal of Statistical Software}, 81 | year = {2005}, 82 | volume = {14}, 83 | number = {6}, 84 | pages = {1--27}, 85 | url = {https://CRAN.R-Project.org/package=zoo}, 86 | doi = {10.18637/jss.v014.i06}, 87 | } 88 | 89 | @Misc{Redis, 90 | author = {Salvatore Sanfilippo}, 91 | title = {Redis In-memory Data Structure Server}, 92 | howpublished = {\url{https://redis.io}}, 93 | month = {May}, 94 | year = {2009} 95 | } 96 | 97 | @Booklet{Seguin:2021:Redis, 98 | title = {{The Little Redis Book}}, 99 | author = {Karl Seguin}, 100 | howpublished = {\url{https://www.openmymind.net/redis.pdf}}, 101 | month = {January}, 102 | year = 2012, 103 | annote = {Sources at 104 | \url{https://github.com/karlseguin/the-little-redis-book}} 105 | } 106 | 107 | @Article{TAS:Rcpp, 108 | author = {Dirk Eddelbuettel and James Joseph Balamuta}, 109 | title = {Extending R with C++: A Brief Introduction to Rcpp}, 110 | journal = {The American Statistician}, 111 | volume = 72, 112 | number = 1, 113 | year = 2018, 114 | month = {August}, 115 | doi = {10.1080/00031305.2017.1375990} 116 | } 117 | 118 | @Misc{Ulrich:2021:gist, 119 | author = {Joshua M. Ulrich}, 120 | title = {Market-Monitoring with R}, 121 | howpublished = {\url{https://gist.github.com/joshuaulrich/ee11ef67b1461df399b84efd3c8f9f67#file-intraday-sp500-r}}, 122 | year = {2021} 123 | } 124 | 125 | @Misc{ZeroMQ, 126 | author = {Pieter Hintjens and Martin Sustrik}, 127 | title = {ZeroMQ: An open-source universal messaging library}, 128 | howpublished = {\url{https://zeromq.org}}, 129 | month = {March}, 130 | year = {2010} 131 | } 132 | --------------------------------------------------------------------------------