Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaed0894cc | ||
|
|
4f4e0f1cdd | ||
|
|
47ae4b6a17 | ||
|
|
db7633e888 | ||
|
|
4212dd5402 | ||
|
|
a3267b8084 | ||
|
|
29a8c8d20b | ||
|
|
7caf0591a7 | ||
|
|
eaaf62f6a4 | ||
|
|
eff09a7bd8 | ||
|
|
efc8a6beee | ||
|
|
b059a563d9 | ||
|
|
a5eddcdff1 | ||
|
|
f8fa3e2833 | ||
|
|
32241a0c4e | ||
|
|
dda93651f7 | ||
|
|
3a1ab86a35 | ||
|
|
c6375e6a60 | ||
|
|
655f79a933 | ||
|
|
ac10524377 | ||
|
|
30a7f9b387 | ||
|
|
39926d0411 | ||
|
|
7905331ede | ||
|
|
a29b8f58cc | ||
|
|
fe2173ae53 | ||
|
|
de4aebe9fd | ||
|
|
c9c7ecad96 | ||
|
|
dc44326f7a | ||
|
|
d8f25ce3ac | ||
|
|
b25b61eb97 | ||
|
|
797c9ffb6b | ||
|
|
75d675d954 | ||
|
|
243b914bd6 | ||
|
|
e4a3d7fffc | ||
|
|
de8c659026 | ||
|
|
f9231c2c9a | ||
|
|
c7543ab6f1 | ||
|
|
e8de5c7a3f | ||
|
|
61e4ff2f9c | ||
|
|
a0ed0067cc | ||
|
|
bd29be7db5 | ||
|
|
15f763eef6 | ||
|
|
55ebd6a213 | ||
|
|
1f19628bee | ||
|
|
f3f35ced13 | ||
|
|
0319bd1e9f | ||
|
|
19b49c31f3 | ||
|
|
3c5b5e15d4 | ||
|
|
c80ba69173 | ||
|
|
f7b8ff227a | ||
|
|
8a58008b1b | ||
|
|
903b7484af | ||
|
|
b03dce9537 | ||
|
|
1bc183731d | ||
|
|
2691b01bee | ||
|
|
3294ef172a | ||
|
|
d1481e023a |
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Help us improve Redis by reporting a bug
|
||||
title: '[BUG]'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
A short description of the bug.
|
||||
|
||||
**To reproduce**
|
||||
|
||||
Steps to reproduce the behavior and/or a minimal code sample.
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
A description of what you expected to happen.
|
||||
|
||||
**Additional information**
|
||||
|
||||
Any additional information that is relevant to the problem.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Crash report
|
||||
about: Submit a crash report
|
||||
title: '[CRASH]'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Crash report**
|
||||
|
||||
Paste the complete crash log between the quotes below. Please include a few lines from the log preceding the crash report to provide some context.
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
**Aditional information**
|
||||
|
||||
1. OS distribution and version
|
||||
2. Steps to reproduce (if any)
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a feature for Redis
|
||||
title: '[NEW]'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**The problem/use-case that the feature addresses**
|
||||
|
||||
A description of the problem that the feature will solve, or the use-case with which the feature will be used.
|
||||
|
||||
**Description of the feature**
|
||||
|
||||
A description of what you want to happen.
|
||||
|
||||
**Alternatives you've considered**
|
||||
|
||||
Any alternative solutions or features you've considered, including references to existing open and closed feature requests in this repository.
|
||||
|
||||
**Additional information**
|
||||
|
||||
Any additional information that is relevant to the feature request.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
name: Other
|
||||
about: Can't find the right issue type? Use this one!
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask the Redis developers
|
||||
title: '[QUESTION]'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please keep in mind that this issue tracker should be used for reporting bugs or proposing improvements to the Redis server.
|
||||
|
||||
Generally, questions about using Redis should be directed to the [community](https://redis.io/community):
|
||||
|
||||
* [the mailing list](https://groups.google.com/forum/#!forum/redis-db)
|
||||
* [the `redis` tag at StackOverflow](http://stackoverflow.com/questions/tagged/redis)
|
||||
* [/r/redis subreddit](http://www.reddit.com/r/redis)
|
||||
* [the irc channel #redis](http://webchat.freenode.net/?channels=redis) on freenode
|
||||
|
||||
It is also possible that your question was already asked here, so please do a quick issues search before submitting. Lastly, if your question is about one of Redis' [clients](https://redis.io/clients), you may to contact your client's developers for help.
|
||||
|
||||
That said, please feel free to replace all this with your question :)
|
||||
@@ -1,60 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
test-ubuntu-latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
# Fail build if there are warnings
|
||||
# build with TLS just for compilation coverage
|
||||
run: make REDIS_CFLAGS='-Werror' BUILD_TLS=yes
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
|
||||
build-ubuntu-old:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
|
||||
build-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
make REDIS_CFLAGS='-Werror' 32bit
|
||||
|
||||
build-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror' MALLOC=libc
|
||||
|
||||
build-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
container: centos:7
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install gcc make
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
@@ -1,223 +0,0 @@
|
||||
name: Daily
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
# any PR to a release branch.
|
||||
- '[0-9].[0-9]'
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
|
||||
test-ubuntu-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-ubuntu-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make MALLOC=libc
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-ubuntu-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
make 32bit
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: |
|
||||
make -C tests/modules 32bit # the script below doesn't have an argument, we must build manually ahead of time
|
||||
./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-ubuntu-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
make BUILD_TLS=yes
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6 tcl-tls
|
||||
./utils/gen-test-certs.sh
|
||||
./runtest --accurate --verbose --tls --dump-logs
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --tls
|
||||
./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: |
|
||||
./runtest-sentinel --tls
|
||||
./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: |
|
||||
./runtest-cluster --tls
|
||||
./runtest-cluster
|
||||
|
||||
test-ubuntu-io-threads:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
make
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6 tcl-tls
|
||||
./runtest --config io-threads 4 --config io-threads-do-reads yes --accurate --verbose --tags network --dump-logs
|
||||
- name: cluster tests
|
||||
run: |
|
||||
./runtest-cluster --config io-threads 4 --config io-threads-do-reads yes
|
||||
|
||||
test-valgrind:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make valgrind
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 valgrind -y
|
||||
./runtest --valgrind --verbose --clients 1 --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1
|
||||
|
||||
test-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install gcc make
|
||||
make
|
||||
- name: test
|
||||
run: |
|
||||
yum -y install which tcl
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-centos7-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install centos-release-scl epel-release
|
||||
yum -y install devtoolset-7 openssl-devel openssl
|
||||
scl enable devtoolset-7 "make BUILD_TLS=yes"
|
||||
- name: test
|
||||
run: |
|
||||
yum -y install tcl tcltls
|
||||
./utils/gen-test-certs.sh
|
||||
./runtest --accurate --verbose --tls --dump-logs
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --tls
|
||||
./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: |
|
||||
./runtest-sentinel --tls
|
||||
./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: |
|
||||
./runtest-cluster --tls
|
||||
./runtest-cluster
|
||||
|
||||
test-macos-latest:
|
||||
runs-on: macos-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make
|
||||
- name: test
|
||||
run: |
|
||||
./runtest --accurate --verbose --no-latency --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-freebsd:
|
||||
runs-on: macos-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: test
|
||||
uses: vmactions/freebsd-vm@v0.1.2
|
||||
with:
|
||||
usesh: true
|
||||
sync: rsync
|
||||
prepare: pkg install -y bash gmake lang/tcl86
|
||||
run: >
|
||||
gmake &&
|
||||
./runtest --accurate --verbose --no-latency --dump-logs &&
|
||||
MAKE=gmake ./runtest-moduleapi --verbose &&
|
||||
./runtest-sentinel &&
|
||||
./runtest-cluster
|
||||
+7
-32
@@ -1,40 +1,15 @@
|
||||
.*.swp
|
||||
00-RELEASENOTES
|
||||
*.o
|
||||
*.xo
|
||||
*.so
|
||||
*.d
|
||||
*.rdb
|
||||
*.log
|
||||
dump.rdb
|
||||
redis-benchmark
|
||||
redis-check-aof
|
||||
redis-check-rdb
|
||||
redis-check-dump
|
||||
redis-cli
|
||||
redis-sentinel
|
||||
redis-server
|
||||
redis-benchmark
|
||||
redis-check-dump
|
||||
redis-check-aof
|
||||
doc-tools
|
||||
mkrelease.sh
|
||||
release
|
||||
myredis.conf
|
||||
misc/*
|
||||
src/release.h
|
||||
appendonly.aof
|
||||
SHORT_TERM_TODO
|
||||
release.h
|
||||
src/transfer.sh
|
||||
src/configs
|
||||
redis.ds
|
||||
src/redis.conf
|
||||
src/nodes.conf
|
||||
deps/lua/src/lua
|
||||
deps/lua/src/luac
|
||||
deps/lua/src/liblua.a
|
||||
tests/tls/*
|
||||
.make-*
|
||||
.prerequisites
|
||||
*.dSYM
|
||||
Makefile.dep
|
||||
.vscode/*
|
||||
.idea/*
|
||||
.ccls
|
||||
.ccls-cache/*
|
||||
compile_commands.json
|
||||
redis.code-workspace
|
||||
|
||||
-431
@@ -1,431 +0,0 @@
|
||||
Redis 6.2 release notes
|
||||
=======================
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Upgrade urgency levels:
|
||||
|
||||
LOW: No need to upgrade unless there are new features you want to use.
|
||||
MODERATE: Program an upgrade of the server, but it's not urgent.
|
||||
HIGH: There is a critical bug that may affect a subset of users. Upgrade!
|
||||
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
|
||||
SECURITY: There are security fixes in the release.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.0 GA Released Tue Feb 22 14:00:00 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY if you use 32bit build of redis (see bellow), MODERATE
|
||||
if you used earlier versions of Redis 6.2, LOW otherwise.
|
||||
|
||||
Integer overflow on 32-bit systems (CVE-2021-21309):
|
||||
Redis 4.0 or newer uses a configurable limit for the maximum supported bulk
|
||||
input size. By default, it is 512MB which is a safe value for all platforms.
|
||||
If the limit is significantly increased, receiving a large request from a client
|
||||
may trigger several integer overflow scenarios, which would result with buffer
|
||||
overflow and heap corruption.
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 6.2 RC3,
|
||||
each one includes the PR number that added it, so you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
Bug fixes:
|
||||
* Avoid 32-bit overflows when proto-max-bulk-len is set high (#8522)
|
||||
* Fix broken protocol in client tracking tracking-redir-broken message (#8456)
|
||||
* Avoid unsafe field name characters in INFO commandstats, errorstats, modules (#8492)
|
||||
* XINFO able to access expired keys during CLIENT PAUSE WRITE (#8436)
|
||||
* Fix allowed length for REPLCONF ip-address, needed due to Sentinel's support for hostnames (#8517)
|
||||
* Fix broken protocol in redis-benchmark when used with -a or --dbnum (#8486)
|
||||
* XADD counts deleted records too when considering switching to a new listpack (#8390)
|
||||
|
||||
Bug fixes that are only applicable to previous releases of Redis 6.2:
|
||||
* Fixes in GEOSEARCH bybox (accuracy and mismatch between width and height) (#8445)
|
||||
* Fix risk of OOM panic in HRANDFIELD, ZRANDMEMBER commands with huge negative count (#8429)
|
||||
* Fix duplicate replicas issue in Sentinel, needed due to hostname support (#8481)
|
||||
* Fix Sentinel configuration rewrite, an improvement of #8271 (#8480)
|
||||
|
||||
Command behavior changes:
|
||||
* SRANDMEMBER uses RESP3 array type instead of set type (#8504)
|
||||
* EXPIRE, EXPIREAT, SETEX, GETEX: Return error when provided expire time overflows (#8287)
|
||||
|
||||
Other behavior changes:
|
||||
* Remove ACL subcommand validation if fully added command exists. (#8483)
|
||||
|
||||
Improvements:
|
||||
* Optimize sorting in GEORADIUS / GEOSEARCH with COUNT (#8326)
|
||||
* Optimize HRANDFIELD and ZRANDMEMBER case 4 when ziplist encoded (#8444)
|
||||
* Optimize in-place replacement of elements in HSET, HINCRBY, LSET (#8493)
|
||||
* Remove redundant list to store pubsub patterns (#8472)
|
||||
* Add --insecure option to command line tools (#8416)
|
||||
|
||||
Info fields and introspection changes:
|
||||
* Add INFO fields to track progress of BGSAVE, AOFRW, replication (#8414)
|
||||
|
||||
Modules:
|
||||
* RM_ZsetRem: Delete key if empty, the bug could leave empty zset keys (#8453)
|
||||
* RM_HashSet: Add COUNT_ALL flag and set errno (#8446)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2 RC3 Released Tue Feb 1 14:00:00 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the third Release Candidate of Redis 6.2.
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 6.2 RC2,
|
||||
each one includes the PR number that added it, so you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
New commands / args:
|
||||
* Add HRANDFIELD and ZRANDMEMBER commands (#8297)
|
||||
* Add FAILOVER command (#8315)
|
||||
* Add GETEX, GETDEL commands (#8327)
|
||||
* Add PXAT/EXAT arguments to SET command (#8327)
|
||||
* Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH (#8258)
|
||||
|
||||
Sentinel:
|
||||
* Add hostname support to Sentinel (#8282)
|
||||
* Prevent file descriptors from leaking into Sentinel scripts (#8242)
|
||||
* Fix config file line order dependency and config rewrite sequence (#8271)
|
||||
|
||||
New configuration options:
|
||||
* Add set-proc-title config option to disable changes to the process title (#3623)
|
||||
* Add proc-title-template option to control what's shown in the process title (#8397)
|
||||
* Add lazyfree-lazy-user-flush config option to control FLUSHALL, FLUSHDB and SCRIPT FLUSH (#8258)
|
||||
|
||||
Bug fixes:
|
||||
* AOF: recover from last write error by turning on/off appendonly config (#8030)
|
||||
* Exit on fsync error when the AOF fsync policy is 'always' (#8347)
|
||||
* Avoid assertions (on older kernels) when testing arm64 CoW bug (#8405)
|
||||
* CONFIG REWRITE should honor umask settings (#8371)
|
||||
* Fix firstkey,lastkey,step in COMMAND command for some commands (#8367)
|
||||
|
||||
Special considerations:
|
||||
* Fix misleading description of the save configuration directive (#8337)
|
||||
|
||||
Improvements:
|
||||
* A way to get RDB file via replication without excessive replication buffers (#8303)
|
||||
* Optimize performance of clusterGenNodesDescription for large clusters (#8182)
|
||||
|
||||
Info fields and introspection changes:
|
||||
* SLOWLOG and LATENCY monitor include unblocking time of blocked commands (#7491)
|
||||
|
||||
Modules:
|
||||
* Add modules API for streams (#8288)
|
||||
* Add event for fork child birth and termination (#8289)
|
||||
* Add RM_BlockedClientMeasureTime* etc, to track background processing in commandstats (#7491)
|
||||
* Fix bug in v6.2, wrong value passed to the new unlink callback (#8381)
|
||||
* Fix bug in v6.2, modules blocked on keys unblock on commands like LPUSH (#8356)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2 RC2 Released Tue Jan 12 16:17:20 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the second Release Candidate of Redis 6.2.
|
||||
|
||||
IMPORTANT: If you're running Redis on ARM64 or a big-endian system, upgrade may
|
||||
have significant implications. Please be sure to read the notes below.
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 6.2 RC1,
|
||||
each one includes the PR number that added it, so you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
New commands / args:
|
||||
* Add the REV, BYLEX and BYSCORE arguments to ZRANGE, and the ZRANGESTORE command (#7844)
|
||||
* Add the XAUTOCLAIM command (#7973)
|
||||
* Add the MINID trimming strategy and the LIMIT argument to XADD and XTRIM (#8169)
|
||||
* Add the ANY argument to GEOSEARCH and GEORADIUS (#8259)
|
||||
* Add the CH, NX, XX arguments to GEOADD (#8227)
|
||||
* Add the COUNT argument to LPOP and RPOP (#8179)
|
||||
* Add the WRITE argument to CLIENT PAUSE for pausing write commands exclusively (#8170)
|
||||
* Change the proto-ver argument of HELLO to optional (#7377)
|
||||
* Add the CLIENT TRACKINGINFO subcommand (#7309)
|
||||
|
||||
Command behavior changes:
|
||||
* CLIENT TRACKING yields an error when given overlapping BCAST prefixes (#8176)
|
||||
* SWAPDB invalidates WATCHed keys (#8239)
|
||||
* SORT command behaves differently when used on a writable replica (#8283)
|
||||
|
||||
Other behavior changes:
|
||||
* Avoid propagating MULTI/EXEC for read-only transactions (#8216)
|
||||
* Remove the read-only flag from TIME, ECHO, ROLE, LASTSAVE (#8216)
|
||||
* Fix the command flags of PFDEBUG (#8222)
|
||||
* Tracking clients will no longer receive unnecessary key invalidation messages after FLUSHDB (#8039)
|
||||
* Sentinel: Fix missing updates to the config file after SENTINEL SET command (#8229)
|
||||
|
||||
Bug fixes with compatibility implications (bugs introduced in Redis 6.0):
|
||||
* Fix RDB CRC64 checksum on big-endian systems (#8270)
|
||||
If you're using big-endian please consider the compatibility implications with
|
||||
RESTORE, replication and persistence.
|
||||
* Fix wrong order of key/value in Lua's map response (#8266)
|
||||
If your scripts use redis.setresp() or return a map (new in Redis 6.0), please
|
||||
consider the implications.
|
||||
|
||||
Bug fixes that are only applicable to previous releases of Redis 6.2:
|
||||
* Resolve rare assertions in active defragmentation while loading (#8284, #8281)
|
||||
|
||||
Bug fixes:
|
||||
* Fix the selection of a random element from large hash tables (#8133)
|
||||
* Fix an issue where a forked process deletes the parent's pidfile (#8231)
|
||||
* Fix crashes when enabling io-threads-do-reads (#8230)
|
||||
* Fix a crash in redis-cli after executing cluster backup (#8267)
|
||||
* Fix redis-benchmark to use an IP address for the first cluster node (#8154)
|
||||
* Fix saving of strings larger than 2GB into RDB files (#8306)
|
||||
|
||||
Additional improvements:
|
||||
* Improve replication handshake time (#8214)
|
||||
* Release client tracking table memory asynchronously in cases where the DB is also freed asynchronously (#8039)
|
||||
* Avoid wasteful transient memory allocation in certain cases (#8286, #5954)
|
||||
* Handle binary string values by the 'requirepass' and 'masterauth' configs (#8200)
|
||||
|
||||
Platform and deployment-related changes:
|
||||
* Install redis-check-rdb and redis-check-aof as symlinks to redis-server (#5745)
|
||||
* Add a check for an ARM64 Linux kernel bug (#8224)
|
||||
Due to the potential severity of this issue, Redis will refuse to run on
|
||||
affected platforms by default.
|
||||
|
||||
Info fields and introspection changes:
|
||||
* Add the errorstats section to the INFO command (#8217)
|
||||
* Add the failed_calls and rejected_calls fields INFO's commandstats section (#8217)
|
||||
* Report child copy-on-write metrics continuously (#8264)
|
||||
|
||||
Module API changes:
|
||||
* Add the RedisModule_SendChildCOWInfo API (#8264)
|
||||
* Add the may-replicate command flag (#8170)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2 RC1 Released Mon Dec 14 11:50:00 IST 2020
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the first Release Candidate of Redis 6.2.
|
||||
|
||||
Introduction to the Redis 6.2 release
|
||||
=====================================
|
||||
|
||||
This release is the first significant Redis release managed by the core team
|
||||
under the new project governance model.
|
||||
|
||||
Redis 6.2 includes many new commands and improvements, but no big features. It
|
||||
mainly makes Redis more complete and addresses issues that have been requested
|
||||
by many users frequently or for a long time.
|
||||
|
||||
Many of these changes were not eligible for 6.0.x for several reasons:
|
||||
|
||||
1. They are not backward compatible, which is always the case with new or
|
||||
extended commands (that cannot be replicated to an older replica).
|
||||
2. They require a longer release-candidate test cycle.
|
||||
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 6.0.9,
|
||||
each one includes the PR number that added it, so you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
New commands / args:
|
||||
* Add SMISMEMBER command that checks multiple members (#7615)
|
||||
* Add ZMSCORE command that returns an array of scores (#7593)
|
||||
* Add LMOVE and BLMOVE commands that pop and push arbitrarily (#6929)
|
||||
* Add RESET command that resets client connection state (#7982)
|
||||
* Add COPY command that copies keys (#7953)
|
||||
* Add ZDIFF and ZDIFFSTORE commands (#7961)
|
||||
* Add ZINTER and ZUNION commands (#7794)
|
||||
* Add GEOSEARCH/GEOSEARCHSTORE commands for bounding box spatial queries (#8094)
|
||||
* Add GET parameter to SET command, for more powerful GETSET (#7852)
|
||||
* Add exclusive range query to XPENDING (#8130)
|
||||
* Add exclusive range query to X[REV]RANGE (#8072)
|
||||
* Add GT and LT options to ZADD for conditional score updates (#7818)
|
||||
* Add CLIENT INFO and CLIENT LIST for specific ids (#8113)
|
||||
* Add IDLE argument to XPENDING command (#7972)
|
||||
* Add local address to CLIENT LIST, and a CLIENT KILL filter. (#7913)
|
||||
* Add NOMKSTREAM option to XADD command (#7910)
|
||||
* Add command introspection to Sentinel (#7940)
|
||||
* Add SENTINEL MYID subcommand (#7858)
|
||||
|
||||
New features:
|
||||
* Dump payload sanitization: prevent corrupt payload causing crashes (#7807)
|
||||
Has flags to enable full O(N) validation (disabled by default).
|
||||
* ACL patterns for Pub/Sub channels (#7993)
|
||||
* Support ACL for Sentinel mode (#7888)
|
||||
* Support getting configuration from both stdin and file at the same time (#7893)
|
||||
Lets you avoid storing secrets on the disk.
|
||||
|
||||
New features in CLI tools:
|
||||
* redis-cli RESP3 push support (#7609)
|
||||
* redis-cli cluster import support source and target that require auth (#7994)
|
||||
* redis-cli URIs able to provide user name in addition to password (#8048)
|
||||
* redis-cli/redis-benchmark allow specifying the prefered ciphers/ciphersuites (#8005)
|
||||
* redis-cli add -e option to exit with code when command execution fails (#8136)
|
||||
|
||||
Command behavior changes:
|
||||
* EXISTS should not alter LRU (#8016)
|
||||
In Redis 5.0 and 6.0 it would have touched the LRU/LFU of the key.
|
||||
* OBJECT should not reveal logically expired keys (#8016)
|
||||
Will now behave the same TYPE or any other non-DEBUG command.
|
||||
* Improve db id range check for SELECT and MOVE (#8085)
|
||||
Changes the error message text on a wrong db index.
|
||||
* Modify AUTH / HELLO error message (#7648)
|
||||
Changes the error message text when the user isn't found or is disabled.
|
||||
* BITOPS length limited to proto_max_bulk_len rather than 512MB (#8096)
|
||||
The limit is now configurable like in SETRANGE, and APPEND.
|
||||
* GEORADIUS[BYMEMBER] can fail with -OOM if Redis is over the memory limit (#8107)
|
||||
|
||||
Other behavior changes:
|
||||
* Optionally (default) fail to start if requested bind address is not available (#7936)
|
||||
If you rely on Redis starting successfully even if one of the bind addresses
|
||||
is not available, you'll need to tune the new config.
|
||||
* Limit the main db dictionaries expansion to prevent key eviction (#7954)
|
||||
In the past big dictionary rehashing could result in massive data eviction.
|
||||
Now this rehashing is delayed (up to a limit), which can result in performance
|
||||
loss due to hash collisions.
|
||||
* CONFIG REWRITE is atomic and safer, but requires write access to the config file's folder (#7824, #8051)
|
||||
This change was already present in 6.0.9, but was missing from the release
|
||||
notes.
|
||||
* A new incremental eviction mechanism that reduces latency on eviction spikes (#7653)
|
||||
In pathological cases this can cause memory to grow uncontrolled and may require
|
||||
specific tuning.
|
||||
* Not resetting "save" config when Redis is started with command line arguments. (#7092)
|
||||
In case you provide command line arguments without "save" and count on it
|
||||
being disabled, Now the defaults "save" config will kick in.
|
||||
* Update memory metrics for INFO during loading (#7690)
|
||||
* When "supervised" config is enabled, it takes precedence over "daemonize". (#8036)
|
||||
* Assertion and panic, print crash log without generating SIGSEGV (#7585)
|
||||
* Added crash log report on SIGABRT, instead of silently exiting (#8004)
|
||||
* Disable THP (Transparent Huge Pages) if enabled (#7381)
|
||||
If you deliberately enabled it, you'll need to config Redis to keep it.
|
||||
|
||||
Bug fixes:
|
||||
* Handle output buffer limits for module blocked clients (#8141)
|
||||
Could result in a module sending reply to a blocked client to go beyond the
|
||||
limit.
|
||||
* Fix setproctitle related crashes. (#8150, #8088)
|
||||
Caused various crashes on startup, mainly on Apple M1 chips or under
|
||||
instrumentation.
|
||||
* A module doing RM_Call could cause replicas to get nested MULTI (#8097).
|
||||
* Backup/restore cluster mode keys to slots map for repl-diskless-load=swapdb (#8108)
|
||||
In cluster mode with repl-diskless-load, when loading failed, slot map
|
||||
wouldn't have been restored.
|
||||
* Fix oom-score-adj-values range, and bug when used in config file (#8046)
|
||||
Enabling setting this in the config file in a line after enabling it, would
|
||||
have been buggy.
|
||||
* Reset average ttl when empty databases (#8106)
|
||||
Just causing misleading metric in INFO
|
||||
* Disable rehash when Redis has child process (#8007)
|
||||
This could have caused excessive CoW during BGSAVE, replication or AOFRW.
|
||||
* Further improved ACL algorithm for picking categories (#7966)
|
||||
Output of ACL GETUSER is now more similar to the one provided by ACL SETUSER.
|
||||
* Fix bug with module GIL being released prematurely (#8061)
|
||||
Could in theory (and rarely) cause multi-threaded modules to corrupt memory.
|
||||
* Fix cluster redirect for module command with no firstkey. (#7539)
|
||||
* Reduce effect of client tracking causing feedback loop in key eviction (#8100)
|
||||
* Kill disk-based fork child when all replicas drop and 'save' is not enabled (#7819)
|
||||
* Rewritten commands (modified for propagation) are logged as their original command (#8006)
|
||||
* Fix cluster access to unaligned memory (SIGBUS on old ARM) #7958
|
||||
* If diskless repl child is killed, make sure to reap the child pid (#7742)
|
||||
* Broadcast a PONG message when slot's migration is over, may reduce MOVED responses (#7571)
|
||||
|
||||
Other improvements:
|
||||
* TLS Support in redis-benchmark (#7959)
|
||||
* Accelerate diskless master connections, and general re-connections (#6271)
|
||||
* Run active defrag while blocked / loading (#7726)
|
||||
* Performance and memory reporting improvement - sds take control of its internal fragmentation (#7875)
|
||||
* Speedup cluster failover. (#7948)
|
||||
|
||||
Platform / toolchain support related improvements:
|
||||
* Optionally (not by default) use H/W Monotonic clock for faster time sampling (#7644)
|
||||
* Remove the requirements for C11 and _Atomic supporting compiler (#7707)
|
||||
This would allow to more easily build and use Redis on older systems and
|
||||
compilers again.
|
||||
* Fix crash log registers output on ARM. (#8020)
|
||||
* Raspberry build fix. (#8095)
|
||||
* Setting process title support for Haiku. (#8060)
|
||||
* DragonFlyBSD RSS memory sampling support. (#8023)
|
||||
|
||||
New configuration options:
|
||||
* Enable configuring OpenSSL using the standard openssl.cnf (#8143)
|
||||
* oom-score-adj-values config can now take absolute values (besides relative ones) (#8046)
|
||||
* TLS: Add different client cert support. (#8076)
|
||||
* Note that a few other changes listed above added their config options.
|
||||
|
||||
Info fields and introspection changes:
|
||||
* Add INFO fields to track diskless and disk-based replication progress (#7981)
|
||||
* Add INFO field for main thread cpu time, and scrape system time. (#8132)
|
||||
* Add total_forks to INFO STATS (#8155)
|
||||
* Add maxclients and cluster_connections to INFO CLIENTS (#7979)
|
||||
* Add tracking bcast flag and client redirection in client list (#7995)
|
||||
* Fixed INFO client_recent_max_input_buffer includes argv array (#8065, see #7874)
|
||||
* Note that a few other changes listed above added their info fields.
|
||||
|
||||
Module API changes:
|
||||
* Add CTX_FLAGS_DENY_BLOCKING as a unified the way to know if blocking is allowed (#8025)
|
||||
* Add data type callbacks for lazy free effort, and unlink (#7912)
|
||||
* Add data type callback for COPY command (#8112)
|
||||
* Add callbacks for defrag support. (#8149)
|
||||
* Add module event for repl-diskless-load swapdb (#8153)
|
||||
|
||||
Module related fixes:
|
||||
* Moved RMAPI_FUNC_SUPPORTED so that it's usable (#8037)
|
||||
* Improve timer accuracy (#7987)
|
||||
* Allow '\0' inside of result of RM_CreateStringPrintf (#6260)
|
||||
|
||||
|
||||
Thanks to all the users and developers who made this release possible.
|
||||
We'll follow up with more RC releases, until the code looks production ready
|
||||
and we don't get reports of serious issues for a while.
|
||||
|
||||
A special thank you for the amount of work put into this release by:
|
||||
- Oran Agra
|
||||
- Yossi Gottlieb
|
||||
- Viktor Söderqvist
|
||||
- Yang Bodong
|
||||
- Filipe Oliveira
|
||||
- Guy Benoish
|
||||
- Itamar Haber
|
||||
- Madelyn Olson
|
||||
- Wang Yuan
|
||||
- Felipe Machado
|
||||
- Wen Hui
|
||||
- Tatsuya Arisawa
|
||||
- Jonah H. Harris
|
||||
- Raghav Muddur
|
||||
- Jim Brunner
|
||||
- Yaacov Hazan
|
||||
- Allen Farris
|
||||
- Chen Yang
|
||||
- Nitai Caro
|
||||
- sundb
|
||||
- Meir Shpilraien
|
||||
- maohuazhu
|
||||
- Valentino Geron
|
||||
- Zhao Zhao
|
||||
- Qu Chen
|
||||
- George Prekas
|
||||
- Tyson Andre
|
||||
- Uri Yagelnik
|
||||
- Michael Grunder
|
||||
- Huang Zw
|
||||
- alexronke-channeladvisor
|
||||
- Andy Pan
|
||||
- Wu Yunlong
|
||||
- Wei Kukey
|
||||
- Yoav Steinberg
|
||||
- Greg Femec
|
||||
- Uri Shachar
|
||||
- Nykolas Laurentino de Lima
|
||||
- xhe
|
||||
- zhenwei pi
|
||||
- David CARLIER
|
||||
|
||||
Migrating from 6.0 to 6.2
|
||||
=========================
|
||||
|
||||
Redis 6.2 is mostly a strict superset of 6.0, you should not have any problem
|
||||
upgrading your application from 6.0 to 6.2. However there are some small changes
|
||||
of behavior listed above, please make sure you are not badly affected by any of
|
||||
them.
|
||||
|
||||
Specifically these sections:
|
||||
* Command behavior changes
|
||||
* Other behavior changes
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Cheers,
|
||||
The Redis team
|
||||
@@ -0,0 +1,9 @@
|
||||
This is a stable release, for beta testing make sure to download the latest source code from Git:
|
||||
|
||||
git clone git://github.com/antirez/redis.git
|
||||
|
||||
It's also possibe to download the latest source code as a tarball:
|
||||
|
||||
http://github.com/antirez/redis/tree/master
|
||||
|
||||
(use the download button)
|
||||
@@ -1 +1 @@
|
||||
Please check https://github.com/redis/redis/issues
|
||||
Plese check http://code.google.com/p/redis/issues/list
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
Contributor Covenant Code of Conduct
|
||||
Our Pledge
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
Our Standards
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others’ private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
Enforcement Responsibilities
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
Scope
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
Enforcement
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
this email address: redis@redis.io.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
Enforcement Guidelines
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
1. Correction
|
||||
Community Impact: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
Consequence: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
2. Warning
|
||||
Community Impact: A violation through a single incident or series
|
||||
of actions.
|
||||
Consequence: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
3. Temporary Ban
|
||||
Community Impact: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
Consequence: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
4. Permanent Ban
|
||||
Community Impact: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
Consequence: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
Attribution
|
||||
This Code of Conduct is adapted from the Contributor Covenant,
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
Community Impact Guidelines were inspired by Mozilla’s code of conduct
|
||||
enforcement ladder.
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
@@ -1,74 +0,0 @@
|
||||
Note: by contributing code to the Redis project in any form, including sending
|
||||
a pull request via Github, a code fragment or patch via private email or
|
||||
public discussion groups, you agree to release your code under the terms
|
||||
of the BSD license that you can find in the COPYING file included in the Redis
|
||||
source distribution. You will include BSD license in the COPYING file within
|
||||
each source file that you contribute.
|
||||
|
||||
# IMPORTANT: HOW TO USE REDIS GITHUB ISSUES
|
||||
|
||||
Github issues SHOULD ONLY BE USED to report bugs, and for DETAILED feature
|
||||
requests. Everything else belongs to the Redis Google Group:
|
||||
|
||||
https://groups.google.com/forum/m/#!forum/Redis-db
|
||||
|
||||
PLEASE DO NOT POST GENERAL QUESTIONS that are not about bugs or suspected
|
||||
bugs in the Github issues system. We'll be very happy to help you and provide
|
||||
all the support in the mailing list.
|
||||
|
||||
There is also an active community of Redis users at Stack Overflow:
|
||||
|
||||
http://stackoverflow.com/questions/tagged/redis
|
||||
|
||||
# Reporting Security Bugs
|
||||
|
||||
*If you are reporting a security bug*, please contact the core team privately
|
||||
by emailing redis@redis.io. Your report will be acknowledged by a core team
|
||||
member and once the report has been reviewed you will receive a more detailed
|
||||
response including next steps.
|
||||
|
||||
If you do not receive a reply you can escalate to the Redis Google Group,
|
||||
linked above. Because this group is a public space please do not disclose the
|
||||
issue in detail, only say that you are trying to reach the core team for a
|
||||
security issue.
|
||||
|
||||
Redis follows a responsible disclosure process:
|
||||
|
||||
1. Reports are reviewed and analyzed privately
|
||||
2. Patches are prepared for supported versions of Redis
|
||||
3. Vendor lists are notified with an embargo date to reduce the public impact
|
||||
4. We push a fix release and your bug can be posted publicly with credit in
|
||||
release notes and the version history (and our thanks!)
|
||||
|
||||
Issues and pull requests for documentation belong on the redis-doc repo:
|
||||
|
||||
https://github.com/redis/redis-doc
|
||||
|
||||
# How to provide a patch for a new feature
|
||||
|
||||
1. If it is a major feature or a semantical change, please don't start coding
|
||||
straight away: if your feature is not a conceptual fit you'll lose a lot of
|
||||
time writing the code without any reason. Start by posting in the mailing list
|
||||
and creating an issue at Github with the description of, exactly, what you want
|
||||
to accomplish and why. Use cases are important for features to be accepted.
|
||||
Here you'll see if there is consensus about your idea.
|
||||
|
||||
2. If in step 1 you get an acknowledgment from the project leaders, use the
|
||||
following procedure to submit a patch:
|
||||
|
||||
a. Fork Redis on github ( http://help.github.com/fork-a-repo/ )
|
||||
b. Create a topic branch (git checkout -b my_branch)
|
||||
c. Push to your branch (git push origin my_branch)
|
||||
d. Initiate a pull request on github ( https://help.github.com/articles/creating-a-pull-request/ )
|
||||
e. Done :)
|
||||
|
||||
3. Keep in mind that we are very overloaded, so issues and PRs sometimes wait
|
||||
for a *very* long time. However this is not lack of interest, as the project
|
||||
gets more and more users, we find ourselves in a constant need to prioritize
|
||||
certain issues/PRs over others. If you think your issue/PR is very important
|
||||
try to popularize it, have other users commenting and sharing their point of
|
||||
view and so forth. This helps.
|
||||
|
||||
4. For minor fixes just open a pull request on Github.
|
||||
|
||||
Thanks!
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2006-2020, Salvatore Sanfilippo
|
||||
Copyright (c) 2006-2009, Salvatore Sanfilippo
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -0,0 +1,899 @@
|
||||
2010-07-22 don't open/close log file if log level is not matched (antirez)
|
||||
2010-07-22 fixed a typo preventing compilation (antirez)
|
||||
2010-07-22 version is now 1.3.16 (antirez)
|
||||
2010-07-22 recent VM fixes backported to 2.0.0 branch (antirez)
|
||||
2010-07-22 fix rare condition where 'key' would already be destroyed while is was needed later on (Pieter Noordhuis)
|
||||
2010-07-21 vm_blocked_clients count fixed in INFO, thanks to Pietern Noordhuis (antirez)
|
||||
2010-07-09 old redis test deleted (antirez)
|
||||
2010-07-01 HTML doc updated (antirez)
|
||||
2010-07-01 Version is now 1.3.15 (antirez)
|
||||
2010-07-01 Fixed MONITOR output for consistency: now integer encoded values are also formatted like this: "3932" (antirez)
|
||||
2010-07-01 fixed error code checking for *write operations and return value in AOF rewriting function (antirez)
|
||||
2010-06-07 Fix for VM swapping out at DB loading time when key is shared (antirez)
|
||||
2010-06-03 cas test doesn't belong in 2.0.0 (Pieter Noordhuis)
|
||||
2010-06-03 tag memory leak check on kill server as "leaks" (Pieter Noordhuis)
|
||||
2010-06-03 tag test with sleep() as slow (Pieter Noordhuis)
|
||||
2010-06-03 make sure the config it returned when called without code (Pieter Noordhuis)
|
||||
2010-06-03 tag more slow tests (Pieter Noordhuis)
|
||||
2010-06-03 change how arguments are passed from the AOF tests (Pieter Noordhuis)
|
||||
2010-06-03 scope res variable outside test (Pieter Noordhuis)
|
||||
2010-06-02 tags for existing tests (Pieter Noordhuis)
|
||||
2010-06-02 pass tags to filter and match via arguments (Pieter Noordhuis)
|
||||
2010-06-02 basic support to tag tests (Pieter Noordhuis)
|
||||
2010-06-02 changed how server.tcl accepts options to support more directives without requiring more arguments to the proc (Pieter Noordhuis)
|
||||
2010-06-02 removed obsolete code (Pieter Noordhuis)
|
||||
2010-06-02 catch exceptions in the server proc, to be able to kill the entire chain of running servers (Pieter Noordhuis)
|
||||
2010-06-02 fixed VM object swappability computation (antirez)
|
||||
2010-06-02 smarter swapout policy on AOF too (antirez)
|
||||
2010-06-02 better swapout policy while loading RDB file (antirez)
|
||||
2010-05-26 build command outside while loop (antirez)
|
||||
2010-05-26 require the flag "-c" to be used for redis-cli to read the last argument from stdin (antirez)
|
||||
2010-05-28 Fixed ZINCR Nan bugs leading to server crash and added tests (antirez)
|
||||
2010-05-21 Merge branch 'solaris' of git://github.com/pietern/redis (antirez)
|
||||
2010-05-21 Changelog updated (antirez)
|
||||
2010-05-21 redis version is now 1.3.14 (aka 2.0.0 RC1) (antirez)
|
||||
2010-05-21 html doc updated (antirez)
|
||||
2010-05-21 by default test with valgrind does not show full leak info (antirez)
|
||||
2010-05-21 minor fix for the skiplist code, resulting in a false positive with valgrind, and in general into a useless small allocation (antirez)
|
||||
2010-05-21 Merge branch 'master' of git@github.com:antirez/redis (antirez)
|
||||
2010-05-21 tests suite initial support for valgrind, fixed the old test suite until the new one is able to target a specific host/port (antirez)
|
||||
2010-05-21 include solaris fixes in sha1.c (Pieter Noordhuis)
|
||||
2010-05-20 Don't exit with error in tests temp file cleanup if there are no files to clean (antirez)
|
||||
2010-05-20 fix memory leak on 32-bit builds (Pieter Noordhuis)
|
||||
2010-05-20 Merge branch 'master' of github.com:antirez/redis (antirez)
|
||||
2010-05-20 Fix for DEBUG DIGEST (antirez)
|
||||
2010-05-20 Merge branch 'test_vm' of git://github.com/pietern/redis (antirez)
|
||||
2010-05-20 code to enable running tests with the vm enabled (Pieter Noordhuis)
|
||||
2010-05-20 minor change to shutdown (antirez)
|
||||
2010-05-20 shutdown on SIGTERM (antirez)
|
||||
2010-05-20 Merge http://github.com/ngmoco/redis (antirez)
|
||||
2010-05-20 fix compile error on solaris (Pieter Noordhuis)
|
||||
2010-05-20 added regression for zipmap bug (antirez)
|
||||
2010-05-20 fix lookup of keys with length larger than ZIPMAP_BIGLEN (Pieter Noordhuis)
|
||||
2010-05-19 TODO updated (antirez)
|
||||
2010-05-19 initial tests for AOF (and small changes to server.tcl to support these) (Pieter Noordhuis)
|
||||
2010-05-19 Merge branch 'master' into integration (Pieter Noordhuis)
|
||||
2010-05-19 Fix for 'CONFIG SET appendonly no' (antirez)
|
||||
2010-05-19 It's now possible to turn off and on the AOF via CONFIG (antirez)
|
||||
2010-05-18 git hash 00000000 in reelase.h when git is not found enabled again after some shell scripting fix that is now compatible with most shells (antirez)
|
||||
2010-05-18 build fixed when simpler shells are used to create release.h (antirez)
|
||||
2010-05-18 use git diff when generating release.h to check for dirty status (antirez)
|
||||
2010-05-18 Solaris fixes (antirez)
|
||||
2010-05-18 html doc rebuild (antirez)
|
||||
2010-05-18 buliding of release.h moved into an external script. Avoided recompialtion of redis.c if git sha1 is the same as the previous one (antirez)
|
||||
2010-05-17 create release.h in make process and add this information to INFO listing (Pieter Noordhuis)
|
||||
2010-05-16 Redis version is now 1.3.12 (antirez)
|
||||
2010-05-16 redis version is now 1.3.11 (antirez)
|
||||
2010-05-16 random refactoring and speedups (antirez)
|
||||
2010-05-16 faster INCR with very little efforts... (antirez)
|
||||
2010-05-15 print warnings in redis log when a test raises an exception (very likely to be caused by something like a failed assertion) (Pieter Noordhuis)
|
||||
2010-05-15 Merge branch 'redis-cli-fix' of http://github.com/tizoc/redis (antirez)
|
||||
2010-05-15 added pid info to the check memory leaks test, so that those tests don't appear to be duplicated (antirez)
|
||||
2010-05-15 Merge branch 'integration' of git://github.com/pietern/redis (antirez)
|
||||
2010-05-14 more endianess detection fix for SHA1 (antirez)
|
||||
2010-05-14 fixed a warning seen with some GCC version under Linux (antirez)
|
||||
2010-05-14 initial rough integration test for replication (Pieter Noordhuis)
|
||||
2010-05-14 store entire server object on the stack instead of just the client (Pieter Noordhuis)
|
||||
2010-05-14 proc to retrieve values from INFO properties (Pieter Noordhuis)
|
||||
2010-05-14 one more fix for endianess detection (antirez)
|
||||
2010-05-14 Fixed sha1.c compilation on Linux, due to endianess detection lameness (antirez)
|
||||
2010-05-14 ZUNION,ZINTER -> ZUNIONSTORE,ZINTERSTORE (antirez)
|
||||
2010-05-14 minor fixes to the new test suite, html doc updated (antirez)
|
||||
2010-05-14 wait for redis-server to be settled and ready for connections (Pieter Noordhuis)
|
||||
2010-05-14 fix cleaning up tmp folder (Pieter Noordhuis)
|
||||
2010-05-14 update makefile to use the new test suite (Pieter Noordhuis)
|
||||
2010-05-14 check for memory leaks before killing a server (Pieter Noordhuis)
|
||||
2010-05-14 extract code to kill a server to a separate proc (Pieter Noordhuis)
|
||||
2010-05-14 start servers on different ports to prevent conflicts (Pieter Noordhuis)
|
||||
2010-05-14 use DEBUG DIGEST in new test suite (Pieter Noordhuis)
|
||||
2010-05-14 split test suite into multiple files; runs redis-server in isolation (Pieter Noordhuis)
|
||||
2010-05-14 use DEBUG DIGEST in the test instead of a function that was doing a similar work, but in a much slower and buggy way (antirez)
|
||||
2010-05-14 Don't rely on cliReadReply being able to return on shutdown (Bruno Deferrari)
|
||||
2010-05-14 If command is a shutdown, ignore errors on reply (Bruno Deferrari)
|
||||
2010-05-14 DEBUG DIGEST implemented, in order to improve the ability to test persistence and replication consistency (antirez)
|
||||
2010-05-13 Add SIGTERM shutdown handling. (Ashley Martens)
|
||||
2010-05-13 makefile deps updated (antirez)
|
||||
2010-05-13 conflicts resolved (antirez)
|
||||
2010-05-13 feed SETEX as SET and EXPIREAT to AOF (Pieter Noordhuis)
|
||||
2010-05-13 very strong speedup in saving time performance when there are many integers in the dataset. Instead of decoding the object before to pass them to the rdbSaveObject layer we check asap if the object is integer encoded and can be written on disk as an integer. (antirez)
|
||||
2010-05-13 include limits.h otherwise no double precison macros (antirez)
|
||||
2010-05-13 explicitly checks with ifdefs if our floating point and long long assumptions are verified (antirez)
|
||||
2010-05-13 Yet another version of the double saving code, with comments explaining what's happening there (antirez)
|
||||
2010-05-12 added overflow check in the double -> long long conversion trick to avoid integer overflows. I think this was not needed in practical terms, but it is safer (antirez)
|
||||
2010-05-12 use withscores when performing the dataset digest (antirez)
|
||||
2010-05-12 If a float can be casted to a long long without rounding loss, we can use the integer conversion function to write the score on disk. This is a seriuous speedup (antirez)
|
||||
2010-05-12 fixed compilation warnings in the AOF sanity check tool (antirez)
|
||||
2010-05-12 Merge branch 'vm-speedup' (antirez)
|
||||
2010-05-11 fix to return error when calling INCR on a non-string type (Pieter Noordhuis)
|
||||
2010-05-11 load objects encoded from disk directly without useless conversion (antirez)
|
||||
2010-05-11 fixed a problem leading to crashes, as keys can't be currently specially encoded, so we can't encode integers at object loading time... For now this can be fixed passing a few flags, or later can be fixed allowing encoded keys as well (antirez)
|
||||
2010-05-11 long long to string conversion speedup applied in other places as well. Still the code has bugs, fixing right now... (antirez)
|
||||
2010-05-11 hand written code to turn a long long into a string -> very big speed win (antirez)
|
||||
2010-05-11 added specialized function to compare string objects for perfect match that is optimized for this task (antirez)
|
||||
2010-05-11 better use of encoding inforamtion in dictEncObjKeyCompare (antirez)
|
||||
2010-05-10 CONFIG now can change appendfsync policy at run time (antirez)
|
||||
2010-05-10 CONFIG command now supports hot modification of RDB saving parameters. (antirez)
|
||||
2010-05-10 while loading the rdb file don't add the key to the dictionary at all if it's already expired, instead of removing it just after the insertion. (antirez)
|
||||
2010-05-10 Merge branch 'check-aof' of git://github.com/pietern/redis (antirez)
|
||||
2010-05-08 minor changes to improve code readability (antirez)
|
||||
2010-05-08 swap objects out directly while loading an RDB file if we detect we can't stay in the vm max memory limits anyway (antirez)
|
||||
2010-05-07 change command names no longer used to zunion/zinter (Pieter Noordhuis)
|
||||
2010-05-07 DEBUG POPULATE command for fast creation of test databases (antirez)
|
||||
2010-05-07 update TODO (Pieter Noordhuis)
|
||||
2010-05-07 swap arguments in blockClientOnSwappedKeys to be consistent (Pieter Noordhuis)
|
||||
2010-05-07 added function that preloads all keys needed to execute a MULTI/EXEC block (Pieter Noordhuis)
|
||||
2010-05-07 add sanity check to zunionInterBlockClientOnSwappedKeys, as the number of keys used is provided as argument to the function (Pieter Noordhuis)
|
||||
2010-05-07 make prototype of custom function to preload keys from the vm match the prototype of waitForMultipleSwappedKeys (Pieter Noordhuis)
|
||||
2010-05-07 extract preloading of multiple keys according to the command prototype to a separate function (Pieter Noordhuis)
|
||||
2010-05-07 make append only filename configurable (Pieter Noordhuis)
|
||||
2010-05-07 don't load value from VM for EXISTS (Pieter Noordhuis)
|
||||
2010-05-07 swap file name pid expansion removed. Not suited for mission critical software... (antirez)
|
||||
2010-05-07 Swap file is now locked (antirez)
|
||||
2010-05-06 Merge branch 'master' into aof-speedup (antirez)
|
||||
2010-05-06 log error and quit when the AOF contains an unfinished MULTI (antirez)
|
||||
2010-05-06 log error and quit when the AOF contains an unfinished MULTI (Pieter Noordhuis)
|
||||
2010-05-06 Merge branch 'master' into check-aof (Pieter Noordhuis)
|
||||
2010-05-06 hincrby should report an error when called against a hash key that doesn't contain an integer (Pieter Noordhuis)
|
||||
2010-05-06 AOF writes are now accumulated into a buffer and flushed into disk just before re-entering the event loop. A lot less writes but still this guarantees that AOF is written before the client gets a positive reply about a write operation, as no reply is trasnmitted before re-entering into the event loop. (antirez)
|
||||
2010-05-06 clarified a few messages in redis.conf (antirez)
|
||||
2010-05-05 ask for confirmation before AOF is truncated (Pieter Noordhuis)
|
||||
2010-05-05 str can be free'd outside readString (Pieter Noordhuis)
|
||||
2010-05-05 moved argument parsing around (Pieter Noordhuis)
|
||||
2010-05-05 ignore redis-check-aof binary (Pieter Noordhuis)
|
||||
2010-05-05 allow AOF to be fixed by truncating to the portion of the file that is valid (Pieter Noordhuis)
|
||||
2010-05-05 tool to check if AOF is valid (Pieter Noordhuis)
|
||||
2010-05-02 included fmacros.h in linenose.c to avoid compilation warnings on Linux (antirez)
|
||||
2010-05-02 compilation fix for mac os x (antirez)
|
||||
2010-05-02 Merge branch 'master' of git@github.com:antirez/redis (antirez)
|
||||
2010-05-02 On Linux now fdatasync() is used insetad of fsync() in order to flush the AOF file kernel buffers (antirez)
|
||||
2010-04-30 More tests for APPEND and tests for SUBSTR (antirez)
|
||||
2010-04-30 linenoise.c updated, now redis-cli can be used in a pipe (antirez)
|
||||
2010-04-29 redis-cli minor fix (less segfault is better) (antirez)
|
||||
2010-04-29 New MONITOR output format with timestamp, every command in a single line, string representations (antirez)
|
||||
2010-04-29 redis-cli INFO output format is now raw again (antirez)
|
||||
2010-04-29 Added more information about slave election in Redis Cluster alternative doc (antirez)
|
||||
2010-04-29 Redis cluster version 2 (antirez)
|
||||
2010-04-27 Fixed a redis-cli bug, was using free instead of zfree call (antirez)
|
||||
2010-04-27 AOF is now rewritten on slave after SYNC with master. Thanks to @_km for finding this bug and any others' (antirez)
|
||||
2010-04-27 redis-cli is now using only the new protocol (antirez)
|
||||
2010-04-27 Minimal support for subscribe/psubscribe in redis-cli (antirez)
|
||||
2010-04-26 don't output the newline when stdout is not a tty (antirez)
|
||||
2010-04-26 redis-cli now is able to also output the string representation instead of the raw string. Much better for debugging (antirez)
|
||||
2010-04-26 Initial support for quoted strings in redis-cli (antirez)
|
||||
2010-04-23 SETEX implemented (antirez)
|
||||
2010-04-23 Pub/Sub API change: now messages received via pattern matching have a different message type and an additional field representing the original pattern the message matched (antirez)
|
||||
2010-04-22 typo fixed, reloaded (antirez)
|
||||
2010-04-22 typo fixed (antirez)
|
||||
2010-04-22 REDIS-CLUSTER doc updated (antirez)
|
||||
2010-04-22 Virtual memory design document removed, no longer needed as we have a full specification and implementation (antirez)
|
||||
2010-04-22 new units for bytes specification (antirez)
|
||||
2010-04-22 Now in redis.conf it is possible to specify units where appropriate instead of amounts of bytes, like 2Gi or 4M and so forth (antirez)
|
||||
2010-04-21 binary safe keys ready implementation of RANDOMKEYS (antirez)
|
||||
2010-04-21 Now that's the right 1.3.10 (antirez)
|
||||
2010-04-21 Revert "fsync always now uses O_DIRECT on Linux" (antirez)
|
||||
2010-04-21 Revert "define __USE_GNU to get O_DIRECT" (antirez)
|
||||
2010-04-21 Merge branch 'master' of github.com:antirez/redis (antirez)
|
||||
2010-04-21 Revert "version 1.3.10" (antirez)
|
||||
2010-04-21 version 1.3.10 (antirez)
|
||||
2010-04-20 define __USE_GNU to get O_DIRECT (antirez)
|
||||
2010-04-20 fsync always now uses O_DIRECT on Linux (antirez)
|
||||
2010-04-20 More precise memory used guesswork in zmalloc.c (antirez)
|
||||
2010-04-19 Fix for MULTI/EXEC and Replication/AOF: now the block is correctly sent as MULTI/..writing operations../EXEC. Ok for slaves but more work needed for the AOF as it should be a write-all-or-nothing business (antirez)
|
||||
2010-04-19 running the test using tcl8.5 directly instead of tclsh that too often it's a symlink to 8.4 (antirez)
|
||||
2010-04-19 Added package require Tcl 8.5 in redis.tcl so it will show a clear error when the test suit is attempted to run under 8.4 (antirez)
|
||||
2010-04-18 Fix for a SORT bug introduced with commit 16fa22f1, regression test added (antirez)
|
||||
2010-04-18 Guru mediation -> meditation (antirez)
|
||||
2010-04-16 check eptr inline (Pieter Noordhuis)
|
||||
2010-04-16 refactor code that retrieves value from object or replies to client (Pieter Noordhuis)
|
||||
2010-04-17 Merge branch 'hash' of git://github.com/pietern/redis (antirez)
|
||||
2010-04-17 redisAssert(0) => redisPanic("something meaningful") (antirez)
|
||||
2010-04-17 make sure that the resulting value in hincrby is encoded when possible (Pieter Noordhuis)
|
||||
2010-04-17 increment dirty counter after hmset (Pieter Noordhuis)
|
||||
2010-04-17 strip tryObjectEncoding from hashSet, to enable the arguments being encoded in-place (Pieter Noordhuis)
|
||||
2010-04-17 Added support for Guru Mediation, and raising a guru mediation if refCount <= 0 but decrRefCount is called against such an object (antirez)
|
||||
2010-04-16 fix small error and memory leaks in SORT (Pieter Noordhuis)
|
||||
2010-04-16 SORT/GET test added (antirez)
|
||||
2010-04-16 Added tests for GET/BY against hashes fields (antirez)
|
||||
2010-04-16 Merge branch 'hash-refactor' of git://github.com/pietern/redis (antirez)
|
||||
2010-04-16 check object type in lookupKeyByPattern (Pieter Noordhuis)
|
||||
2010-04-16 make sortCommand aware that lookupKeyByPattern always increased the refcount of the returned value (Pieter Noordhuis)
|
||||
2010-04-16 revert 0c390a to stop using tricks with o->refcount (Pieter Noordhuis)
|
||||
2010-04-16 store the hash iterator on the heap instead of the stack (Pieter Noordhuis)
|
||||
2010-04-16 drop inline directive (Pieter Noordhuis)
|
||||
2010-04-16 rename hashReplace to hashSet (Pieter Noordhuis)
|
||||
2010-04-16 added dictFetchValue() to dict.c to make hash table API a bit less verbose in the common cases (antirez)
|
||||
2010-04-03 Don't set expire to keys with ttl=0, remove them immediately. (antirez)
|
||||
2010-04-15 make sure that cmpobj is in decoded form when sorting by ALPHA (this solves edge case from previous commit where (!sortby && alpha) == 1) (Pieter Noordhuis)
|
||||
2010-04-15 enable hash dereference in SORT on BY and GET (Pieter Noordhuis)
|
||||
2010-04-15 use shared replies for hset (Pieter Noordhuis)
|
||||
2010-04-15 set refcount of string objects retrieved from zipmaps to 0, so we don't have to touch the refcount of the objects inside dicts (Pieter Noordhuis)
|
||||
2010-04-15 added HSETNX (Pieter Noordhuis)
|
||||
2010-04-14 refactor of hash commands to use specialized api that abstracts zipmap and dict apis (Pieter Noordhuis)
|
||||
2010-04-13 move retrieval of long up to prevent an empty hash from being created (Pieter Noordhuis)
|
||||
2010-04-15 more advanced leaks detection in test redis (antirez)
|
||||
2010-04-15 ability to select port/host from make test (antirez)
|
||||
2010-04-15 Active rehashing (antirez)
|
||||
2010-04-15 Incrementally rehahsing hash table! Thanks to Derek Collison and Pieter Noordhuis for feedbacks/help (antirez)
|
||||
2010-04-14 Does not allow commands other than Pub/Sub commands when there is at least one pattern (antirez)
|
||||
2010-04-13 Fixed a tiny memory leak when loading the configuration file. (Alex McHale)
|
||||
2010-04-13 Merge branch 'hmget' of git://github.com/pietern/redis (antirez)
|
||||
2010-03-29 Validate numeric inputs. (Alex McHale)
|
||||
2010-03-24 Remove trailing whitespace. (Alex McHale)
|
||||
2010-04-12 Now all the commands returning a multi bulk reply against non existing keys will return an empty multi bulk, not a nil one (antirez)
|
||||
2010-04-12 implemented HMGET (Pieter Noordhuis)
|
||||
2010-04-12 implemented HMSET (Pieter Noordhuis)
|
||||
2010-04-12 Sharing of small integer objects: may save a lot of memory with datasets having many of this (antirez)
|
||||
2010-04-10 dict.c fixed to play well with enabling/disabling of the hash table (antirez)
|
||||
2010-04-09 removed a no longer true assert in the VM code (antirez)
|
||||
2010-04-09 shareobjects feautres killed - no gains most of the time, but VM complexities (antirez)
|
||||
2010-04-09 use directly the real key object in VM I/O jobs to match by pointer, and to handle different keys with the same name living in different DBs, but being at the same moment in the IO job queues (antirez)
|
||||
2010-04-08 last change reverted as it was unstable... more testing needed (antirez)
|
||||
2010-04-08 Prevent hash table resize while there are active child processes in order to play well with copy on write (antirez)
|
||||
2010-04-08 Merge branch 'issue_218' of git://github.com/pietern/redis (antirez)
|
||||
2010-04-08 -1 not needed... (antirez)
|
||||
2010-04-08 Skiplist theoretical fix (antirez)
|
||||
2010-04-07 Now when a child is terminated by a signal, the signal number is logged as well (antirez)
|
||||
2010-04-07 First version of evented Redis Tcl client, that will be used for BLPOP and Pub/Sub tests (antirez)
|
||||
2010-04-05 use long long reply type for HINCRBY (Pieter Noordhuis)
|
||||
2010-04-05 last argument is never encoded for HINCRBY (Pieter Noordhuis)
|
||||
2010-04-02 Now PUBLISH commands are replicated to slaves (antirez)
|
||||
2010-04-01 use the right object when cleaning up after zunion/zinter (fixes issue 216) (Pieter Noordhuis)
|
||||
2010-04-01 Merge branch 'zipmap' of git://github.com/pietern/redis (antirez)
|
||||
2010-04-01 reduce code complexity because zipmapLen now is O(1) (Pieter Noordhuis)
|
||||
2010-04-01 update the zipmap entry in-place instead of appending it (Pieter Noordhuis)
|
||||
2010-04-01 updated zipmap documentation to match the implementation (Pieter Noordhuis)
|
||||
2010-04-01 allow 4 free trailing bytes for each value (Pieter Noordhuis)
|
||||
2010-04-01 Pub/Sub pattern matching capabilities (antirez)
|
||||
2010-04-01 use function to determine length of a single entry (Pieter Noordhuis)
|
||||
2010-03-31 Deny EXEC under out of memory (antirez)
|
||||
2010-03-29 No timeouts nor other commands for clients in a Pub/Sub context (antirez)
|
||||
2010-03-29 free hash table entries about no longer active classes, so that PUBSUB can be abused with millions of different classes (antirez)
|
||||
2010-03-29 Fixed a refcount stuff leading to PUBSUB crashes (antirez)
|
||||
2010-03-29 fmacros added to linenoise, avoiding all the nice warnings... (antirez)
|
||||
2010-03-29 First pubsub fix (antirez)
|
||||
2010-03-29 PUBSUB implemented (antirez)
|
||||
2010-03-29 Redis version is now 1.3.8 (antirez)
|
||||
2010-03-28 removed references in code to ZIPMAP_EMPTY (Pieter Noordhuis)
|
||||
2010-03-28 use first byte of zipmap to store length (Pieter Noordhuis)
|
||||
2010-03-28 implemented strategy that doesn't use free blocks in zipmaps (Pieter Noordhuis)
|
||||
2010-03-26 Merge branch 'hincrby' of git://github.com/pietern/redis (antirez)
|
||||
2010-03-26 removed unnecessary refcount increase that caused the HINCRBY memleak (Pieter Noordhuis)
|
||||
2010-03-26 implements HINCRBY and tests (todo: find and fix small memleak) (Pieter Noordhuis)
|
||||
2010-03-26 Removed a useless if spotted by Pieter Noordhuis (antirez)
|
||||
2010-03-26 Fixed a critical replication bug: binary values issued with the multi bulk protocol caused a protocol desync with slaves. (antirez)
|
||||
2010-03-24 Fixed the reply about denied write commands under maxmemory reached condition: now the error will no longer lead to a client-server protocol desync (antirez)
|
||||
2010-03-24 CONFIG command implemened -- just a start but already useful (antirez)
|
||||
2010-03-24 redis-cli prompt is now redis> (antirez)
|
||||
2010-03-23 with --help states that you can use - as config file name to feed config via stdin (antirez)
|
||||
2010-03-23 New INFO field: expired_keys (antirez)
|
||||
2010-03-23 the Cron timer function is now called 10 times per second instead of 1 time per second to make Redis more responsibe to BGSAVE and to delete expired keys more incrementally (antirez)
|
||||
2010-03-23 Use linenoise for line editing on redis-cli. (Michel Martens)
|
||||
2010-03-23 Fix authentication for redis-cli on non-interactive mode. (Michel Martens)
|
||||
2010-03-23 key deletion on empty value fix + some refactoring (antirez)
|
||||
2010-03-23 Empty value trigger key removal in all the operations (antirez)
|
||||
2010-03-22 Merged gnrfan patches fixing issues 191, 193, 194 (antirez)
|
||||
2010-03-22 Merge branch 'issue_193' of git://github.com/gnrfan/redis (antirez)
|
||||
2010-03-22 Merge branch 'issue_191' of git://github.com/gnrfan/redis (antirez)
|
||||
2010-03-22 Redis master version is now 1.3.7 (antirez)
|
||||
2010-03-19 support for include directive in config parser (Jeremy Zawodny)
|
||||
2010-03-19 Removed a stupid overriding of config values due to a wrong cut&paste (antirez)
|
||||
2010-03-19 VM hash type swappability implemented. Handling of failed pthread_create() call. (antirez)
|
||||
2010-03-19 Solving issue #191 on Google Code: -v and --version should print the version of Redis (Antonio Ognio)
|
||||
2010-03-19 Solves issue #194 on Google Code: --help parameter to redis-srver prints the usage message (Antonio Ognio)
|
||||
2010-03-19 Fixing issue 193 (Antonio Ognio)
|
||||
2010-03-18 increment server.dirty on HDEL (antirez)
|
||||
2010-03-18 Redis 1.3.6 (antirez)
|
||||
2010-03-18 test-redis.tcl dataset digest function Hash support (antirez)
|
||||
2010-03-18 zipmap fix for large values (antirez)
|
||||
2010-03-18 Optimization fixed and re-activated (antirez)
|
||||
2010-03-18 reverted an optimization that makes Redis not stable (antirez)
|
||||
2010-03-18 Fixed redis-cli auth code (antirez)
|
||||
2010-03-17 HDEL fix, an optimization for comparison of objects in hash table lookups when they are integer encoding (antirez)
|
||||
2010-03-17 Version is now 1.3.5 (antirez)
|
||||
2010-03-17 Merged Pietern patch for VM key args helper function. Fixed an obvious bug in the redis-cli passwd auth stuff (antirez)
|
||||
2010-03-17 Merge branch 'aggregates' of git://github.com/pietern/redis (antirez)
|
||||
2010-03-17 Added Authentication to redis-cli.c using -a switch Update usage fixed Makefile to delete redis-check-dump during make clean (root)
|
||||
2010-03-17 HEXISTS and tests implemented (antirez)
|
||||
2010-03-17 More hash tests (antirez)
|
||||
2010-03-17 better HSET test (antirez)
|
||||
2010-03-17 Fixed a bug in HSET, a memory leak, and a theoretical bug in dict.c (antirez)
|
||||
2010-03-17 More Hash tests (antirez)
|
||||
2010-03-13 added preloading keys from VM when using ZINTER or ZUNION (Pieter Noordhuis)
|
||||
2010-03-13 added explicit AGGREGATE [SUM|MIN|MAX] option to ZUNION/ZINTER (Pieter Noordhuis)
|
||||
2010-03-16 HGET fix for integer encoded field against zipmap encoded hash (antirez)
|
||||
2010-03-16 zrevrank support in redis-cli (antirez)
|
||||
2010-03-16 HKEYS / HVALS / HGETALL (antirez)
|
||||
2010-03-16 Solved a memory leak with Hashes (antirez)
|
||||
2010-03-15 pretty big refactoring (antirez)
|
||||
2010-03-15 An interesting refactoring + more expressive internal API (antirez)
|
||||
2010-03-15 Fixed the same problem in ZREVRANK (antirez)
|
||||
2010-03-15 Fixed a ZRANK bug (antirez)
|
||||
2010-03-15 zipmap to hash conversion in HSET (antirez)
|
||||
2010-03-14 max zipmap entries and max zipmap value parameters added into INFO output (antirez)
|
||||
2010-03-14 HDEL and some improvement in DEBUG OBJECT command (antirez)
|
||||
2010-03-14 Append only file support for hashes (antirez)
|
||||
2010-03-13 utility to check rdb files for unprocessable opcodes (Pieter Noordhuis)
|
||||
2010-03-12 A minor fix and a few debug messages removed (antirez)
|
||||
2010-03-12 Applied the replication bug patch provided by Jeremy Zawodny, removing temp file collision after the slave got the dump.rdb file in the SYNC stage (antirez)
|
||||
2010-03-11 Fix for HGET against non Hash type, debug messages used to understand a bit better a corrupted rdb file (antirez)
|
||||
2010-03-09 fix: use zmalloc instead of malloc (Pieter Noordhuis)
|
||||
2010-03-09 Merged zsetops branch from Pietern (antirez)
|
||||
2010-03-09 Merged ZREMBYRANK from Pietern (antirez)
|
||||
2010-03-09 Merged ZREVRANK from Pietern (antirez)
|
||||
2010-03-09 use a struct to store both a dict and its weight for ZUNION and ZINTER, so qsort can be applied (Pieter Noordhuis)
|
||||
2010-03-09 Hash auto conversion from zipmap to hash table, type fixed for hashes, hash loading from disk (antirez)
|
||||
2010-03-09 replaced ZMERGE by ZUNION and ZINTER. note: key preloading by the VM does not yet work (Pieter Noordhuis)
|
||||
2010-03-08 Hashes saving / fixes (antirez)
|
||||
2010-03-08 use ZMERGE as starting point (Pieter Noordhuis)
|
||||
2010-03-07 HSET fixes, now the new pointer is stored back in the object pointer field (antirez)
|
||||
2010-03-07 added ZREVRANK (Pieter Noordhuis)
|
||||
2010-03-06 Fix for replicaiton with over 2GB dump file initial SYNC stage (antirez)
|
||||
2010-03-06 first implementation of HSET/HSET. More work needed (antirez)
|
||||
2010-03-05 zipmaps functions to get, iterate, test for existence. Initial works for Hash data type (antirez)
|
||||
2010-03-04 redis-benchmark now implements Set commands benchmarks (antirez)
|
||||
2010-03-04 zipmap iteration code (antirez)
|
||||
2010-03-04 moved code to delete a single node from a zset to a separate function (Pieter Noordhuis)
|
||||
2010-03-04 rename zslDeleteRange to zslDeleteRangeByScore (to differentiate between deleting using score or rank) (Pieter Noordhuis)
|
||||
2010-03-04 use 1-based rank across zsl*Rank functions consistently (Pieter Noordhuis)
|
||||
2010-03-04 implemented ZREMBYRANK (Pieter Noordhuis)
|
||||
2010-03-04 A fix for initialization of augmented skip lists (antirez)
|
||||
2010-03-04 A fix for an invalid access when VM is disabled (antirez)
|
||||
2010-03-04 Merge branch 'zsl-get-rank' of git://github.com/pietern/redis (antirez)
|
||||
2010-03-04 redis-cli now runs in interactive mode if no command is provided (antirez)
|
||||
2010-03-04 merged memory reduction patch (Pieter Noordhuis)
|
||||
2010-03-04 Now list push commands return the length of the new list, thanks to Gustavo Picon (antirez)
|
||||
2010-03-04 first check if starting point is trivial (head or tail) before applying log(N) search (Pieter Noordhuis)
|
||||
2010-03-04 use rank to find starting point for ZRANGE and ZREVRANGE (Pieter Noordhuis)
|
||||
2010-03-04 lookup rank of a zset entry in a different function (Pieter Noordhuis)
|
||||
2010-03-04 SUBSTR fix for integer encoded vals (antirez)
|
||||
2010-03-04 fix ZRANK (realize that rank is 1-based due to the skip list header) (Pieter Noordhuis)
|
||||
2010-03-03 initial implementation of SUBSTR (antirez)
|
||||
2010-03-03 TODO updated (antirez)
|
||||
2010-03-03 fpurge call removed from redis-cli (antirez)
|
||||
2010-03-03 ZRANK stress tester (antirez)
|
||||
2010-03-03 use less memory as element->span[0] will always be 1; any level 0 skip list is essentially a linked list (Pieter Noordhuis)
|
||||
2010-03-03 rank is very unlikely to overflow integer range (Pieter Noordhuis)
|
||||
2010-03-03 x->backward never equals zsl->header (Pieter Noordhuis)
|
||||
2010-03-03 initial implementation for augmented zsets and the zrank command (Pieter Noordhuis)
|
||||
2010-03-03 zipampDel() implemented (antirez)
|
||||
2010-03-03 added quit and exit commands to redis-cli in order to quit the interactive mode (antirez)
|
||||
2010-03-03 Merge remote branch 'djanowski/interactive' (antirez)
|
||||
2010-03-02 Add support for MULTI/EXEC. (Damian Janowski & Michel Martens)
|
||||
2010-03-02 Remove trailing newline in interactive mode. (Damian Janowski & Michel Martens)
|
||||
2010-03-02 minor fix for a Linux warning (antirez)
|
||||
2010-03-02 Add interactive mode to redis-cli. (Michel Martens & Damian Janowski)
|
||||
2010-03-02 Better to increment the version minor number when a VM bug is fixed... it will be simpler to understand what's going on when users will report problems with the INFO trace. (antirez)
|
||||
2010-03-02 Fixed a subtle VM bug... was not flushing the buffer so the child process read truncated data (antirez)
|
||||
2010-03-01 KEYS now returns a multi bulk reply (antirez)
|
||||
2010-02-27 Add DISCARD command to discard queued MULTI commands. (antirez)
|
||||
2010-03-01 Swappability bug due to a typo fixed thanks to code review by Felix Geisendörfer @felixge (antirez)
|
||||
2010-02-28 minor fixes for zipmap.c (antirez)
|
||||
2010-02-27 first zipmap fix of a long sequence in the days to come ;) (antirez)
|
||||
2010-02-27 initial zipmap.c implementation (antirez)
|
||||
2010-02-27 Bug #169 fixed (BLOP/BRPOP interrupted connections are not cleared from the queue) (antirez)
|
||||
2010-02-22 Fixed 32bit make target to work on Linux out of the box (antirez)
|
||||
2010-02-19 A problem with replication with multiple slaves connectiong to a single master fixed. It was due to a typo, and reported on github by the user micmac. Also the copyright year fixed from many files. (antirez)
|
||||
2010-02-10 Saner VM defaults for redis.conf (antirez)
|
||||
2010-02-09 VM now is able to block clients on swapped keys for all the commands (antirez)
|
||||
2010-02-07 ZCOUNT and ZRANGEBYSCORE new tests (antirez)
|
||||
2010-02-07 ZRANGEBYSCORE now supports open intervals, prefixing double values with a open paren. Added ZCOUNT that can count the elements inside an interval of scores, this supports open intervals too (antirez)
|
||||
2010-02-07 WITHSCORES in ZRANGEBYSCORE thanks to Sam Hendley (antirez)
|
||||
2010-02-06 Added "withscores" option to zrangebyscore command. Based on withscores support in zrange function, ugliest part was the argument parsing to handle using it with the limit option. (Sam Hendley)
|
||||
2010-02-06 DEBUG OBJECT provide info about serialized object length even when VM is disabled (antirez)
|
||||
2010-02-06 multi bulk requests in redis-benchmark, default fsync policy changed to everysec, added a prefix character for DEBUG logs (antirez)
|
||||
2010-02-04 APPEND tests (antirez)
|
||||
2010-02-04 APPEND command (antirez)
|
||||
2010-02-02 Faster version of the function hashing possibly encoded objects, leading to a general speed gain when working with Sets of integers (antirez)
|
||||
2010-02-02 faster Set loading time from .rdb file resizing the hash table to the right size before loading elements (antirez)
|
||||
2010-02-02 Log time taken to load the DB at startup, in seconds (antirez)
|
||||
2010-01-31 Fixed VM corruption due to child fclosing the VM file directly or indirectly calling exit(), now replaced with _exit() in all the sensible places. Masked a few signals from IO threads. (antirez)
|
||||
2010-01-28 loading side of the threaded VM (antirez)
|
||||
2010-01-26 TODO cahnges (antirez)
|
||||
2010-01-23 Fixed memory human style memory reporting, removed server.usedmemory, now zmalloc_used_memory() is used always. (antirez)
|
||||
2010-01-22 VM tuning thanks to redis-stat vmstat. Now it performs much better under high load (antirez)
|
||||
2010-01-21 Changelog updated (antirez)
|
||||
2010-01-21 REDIS_MAX_COMPLETED_JOBS_PROCESSED is now in percentage, not number of jobs. Moved a debugging message a few lines forward as it was called where a few logged parameters where invalid, leading to a crash (antirez)
|
||||
2010-01-20 fixed a deadlock caused by too much finished processes in queue so that I/O clients writing to the wirte side of the pipe used to awake the main thread where blocking. Then a BGSAVE started waiting for the last active thread to finish, condition impossible because all the I/O threads where blocking on threads. Takes this as a note to myself... (antirez)
|
||||
2010-01-20 ae.c event loop does no longer support exception notifications, as they are fully pointless. Also a theoretical bug that never happens in practice fixed. (antirez)
|
||||
2010-01-19 commercial tools stuff removed from the Redis makefile. cotools are now migrated into a different repos (antirez)
|
||||
2010-01-19 removed a bug in the function to cancel an I/O job (antirez)
|
||||
2010-01-17 static symbols update (antirez)
|
||||
2010-01-16 removed support for REDIS_HELGRIND_FRIENDLY since Helgrind 3.5.0 is friendly enough even with many threads created and destroyed (antirez)
|
||||
2010-01-15 now redis-cli understands -h (antirez)
|
||||
2010-01-15 Create swap file only if not exists (antirez)
|
||||
2010-01-15 I hate warnings (antirez)
|
||||
2010-01-15 fixed a minor memory leak in configuration file parsing (antirez)
|
||||
2010-01-15 minor fix (antirez)
|
||||
2010-01-15 support for named VM swap file. Fixed a few important interaction issues between the background saving processes and IO threads (antirez)
|
||||
2010-01-15 fix for the just added new test (antirez)
|
||||
2010-01-15 useless debugging messages removed (antirez)
|
||||
2010-01-15 new test added (antirez)
|
||||
2010-01-15 thread safe zmalloc used memory counter (antirez)
|
||||
2010-01-15 A define to make Redis more helgrind friendly (antirez)
|
||||
2010-01-15 removed a few races from threaded VM (antirez)
|
||||
2010-01-14 Fixed a never experienced, theoretical bug that can actually happen in practice. Basically when a thread is working on a I/O Job we need to wait it to finish before to cancel the Job in vmCancelThreadedIOJob(), otherwise the thread may mess with an object that is being manipulated by the main thread as well. (antirez)
|
||||
2010-01-14 Set the new threads stack size to a LZF friendly amount (antirez)
|
||||
2010-01-13 access to already freed job structure fixed by statements reoredering (antirez)
|
||||
2010-01-13 removed a useless debugging message (antirez)
|
||||
2010-01-13 Wait zero active threads condition before to fork() for BGSAVE or BGREWRITEAOF (antirez)
|
||||
2010-01-13 list API is now thread safe (antirez)
|
||||
2010-01-13 minor TODO and debugging info changes (antirez)
|
||||
2010-01-12 support for blocking VM in config file (antirez)
|
||||
2010-01-12 more non blocking VM changes (antirez)
|
||||
2010-01-12 fix for test #11 (antirez)
|
||||
2010-01-12 a few more stuff in INFO about VM. Test #11 changed a bit in order to be less lame (antirez)
|
||||
2010-01-12 Added a define to configure how many completed IO jobs the handler should process at every call. (antirez)
|
||||
2010-01-11 Fixed a bug in the IO Job canceling funtion (antirez)
|
||||
2010-01-11 more steps towards a working non blocking VM (antirez)
|
||||
2010-01-11 converted random printfs in debug logs (antirez)
|
||||
2010-01-11 removed a bug introduced with non blocking VM (antirez)
|
||||
2010-01-11 a few non blocking VM bugs fixed (antirez)
|
||||
2010-01-11 More work on non-blocking VM. Should work in a few days (antirez)
|
||||
2010-01-11 More threaded I/O VM work + Redis init script (antirez)
|
||||
2010-01-10 more work on VM threaded I/O. Still nothing of usable (antirez)
|
||||
2010-01-09 non-blocking VM data structures, just a start (antirez)
|
||||
2010-01-08 used_memory_human added to INFO output. Human readable amount of memory used. (antirez)
|
||||
2010-01-07 Now DEBUG OBJECT plays well with swapped out objects (antirez)
|
||||
2010-01-07 fflush VM swap file after object swapping (antirez)
|
||||
2010-01-07 added the fmacros to enable support for fseeko() lseeko() with 64bit off_t (antirez)
|
||||
2010-01-07 VM now swaps objects out while loading datasets not fitting into vm-max-memory bytes of RAM (antirez)
|
||||
2010-01-07 added process id information in INFO (antirez)
|
||||
2010-01-06 vm-enabled set to no by default in redis.conf (antirez)
|
||||
2010-01-06 a new default redis.conf (antirez)
|
||||
2010-01-06 VM stats in INFO command (antirez)
|
||||
2010-01-06 Introduced a new log verbosity level, so now DEBUG is really for debugging. Refactored a bit maxmemory. When virtual memory is short in RAM free the objects freelist as well as swapping things out. (antirez)
|
||||
2010-01-05 fixed a bug in bgsave when VM is off but still it was testing for obj->storage field (antirez)
|
||||
2010-01-05 converted a few calls to assert() => redisAssert() to print stack trace (antirez)
|
||||
2010-01-05 BGREWRITEAOF now works with swapping on (antirez)
|
||||
2010-01-05 A first fix for SET key overwrite (antirez)
|
||||
2010-01-05 SAVE now works with VM (antirez)
|
||||
2010-01-05 swapping algorithm a bit more aggressive under low memory (antirez)
|
||||
2010-01-05 basic VM mostly working! (antirez)
|
||||
2010-01-05 New object field (one of the unused bytes) to hold the type of the swapped out value object in key objects (antirez)
|
||||
2010-01-05 VM internals bugfixes, set 1 (antirez)
|
||||
2010-01-05 load key from swap on key lookup (antirez)
|
||||
2010-01-05 more object-level VM primitives (antirez)
|
||||
2010-01-05 Redis objects swapping / loading (antirez)
|
||||
2010-01-05 rdbLoadObject() as a separated function to load objects from disk. Dropped support for RDB version 0, I guess no longer has this legacy DBs around (antirez)
|
||||
2010-01-04 VM low level pages handling (antirez)
|
||||
2010-01-04 vm swap file creation, and some basic configuration (antirez)
|
||||
2010-01-04 version marked 1.3.2 (antirez)
|
||||
2010-01-04 saving code refactored a bit, added a function returning the number of bytes an object will use on disk (antirez)
|
||||
2010-01-02 Now the PUSH side of RPOPLPUSH is able to unblock clients blocked on BLPOP (antirez)
|
||||
2010-01-02 Version is now 1.3.1 (antirez)
|
||||
2010-01-02 New vararg BLPOP able to block against multiple keys (antirez)
|
||||
2009-12-29 fixed a problem with BLPOP timeout of zero, now it blocks forever (antirez)
|
||||
2009-12-29 BLPOP timeouts implemented (antirez)
|
||||
2009-12-29 first working implementation of BLPOP and BRPOP, still everything is to test well (antirez)
|
||||
2009-12-29 a few more fixes, still broken (antirez)
|
||||
2009-12-29 First fix, still broken (antirez)
|
||||
2009-12-29 minor fix for Linux 64 bit (antirez)
|
||||
2009-12-29 not yet working BLPOP implementation (antirez)
|
||||
2009-12-27 AOFSYNC removed, got a better idea... (antirez)
|
||||
2009-12-27 AOFSYNC command implemented (antirez)
|
||||
2009-12-27 Version changed to 1.3.0, welcome to the new unstable (antirez)
|
||||
2009-12-27 Now MULTI returns +OK as well (antirez)
|
||||
2009-12-27 MULTI/EXEC first implementation (antirez)
|
||||
2009-12-24 Fixed a minor bug in GETSET, now the SET part is not performed if the GET fails because the key does not contain a string value (antirez)
|
||||
2009-12-23 html doc readded (antirez)
|
||||
2009-12-23 ZRANGE WITHSCORES test added (antirez)
|
||||
2009-12-23 version is now 1.1.94 (antirez)
|
||||
2009-12-23 Add the command name in the unknown command error message. (antirez)
|
||||
2009-12-22 ZRANGE, ZREVRANGE now support WITHSCORES options (antirez)
|
||||
2009-12-22 html docs update (ZINCRBY added) (antirez)
|
||||
2009-12-18 TODO list update (antirez)
|
||||
2009-12-18 the pipelining test was ran against DB 1 for error, now it runs on DB 9 like all the other tests (antirez)
|
||||
2009-12-18 still more tests (antirez)
|
||||
2009-12-18 SORT STORE test added (antirez)
|
||||
2009-12-18 Now SORT returns an empty bulk reply if the key does not exist (antirez)
|
||||
2009-12-18 modified a bit the ZREVRANGE test to cover a few lines of code more (antirez)
|
||||
2009-12-18 SHUTDOWN now does the right thing when append only is on, that is, fsync instead to save the snapshot. (antirez)
|
||||
2009-12-18 Added a missing server.dirty increment in a non critical place, added more tests (antirez)
|
||||
2009-12-18 LTRIM stress testing test added (antirez)
|
||||
2009-12-18 LTRIM now returns +OK against non existing keys. More tests in test-redis.tcl (antirez)
|
||||
2009-12-18 added sdstoupper() declaration in sds.h (antirez)
|
||||
2009-12-18 Fixed sds.c bug #124 (antirez)
|
||||
2009-12-16 LZF compression re-enabled by default, but with INIT_HTAB set to 0 to avoid the very costly memset initialization. Note that with this option set valgrind will output some false positive about lzf_c.c (antirez)
|
||||
2009-12-16 lzf compression switched off by default now, with config file option to enable it in redis.conf (antirez)
|
||||
2009-12-16 Regression for epoll bug in redis-test.tcl, version is now 1.1.93 (antirez)
|
||||
2009-12-16 Fixed a lame epoll issue (antirez)
|
||||
2009-12-15 html doc updated (antirez)
|
||||
2009-12-15 version is now 1.1.92 (antirez)
|
||||
2009-12-15 Two important fixes to append only file: zero length values and expires. A pretty neat new test to check consistency of randomly build datasets against snapshotting and AOF. (antirez)
|
||||
2009-12-15 debug loadaof implemented in order to add more consistency tests in test-redis.tcl (antirez)
|
||||
2009-12-15 Added a new test able to stress a lot the snapshotting engine (antirez)
|
||||
2009-12-15 Unified handling of empty queries with normal queries. (antirez)
|
||||
2009-12-15 Fixed some subtle bug in the command processing code almost impossible to spot in the real world, thanks to gcov (antirez)
|
||||
2009-12-15 Regression test for SINTERSTORE added (antirez)
|
||||
2009-12-15 Fixed issue #121 (antirez)
|
||||
2009-12-14 a few more tests and ability to run a specific test in test-redis.tcl (antirez)
|
||||
2009-12-13 Changed the reply of BGSAVE and BGREWRITEAOF from +OK to a more meaningful message that makes the user aware of an operation that just started and is not yet finished. (antirez)
|
||||
2009-12-13 Set the master->slave logical client as authenticated on creation, so that if the slave requires a password replication works anyway (antirez)
|
||||
2009-12-13 TODO update (antirez)
|
||||
2009-12-12 bgrewriteaof_in_progress added to INFO (antirez)
|
||||
2009-12-12 TODO list modified. What's planned for 1.4 is now written in the stone ;) (antirez)
|
||||
2009-12-12 better handling of non blocking connect on redis-benchmark: EPIPE on read does not print an error message now (antirez)
|
||||
2009-12-11 some change to redis-sha1.rb utility to make it more robust against non-meaningful changes in the dataset (antirez)
|
||||
2009-12-10 redis-sha1.rb utility updated (antirez)
|
||||
2009-12-10 a bit more verbose -ERR wrong number o arguments error, now gives info about the command name causing the error (antirez)
|
||||
2009-12-10 TODO change and minor SETNX optimization (antirez)
|
||||
2009-12-06 in rdbLoadDoubleValue now the buffer is nul terminated correctly. Thanks valgrind. (antirez)
|
||||
2009-12-06 printf format warnings fixed by casting (antirez)
|
||||
2009-12-06 Regression tests for SETNX and MSETNX bugs added (antirez)
|
||||
2009-12-06 SETNX and MSETNX now respect the delete-on-write operation of EXPIREing keys (antirez)
|
||||
2009-12-06 Fixed daemonization when using kqueue/kevent. Now the server initialization is performed *after* the daemonization (antirez)
|
||||
2009-12-05 more HTML doc changes (antirez)
|
||||
2009-12-05 HTML doc update (antirez)
|
||||
2009-12-05 a few redis-cli format specified fixed (antirez)
|
||||
2009-12-05 use __attribute__ format in sdscatprintf() when the compiler is GCC. Fixed format bugs resulting from the new warnings. (antirez)
|
||||
2009-12-01 TODO update (antirez)
|
||||
2009-12-01 compilation problem on 64bit mac os x 10.5 possibly fixed (antirez)
|
||||
2009-12-01 virtual memory design doc typos (antirez)
|
||||
2009-12-01 design documents added to the project (antirez)
|
||||
2009-11-30 Fixed issued #85 (getDecodedObject: Assertion 1 != 1 failed. While sorting a set), added a smarter assert() function to dump the stacktrace, provided a macro to initalize Redis objects on the stack to avoid this kind of bugs. (antirez)
|
||||
2009-11-30 fixed a subtle bug in redis-cli not having visible effects (antirez)
|
||||
2009-11-29 TODO updated (antirez)
|
||||
2009-11-29 Version chagned to 1.100, also known as the first first 2.0 beta version (antirez)
|
||||
2009-11-29 more tests in test-redis.tcl, some minor fix (antirez)
|
||||
2009-11-29 SORT support for sorted sets (antirez)
|
||||
2009-11-28 Implemented LIMIT option in ZRANGEBYSCORE. We now enter feature-freeze (antirez)
|
||||
2009-11-28 Changelog updated (antirez)
|
||||
2009-11-28 html doc updated (antirez)
|
||||
2009-11-28 enable kqueue/kevent only for Mac OS X 10.6.x as it seems that 10.5.x has a broken implementation of this syscalls. (antirez)
|
||||
2009-11-28 TODO updated (antirez)
|
||||
2009-11-28 ZRANGEBYSCORE fuzzy test (antirez)
|
||||
2009-11-28 ZRANGEBYSCORE memory leak fixed, ZRANGEBYSCORE initial test added (antirez)
|
||||
2009-11-28 INFO refactored. Stack trace on memory corruption now dumps the same information as the INFO command (antirez)
|
||||
2009-11-28 ifdefs added to use kevent on Free Open and Net BSD as well. INFO and ae.c modified in order to report the multiplexing API in use (antirez)
|
||||
2009-11-28 Enabled object encoding for multiple keys in MSET. Added a test for memory leaks in test-redis.tcl when running on Mac OS X (antirez)
|
||||
2009-11-28 Merge branch 'kqueue' of git://github.com/mallipeddi/redis (antirez)
|
||||
2009-11-28 Changes to TODO list, commented a function in redis.c (antirez)
|
||||
2009-11-28 Added support for kqueue. (Harish Mallipeddi)
|
||||
2009-11-27 TODO updated (antirez)
|
||||
2009-11-26 zero length bulk data reading fixed in loadAppendOnlyFile() (antirez)
|
||||
2009-11-26 append only file fixes (antirez)
|
||||
2009-11-26 log rebuilding, random refactoring, work in progress please wait for an OK commit before to use this version (antirez)
|
||||
2009-11-24 DEBUG RELOAD implemented, and test-redis.tcl modified to use it to check for persistence consistency. (antirez)
|
||||
2009-11-24 Redis version set to 1.07 (antirez)
|
||||
2009-11-24 sorted sets saving fixed (antirez)
|
||||
2009-11-24 minor TODO change (antirez)
|
||||
2009-11-24 minor fix to avoid a false valgrind warning. (antirez)
|
||||
2009-11-23 epoll support enabled by default for Linux builds (antirez)
|
||||
2009-11-23 epoll module for ae.c implemented. Some more testing needed (antirez)
|
||||
2009-11-23 commented the HAVE_EPOLL test in config.h to allow compilation under Linux now that the epoll module is still missing (antirez)
|
||||
2009-11-23 ae_select module added (antirez)
|
||||
2009-11-23 ae.c now supports multiple polling API modules, even if only ae_select.c is implemented currently. Also adding and removing an event is now O(1). (antirez)
|
||||
2009-11-23 ae.c initial refactoring for epoll implementation (antirez)
|
||||
2009-11-21 version incremented up to 1.06 (antirez)
|
||||
2009-11-21 TODO aesthetic changes (antirez)
|
||||
2009-11-21 TODO updated with plans up to 1.5 (antirez)
|
||||
2009-11-21 SRANDMEMBER test (antirez)
|
||||
2009-11-21 Fixed a SORT memory leak that should never happen in practice (antirez)
|
||||
2009-11-21 SORT GET # implemented, with a test (antirez)
|
||||
2009-11-21 EXPIREAT test (antirez)
|
||||
2009-11-20 EXPIRE tests (antirez)
|
||||
2009-11-20 more RPOPLPUSH tests (antirez)
|
||||
2009-11-20 RPOPLPUSH tests added (antirez)
|
||||
2009-11-20 ZINCRBY return value fixed (antirez)
|
||||
2009-11-20 ZINCRSCOREBY => ZINCRBY (antirez)
|
||||
2009-11-19 ZINCRSCOREBY implemented (antirez)
|
||||
2009-11-19 writev() finally uncommented again (antirez)
|
||||
2009-11-19 redis-benchmark hopefully last bug with multi bulk reply fixed (antirez)
|
||||
2009-11-19 debug mode in redis-bench (antirez)
|
||||
2009-11-19 Use writev(2) if glue output buffers is disabled (antirez)
|
||||
2009-11-19 benchmark.c fixes (antirez)
|
||||
2009-11-18 more experiments with long replies, glue output buffer, and writev. (antirez)
|
||||
2009-11-18 benchmarking with different number of LRANGE elements. Ability to change the glue output buffer limit by #define (antirez)
|
||||
2009-11-18 more writev tests/work (antirez)
|
||||
2009-11-18 redis-benchmark multi bulk reply support hopefully fixed (antirez)
|
||||
2009-11-17 support for writev implemented but currently ifdef-ed in order to understan why I can't see the improvements expected. Btw code provided by Stefano Barbato (antirez)
|
||||
2009-11-17 multi-bulk reply support for redis-bench, and as a result LRANGE is not tested, providing some number for the tuning of multi-bulk requests performances server-side (antirez)
|
||||
2009-11-12 Solaris fix thanks to Alan Harder (antirez)
|
||||
2009-11-12 Merge git://github.com/ianxm/redis (antirez)
|
||||
2009-11-12 ZSCORE fixed, now returns NULL on missing key or missing element (antirez)
|
||||
2009-11-12 Redis test will not fail the SAVE test even if a background save is in progress (antirez)
|
||||
2009-11-12 LPOPPUSH renamed into RPOPLPUSH (antirez)
|
||||
2009-11-11 can select db num (ian)
|
||||
2009-11-11 Workaround for test-redis.tcl and Tcl 8.4.x about ZSCORE test (antirez)
|
||||
2009-11-11 Removed a long time warning compiling with recent GCC on Linux (antirez)
|
||||
2009-11-11 TODO updated (antirez)
|
||||
2009-11-11 LPUSHPOP first implementation (antirez)
|
||||
2009-11-10 Tcl script, make target, and redis.c changes to build the static symbol table automagically (antirez)
|
||||
2009-11-10 Implemented a much better lazy expiring algorithm for EXPIRE (antirez)
|
||||
2009-11-10 Fixed issue 92 in redis: redis-cli (nil) return value lacks CR/LF (antirez)
|
||||
2009-11-10 Minor TODO change with new expiring algorithm description. New expiring algorithm moved since it'll go in 1.1 (antirez)
|
||||
2009-11-04 redis-test is now a better Redis citizen, testing everything against DB 9 and 10 and only if this DBs are empty. (antirez)
|
||||
2009-11-04 fixed a refcounting bug with SORT ... STORE leading to random crashes (root)
|
||||
2009-11-04 masterauth option merged, thanks to Anthony Lauzon (antirez)
|
||||
2009-11-03 ZSets double to string serialization fixed (antirez)
|
||||
2009-11-03 client-libraries directory readded (antirez)
|
||||
2009-11-03 redis.tcl put at toplevel since it's uesd for the test-redis.tcl script (antirez)
|
||||
2009-11-03 client libs removed from Redis git (antirez)
|
||||
2009-11-03 redis-cli now accepts a -r (repeat) switch. Still there is a memory leaks to fix (antirez)
|
||||
2009-11-01 TODO updated again (antirez)
|
||||
2009-11-01 TODO updated (antirez)
|
||||
2009-11-01 redis-cli now makes clear when the returned string is an integer (antirez)
|
||||
2009-11-01 SORT STORE option (antirez)
|
||||
2009-11-01 now Redis prints DB stats just after the startup without to wait a second for the first report (antirez)
|
||||
2009-11-01 another fix for append only mode, now read-only operations are not appended (antirez)
|
||||
2009-11-01 appendfsync parsing in config file fixed. If you benchmarked Redis against different appendfsync options is time to try again ;) (antirez)
|
||||
2009-11-01 append only file loading fixed (antirez)
|
||||
2009-11-01 first version of append only file loading -- STILL BROKEN don't use it (antirez)
|
||||
2009-10-31 Fixed Issue 83:Using TYPE on a zset results in a malformed response from the Redis server (antirez)
|
||||
2009-10-31 Fixed compilation on Linux (antirez)
|
||||
2009-10-30 append only mode is now able to translate EXPIRE into EXPIREAT transparently (antirez)
|
||||
2009-10-30 appendfsync is now set to NO by default (antirez)
|
||||
2009-10-30 support for appendonly mode no, always, everysec (antirez)
|
||||
2009-10-30 first fix for append only mode (antirez)
|
||||
2009-10-30 Initial implementation of append-only mode. Loading still not implemented. (antirez)
|
||||
2009-10-30 EXPIRE behaviour changed a bit, a negative TTL or an EXPIREAT with unix time in the past will now delete the key. It seems saner to me than doing nothing. (antirez)
|
||||
2009-10-30 EXPIREAT implemented, will be useful for the append-only mode (antirez)
|
||||
2009-10-29 Fixed Issue 74 (ERR just returned on invalid password), now the error message is -ERR invalid password. (antirez)
|
||||
2009-10-29 Fixed issue 72 (SLAVEOF shutdowns redis-server on malformed reply) (antirez)
|
||||
2009-10-29 Fixed issue 77 (Incorrect time in log files) thanks to youwantalex (antirez)
|
||||
2009-10-29 Fixed Issue 76 (redis-server crashes when it can't connect to MASTER and client connects to SLAVE) (antirez)
|
||||
2009-10-29 ZREMRANGEBYSCORE implemented. Remove a range of elements with score between min and max (antirez)
|
||||
2009-10-28 TODO changes and mostly theoretical minor skiplist change (antirez)
|
||||
2009-10-28 ZLEN renamed ZCARD for consistency with SCARD (antirez)
|
||||
2009-10-27 TODO reworked to reflect the real roadmap (antirez)
|
||||
2009-10-27 Fix for 'make 32bit' (antirez)
|
||||
2009-10-27 a fix for the solaris fix itself ;) (antirez)
|
||||
2009-10-27 More Solaris fixes (antirez)
|
||||
2009-10-27 A lot of ZSETs tests implemented, and a bug fixed thanks to this new tests (antirez)
|
||||
2009-10-27 zmalloc Solaris fixes thanks to Alan Harder (antirez)
|
||||
2009-10-27 ZSCORE implemented (antirez)
|
||||
2009-10-26 fix for ZRANGEBYSCORE (antirez)
|
||||
2009-10-26 ZRANGEBYSCORE implemented. Redis got range queries! (antirez)
|
||||
2009-10-26 A trivial change makes the new implementation O(log(N)) instead of O(log(N))+O(M) when there are M repeated scores! (antirez)
|
||||
2009-10-26 ZSET now saved on disk like any other type (antirez)
|
||||
2009-10-26 double serialization routines implemented (antirez)
|
||||
2009-10-26 ZSETs random fixes. Now the implementation appears to be pretty stable (antirez)
|
||||
2009-10-26 another leak fixed. Can't find more for now, but still a bug in ZSETs to fix (antirez)
|
||||
2009-10-26 ZSETs memory leak #1 solved, another one missing (antirez)
|
||||
2009-10-26 Fix for skiplists backward link (antirez)
|
||||
2009-10-26 Merged Solaris patches provided by Alan Harder (antirez)
|
||||
2009-10-26 backward support to skiplists for ZREVRANGE, still broken, committing since I've to merge the Solaris patches (antirez)
|
||||
2009-10-26 TODO updated (antirez)
|
||||
2009-10-26 ZREM implemented (antirez)
|
||||
2009-10-24 fix for ZADD in score update mode (antirez)
|
||||
2009-10-24 some work on ZADD against existing element (score update), still broken... (antirez)
|
||||
2009-10-23 zrange now starts to work. zadd still does not support update and will crash or leak or b000mmmmm (antirez)
|
||||
2009-10-23 zrange initial hack (not working for now) (antirez)
|
||||
2009-10-23 first skiplist fix, courtesy of valgrind (antirez)
|
||||
2009-10-23 zset symbols added to stack trace code. ZSets will simply crash at the moment (antirez)
|
||||
2009-10-23 more work on ZSETs and a new make target called 32bit to build i386 binaries on mac os x leopard (antirez)
|
||||
2009-10-23 initial skiplist implementation. Most memory checks removed and zmalloc() modified to fail with an error message and abort. Anyway Redis is not designed to recover from out of memory conditions. (antirez)
|
||||
2009-10-23 Fixed compilation in mac os x snow leopard when compiling a 32 bit binary. (antirez)
|
||||
2009-10-22 version incremented to 1.050 to distinguish from 1.001 stable and next stable versions with minor fixes (antirez)
|
||||
2009-10-21 TODO updated (antirez)
|
||||
2009-10-21 SRANDMEMBER added (antirez)
|
||||
2009-10-20 Imporant bug leading to data corruption fixed (NOT affecting stable distribution), Tcl client lib MSET/MSETNX implementation fixed, Added new tests for MSET and MSETNX in test-redis.tcl (antirez)
|
||||
2009-10-17 added multi-bulk protocol support to redis-cli and support for MSET and MSETNX (antirez)
|
||||
2009-10-17 MSET fixed, was not able to replace keys already set for a stupid bug (antirez)
|
||||
2009-10-16 some dead code removed (antirez)
|
||||
2009-10-16 multi bulk input protocol fixed (antirez)
|
||||
2009-10-16 MSET and MSETNX commands implemented (antirez)
|
||||
2009-10-07 undoed all the sds hacking that lead just to random bugs and no memory saving ;) (antirez)
|
||||
2009-10-07 initial multi-bulk query protocol, this will allow MSET and other interesting features. (antirez)
|
||||
2009-10-03 benchmark now outputs the right command line to shorten the TIME_WAIT interval on Mac OS X when keep alive is set (antirez)
|
||||
2009-10-02 Issue 69 fixed. Object integer encoding now works with replication and MONITORing again. (antirez)
|
||||
2009-09-18 LREM fixed, used to crash since the new object integer encoding is on the stage (antirez)
|
||||
2009-09-17 maxmemory didn't worked in 64 systems for values > 4GB since it used to be an unsigned int. Fixed (antirez)
|
||||
2009-09-10 incremented version number to 1.001, AKA Redis edge is no longer stable... (antirez)
|
||||
2009-09-10 in-memory specialized object encoding (for now 32 signed integers only) (antirez)
|
||||
2009-09-03 Latest doc changes for 1.0 (antirez)
|
||||
2009-09-03 Redis 1.0.0 release (antirez)
|
||||
2009-09-02 Redis version pushed to 1.0 (antirez)
|
||||
2009-09-02 Ruby client lib updated to the latest git version (antirez)
|
||||
2009-09-02 update-scala-client script added (antirez)
|
||||
2009-09-02 Scala client added thanks to Alejanro Crosa (antirez)
|
||||
2009-09-02 QuickStart added (antirez)
|
||||
2009-09-01 Fixed crash with only space and newline as command (issue 61), thanks to a guy having as nick "fixxxerrr" (antirez)
|
||||
2009-08-11 TODO list modified (antirez)
|
||||
2009-07-24 more snow leopard related fixes (for 32bit systems) (antirez)
|
||||
2009-07-24 fixed compilation with Snow Leopard, thanks to Lon Baker for providing SSH access to Snow Leopard box (antirez)
|
||||
2009-07-22 Fixed NetBSD compile problems (antirez)
|
||||
2009-07-17 now the size of the shared pool can be really modified via config, also the number of objects in the sharing pool is logged when the log level is set to debug. Thanks to Aman Gupta (antirez)
|
||||
2009-07-05 added utils/redis-copy.rb, a script that is able to copy data from one Redis server to another one on the fly. (antirez)
|
||||
2009-07-04 Applied three different patches thanks to Chris Lamb, one to fix compilation and get the IP register value on Linux IA64 and other systems. One in order to log the overcommit problem on the logs instead of the standard output when Redis is demonized. The latest in order to suggest a more consistent way in order to switch to 1 the memory overcommit Linux feature. (antirez)
|
||||
2009-07-03 bugfix: EXPIRE now propagates to the Slave. (antirez)
|
||||
2009-06-16 Redis version modified to 0.900 (antirez)
|
||||
2009-06-16 update-ruby-client script already points to ezmobius repo (antirez)
|
||||
2009-06-16 client libraries updated (antirez)
|
||||
2009-06-16 Redis release candidate 1 (antirez)
|
||||
2009-06-16 Better handling of background saving process killed or crashed (antirez)
|
||||
2009-06-14 number of keys info in INFO command thanks to Diego Rosario Brogna (antirez)
|
||||
2009-06-14 SPOP documented (antirez)
|
||||
2009-06-14 Clojure library thanks to Ragnar Dahlén (antirez)
|
||||
2009-06-10 It is now possible to specify - as config file name to read it from stdin (antirez)
|
||||
2009-06-10 sync with jodosha redis-rb (antirez)
|
||||
2009-06-10 Redis-rb sync (antirez)
|
||||
2009-06-10 max inline request raised again to 1024*1024*256 bytes (antirez)
|
||||
2009-06-10 max bytes in an inline command raised to 1024*1024 bytes, in order to allow for very large MGETs and still protect from client crashes (antirez)
|
||||
2009-06-08 SPOP implemented. Hash table resizing for Sets and Expires too. Changed the resize policy to play better with RANDOMKEY and SPOP. (antirez)
|
||||
2009-06-07 some minor changes to the backtrace code (antirez)
|
||||
2009-06-07 enable backtrace capabilities only for Linux and MacOSX (antirez)
|
||||
2009-06-07 Dump a backtrace on sigsegv/sigbus, original coded thanks to Diego Rosario Brogna, modified in order to work on different OSes and to enhance reliability (antirez)
|
||||
2009-06-06 Merge git://github.com/dierbro/redis (antirez)
|
||||
2009-06-06 add more output (hrothgar)
|
||||
2009-06-06 store static function pointer for a useful stack trace (hrothgar)
|
||||
2009-06-06 TODO updated (antirez)
|
||||
2009-06-06 Makefile dependencies updated (antirez)
|
||||
2009-06-05 Avoid a busy loop while sending very large replies against very fast links, this allows to be more responsive with other clients even under a KEY * against the loopback interface (antirez)
|
||||
2009-06-05 Kill the background saving process before performing SHUTDOWN to avoid races (antirez)
|
||||
2009-06-05 LREM now returns :0 for non existing keys (antirez)
|
||||
2009-06-05 - put some order in code - better output (hrothgar)
|
||||
2009-06-05 added config.h for #ifdef business isolation, added fstat64 for Mac OS X (antirez)
|
||||
2009-06-04 remove die() :-) (hrothgar)
|
||||
2009-06-04 add compile options to debug (hrothgar)
|
||||
2009-06-04 initial commit print stack trace (hrothgar)
|
||||
2009-06-04 initial commit print stack trace (hrothgar)
|
||||
2009-06-04 macosx specific zmalloc.c, uses malloc_size function in order to avoid to waste memory and time to put an additional header (antirez)
|
||||
2009-06-04 DEBUG OBJECT implemented (antirez)
|
||||
2009-06-04 backtrace support removed: unreliable stack trace :( (antirez)
|
||||
2009-06-04 initial backtrace dumping on sigsegv/sigbus + debug command (antirez)
|
||||
2009-06-03 Python lib updated (antirez)
|
||||
2009-06-03 shareobjectspoolsize implemented in reds.conf, in order to control the pool size when object sharing is on (antirez)
|
||||
2009-05-30 Erlang client updated (antirez)
|
||||
2009-05-30 Python client library updated (antirez)
|
||||
2009-05-29 Redis-rb minor bool convertion fix (antirez)
|
||||
2009-05-29 ruby library client is not Redis-rb merged with RubyRedis "engine" by Brian McKinney (antirez)
|
||||
2009-05-28 __P completely removed from pqsort.c/h (antirez)
|
||||
2009-05-28 another minor fix for Solaris boxes (antirez)
|
||||
2009-05-28 minor fix for Solaris boxes (antirez)
|
||||
2009-05-28 minor fix for Solaris boxes (antirez)
|
||||
2009-05-27 maxmemory implemented (antirez)
|
||||
2009-05-26 Redis git version modified to 0.101 in order to distinguish that from the latest tar.gz via INFO ;) (antirez)
|
||||
2009-05-26 Redis 0.100 released (antirez)
|
||||
2009-05-26 client libraries synched in git (antirez)
|
||||
2009-05-26 ignore gcc warning about write() return code not checked. It is esplicitily this way since the "max number of clients reached" is a best-effort error (antirez)
|
||||
2009-05-26 max bytes of a received command enlarged from 1k to 16k (antirez)
|
||||
2009-05-26 RubyRedis: set TCP_NODELAY TCP socket option to to disable the neagle algorithm. Makes a huge difference under some OS, notably Linux (antirez)
|
||||
2009-05-25 maxclients implemented, see redis.conf for details (antirez)
|
||||
2009-05-25 INFO command now reports replication info (antirez)
|
||||
2009-05-25 minor fix to RubyRedis about bulk commands sent without arguments (antirez)
|
||||
2009-05-24 Warns if using the default config (antirez)
|
||||
2009-05-24 Issue with redis-client used in scripts solved, now to check if the latest argument must come from standard input we do not check that stdin is or not a tty but the command arity (antirez)
|
||||
2009-05-23 RubyRedis: now sets are returned as arrays again, and not as Set objects (antirez)
|
||||
2009-05-23 SLAVEOF command documented (antirez)
|
||||
2009-05-23 SLAVEOF command implemented for replication remote control (antirez)
|
||||
2009-05-22 Fix: no connection timeout for the master! (antirez)
|
||||
2009-05-22 replication slave timeout when receiving the initial bulk data set to 3600 seconds, now that replication is non-blocking the server must save the db before to start the async replication and this can take a lot of time with huge datasets (antirez)
|
||||
2009-05-22 README tutorial now reflects the new proto (antirez)
|
||||
2009-05-22 critical bug about glueoutputbuffers=yes fixed. Under load and with pipelining and clients disconnecting on the middle of the chat with the server, Redis could block. Now it's ok (antirez)
|
||||
2009-05-22 TTL command doc added (antirez)
|
||||
2009-05-22 TTL command implemented (antirez)
|
||||
2009-05-22 S*STORE now return the cardinality of the resulting set (antirez)
|
||||
2009-05-22 rubyredis more compatible with Redis-rb (antirez)
|
||||
2009-05-21 minor indentation fix (antirez)
|
||||
2009-05-21 timeout support and Redis-rb compatibility aliases implemented in RubyRedis (antirez)
|
||||
2009-05-21 RubyRedis info postprocessor rewritten in a more functional way (antirez)
|
||||
2009-05-21 dead code removed from RubyRedis (antirez)
|
||||
2009-05-21 command postprocessing implemented into RubyRedis (antirez)
|
||||
2009-05-20 Automagically reconnection of RubyRedis (antirez)
|
||||
2009-05-20 RubyRedis: Array alike operators implemented (antirez)
|
||||
2009-05-20 random testing code removed (antirez)
|
||||
2009-05-20 RubyRedis DB selection forced at object creation (antirez)
|
||||
2009-05-20 Initial version of an alternative Ruby client added (antirez)
|
||||
2009-05-20 SDIFF / SDIFFSTORE added to doc (antirez)
|
||||
2009-05-20 Aman Gupta changes merged (antirez)
|
||||
2009-05-20 Merge git://github.com/tmm1/redis (antirez)
|
||||
2009-05-19 Allow timeout=0 config to disable client timeouts (Aman Gupta)
|
||||
2009-05-19 Partial qsort implemented in SORT command, only when both BY and LIMIT is used. minor fix for a warning compiling under Linux. (antirez)
|
||||
2009-05-19 psort.c/h added. This is a partial qsort implementation that Redis will use when SORT+LIMIT is requested (antirez)
|
||||
2009-05-17 Fix SINTER/UNIONSTORE to allow for &=/|= style operations (i.e. SINTERSTORE set1 set1 set2) (Aman Gupta)
|
||||
2009-05-17 Optimize SDIFF to return as soon as the result set is empty (Aman Gupta)
|
||||
2009-05-17 SDIFF/SDIFFSTORE implemnted unifying it with the implementation of SUNION/SUNIONSTORE (antirez)
|
||||
2009-05-11 timestamp in log lines (antirez)
|
||||
2009-05-11 Python client updated pushing from Ludo's repository (antirez)
|
||||
2009-05-11 disconnect when we cannot read from the socket (Ludovico Magnocavallo)
|
||||
2009-05-11 benchmark utility now supports random keys (antirez)
|
||||
2009-05-10 minor doc changes (antirez)
|
||||
2009-05-09 added tests for vararg DEL (antirez)
|
||||
2009-05-09 DEL is now a vararg, IMPORTANT: memory leak fixed in loading DB code (antirez)
|
||||
2009-05-09 doc changes (antirez)
|
||||
2009-05-09 CPP client added thanks to Brian Hammond (antirez)
|
||||
2009-05-06 Infinite number of arguments for MGET and all the other commands (antirez)
|
||||
2009-05-04 Warns if /proc/sys/vm/overcommit_memory is set to 0 on Linux. Also make sure to don't resize the hash tables while the child process is saving in order to avoid copy-on-write of memory pages (antirez)
|
||||
2009-04-30 zmalloc fix, return NULL or real malloc failure (antirez)
|
||||
2009-04-30 more fixes for dict.c and the 150 million keys limit (antirez)
|
||||
2009-04-30 dict.c modified to be able to handle more than 150,000,000 keys (antirez)
|
||||
2009-04-29 fuzz stresser implemented in redis-test (antirez)
|
||||
2009-04-29 fixed for HT resize check 32bits overflow (antirez)
|
||||
2009-04-29 Check for fork() failure in background saving (antirez)
|
||||
2009-04-29 fix for the LZF off-by-one bug added (antirez)
|
||||
2009-04-28 print bytes used at exit on SHUTDOWN (antirez)
|
||||
2009-04-28 SMOVE test added (antirez)
|
||||
2009-04-28 SMOVE command implemented (antirez)
|
||||
2009-04-28 less CPU usage in command parsing, case insensitive config directives (antirez)
|
||||
2009-04-28 GETSET command doc added (antirez)
|
||||
2009-04-28 GETSET tests (antirez)
|
||||
2009-04-28 GETSET implemented (antirez)
|
||||
2009-04-27 ability to specify a different file name for the DB (antirez)
|
||||
2009-04-27 log file parsing code improved a bit (antirez)
|
||||
2009-04-27 bgsave_in_progress field in INFO output (antirez)
|
||||
2009-04-27 INCRBY/DECRBY now support 64bit increments, with tests (antirez)
|
||||
2009-04-23 RANDOMKEY regression test added (antirez)
|
||||
2009-04-23 dictGetRandomKey bug fixed, RANDOMKEY will not block the server anymore (antirez)
|
||||
2009-04-22 FLUSHALL/FLUSHDB no longer sync on disk. Just increment the dirty counter by the number of elements removed, that will probably trigger a background saving operation (antirez)
|
||||
2009-04-21 forgot to comment testing code in PHP lib. Now it is ok (antirez)
|
||||
2009-04-21 PHP client ported to PHP5 and fixed (antirez)
|
||||
2009-04-21 doc update (antirez)
|
||||
2009-04-20 Non blocking replication (finally!). C-side linked lists API improved. (antirez)
|
||||
2009-04-19 SUNION, SUNIONSTORE, Initial work on non blocking replication (antirez)
|
||||
2009-04-10 Redis 0.091 released (antirez)
|
||||
2009-04-10 SINTER/SINTERSTORE/SLEMENTS fix: misisng keys are now not errors, but just like empty sets (antirez)
|
||||
2009-04-09 doc changes (antirez)
|
||||
2009-04-08 TODO changes, minor change to default redis.conf (antirez)
|
||||
2009-04-08 html doc updated (antirez)
|
||||
2009-04-08 library clients update scripts (antirez)
|
||||
2009-04-08 Ruby client updated (antirez)
|
||||
2009-04-08 Lua client updated (antirez)
|
||||
2009-04-08 Changelog updated (antirez)
|
||||
2009-04-08 Merge git://github.com/ludoo/redis (antirez)
|
||||
2009-04-08 add expire command to the php lib (Ludovico Magnocavallo)
|
||||
2009-04-08 fix decode bug, add flush and info commands (Ludovico Magnocavallo)
|
||||
2009-04-07 Rearrange redisObject struct to reduce memory usage in 64bit environments (as recommended http://groups.google.com/group/redis-db/msg/68f5a743f8f4e287) (Bob Potter)
|
||||
2009-04-07 ruby19 compat: use each_line on string (Bob Potter)
|
||||
2009-04-07 64bit fixes for usedmemory (Bob Potter)
|
||||
2009-04-08 RANDOMKEY issue 26 fixed, generic test + regression added (antirez)
|
||||
2009-04-06 Don't accept SAVE if BGSAVE is in progress (antirez)
|
||||
2009-04-06 add expire command to the python lib (Ludovico Magnocavallo)
|
||||
2009-04-03 persistent EXPIRE (antirez)
|
||||
2009-04-03 dirty increment was missing in two points. TODO updated (antirez)
|
||||
2009-04-02 LZF configured to initalize the HT in order to be determinsitic and play well with valgrind (antirez)
|
||||
2009-04-02 fix select test (Ludovico Magnocavallo)
|
||||
2009-04-02 fix trailing cr+nl in values (Ludovico Magnocavallo)
|
||||
2009-04-02 compression/decompression of large values on disk now working (antirez)
|
||||
2009-04-02 disable LZF compression since it's not able to load the DB for now, the load part is missing (antirez)
|
||||
2009-04-02 new LZF files added (antirez)
|
||||
2009-04-02 Fixed issue 23 about AUTH (antirez)
|
||||
2009-04-02 Issue 22 fixed (antirez)
|
||||
2009-04-01 non-lazy expired keys purging implemented (antirez)
|
||||
2009-04-01 fastlz dependence removed (antirez)
|
||||
2009-04-01 Initial implementation of EXPIRE (antirez)
|
||||
2009-03-30 TODO updated (antirez)
|
||||
2009-03-30 changelog added (antirez)
|
||||
2009-03-28 redis-sha1 utility added (antirez)
|
||||
2009-03-28 Integer encoding implemented in dump file. Doc updated (antirez)
|
||||
2009-03-27 feature macros defined to play well with C99 (antirez)
|
||||
2009-03-27 feature macros defined to play well with C99 (antirez)
|
||||
2009-03-27 now Redis is C99-ok (antirez)
|
||||
2009-03-27 IMPORTANT FIX: new dump format implementation was broken. Now it's ok but tests for the 32-bit case values are needed (antirez)
|
||||
2009-03-27 ANSI-C compatibility changes (antirez)
|
||||
2009-03-27 Ruby client library updated. Important changes in this new version! (antirez)
|
||||
2009-03-26 Lua client added thanks to Daniele Alessandri (antirez)
|
||||
2009-03-26 Lua client added thanks to Daniele Alessandri (antirez)
|
||||
2009-03-26 AUTH merged from Brian Hammond fork, reworked a bit to fix minor problems (antirez)
|
||||
2009-03-25 Adds AUTH command. (Brian Hammond)
|
||||
2009-03-25 Nasty bug of the new DB format fixed, objects sharing implemented (antirez)
|
||||
2009-03-25 doc update (antirez)
|
||||
2009-03-25 Erlang client synched with Valentiono's repo (antirez)
|
||||
2009-03-25 New file dump format, perl client library added (antirez)
|
||||
2009-03-25 New protocol fix for LREM (antirez)
|
||||
2009-03-24 two typos fixed (antirez)
|
||||
2009-03-24 Now the Redis test uses the proper Tcl client library (antirez)
|
||||
2009-03-24 Tcl client library (antirez)
|
||||
2009-03-24 redis-benchmark sync with the new protocol (antirez)
|
||||
2009-03-24 git mess :) (Ludovico Magnocavallo)
|
||||
2009-03-24 sync python client to the new protocol (Ludovico Magnocavallo)
|
||||
2009-03-24 protocol fix in SORT reply with null elements (antirez)
|
||||
2009-03-24 protocol doc changed (antirez)
|
||||
2009-03-24 Server replies now in the new format, test-redis.tcl and redis-cli modified accordingly (antirez)
|
||||
2009-03-24 Python client library updated, thanks to Ludo! (antirez)
|
||||
2009-03-24 random tested mode for test-redis.tcl, minor other stuff, version switched to 0.8 (antirez)
|
||||
2009-03-23 Now MONITOR/SYNC cannot be issued multiple times (antirez)
|
||||
2009-03-23 MONITOR command implemented. (antirez)
|
||||
2009-03-23 lucsky changes imported. pid file path can now be configured, redis-cli fixes (antirez)
|
||||
2009-03-23 Merge git://github.com/lucsky/redis (antirez)
|
||||
2009-03-23 another missing free->zfree replacement fixed. Thanks to Ludo (antirez)
|
||||
2009-03-23 Fixed redis-cli readLine loop to correctly handle EOF. (Luc Heinrich)
|
||||
2009-03-23 Display the port on server startup. (Luc Heinrich)
|
||||
2009-03-23 Allow to specify the pid file from the config file. (Luc Heinrich)
|
||||
2009-03-23 Added gitignore file. (Luc Heinrich)
|
||||
2009-03-22 MGET tests added (antirez)
|
||||
2009-03-22 doc changes (antirez)
|
||||
2009-03-22 added doc for MGET (antirez)
|
||||
2009-03-22 redis-cli now checks the arity of vararg commnads (antirez)
|
||||
2009-03-22 INFO fixed, MGET implemented, redis-cli implements INFO/MGET (antirez)
|
||||
2009-03-22 first commit (antirez)
|
||||
@@ -1,106 +0,0 @@
|
||||
[Note: this is the Redis manifesto, for general information about
|
||||
installing and running Redis read the README file instead.]
|
||||
|
||||
Redis Manifesto
|
||||
===============
|
||||
|
||||
1 - A DSL for Abstract Data Types. Redis is a DSL (Domain Specific Language)
|
||||
that manipulates abstract data types and implemented as a TCP daemon.
|
||||
Commands manipulate a key space where keys are binary-safe strings and
|
||||
values are different kinds of abstract data types. Every data type
|
||||
represents an abstract version of a fundamental data structure. For instance
|
||||
Redis Lists are an abstract representation of linked lists. In Redis, the
|
||||
essence of a data type isn't just the kind of operations that the data types
|
||||
support, but also the space and time complexity of the data type and the
|
||||
operations performed upon it.
|
||||
|
||||
2 - Memory storage is #1. The Redis data set, composed of defined key-value
|
||||
pairs, is primarily stored in the computer's memory. The amount of memory in
|
||||
all kinds of computers, including entry-level servers, is increasing
|
||||
significantly each year. Memory is fast, and allows Redis to have very
|
||||
predictable performance. Datasets composed of 10k or 40 millions keys will
|
||||
perform similarly. Complex data types like Redis Sorted Sets are easy to
|
||||
implement and manipulate in memory with good performance, making Redis very
|
||||
simple. Redis will continue to explore alternative options (where data can
|
||||
be optionally stored on disk, say) but the main goal of the project remains
|
||||
the development of an in-memory database.
|
||||
|
||||
3 - Fundamental data structures for a fundamental API. The Redis API is a direct
|
||||
consequence of fundamental data structures. APIs can often be arbitrary but
|
||||
not an API that resembles the nature of fundamental data structures. If we
|
||||
ever meet intelligent life forms from another part of the universe, they'll
|
||||
likely know, understand and recognize the same basic data structures we have
|
||||
in our computer science books. Redis will avoid intermediate layers in API,
|
||||
so that the complexity is obvious and more complex operations can be
|
||||
performed as the sum of the basic operations.
|
||||
|
||||
4 - We believe in code efficiency. Computers get faster and faster, yet we
|
||||
believe that abusing computing capabilities is not wise: the amount of
|
||||
operations you can do for a given amount of energy remains anyway a
|
||||
significant parameter: it allows to do more with less computers and, at
|
||||
the same time, having a smaller environmental impact. Similarly Redis is
|
||||
able to "scale down" to smaller devices. It is perfectly usable in a
|
||||
Raspberry Pi and other small ARM based computers. Faster code having
|
||||
just the layers of abstractions that are really needed will also result,
|
||||
often, in more predictable performances. We think likewise about memory
|
||||
usage, one of the fundamental goals of the Redis project is to
|
||||
incrementally build more and more memory efficient data structures, so that
|
||||
problems that were not approachable in RAM in the past will be perfectly
|
||||
fine to handle in the future.
|
||||
|
||||
5 - Code is like a poem; it's not just something we write to reach some
|
||||
practical result. Sometimes people that are far from the Redis philosophy
|
||||
suggest using other code written by other authors (frequently in other
|
||||
languages) in order to implement something Redis currently lacks. But to us
|
||||
this is like if Shakespeare decided to end Enrico IV using the Paradiso from
|
||||
the Divina Commedia. Is using any external code a bad idea? Not at all. Like
|
||||
in "One Thousand and One Nights" smaller self contained stories are embedded
|
||||
in a bigger story, we'll be happy to use beautiful self contained libraries
|
||||
when needed. At the same time, when writing the Redis story we're trying to
|
||||
write smaller stories that will fit in to other code.
|
||||
|
||||
6 - We're against complexity. We believe designing systems is a fight against
|
||||
complexity. We'll accept to fight the complexity when it's worthwhile but
|
||||
we'll try hard to recognize when a small feature is not worth 1000s of lines
|
||||
of code. Most of the time the best way to fight complexity is by not
|
||||
creating it at all. Complexity is also a form of lock-in: code that is
|
||||
very hard to understand cannot be modified by users in an independent way
|
||||
regardless of the license. One of the main Redis goals is to remain
|
||||
understandable, enough for a single programmer to have a clear idea of how
|
||||
it works in detail just reading the source code for a couple of weeks.
|
||||
|
||||
7 - Threading is not a silver bullet. Instead of making Redis threaded we
|
||||
believe on the idea of an efficient (mostly) single threaded Redis core.
|
||||
Multiple of such cores, that may run in the same computer or may run
|
||||
in multiple computers, are abstracted away as a single big system by
|
||||
higher order protocols and features: Redis Cluster and the upcoming
|
||||
Redis Proxy are our main goals. A shared nothing approach is not just
|
||||
much simpler (see the previous point in this document), is also optimal
|
||||
in NUMA systems. In the specific case of Redis it allows for each instance
|
||||
to have a more limited amount of data, making the Redis persist-by-fork
|
||||
approach more sounding. In the future we may explore parallelism only for
|
||||
I/O, which is the low hanging fruit: minimal complexity could provide an
|
||||
improved single process experience.
|
||||
|
||||
8 - Two levels of API. The Redis API has two levels: 1) a subset of the API fits
|
||||
naturally into a distributed version of Redis and 2) a more complex API that
|
||||
supports multi-key operations. Both are useful if used judiciously but
|
||||
there's no way to make the more complex multi-keys API distributed in an
|
||||
opaque way without violating our other principles. We don't want to provide
|
||||
the illusion of something that will work magically when actually it can't in
|
||||
all cases. Instead we'll provide commands to quickly migrate keys from one
|
||||
instance to another to perform multi-key operations and expose the
|
||||
trade-offs to the user.
|
||||
|
||||
9 - We optimize for joy. We believe writing code is a lot of hard work, and the
|
||||
only way it can be worth is by enjoying it. When there is no longer joy in
|
||||
writing code, the best thing to do is stop. To prevent this, we'll avoid
|
||||
taking paths that will make Redis less of a joy to develop.
|
||||
|
||||
10 - All the above points are put together in what we call opportunistic
|
||||
programming: trying to get the most for the user with minimal increases
|
||||
in complexity (hanging fruits). Solve 95% of the problem with 5% of the
|
||||
code when it is acceptable. Avoid a fixed schedule but follow the flow of
|
||||
user requests, inspiration, Redis internal readiness for certain features
|
||||
(sometimes many past changes reach a critical point making a previously
|
||||
complex feature very easy to obtain).
|
||||
@@ -1,11 +1,115 @@
|
||||
# Top level makefile, the real shit is at src/Makefile
|
||||
# Redis Makefile
|
||||
# Copyright (C) 2009 Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
# This file is released under the BSD license, see the COPYING file
|
||||
|
||||
default: all
|
||||
release_hdr := $(shell sh -c './mkreleasehdr.sh')
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
OPTIMIZATION?=-O2
|
||||
ifeq ($(uname_S),SunOS)
|
||||
CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6
|
||||
CCLINK?= -ldl -lnsl -lsocket -lm -lpthread
|
||||
else
|
||||
CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W $(ARCH) $(PROF)
|
||||
CCLINK?= -lm -pthread
|
||||
endif
|
||||
CCOPT= $(CFLAGS) $(CCLINK) $(ARCH) $(PROF)
|
||||
DEBUG?= -g -rdynamic -ggdb
|
||||
|
||||
.DEFAULT:
|
||||
cd src && $(MAKE) $@
|
||||
OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o
|
||||
BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
|
||||
CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o linenoise.o
|
||||
CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o
|
||||
CHECKAOFOBJ = redis-check-aof.o
|
||||
|
||||
install:
|
||||
cd src && $(MAKE) $@
|
||||
PRGNAME = redis-server
|
||||
BENCHPRGNAME = redis-benchmark
|
||||
CLIPRGNAME = redis-cli
|
||||
CHECKDUMPPRGNAME = redis-check-dump
|
||||
CHECKAOFPRGNAME = redis-check-aof
|
||||
|
||||
.PHONY: install
|
||||
all: redis-server redis-benchmark redis-cli redis-check-dump redis-check-aof
|
||||
|
||||
# Deps (use make dep to generate this)
|
||||
adlist.o: adlist.c adlist.h zmalloc.h
|
||||
ae.o: ae.c ae.h zmalloc.h config.h ae_kqueue.c
|
||||
ae_epoll.o: ae_epoll.c
|
||||
ae_kqueue.o: ae_kqueue.c
|
||||
ae_select.o: ae_select.c
|
||||
anet.o: anet.c fmacros.h anet.h
|
||||
dict.o: dict.c fmacros.h dict.h zmalloc.h
|
||||
dict2.o: dict2.c fmacros.h dict2.h zmalloc.h
|
||||
linenoise.o: linenoise.c fmacros.h
|
||||
lzf_c.o: lzf_c.c lzfP.h
|
||||
lzf_d.o: lzf_d.c lzfP.h
|
||||
pqsort.o: pqsort.c
|
||||
printraw.o: printraw.c
|
||||
redis-benchmark.o: redis-benchmark.c fmacros.h ae.h anet.h sds.h adlist.h \
|
||||
zmalloc.h
|
||||
redis-check-aof.o: redis-check-aof.c fmacros.h config.h
|
||||
redis-check-dump.o: redis-check-dump.c lzf.h
|
||||
redis-cli.o: redis-cli.c fmacros.h anet.h sds.h adlist.h zmalloc.h \
|
||||
linenoise.h
|
||||
redis.o: redis.c fmacros.h config.h redis.h ae.h sds.h anet.h dict.h \
|
||||
adlist.h zmalloc.h lzf.h pqsort.h zipmap.h staticsymbols.h sha1.h
|
||||
sds.o: sds.c sds.h zmalloc.h
|
||||
zipmap.o: zipmap.c zmalloc.h
|
||||
zmalloc.o: zmalloc.c config.h
|
||||
|
||||
redis-server: $(OBJ)
|
||||
$(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ)
|
||||
@echo ""
|
||||
@echo "Hint: To run the test-redis.tcl script is a good idea."
|
||||
@echo "Launch the redis server with ./redis-server, then in another"
|
||||
@echo "terminal window enter this directory and run 'make test'."
|
||||
@echo ""
|
||||
|
||||
redis-benchmark: $(BENCHOBJ)
|
||||
$(CC) -o $(BENCHPRGNAME) $(CCOPT) $(DEBUG) $(BENCHOBJ)
|
||||
|
||||
redis-cli: $(CLIOBJ)
|
||||
$(CC) -o $(CLIPRGNAME) $(CCOPT) $(DEBUG) $(CLIOBJ)
|
||||
|
||||
redis-check-dump: $(CHECKDUMPOBJ)
|
||||
$(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ)
|
||||
|
||||
redis-check-aof: $(CHECKAOFOBJ)
|
||||
$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ)
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $<
|
||||
|
||||
clean:
|
||||
rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) $(CHECKAOFPRGNAME) *.o *.gcda *.gcno *.gcov
|
||||
|
||||
dep:
|
||||
$(CC) -MM *.c
|
||||
|
||||
staticsymbols:
|
||||
tclsh utils/build-static-symbols.tcl > staticsymbols.h
|
||||
|
||||
test:
|
||||
tclsh8.5 tests/test_helper.tcl --tags "${TAGS}"
|
||||
|
||||
bench:
|
||||
./redis-benchmark
|
||||
|
||||
log:
|
||||
git log '--pretty=format:%ad %s (%cn)' --date=short > Changelog
|
||||
|
||||
32bit:
|
||||
@echo ""
|
||||
@echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386"
|
||||
@echo ""
|
||||
make ARCH="-m32"
|
||||
|
||||
gprof:
|
||||
make PROF="-pg"
|
||||
|
||||
gcov:
|
||||
make PROF="-fprofile-arcs -ftest-coverage"
|
||||
|
||||
noopt:
|
||||
make OPTIMIZATION=""
|
||||
|
||||
32bitgprof:
|
||||
make PROF="-pg" ARCH="-arch i386"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
Where to find complete Redis documentation?
|
||||
-------------------------------------------
|
||||
|
||||
This README is just a fast "quick start" document. You can find more detailed
|
||||
documentation here:
|
||||
|
||||
1) http://code.google.com/p/redis
|
||||
2) Check the 'doc' directory. doc/README.html is a good starting point :)
|
||||
|
||||
Building Redis
|
||||
--------------
|
||||
|
||||
It is as simple as:
|
||||
|
||||
% make
|
||||
|
||||
Redis is just a single binary, but if you want to install it you can use
|
||||
the "make install" target that will copy the binary in /usr/local/bin
|
||||
for default.
|
||||
|
||||
You can run a 32 bit Redis binary using:
|
||||
|
||||
% make 32bit
|
||||
|
||||
After you build Redis is a good idea to test it, using:
|
||||
|
||||
% make test
|
||||
|
||||
Running Redis
|
||||
-------------
|
||||
|
||||
To run Redis with the default configuration just type:
|
||||
|
||||
% ./redis-server
|
||||
|
||||
If you want to provide your redis.conf, you have to run it using an additional
|
||||
parameter (the path of the configuration file):
|
||||
|
||||
% ./redis-server /path/to/redis.conf
|
||||
|
||||
Playing with Redis
|
||||
------------------
|
||||
|
||||
You can use redis-cli to play with Redis. Start a redis-server instance,
|
||||
then in another terminal try the following:
|
||||
|
||||
% ./redis-cli
|
||||
redis> ping
|
||||
PONG
|
||||
redis> set foo bar
|
||||
OK
|
||||
redis> get foo
|
||||
"bar"
|
||||
redis> incr mycounter
|
||||
(integer) 1
|
||||
redis> incr mycounter
|
||||
(integer) 2
|
||||
redis>
|
||||
|
||||
You can find the list of all the available commands here:
|
||||
|
||||
http://code.google.com/p/redis/wiki/CommandReference
|
||||
|
||||
Enjoy!
|
||||
|
||||
@@ -1,487 +0,0 @@
|
||||
This README is just a fast *quick start* document. You can find more detailed documentation at [redis.io](https://redis.io).
|
||||
|
||||
What is Redis?
|
||||
--------------
|
||||
|
||||
Redis is often referred to as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.
|
||||
|
||||
Data structures implemented into Redis have a few special properties:
|
||||
|
||||
* Redis cares to store them on disk, even if they are always served and modified into the server memory. This means that Redis is fast, but that it is also non-volatile.
|
||||
* The implementation of data structures emphasizes memory efficiency, so data structures inside Redis will likely use less memory compared to the same data structure modelled using a high-level programming language.
|
||||
* Redis offers a number of features that are natural to find in a database, like replication, tunable levels of durability, clustering, and high availability.
|
||||
|
||||
Another good example is to think of Redis as a more complex version of memcached, where the operations are not just SETs and GETs, but operations that work with complex data types like Lists, Sets, ordered data structures, and so forth.
|
||||
|
||||
If you want to know more, this is a list of selected starting points:
|
||||
|
||||
* Introduction to Redis data types. http://redis.io/topics/data-types-intro
|
||||
* Try Redis directly inside your browser. http://try.redis.io
|
||||
* The full list of Redis commands. http://redis.io/commands
|
||||
* There is much more inside the official Redis documentation. http://redis.io/documentation
|
||||
|
||||
Building Redis
|
||||
--------------
|
||||
|
||||
Redis can be compiled and used on Linux, OSX, OpenBSD, NetBSD, FreeBSD.
|
||||
We support big endian and little endian architectures, and both 32 bit
|
||||
and 64 bit systems.
|
||||
|
||||
It may compile on Solaris derived systems (for instance SmartOS) but our
|
||||
support for this platform is *best effort* and Redis is not guaranteed to
|
||||
work as well as in Linux, OSX, and \*BSD.
|
||||
|
||||
It is as simple as:
|
||||
|
||||
% make
|
||||
|
||||
To build with TLS support, you'll need OpenSSL development libraries (e.g.
|
||||
libssl-dev on Debian/Ubuntu) and run:
|
||||
|
||||
% make BUILD_TLS=yes
|
||||
|
||||
To build with systemd support, you'll need systemd development libraries (such
|
||||
as libsystemd-dev on Debian/Ubuntu or systemd-devel on CentOS) and run:
|
||||
|
||||
% make USE_SYSTEMD=yes
|
||||
|
||||
To append a suffix to Redis program names, use:
|
||||
|
||||
% make PROG_SUFFIX="-alt"
|
||||
|
||||
You can run a 32 bit Redis binary using:
|
||||
|
||||
% make 32bit
|
||||
|
||||
After building Redis, it is a good idea to test it using:
|
||||
|
||||
% make test
|
||||
|
||||
If TLS is built, running the tests with TLS enabled (you will need `tcl-tls`
|
||||
installed):
|
||||
|
||||
% ./utils/gen-test-certs.sh
|
||||
% ./runtest --tls
|
||||
|
||||
|
||||
Fixing build problems with dependencies or cached build options
|
||||
---------
|
||||
|
||||
Redis has some dependencies which are included in the `deps` directory.
|
||||
`make` does not automatically rebuild dependencies even if something in
|
||||
the source code of dependencies changes.
|
||||
|
||||
When you update the source code with `git pull` or when code inside the
|
||||
dependencies tree is modified in any other way, make sure to use the following
|
||||
command in order to really clean everything and rebuild from scratch:
|
||||
|
||||
make distclean
|
||||
|
||||
This will clean: jemalloc, lua, hiredis, linenoise.
|
||||
|
||||
Also if you force certain build options like 32bit target, no C compiler
|
||||
optimizations (for debugging purposes), and other similar build time options,
|
||||
those options are cached indefinitely until you issue a `make distclean`
|
||||
command.
|
||||
|
||||
Fixing problems building 32 bit binaries
|
||||
---------
|
||||
|
||||
If after building Redis with a 32 bit target you need to rebuild it
|
||||
with a 64 bit target, or the other way around, you need to perform a
|
||||
`make distclean` in the root directory of the Redis distribution.
|
||||
|
||||
In case of build errors when trying to build a 32 bit binary of Redis, try
|
||||
the following steps:
|
||||
|
||||
* Install the package libc6-dev-i386 (also try g++-multilib).
|
||||
* Try using the following command line instead of `make 32bit`:
|
||||
`make CFLAGS="-m32 -march=native" LDFLAGS="-m32"`
|
||||
|
||||
Allocator
|
||||
---------
|
||||
|
||||
Selecting a non-default memory allocator when building Redis is done by setting
|
||||
the `MALLOC` environment variable. Redis is compiled and linked against libc
|
||||
malloc by default, with the exception of jemalloc being the default on Linux
|
||||
systems. This default was picked because jemalloc has proven to have fewer
|
||||
fragmentation problems than libc malloc.
|
||||
|
||||
To force compiling against libc malloc, use:
|
||||
|
||||
% make MALLOC=libc
|
||||
|
||||
To compile against jemalloc on Mac OS X systems, use:
|
||||
|
||||
% make MALLOC=jemalloc
|
||||
|
||||
Monotonic clock
|
||||
---------------
|
||||
|
||||
By default, Redis will build using the POSIX clock_gettime function as the
|
||||
monotonic clock source. On most modern systems, the internal processor clock
|
||||
can be used to improve performance. Cautions can be found here:
|
||||
http://oliveryang.net/2015/09/pitfalls-of-TSC-usage/
|
||||
|
||||
To build with support for the processor's internal instruction clock, use:
|
||||
|
||||
% make CFLAGS="-DUSE_PROCESSOR_CLOCK"
|
||||
|
||||
Verbose build
|
||||
-------------
|
||||
|
||||
Redis will build with a user-friendly colorized output by default.
|
||||
If you want to see a more verbose output, use the following:
|
||||
|
||||
% make V=1
|
||||
|
||||
Running Redis
|
||||
-------------
|
||||
|
||||
To run Redis with the default configuration, just type:
|
||||
|
||||
% cd src
|
||||
% ./redis-server
|
||||
|
||||
If you want to provide your redis.conf, you have to run it using an additional
|
||||
parameter (the path of the configuration file):
|
||||
|
||||
% cd src
|
||||
% ./redis-server /path/to/redis.conf
|
||||
|
||||
It is possible to alter the Redis configuration by passing parameters directly
|
||||
as options using the command line. Examples:
|
||||
|
||||
% ./redis-server --port 9999 --replicaof 127.0.0.1 6379
|
||||
% ./redis-server /etc/redis/6379.conf --loglevel debug
|
||||
|
||||
All the options in redis.conf are also supported as options using the command
|
||||
line, with exactly the same name.
|
||||
|
||||
Running Redis with TLS:
|
||||
------------------
|
||||
|
||||
Please consult the [TLS.md](TLS.md) file for more information on
|
||||
how to use Redis with TLS.
|
||||
|
||||
Playing with Redis
|
||||
------------------
|
||||
|
||||
You can use redis-cli to play with Redis. Start a redis-server instance,
|
||||
then in another terminal try the following:
|
||||
|
||||
% cd src
|
||||
% ./redis-cli
|
||||
redis> ping
|
||||
PONG
|
||||
redis> set foo bar
|
||||
OK
|
||||
redis> get foo
|
||||
"bar"
|
||||
redis> incr mycounter
|
||||
(integer) 1
|
||||
redis> incr mycounter
|
||||
(integer) 2
|
||||
redis>
|
||||
|
||||
You can find the list of all the available commands at http://redis.io/commands.
|
||||
|
||||
Installing Redis
|
||||
-----------------
|
||||
|
||||
In order to install Redis binaries into /usr/local/bin, just use:
|
||||
|
||||
% make install
|
||||
|
||||
You can use `make PREFIX=/some/other/directory install` if you wish to use a
|
||||
different destination.
|
||||
|
||||
Make install will just install binaries in your system, but will not configure
|
||||
init scripts and configuration files in the appropriate place. This is not
|
||||
needed if you just want to play a bit with Redis, but if you are installing
|
||||
it the proper way for a production system, we have a script that does this
|
||||
for Ubuntu and Debian systems:
|
||||
|
||||
% cd utils
|
||||
% ./install_server.sh
|
||||
|
||||
_Note_: `install_server.sh` will not work on Mac OSX; it is built for Linux only.
|
||||
|
||||
The script will ask you a few questions and will setup everything you need
|
||||
to run Redis properly as a background daemon that will start again on
|
||||
system reboots.
|
||||
|
||||
You'll be able to stop and start Redis using the script named
|
||||
`/etc/init.d/redis_<portnumber>`, for instance `/etc/init.d/redis_6379`.
|
||||
|
||||
Code contributions
|
||||
-----------------
|
||||
|
||||
Note: By contributing code to the Redis project in any form, including sending
|
||||
a pull request via Github, a code fragment or patch via private email or
|
||||
public discussion groups, you agree to release your code under the terms
|
||||
of the BSD license that you can find in the [COPYING][1] file included in the Redis
|
||||
source distribution.
|
||||
|
||||
Please see the [CONTRIBUTING][2] file in this source distribution for more
|
||||
information, including details on our process for security bugs/vulnerabilities.
|
||||
|
||||
[1]: https://github.com/redis/redis/blob/unstable/COPYING
|
||||
[2]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING
|
||||
|
||||
Redis internals
|
||||
===
|
||||
|
||||
If you are reading this README you are likely in front of a Github page
|
||||
or you just untarred the Redis distribution tar ball. In both the cases
|
||||
you are basically one step away from the source code, so here we explain
|
||||
the Redis source code layout, what is in each file as a general idea, the
|
||||
most important functions and structures inside the Redis server and so forth.
|
||||
We keep all the discussion at a high level without digging into the details
|
||||
since this document would be huge otherwise and our code base changes
|
||||
continuously, but a general idea should be a good starting point to
|
||||
understand more. Moreover most of the code is heavily commented and easy
|
||||
to follow.
|
||||
|
||||
Source code layout
|
||||
---
|
||||
|
||||
The Redis root directory just contains this README, the Makefile which
|
||||
calls the real Makefile inside the `src` directory and an example
|
||||
configuration for Redis and Sentinel. You can find a few shell
|
||||
scripts that are used in order to execute the Redis, Redis Cluster and
|
||||
Redis Sentinel unit tests, which are implemented inside the `tests`
|
||||
directory.
|
||||
|
||||
Inside the root are the following important directories:
|
||||
|
||||
* `src`: contains the Redis implementation, written in C.
|
||||
* `tests`: contains the unit tests, implemented in Tcl.
|
||||
* `deps`: contains libraries Redis uses. Everything needed to compile Redis is inside this directory; your system just needs to provide `libc`, a POSIX compatible interface and a C compiler. Notably `deps` contains a copy of `jemalloc`, which is the default allocator of Redis under Linux. Note that under `deps` there are also things which started with the Redis project, but for which the main repository is not `redis/redis`.
|
||||
|
||||
There are a few more directories but they are not very important for our goals
|
||||
here. We'll focus mostly on `src`, where the Redis implementation is contained,
|
||||
exploring what there is inside each file. The order in which files are
|
||||
exposed is the logical one to follow in order to disclose different layers
|
||||
of complexity incrementally.
|
||||
|
||||
Note: lately Redis was refactored quite a bit. Function names and file
|
||||
names have been changed, so you may find that this documentation reflects the
|
||||
`unstable` branch more closely. For instance, in Redis 3.0 the `server.c`
|
||||
and `server.h` files were named `redis.c` and `redis.h`. However the overall
|
||||
structure is the same. Keep in mind that all the new developments and pull
|
||||
requests should be performed against the `unstable` branch.
|
||||
|
||||
server.h
|
||||
---
|
||||
|
||||
The simplest way to understand how a program works is to understand the
|
||||
data structures it uses. So we'll start from the main header file of
|
||||
Redis, which is `server.h`.
|
||||
|
||||
All the server configuration and in general all the shared state is
|
||||
defined in a global structure called `server`, of type `struct redisServer`.
|
||||
A few important fields in this structure are:
|
||||
|
||||
* `server.db` is an array of Redis databases, where data is stored.
|
||||
* `server.commands` is the command table.
|
||||
* `server.clients` is a linked list of clients connected to the server.
|
||||
* `server.master` is a special client, the master, if the instance is a replica.
|
||||
|
||||
There are tons of other fields. Most fields are commented directly inside
|
||||
the structure definition.
|
||||
|
||||
Another important Redis data structure is the one defining a client.
|
||||
In the past it was called `redisClient`, now just `client`. The structure
|
||||
has many fields, here we'll just show the main ones:
|
||||
|
||||
struct client {
|
||||
int fd;
|
||||
sds querybuf;
|
||||
int argc;
|
||||
robj **argv;
|
||||
redisDb *db;
|
||||
int flags;
|
||||
list *reply;
|
||||
char buf[PROTO_REPLY_CHUNK_BYTES];
|
||||
... many other fields ...
|
||||
}
|
||||
|
||||
The client structure defines a *connected client*:
|
||||
|
||||
* The `fd` field is the client socket file descriptor.
|
||||
* `argc` and `argv` are populated with the command the client is executing, so that functions implementing a given Redis command can read the arguments.
|
||||
* `querybuf` accumulates the requests from the client, which are parsed by the Redis server according to the Redis protocol and executed by calling the implementations of the commands the client is executing.
|
||||
* `reply` and `buf` are dynamic and static buffers that accumulate the replies the server sends to the client. These buffers are incrementally written to the socket as soon as the file descriptor is writeable.
|
||||
|
||||
As you can see in the client structure above, arguments in a command
|
||||
are described as `robj` structures. The following is the full `robj`
|
||||
structure, which defines a *Redis object*:
|
||||
|
||||
typedef struct redisObject {
|
||||
unsigned type:4;
|
||||
unsigned encoding:4;
|
||||
unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
|
||||
int refcount;
|
||||
void *ptr;
|
||||
} robj;
|
||||
|
||||
Basically this structure can represent all the basic Redis data types like
|
||||
strings, lists, sets, sorted sets and so forth. The interesting thing is that
|
||||
it has a `type` field, so that it is possible to know what type a given
|
||||
object has, and a `refcount`, so that the same object can be referenced
|
||||
in multiple places without allocating it multiple times. Finally the `ptr`
|
||||
field points to the actual representation of the object, which might vary
|
||||
even for the same type, depending on the `encoding` used.
|
||||
|
||||
Redis objects are used extensively in the Redis internals, however in order
|
||||
to avoid the overhead of indirect accesses, recently in many places
|
||||
we just use plain dynamic strings not wrapped inside a Redis object.
|
||||
|
||||
server.c
|
||||
---
|
||||
|
||||
This is the entry point of the Redis server, where the `main()` function
|
||||
is defined. The following are the most important steps in order to startup
|
||||
the Redis server.
|
||||
|
||||
* `initServerConfig()` sets up the default values of the `server` structure.
|
||||
* `initServer()` allocates the data structures needed to operate, setup the listening socket, and so forth.
|
||||
* `aeMain()` starts the event loop which listens for new connections.
|
||||
|
||||
There are two special functions called periodically by the event loop:
|
||||
|
||||
1. `serverCron()` is called periodically (according to `server.hz` frequency), and performs tasks that must be performed from time to time, like checking for timed out clients.
|
||||
2. `beforeSleep()` is called every time the event loop fired, Redis served a few requests, and is returning back into the event loop.
|
||||
|
||||
Inside server.c you can find code that handles other vital things of the Redis server:
|
||||
|
||||
* `call()` is used in order to call a given command in the context of a given client.
|
||||
* `activeExpireCycle()` handles eviction of keys with a time to live set via the `EXPIRE` command.
|
||||
* `performEvictions()` is called when a new write command should be performed but Redis is out of memory according to the `maxmemory` directive.
|
||||
* The global variable `redisCommandTable` defines all the Redis commands, specifying the name of the command, the function implementing the command, the number of arguments required, and other properties of each command.
|
||||
|
||||
networking.c
|
||||
---
|
||||
|
||||
This file defines all the I/O functions with clients, masters and replicas
|
||||
(which in Redis are just special clients):
|
||||
|
||||
* `createClient()` allocates and initializes a new client.
|
||||
* the `addReply*()` family of functions are used by command implementations in order to append data to the client structure, that will be transmitted to the client as a reply for a given command executed.
|
||||
* `writeToClient()` transmits the data pending in the output buffers to the client and is called by the *writable event handler* `sendReplyToClient()`.
|
||||
* `readQueryFromClient()` is the *readable event handler* and accumulates data read from the client into the query buffer.
|
||||
* `processInputBuffer()` is the entry point in order to parse the client query buffer according to the Redis protocol. Once commands are ready to be processed, it calls `processCommand()` which is defined inside `server.c` in order to actually execute the command.
|
||||
* `freeClient()` deallocates, disconnects and removes a client.
|
||||
|
||||
aof.c and rdb.c
|
||||
---
|
||||
|
||||
As you can guess from the names, these files implement the RDB and AOF
|
||||
persistence for Redis. Redis uses a persistence model based on the `fork()`
|
||||
system call in order to create a thread with the same (shared) memory
|
||||
content of the main Redis thread. This secondary thread dumps the content
|
||||
of the memory on disk. This is used by `rdb.c` to create the snapshots
|
||||
on disk and by `aof.c` in order to perform the AOF rewrite when the
|
||||
append only file gets too big.
|
||||
|
||||
The implementation inside `aof.c` has additional functions in order to
|
||||
implement an API that allows commands to append new commands into the AOF
|
||||
file as clients execute them.
|
||||
|
||||
The `call()` function defined inside `server.c` is responsible for calling
|
||||
the functions that in turn will write the commands into the AOF.
|
||||
|
||||
db.c
|
||||
---
|
||||
|
||||
Certain Redis commands operate on specific data types; others are general.
|
||||
Examples of generic commands are `DEL` and `EXPIRE`. They operate on keys
|
||||
and not on their values specifically. All those generic commands are
|
||||
defined inside `db.c`.
|
||||
|
||||
Moreover `db.c` implements an API in order to perform certain operations
|
||||
on the Redis dataset without directly accessing the internal data structures.
|
||||
|
||||
The most important functions inside `db.c` which are used in many command
|
||||
implementations are the following:
|
||||
|
||||
* `lookupKeyRead()` and `lookupKeyWrite()` are used in order to get a pointer to the value associated to a given key, or `NULL` if the key does not exist.
|
||||
* `dbAdd()` and its higher level counterpart `setKey()` create a new key in a Redis database.
|
||||
* `dbDelete()` removes a key and its associated value.
|
||||
* `emptyDb()` removes an entire single database or all the databases defined.
|
||||
|
||||
The rest of the file implements the generic commands exposed to the client.
|
||||
|
||||
object.c
|
||||
---
|
||||
|
||||
The `robj` structure defining Redis objects was already described. Inside
|
||||
`object.c` there are all the functions that operate with Redis objects at
|
||||
a basic level, like functions to allocate new objects, handle the reference
|
||||
counting and so forth. Notable functions inside this file:
|
||||
|
||||
* `incrRefCount()` and `decrRefCount()` are used in order to increment or decrement an object reference count. When it drops to 0 the object is finally freed.
|
||||
* `createObject()` allocates a new object. There are also specialized functions to allocate string objects having a specific content, like `createStringObjectFromLongLong()` and similar functions.
|
||||
|
||||
This file also implements the `OBJECT` command.
|
||||
|
||||
replication.c
|
||||
---
|
||||
|
||||
This is one of the most complex files inside Redis, it is recommended to
|
||||
approach it only after getting a bit familiar with the rest of the code base.
|
||||
In this file there is the implementation of both the master and replica role
|
||||
of Redis.
|
||||
|
||||
One of the most important functions inside this file is `replicationFeedSlaves()` that writes commands to the clients representing replica instances connected
|
||||
to our master, so that the replicas can get the writes performed by the clients:
|
||||
this way their data set will remain synchronized with the one in the master.
|
||||
|
||||
This file also implements both the `SYNC` and `PSYNC` commands that are
|
||||
used in order to perform the first synchronization between masters and
|
||||
replicas, or to continue the replication after a disconnection.
|
||||
|
||||
Other C files
|
||||
---
|
||||
|
||||
* `t_hash.c`, `t_list.c`, `t_set.c`, `t_string.c`, `t_zset.c` and `t_stream.c` contains the implementation of the Redis data types. They implement both an API to access a given data type, and the client command implementations for these data types.
|
||||
* `ae.c` implements the Redis event loop, it's a self contained library which is simple to read and understand.
|
||||
* `sds.c` is the Redis string library, check http://github.com/antirez/sds for more information.
|
||||
* `anet.c` is a library to use POSIX networking in a simpler way compared to the raw interface exposed by the kernel.
|
||||
* `dict.c` is an implementation of a non-blocking hash table which rehashes incrementally.
|
||||
* `scripting.c` implements Lua scripting. It is completely self-contained and isolated from the rest of the Redis implementation and is simple enough to understand if you are familiar with the Lua API.
|
||||
* `cluster.c` implements the Redis Cluster. Probably a good read only after being very familiar with the rest of the Redis code base. If you want to read `cluster.c` make sure to read the [Redis Cluster specification][3].
|
||||
|
||||
[3]: http://redis.io/topics/cluster-spec
|
||||
|
||||
Anatomy of a Redis command
|
||||
---
|
||||
|
||||
All the Redis commands are defined in the following way:
|
||||
|
||||
void foobarCommand(client *c) {
|
||||
printf("%s",c->argv[1]->ptr); /* Do something with the argument. */
|
||||
addReply(c,shared.ok); /* Reply something to the client. */
|
||||
}
|
||||
|
||||
The command is then referenced inside `server.c` in the command table:
|
||||
|
||||
{"foobar",foobarCommand,2,"rtF",0,NULL,0,0,0,0,0},
|
||||
|
||||
In the above example `2` is the number of arguments the command takes,
|
||||
while `"rtF"` are the command flags, as documented in the command table
|
||||
top comment inside `server.c`.
|
||||
|
||||
After the command operates in some way, it returns a reply to the client,
|
||||
usually using `addReply()` or a similar function defined inside `networking.c`.
|
||||
|
||||
There are tons of command implementations inside the Redis source code
|
||||
that can serve as examples of actual commands implementations. Writing
|
||||
a few toy commands can be a good exercise to get familiar with the code base.
|
||||
|
||||
There are also many other files not described here, but it is useless to
|
||||
cover everything. We just want to help you with the first steps.
|
||||
Eventually you'll find your way inside the Redis code base :-)
|
||||
|
||||
Enjoy!
|
||||
@@ -1,87 +0,0 @@
|
||||
TLS Support
|
||||
===========
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
### Building
|
||||
|
||||
To build with TLS support you'll need OpenSSL development libraries (e.g.
|
||||
libssl-dev on Debian/Ubuntu).
|
||||
|
||||
Run `make BUILD_TLS=yes`.
|
||||
|
||||
### Tests
|
||||
|
||||
To run Redis test suite with TLS, you'll need TLS support for TCL (i.e.
|
||||
`tcl-tls` package on Debian/Ubuntu).
|
||||
|
||||
1. Run `./utils/gen-test-certs.sh` to generate a root CA and a server
|
||||
certificate.
|
||||
|
||||
2. Run `./runtest --tls` or `./runtest-cluster --tls` to run Redis and Redis
|
||||
Cluster tests in TLS mode.
|
||||
|
||||
### Running manually
|
||||
|
||||
To manually run a Redis server with TLS mode (assuming `gen-test-certs.sh` was
|
||||
invoked so sample certificates/keys are available):
|
||||
|
||||
./src/redis-server --tls-port 6379 --port 0 \
|
||||
--tls-cert-file ./tests/tls/redis.crt \
|
||||
--tls-key-file ./tests/tls/redis.key \
|
||||
--tls-ca-cert-file ./tests/tls/ca.crt
|
||||
|
||||
To connect to this Redis server with `redis-cli`:
|
||||
|
||||
./src/redis-cli --tls \
|
||||
--cert ./tests/tls/redis.crt \
|
||||
--key ./tests/tls/redis.key \
|
||||
--cacert ./tests/tls/ca.crt
|
||||
|
||||
This will disable TCP and enable TLS on port 6379. It's also possible to have
|
||||
both TCP and TLS available, but you'll need to assign different ports.
|
||||
|
||||
To make a Replica connect to the master using TLS, use `--tls-replication yes`,
|
||||
and to make Redis Cluster use TLS across nodes use `--tls-cluster yes`.
|
||||
|
||||
Connections
|
||||
-----------
|
||||
|
||||
All socket operations now go through a connection abstraction layer that hides
|
||||
I/O and read/write event handling from the caller.
|
||||
|
||||
**Multi-threading I/O is not currently supported for TLS**, as a TLS connection
|
||||
needs to do its own manipulation of AE events which is not thread safe. The
|
||||
solution is probably to manage independent AE loops for I/O threads and longer
|
||||
term association of connections with threads. This may potentially improve
|
||||
overall performance as well.
|
||||
|
||||
Sync IO for TLS is currently implemented in a hackish way, i.e. making the
|
||||
socket blocking and configuring socket-level timeout. This means the timeout
|
||||
value may not be so accurate, and there would be a lot of syscall overhead.
|
||||
However I believe that getting rid of syncio completely in favor of pure async
|
||||
work is probably a better move than trying to fix that. For replication it would
|
||||
probably not be so hard. For cluster keys migration it might be more difficult,
|
||||
but there are probably other good reasons to improve that part anyway.
|
||||
|
||||
To-Do List
|
||||
----------
|
||||
|
||||
- [ ] redis-benchmark support. The current implementation is a mix of using
|
||||
hiredis for parsing and basic networking (establishing connections), but
|
||||
directly manipulating sockets for most actions. This will need to be cleaned
|
||||
up for proper TLS support. The best approach is probably to migrate to hiredis
|
||||
async mode.
|
||||
- [ ] redis-cli `--slave` and `--rdb` support.
|
||||
|
||||
Multi-port
|
||||
----------
|
||||
|
||||
Consider the implications of allowing TLS to be configured on a separate port,
|
||||
making Redis listening on multiple ports:
|
||||
|
||||
1. Startup banner port notification
|
||||
2. Proctitle
|
||||
3. How slaves announce themselves
|
||||
4. Cluster bus port calculation
|
||||
@@ -0,0 +1,66 @@
|
||||
Redis TODO and Roadmap
|
||||
----------------------
|
||||
|
||||
VERSION 2.2 TODO (Optimizations and latency)
|
||||
============================================
|
||||
|
||||
* Lower the CPU usage.
|
||||
* Lower the RAM usage everywhere possible.
|
||||
* Specially encoded Sets (like Hashes).
|
||||
* Implement an UDP interface for low-latency operations.
|
||||
* What about a special coding that is about storing the "rdb" serialized format instead of the actual value? This can be used when we have LRU in order to super-compress data into memory, for data not accessed frequetly. It's a VM-alike strategy but fully in memory, may reduce the space to hold some dataset in an impressive way. Trivial to implement.
|
||||
* Another idea: LRU does not need to be super precise right? Maybe it's a good idea to just put into the skiplist implementing the LRU just the pointer to the key without evne incr/decr business, nor the need to remove the pointer when the key is deleted. There is to think more about that.
|
||||
|
||||
VERSION 2.x TODO
|
||||
================
|
||||
|
||||
* BRPOPLPUSH
|
||||
* Save dataset / fsync() on SIGTERM
|
||||
* Change the implementation of ZCOUNT to use the augmented skiplist in order to be much faster.
|
||||
|
||||
Virtual Memory optimizations:
|
||||
* Use multiple open FDs against the VM file, one for thread.
|
||||
* Check what happens performance-wise if instead of creating threads again and again the same threads are reused forever. Note: this requires a way to disable this clients in the child, but waiting for empty new jobs queue can be enough.
|
||||
* Implement LEN, PEEK, POKE, SETBIT, GETBIT
|
||||
|
||||
OTHER IMPORTANT THINGS THAT WILL BE ADDED BUT I'M NOT SURE WHEN
|
||||
===============================================================
|
||||
|
||||
BIG ONES:
|
||||
|
||||
* Specially encoded memory-saving integer sets.
|
||||
* A command to export a JSON dump (there should be mostly working patch needing major reworking).
|
||||
* Specially encoded sets of integers (this includes a big refactoring providing an higher level layer for Sets manipulation)
|
||||
|
||||
SMALL ONES:
|
||||
|
||||
* If sizeof(double) == sizeof(void*) we could store the double value of sorted sets directly in place of the pointer instead of allocating it in the heap.
|
||||
* Delete on writes against expire policy should only happen after argument parsing for commands doing their own arg parsing stuff.
|
||||
* Give errors when incrementing a key that does not look like an integer, when providing as a sorted set score something can't be parsed as a double, and so forth.
|
||||
* MSADD (n keys) (n values). See this thread in the Redis google group: http://groups.google.com/group/redis-db/browse_thread/thread/e766d84eb375cd41
|
||||
* Don't save empty lists / sets / zsets on disk with snapshotting.
|
||||
* Remove keys when a list / set / zset reaches length of 0.
|
||||
* An option to exec a command slave-side if the master connection is lost: even cooler: if the script returns "0" the slave elects itself as master, otherwise continue trying to reconnect.
|
||||
* PING the master from time to time to check if it's gone.
|
||||
|
||||
THE "MAYBE" TODO LIST: things that may or may not get implemented
|
||||
=================================================================
|
||||
|
||||
Most of this can be seen just as proposals, the fact they are in this list
|
||||
it's not a guarantee they'll ever get implemented ;)
|
||||
|
||||
* Move dict.c from hash table to skip list, in order to avoid the blocking resize operation needed for the hash table.
|
||||
* FORK command (fork()s executing the commands received by the current
|
||||
client in the new process). Hint: large SORTs can use more cores,
|
||||
copy-on-write will avoid memory problems.
|
||||
* DUP command? DUP srckey dstkey, creates an exact clone of srckey value in dstkey.
|
||||
* SORT: Don't copy the list into a vector when BY argument is constant.
|
||||
* Write the hash table size of every db in the dump, so that Redis can resize the hash table just one time when loading a big DB.
|
||||
* LOCK / TRYLOCK / UNLOCK as described many times in the google group
|
||||
* Replication automated tests
|
||||
* Byte Array type (BA prefixed commands): BASETBIT BAGETBIT BASETU8 U16 U32 U64 S8 S16 S32 S64, ability to atomically INCRBY all the base types. BARANGE to get a range of bytes as a bulk value, BASETRANGE to set a range of bytes.
|
||||
* zmalloc() should avoid to add a private header for archs where there is some other kind of libc-specific way to get the size of a malloced block. Already done for Mac OS X.
|
||||
* Read-only mode.
|
||||
* Pattern-matching replication.
|
||||
* Add an option to relax the delete-expiring-keys-on-write semantic *denying* replication and AOF when this is on? Can be handy sometimes, when using Redis for non persistent state, but can create problems. For instance should rename and move also "move" the timeouts? How does this affect other commands?
|
||||
* Multiple BY in SORT.
|
||||
+29
-111
@@ -34,9 +34,8 @@
|
||||
#include "zmalloc.h"
|
||||
|
||||
/* Create a new list. The created list can be freed with
|
||||
* listRelease(), but private value of every node need to be freed
|
||||
* by the user before to call listRelease(), or by setting a free method using
|
||||
* listSetFreeMethod.
|
||||
* AlFreeList(), but private value of every node need to be freed
|
||||
* by the user before to call AlFreeList().
|
||||
*
|
||||
* On error, NULL is returned. Otherwise the pointer to the new list. */
|
||||
list *listCreate(void)
|
||||
@@ -53,10 +52,12 @@ list *listCreate(void)
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Remove all the elements from the list without destroying the list itself. */
|
||||
void listEmpty(list *list)
|
||||
/* Free the whole list.
|
||||
*
|
||||
* This function can't fail. */
|
||||
void listRelease(list *list)
|
||||
{
|
||||
unsigned long len;
|
||||
unsigned int len;
|
||||
listNode *current, *next;
|
||||
|
||||
current = list->head;
|
||||
@@ -67,20 +68,10 @@ void listEmpty(list *list)
|
||||
zfree(current);
|
||||
current = next;
|
||||
}
|
||||
list->head = list->tail = NULL;
|
||||
list->len = 0;
|
||||
}
|
||||
|
||||
/* Free the whole list.
|
||||
*
|
||||
* This function can't fail. */
|
||||
void listRelease(list *list)
|
||||
{
|
||||
listEmpty(list);
|
||||
zfree(list);
|
||||
}
|
||||
|
||||
/* Add a new node to the list, to head, containing the specified 'value'
|
||||
/* Add a new node to the list, to head, contaning the specified 'value'
|
||||
* pointer as value.
|
||||
*
|
||||
* On error, NULL is returned and no operation is performed (i.e. the
|
||||
@@ -106,7 +97,7 @@ list *listAddNodeHead(list *list, void *value)
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Add a new node to the list, to tail, containing the specified 'value'
|
||||
/* Add a new node to the list, to tail, contaning the specified 'value'
|
||||
* pointer as value.
|
||||
*
|
||||
* On error, NULL is returned and no operation is performed (i.e. the
|
||||
@@ -132,35 +123,6 @@ list *listAddNodeTail(list *list, void *value)
|
||||
return list;
|
||||
}
|
||||
|
||||
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
|
||||
listNode *node;
|
||||
|
||||
if ((node = zmalloc(sizeof(*node))) == NULL)
|
||||
return NULL;
|
||||
node->value = value;
|
||||
if (after) {
|
||||
node->prev = old_node;
|
||||
node->next = old_node->next;
|
||||
if (list->tail == old_node) {
|
||||
list->tail = node;
|
||||
}
|
||||
} else {
|
||||
node->next = old_node;
|
||||
node->prev = old_node->prev;
|
||||
if (list->head == old_node) {
|
||||
list->head = node;
|
||||
}
|
||||
}
|
||||
if (node->prev != NULL) {
|
||||
node->prev->next = node;
|
||||
}
|
||||
if (node->next != NULL) {
|
||||
node->next->prev = node;
|
||||
}
|
||||
list->len++;
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Remove the specified node from the specified list.
|
||||
* It's up to the caller to free the private value of the node.
|
||||
*
|
||||
@@ -187,7 +149,7 @@ void listDelNode(list *list, listNode *node)
|
||||
listIter *listGetIterator(list *list, int direction)
|
||||
{
|
||||
listIter *iter;
|
||||
|
||||
|
||||
if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
|
||||
if (direction == AL_START_HEAD)
|
||||
iter->next = list->head;
|
||||
@@ -218,12 +180,12 @@ void listRewindTail(list *list, listIter *li) {
|
||||
* listDelNode(), but not to remove other elements.
|
||||
*
|
||||
* The function returns a pointer to the next element of the list,
|
||||
* or NULL if there are no more elements, so the classical usage
|
||||
* pattern is:
|
||||
* or NULL if there are no more elements, so the classical usage patter
|
||||
* is:
|
||||
*
|
||||
* iter = listGetIterator(list,<direction>);
|
||||
* while ((node = listNext(iter)) != NULL) {
|
||||
* doSomethingWith(listNodeValue(node));
|
||||
* iter = listGetItarotr(list,<direction>);
|
||||
* while ((node = listNextIterator(iter)) != NULL) {
|
||||
* DoSomethingWith(listNodeValue(node));
|
||||
* }
|
||||
*
|
||||
* */
|
||||
@@ -251,7 +213,7 @@ listNode *listNext(listIter *iter)
|
||||
list *listDup(list *orig)
|
||||
{
|
||||
list *copy;
|
||||
listIter iter;
|
||||
listIter *iter;
|
||||
listNode *node;
|
||||
|
||||
if ((copy = listCreate()) == NULL)
|
||||
@@ -259,23 +221,26 @@ list *listDup(list *orig)
|
||||
copy->dup = orig->dup;
|
||||
copy->free = orig->free;
|
||||
copy->match = orig->match;
|
||||
listRewind(orig, &iter);
|
||||
while((node = listNext(&iter)) != NULL) {
|
||||
iter = listGetIterator(orig, AL_START_HEAD);
|
||||
while((node = listNext(iter)) != NULL) {
|
||||
void *value;
|
||||
|
||||
if (copy->dup) {
|
||||
value = copy->dup(node->value);
|
||||
if (value == NULL) {
|
||||
listRelease(copy);
|
||||
listReleaseIterator(iter);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
value = node->value;
|
||||
if (listAddNodeTail(copy, value) == NULL) {
|
||||
listRelease(copy);
|
||||
listReleaseIterator(iter);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
listReleaseIterator(iter);
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -290,30 +255,33 @@ list *listDup(list *orig)
|
||||
* NULL is returned. */
|
||||
listNode *listSearchKey(list *list, void *key)
|
||||
{
|
||||
listIter iter;
|
||||
listIter *iter;
|
||||
listNode *node;
|
||||
|
||||
listRewind(list, &iter);
|
||||
while((node = listNext(&iter)) != NULL) {
|
||||
iter = listGetIterator(list, AL_START_HEAD);
|
||||
while((node = listNext(iter)) != NULL) {
|
||||
if (list->match) {
|
||||
if (list->match(node->value, key)) {
|
||||
listReleaseIterator(iter);
|
||||
return node;
|
||||
}
|
||||
} else {
|
||||
if (key == node->value) {
|
||||
listReleaseIterator(iter);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
listReleaseIterator(iter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return the element at the specified zero-based index
|
||||
* where 0 is the head, 1 is the element next to head
|
||||
* and so on. Negative integers are used in order to count
|
||||
* from the tail, -1 is the last element, -2 the penultimate
|
||||
* from the tail, -1 is the last element, -2 the penultimante
|
||||
* and so on. If the index is out of range NULL is returned. */
|
||||
listNode *listIndex(list *list, long index) {
|
||||
listNode *listIndex(list *list, int index) {
|
||||
listNode *n;
|
||||
|
||||
if (index < 0) {
|
||||
@@ -326,53 +294,3 @@ listNode *listIndex(list *list, long index) {
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Rotate the list removing the tail node and inserting it to the head. */
|
||||
void listRotateTailToHead(list *list) {
|
||||
if (listLength(list) <= 1) return;
|
||||
|
||||
/* Detach current tail */
|
||||
listNode *tail = list->tail;
|
||||
list->tail = tail->prev;
|
||||
list->tail->next = NULL;
|
||||
/* Move it as head */
|
||||
list->head->prev = tail;
|
||||
tail->prev = NULL;
|
||||
tail->next = list->head;
|
||||
list->head = tail;
|
||||
}
|
||||
|
||||
/* Rotate the list removing the head node and inserting it to the tail. */
|
||||
void listRotateHeadToTail(list *list) {
|
||||
if (listLength(list) <= 1) return;
|
||||
|
||||
listNode *head = list->head;
|
||||
/* Detach current head */
|
||||
list->head = head->next;
|
||||
list->head->prev = NULL;
|
||||
/* Move it as tail */
|
||||
list->tail->next = head;
|
||||
head->next = NULL;
|
||||
head->prev = list->tail;
|
||||
list->tail = head;
|
||||
}
|
||||
|
||||
/* Add all the elements of the list 'o' at the end of the
|
||||
* list 'l'. The list 'other' remains empty but otherwise valid. */
|
||||
void listJoin(list *l, list *o) {
|
||||
if (o->len == 0) return;
|
||||
|
||||
o->head->prev = l->tail;
|
||||
|
||||
if (l->tail)
|
||||
l->tail->next = o->head;
|
||||
else
|
||||
l->head = o->head;
|
||||
|
||||
l->tail = o->tail;
|
||||
l->len += o->len;
|
||||
|
||||
/* Setup other as an empty list. */
|
||||
o->head = o->tail = NULL;
|
||||
o->len = 0;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* adlist.h - A generic doubly linked list implementation
|
||||
*
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -50,7 +50,7 @@ typedef struct list {
|
||||
void *(*dup)(void *ptr);
|
||||
void (*free)(void *ptr);
|
||||
int (*match)(void *ptr, void *key);
|
||||
unsigned long len;
|
||||
unsigned int len;
|
||||
} list;
|
||||
|
||||
/* Functions implemented as macros */
|
||||
@@ -66,28 +66,23 @@ typedef struct list {
|
||||
#define listSetMatchMethod(l,m) ((l)->match = (m))
|
||||
|
||||
#define listGetDupMethod(l) ((l)->dup)
|
||||
#define listGetFreeMethod(l) ((l)->free)
|
||||
#define listGetFree(l) ((l)->free)
|
||||
#define listGetMatchMethod(l) ((l)->match)
|
||||
|
||||
/* Prototypes */
|
||||
list *listCreate(void);
|
||||
void listRelease(list *list);
|
||||
void listEmpty(list *list);
|
||||
list *listAddNodeHead(list *list, void *value);
|
||||
list *listAddNodeTail(list *list, void *value);
|
||||
list *listInsertNode(list *list, listNode *old_node, void *value, int after);
|
||||
void listDelNode(list *list, listNode *node);
|
||||
listIter *listGetIterator(list *list, int direction);
|
||||
listNode *listNext(listIter *iter);
|
||||
void listReleaseIterator(listIter *iter);
|
||||
list *listDup(list *orig);
|
||||
listNode *listSearchKey(list *list, void *key);
|
||||
listNode *listIndex(list *list, long index);
|
||||
listNode *listIndex(list *list, int index);
|
||||
void listRewind(list *list, listIter *li);
|
||||
void listRewindTail(list *list, listIter *li);
|
||||
void listRotateTailToHead(list *list);
|
||||
void listRotateHeadToTail(list *list);
|
||||
void listJoin(list *l, list *o);
|
||||
|
||||
/* Directions for iterators */
|
||||
#define AL_START_HEAD 0
|
||||
@@ -0,0 +1,390 @@
|
||||
/* A simple event-driven programming library. Originally I wrote this code
|
||||
* for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
|
||||
* it in form of a library for easy reuse.
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ae.h"
|
||||
#include "zmalloc.h"
|
||||
#include "config.h"
|
||||
|
||||
/* Include the best multiplexing layer supported by this system.
|
||||
* The following should be ordered by performances, descending. */
|
||||
#ifdef HAVE_EPOLL
|
||||
#include "ae_epoll.c"
|
||||
#else
|
||||
#ifdef HAVE_KQUEUE
|
||||
#include "ae_kqueue.c"
|
||||
#else
|
||||
#include "ae_select.c"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
aeEventLoop *aeCreateEventLoop(void) {
|
||||
aeEventLoop *eventLoop;
|
||||
int i;
|
||||
|
||||
eventLoop = zmalloc(sizeof(*eventLoop));
|
||||
if (!eventLoop) return NULL;
|
||||
eventLoop->timeEventHead = NULL;
|
||||
eventLoop->timeEventNextId = 0;
|
||||
eventLoop->stop = 0;
|
||||
eventLoop->maxfd = -1;
|
||||
eventLoop->beforesleep = NULL;
|
||||
if (aeApiCreate(eventLoop) == -1) {
|
||||
zfree(eventLoop);
|
||||
return NULL;
|
||||
}
|
||||
/* Events with mask == AE_NONE are not set. So let's initialize the
|
||||
* vector with it. */
|
||||
for (i = 0; i < AE_SETSIZE; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
return eventLoop;
|
||||
}
|
||||
|
||||
void aeDeleteEventLoop(aeEventLoop *eventLoop) {
|
||||
aeApiFree(eventLoop);
|
||||
zfree(eventLoop);
|
||||
}
|
||||
|
||||
void aeStop(aeEventLoop *eventLoop) {
|
||||
eventLoop->stop = 1;
|
||||
}
|
||||
|
||||
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
aeFileProc *proc, void *clientData)
|
||||
{
|
||||
if (fd >= AE_SETSIZE) return AE_ERR;
|
||||
aeFileEvent *fe = &eventLoop->events[fd];
|
||||
|
||||
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
|
||||
return AE_ERR;
|
||||
fe->mask |= mask;
|
||||
if (mask & AE_READABLE) fe->rfileProc = proc;
|
||||
if (mask & AE_WRITABLE) fe->wfileProc = proc;
|
||||
fe->clientData = clientData;
|
||||
if (fd > eventLoop->maxfd)
|
||||
eventLoop->maxfd = fd;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
|
||||
{
|
||||
if (fd >= AE_SETSIZE) return;
|
||||
aeFileEvent *fe = &eventLoop->events[fd];
|
||||
|
||||
if (fe->mask == AE_NONE) return;
|
||||
fe->mask = fe->mask & (~mask);
|
||||
if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
|
||||
/* Update the max fd */
|
||||
int j;
|
||||
|
||||
for (j = eventLoop->maxfd-1; j >= 0; j--)
|
||||
if (eventLoop->events[j].mask != AE_NONE) break;
|
||||
eventLoop->maxfd = j;
|
||||
}
|
||||
aeApiDelEvent(eventLoop, fd, mask);
|
||||
}
|
||||
|
||||
static void aeGetTime(long *seconds, long *milliseconds)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
*seconds = tv.tv_sec;
|
||||
*milliseconds = tv.tv_usec/1000;
|
||||
}
|
||||
|
||||
static void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {
|
||||
long cur_sec, cur_ms, when_sec, when_ms;
|
||||
|
||||
aeGetTime(&cur_sec, &cur_ms);
|
||||
when_sec = cur_sec + milliseconds/1000;
|
||||
when_ms = cur_ms + milliseconds%1000;
|
||||
if (when_ms >= 1000) {
|
||||
when_sec ++;
|
||||
when_ms -= 1000;
|
||||
}
|
||||
*sec = when_sec;
|
||||
*ms = when_ms;
|
||||
}
|
||||
|
||||
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
|
||||
aeTimeProc *proc, void *clientData,
|
||||
aeEventFinalizerProc *finalizerProc)
|
||||
{
|
||||
long long id = eventLoop->timeEventNextId++;
|
||||
aeTimeEvent *te;
|
||||
|
||||
te = zmalloc(sizeof(*te));
|
||||
if (te == NULL) return AE_ERR;
|
||||
te->id = id;
|
||||
aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
|
||||
te->timeProc = proc;
|
||||
te->finalizerProc = finalizerProc;
|
||||
te->clientData = clientData;
|
||||
te->next = eventLoop->timeEventHead;
|
||||
eventLoop->timeEventHead = te;
|
||||
return id;
|
||||
}
|
||||
|
||||
int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
|
||||
{
|
||||
aeTimeEvent *te, *prev = NULL;
|
||||
|
||||
te = eventLoop->timeEventHead;
|
||||
while(te) {
|
||||
if (te->id == id) {
|
||||
if (prev == NULL)
|
||||
eventLoop->timeEventHead = te->next;
|
||||
else
|
||||
prev->next = te->next;
|
||||
if (te->finalizerProc)
|
||||
te->finalizerProc(eventLoop, te->clientData);
|
||||
zfree(te);
|
||||
return AE_OK;
|
||||
}
|
||||
prev = te;
|
||||
te = te->next;
|
||||
}
|
||||
return AE_ERR; /* NO event with the specified ID found */
|
||||
}
|
||||
|
||||
/* Search the first timer to fire.
|
||||
* This operation is useful to know how many time the select can be
|
||||
* put in sleep without to delay any event.
|
||||
* If there are no timers NULL is returned.
|
||||
*
|
||||
* Note that's O(N) since time events are unsorted.
|
||||
* Possible optimizations (not needed by Redis so far, but...):
|
||||
* 1) Insert the event in order, so that the nearest is just the head.
|
||||
* Much better but still insertion or deletion of timers is O(N).
|
||||
* 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
|
||||
*/
|
||||
static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
|
||||
{
|
||||
aeTimeEvent *te = eventLoop->timeEventHead;
|
||||
aeTimeEvent *nearest = NULL;
|
||||
|
||||
while(te) {
|
||||
if (!nearest || te->when_sec < nearest->when_sec ||
|
||||
(te->when_sec == nearest->when_sec &&
|
||||
te->when_ms < nearest->when_ms))
|
||||
nearest = te;
|
||||
te = te->next;
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
/* Process time events */
|
||||
static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
int processed = 0;
|
||||
aeTimeEvent *te;
|
||||
long long maxId;
|
||||
|
||||
te = eventLoop->timeEventHead;
|
||||
maxId = eventLoop->timeEventNextId-1;
|
||||
while(te) {
|
||||
long now_sec, now_ms;
|
||||
long long id;
|
||||
|
||||
if (te->id > maxId) {
|
||||
te = te->next;
|
||||
continue;
|
||||
}
|
||||
aeGetTime(&now_sec, &now_ms);
|
||||
if (now_sec > te->when_sec ||
|
||||
(now_sec == te->when_sec && now_ms >= te->when_ms))
|
||||
{
|
||||
int retval;
|
||||
|
||||
id = te->id;
|
||||
retval = te->timeProc(eventLoop, id, te->clientData);
|
||||
processed++;
|
||||
/* After an event is processed our time event list may
|
||||
* no longer be the same, so we restart from head.
|
||||
* Still we make sure to don't process events registered
|
||||
* by event handlers itself in order to don't loop forever.
|
||||
* To do so we saved the max ID we want to handle.
|
||||
*
|
||||
* FUTURE OPTIMIZATIONS:
|
||||
* Note that this is NOT great algorithmically. Redis uses
|
||||
* a single time event so it's not a problem but the right
|
||||
* way to do this is to add the new elements on head, and
|
||||
* to flag deleted elements in a special way for later
|
||||
* deletion (putting references to the nodes to delete into
|
||||
* another linked list). */
|
||||
if (retval != AE_NOMORE) {
|
||||
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
|
||||
} else {
|
||||
aeDeleteTimeEvent(eventLoop, id);
|
||||
}
|
||||
te = eventLoop->timeEventHead;
|
||||
} else {
|
||||
te = te->next;
|
||||
}
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
/* Process every pending time event, then every pending file event
|
||||
* (that may be registered by time event callbacks just processed).
|
||||
* Without special flags the function sleeps until some file event
|
||||
* fires, or when the next time event occurrs (if any).
|
||||
*
|
||||
* If flags is 0, the function does nothing and returns.
|
||||
* if flags has AE_ALL_EVENTS set, all the kind of events are processed.
|
||||
* if flags has AE_FILE_EVENTS set, file events are processed.
|
||||
* if flags has AE_TIME_EVENTS set, time events are processed.
|
||||
* if flags has AE_DONT_WAIT set the function returns ASAP until all
|
||||
* the events that's possible to process without to wait are processed.
|
||||
*
|
||||
* The function returns the number of events processed. */
|
||||
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
{
|
||||
int processed = 0, numevents;
|
||||
|
||||
/* Nothing to do? return ASAP */
|
||||
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
|
||||
|
||||
/* Note that we want call select() even if there are no
|
||||
* file events to process as long as we want to process time
|
||||
* events, in order to sleep until the next time event is ready
|
||||
* to fire. */
|
||||
if (eventLoop->maxfd != -1 ||
|
||||
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
|
||||
int j;
|
||||
aeTimeEvent *shortest = NULL;
|
||||
struct timeval tv, *tvp;
|
||||
|
||||
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
|
||||
shortest = aeSearchNearestTimer(eventLoop);
|
||||
if (shortest) {
|
||||
long now_sec, now_ms;
|
||||
|
||||
/* Calculate the time missing for the nearest
|
||||
* timer to fire. */
|
||||
aeGetTime(&now_sec, &now_ms);
|
||||
tvp = &tv;
|
||||
tvp->tv_sec = shortest->when_sec - now_sec;
|
||||
if (shortest->when_ms < now_ms) {
|
||||
tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;
|
||||
tvp->tv_sec --;
|
||||
} else {
|
||||
tvp->tv_usec = (shortest->when_ms - now_ms)*1000;
|
||||
}
|
||||
if (tvp->tv_sec < 0) tvp->tv_sec = 0;
|
||||
if (tvp->tv_usec < 0) tvp->tv_usec = 0;
|
||||
} else {
|
||||
/* If we have to check for events but need to return
|
||||
* ASAP because of AE_DONT_WAIT we need to se the timeout
|
||||
* to zero */
|
||||
if (flags & AE_DONT_WAIT) {
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
tvp = &tv;
|
||||
} else {
|
||||
/* Otherwise we can block */
|
||||
tvp = NULL; /* wait forever */
|
||||
}
|
||||
}
|
||||
|
||||
numevents = aeApiPoll(eventLoop, tvp);
|
||||
for (j = 0; j < numevents; j++) {
|
||||
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
|
||||
int mask = eventLoop->fired[j].mask;
|
||||
int fd = eventLoop->fired[j].fd;
|
||||
int rfired = 0;
|
||||
|
||||
/* note the fe->mask & mask & ... code: maybe an already processed
|
||||
* event removed an element that fired and we still didn't
|
||||
* processed, so we check if the event is still valid. */
|
||||
if (fe->mask & mask & AE_READABLE) {
|
||||
rfired = 1;
|
||||
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
|
||||
}
|
||||
if (fe->mask & mask & AE_WRITABLE) {
|
||||
if (!rfired || fe->wfileProc != fe->rfileProc)
|
||||
fe->wfileProc(eventLoop,fd,fe->clientData,mask);
|
||||
}
|
||||
processed++;
|
||||
}
|
||||
}
|
||||
/* Check time events */
|
||||
if (flags & AE_TIME_EVENTS)
|
||||
processed += processTimeEvents(eventLoop);
|
||||
|
||||
return processed; /* return the number of processed file/time events */
|
||||
}
|
||||
|
||||
/* Wait for millseconds until the given file descriptor becomes
|
||||
* writable/readable/exception */
|
||||
int aeWait(int fd, int mask, long long milliseconds) {
|
||||
struct timeval tv;
|
||||
fd_set rfds, wfds, efds;
|
||||
int retmask = 0, retval;
|
||||
|
||||
tv.tv_sec = milliseconds/1000;
|
||||
tv.tv_usec = (milliseconds%1000)*1000;
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
FD_ZERO(&efds);
|
||||
|
||||
if (mask & AE_READABLE) FD_SET(fd,&rfds);
|
||||
if (mask & AE_WRITABLE) FD_SET(fd,&wfds);
|
||||
if ((retval = select(fd+1, &rfds, &wfds, &efds, &tv)) > 0) {
|
||||
if (FD_ISSET(fd,&rfds)) retmask |= AE_READABLE;
|
||||
if (FD_ISSET(fd,&wfds)) retmask |= AE_WRITABLE;
|
||||
return retmask;
|
||||
} else {
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
void aeMain(aeEventLoop *eventLoop) {
|
||||
eventLoop->stop = 0;
|
||||
while (!eventLoop->stop) {
|
||||
if (eventLoop->beforesleep != NULL)
|
||||
eventLoop->beforesleep(eventLoop);
|
||||
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
|
||||
}
|
||||
}
|
||||
|
||||
char *aeGetApiName(void) {
|
||||
return aeApiName();
|
||||
}
|
||||
|
||||
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {
|
||||
eventLoop->beforesleep = beforesleep;
|
||||
}
|
||||
+15
-33
@@ -2,7 +2,7 @@
|
||||
* for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
|
||||
* it in form of a library for easy reuse.
|
||||
*
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -33,29 +33,21 @@
|
||||
#ifndef __AE_H__
|
||||
#define __AE_H__
|
||||
|
||||
#include "monotonic.h"
|
||||
#define AE_SETSIZE (1024*10) /* Max number of fd supported */
|
||||
|
||||
#define AE_OK 0
|
||||
#define AE_ERR -1
|
||||
|
||||
#define AE_NONE 0 /* No events registered. */
|
||||
#define AE_READABLE 1 /* Fire when descriptor is readable. */
|
||||
#define AE_WRITABLE 2 /* Fire when descriptor is writable. */
|
||||
#define AE_BARRIER 4 /* With WRITABLE, never fire the event if the
|
||||
READABLE event already fired in the same event
|
||||
loop iteration. Useful when you want to persist
|
||||
things to disk before sending replies, and want
|
||||
to do that in a group fashion. */
|
||||
#define AE_NONE 0
|
||||
#define AE_READABLE 1
|
||||
#define AE_WRITABLE 2
|
||||
|
||||
#define AE_FILE_EVENTS (1<<0)
|
||||
#define AE_TIME_EVENTS (1<<1)
|
||||
#define AE_FILE_EVENTS 1
|
||||
#define AE_TIME_EVENTS 2
|
||||
#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)
|
||||
#define AE_DONT_WAIT (1<<2)
|
||||
#define AE_CALL_BEFORE_SLEEP (1<<3)
|
||||
#define AE_CALL_AFTER_SLEEP (1<<4)
|
||||
#define AE_DONT_WAIT 4
|
||||
|
||||
#define AE_NOMORE -1
|
||||
#define AE_DELETED_EVENT_ID -1
|
||||
|
||||
/* Macros */
|
||||
#define AE_NOTUSED(V) ((void) V)
|
||||
@@ -70,7 +62,7 @@ typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);
|
||||
|
||||
/* File event structure */
|
||||
typedef struct aeFileEvent {
|
||||
int mask; /* one of AE_(READABLE|WRITABLE|BARRIER) */
|
||||
int mask; /* one of AE_(READABLE|WRITABLE) */
|
||||
aeFileProc *rfileProc;
|
||||
aeFileProc *wfileProc;
|
||||
void *clientData;
|
||||
@@ -79,14 +71,12 @@ typedef struct aeFileEvent {
|
||||
/* Time event structure */
|
||||
typedef struct aeTimeEvent {
|
||||
long long id; /* time event identifier. */
|
||||
monotime when;
|
||||
long when_sec; /* seconds */
|
||||
long when_ms; /* milliseconds */
|
||||
aeTimeProc *timeProc;
|
||||
aeEventFinalizerProc *finalizerProc;
|
||||
void *clientData;
|
||||
struct aeTimeEvent *prev;
|
||||
struct aeTimeEvent *next;
|
||||
int refcount; /* refcount to prevent timer events from being
|
||||
* freed in recursive time event calls. */
|
||||
} aeTimeEvent;
|
||||
|
||||
/* A fired event */
|
||||
@@ -97,27 +87,23 @@ typedef struct aeFiredEvent {
|
||||
|
||||
/* State of an event based program */
|
||||
typedef struct aeEventLoop {
|
||||
int maxfd; /* highest file descriptor currently registered */
|
||||
int setsize; /* max number of file descriptors tracked */
|
||||
int maxfd;
|
||||
long long timeEventNextId;
|
||||
aeFileEvent *events; /* Registered events */
|
||||
aeFiredEvent *fired; /* Fired events */
|
||||
aeFileEvent events[AE_SETSIZE]; /* Registered events */
|
||||
aeFiredEvent fired[AE_SETSIZE]; /* Fired events */
|
||||
aeTimeEvent *timeEventHead;
|
||||
int stop;
|
||||
void *apidata; /* This is used for polling API specific data */
|
||||
aeBeforeSleepProc *beforesleep;
|
||||
aeBeforeSleepProc *aftersleep;
|
||||
int flags;
|
||||
} aeEventLoop;
|
||||
|
||||
/* Prototypes */
|
||||
aeEventLoop *aeCreateEventLoop(int setsize);
|
||||
aeEventLoop *aeCreateEventLoop(void);
|
||||
void aeDeleteEventLoop(aeEventLoop *eventLoop);
|
||||
void aeStop(aeEventLoop *eventLoop);
|
||||
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
aeFileProc *proc, void *clientData);
|
||||
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);
|
||||
int aeGetFileEvents(aeEventLoop *eventLoop, int fd);
|
||||
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
|
||||
aeTimeProc *proc, void *clientData,
|
||||
aeEventFinalizerProc *finalizerProc);
|
||||
@@ -127,9 +113,5 @@ int aeWait(int fd, int mask, long long milliseconds);
|
||||
void aeMain(aeEventLoop *eventLoop);
|
||||
char *aeGetApiName(void);
|
||||
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);
|
||||
void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep);
|
||||
int aeGetSetSize(aeEventLoop *eventLoop);
|
||||
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);
|
||||
void aeSetDontWait(aeEventLoop *eventLoop, int noWait);
|
||||
|
||||
#endif
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/* Linux epoll(2) based ae.c module
|
||||
* Copyright (C) 2009-2010 Salvatore Sanfilippo - antirez@gmail.com
|
||||
* Released under the BSD license. See the COPYING file for more info. */
|
||||
|
||||
#include <sys/epoll.h>
|
||||
|
||||
typedef struct aeApiState {
|
||||
int epfd;
|
||||
struct epoll_event events[AE_SETSIZE];
|
||||
} aeApiState;
|
||||
|
||||
static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
aeApiState *state = zmalloc(sizeof(aeApiState));
|
||||
|
||||
if (!state) return -1;
|
||||
state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */
|
||||
if (state->epfd == -1) return -1;
|
||||
eventLoop->apidata = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aeApiFree(aeEventLoop *eventLoop) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
|
||||
close(state->epfd);
|
||||
zfree(state);
|
||||
}
|
||||
|
||||
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
struct epoll_event ee;
|
||||
/* If the fd was already monitored for some event, we need a MOD
|
||||
* operation. Otherwise we need an ADD operation. */
|
||||
int op = eventLoop->events[fd].mask == AE_NONE ?
|
||||
EPOLL_CTL_ADD : EPOLL_CTL_MOD;
|
||||
|
||||
ee.events = 0;
|
||||
mask |= eventLoop->events[fd].mask; /* Merge old events */
|
||||
if (mask & AE_READABLE) ee.events |= EPOLLIN;
|
||||
if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
|
||||
ee.data.u64 = 0; /* avoid valgrind warning */
|
||||
ee.data.fd = fd;
|
||||
if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
struct epoll_event ee;
|
||||
int mask = eventLoop->events[fd].mask & (~delmask);
|
||||
|
||||
ee.events = 0;
|
||||
if (mask & AE_READABLE) ee.events |= EPOLLIN;
|
||||
if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
|
||||
ee.data.u64 = 0; /* avoid valgrind warning */
|
||||
ee.data.fd = fd;
|
||||
if (mask != AE_NONE) {
|
||||
epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);
|
||||
} else {
|
||||
/* Note, Kernel < 2.6.9 requires a non null event pointer even for
|
||||
* EPOLL_CTL_DEL. */
|
||||
epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);
|
||||
}
|
||||
}
|
||||
|
||||
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
int retval, numevents = 0;
|
||||
|
||||
retval = epoll_wait(state->epfd,state->events,AE_SETSIZE,
|
||||
tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
|
||||
if (retval > 0) {
|
||||
int j;
|
||||
|
||||
numevents = retval;
|
||||
for (j = 0; j < numevents; j++) {
|
||||
int mask = 0;
|
||||
struct epoll_event *e = state->events+j;
|
||||
|
||||
if (e->events & EPOLLIN) mask |= AE_READABLE;
|
||||
if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
|
||||
eventLoop->fired[j].fd = e->data.fd;
|
||||
eventLoop->fired[j].mask = mask;
|
||||
}
|
||||
}
|
||||
return numevents;
|
||||
}
|
||||
|
||||
static char *aeApiName(void) {
|
||||
return "epoll";
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/* Kqueue(2)-based ae.c module
|
||||
* Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com
|
||||
* Released under the BSD license. See the COPYING file for more info. */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
typedef struct aeApiState {
|
||||
int kqfd;
|
||||
struct kevent events[AE_SETSIZE];
|
||||
} aeApiState;
|
||||
|
||||
static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
aeApiState *state = zmalloc(sizeof(aeApiState));
|
||||
|
||||
if (!state) return -1;
|
||||
state->kqfd = kqueue();
|
||||
if (state->kqfd == -1) return -1;
|
||||
eventLoop->apidata = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aeApiFree(aeEventLoop *eventLoop) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
|
||||
close(state->kqfd);
|
||||
zfree(state);
|
||||
}
|
||||
|
||||
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
struct kevent ke;
|
||||
|
||||
if (mask & AE_READABLE) {
|
||||
EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;
|
||||
}
|
||||
if (mask & AE_WRITABLE) {
|
||||
EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
|
||||
if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
struct kevent ke;
|
||||
|
||||
if (mask & AE_READABLE) {
|
||||
EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
|
||||
kevent(state->kqfd, &ke, 1, NULL, 0, NULL);
|
||||
}
|
||||
if (mask & AE_WRITABLE) {
|
||||
EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
|
||||
kevent(state->kqfd, &ke, 1, NULL, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
int retval, numevents = 0;
|
||||
|
||||
if (tvp != NULL) {
|
||||
struct timespec timeout;
|
||||
timeout.tv_sec = tvp->tv_sec;
|
||||
timeout.tv_nsec = tvp->tv_usec * 1000;
|
||||
retval = kevent(state->kqfd, NULL, 0, state->events, AE_SETSIZE, &timeout);
|
||||
} else {
|
||||
retval = kevent(state->kqfd, NULL, 0, state->events, AE_SETSIZE, NULL);
|
||||
}
|
||||
|
||||
if (retval > 0) {
|
||||
int j;
|
||||
|
||||
numevents = retval;
|
||||
for(j = 0; j < numevents; j++) {
|
||||
int mask = 0;
|
||||
struct kevent *e = state->events+j;
|
||||
|
||||
if (e->filter == EVFILT_READ) mask |= AE_READABLE;
|
||||
if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE;
|
||||
eventLoop->fired[j].fd = e->ident;
|
||||
eventLoop->fired[j].mask = mask;
|
||||
}
|
||||
}
|
||||
return numevents;
|
||||
}
|
||||
|
||||
static char *aeApiName(void) {
|
||||
return "kqueue";
|
||||
}
|
||||
@@ -1,35 +1,7 @@
|
||||
/* Select()-based ae.c module.
|
||||
*
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/* Select()-based ae.c module
|
||||
* Copyright (C) 2009-2010 Salvatore Sanfilippo - antirez@gmail.com
|
||||
* Released under the BSD license. See the COPYING file for more info. */
|
||||
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct aeApiState {
|
||||
@@ -49,12 +21,6 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
|
||||
/* Just ensure we have enough room in the fd_set type. */
|
||||
if (setsize >= FD_SETSIZE) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aeApiFree(aeEventLoop *eventLoop) {
|
||||
zfree(eventLoop->apidata);
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/* anet.c -- Basic TCP socket stuff made a bit less boring
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "anet.h"
|
||||
|
||||
static void anetSetError(char *err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!err) return;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(err, ANET_ERR_LEN, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int anetNonBlock(char *err, int fd)
|
||||
{
|
||||
int flags;
|
||||
|
||||
/* Set the socket nonblocking.
|
||||
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
||||
* interrupted by a signal. */
|
||||
if ((flags = fcntl(fd, F_GETFL)) == -1) {
|
||||
anetSetError(err, "fcntl(F_GETFL): %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetTcpNoDelay(char *err, int fd)
|
||||
{
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1)
|
||||
{
|
||||
anetSetError(err, "setsockopt TCP_NODELAY: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetSetSendBuffer(char *err, int fd, int buffsize)
|
||||
{
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)
|
||||
{
|
||||
anetSetError(err, "setsockopt SO_SNDBUF: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetTcpKeepAlive(char *err, int fd)
|
||||
{
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) {
|
||||
anetSetError(err, "setsockopt SO_KEEPALIVE: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetResolve(char *err, char *host, char *ipbuf)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
if (inet_aton(host, &sa.sin_addr) == 0) {
|
||||
struct hostent *he;
|
||||
|
||||
he = gethostbyname(host);
|
||||
if (he == NULL) {
|
||||
anetSetError(err, "can't resolve: %s\n", host);
|
||||
return ANET_ERR;
|
||||
}
|
||||
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
|
||||
}
|
||||
strcpy(ipbuf,inet_ntoa(sa.sin_addr));
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
#define ANET_CONNECT_NONE 0
|
||||
#define ANET_CONNECT_NONBLOCK 1
|
||||
static int anetTcpGenericConnect(char *err, char *addr, int port, int flags)
|
||||
{
|
||||
int s, on = 1;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
anetSetError(err, "creating socket: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
/* Make sure connection-intensive things like the redis benckmark
|
||||
* will be able to close/open sockets a zillion of times */
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
if (inet_aton(addr, &sa.sin_addr) == 0) {
|
||||
struct hostent *he;
|
||||
|
||||
he = gethostbyname(addr);
|
||||
if (he == NULL) {
|
||||
anetSetError(err, "can't resolve: %s\n", addr);
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
|
||||
}
|
||||
if (flags & ANET_CONNECT_NONBLOCK) {
|
||||
if (anetNonBlock(err,s) != ANET_OK)
|
||||
return ANET_ERR;
|
||||
}
|
||||
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
||||
if (errno == EINPROGRESS &&
|
||||
flags & ANET_CONNECT_NONBLOCK)
|
||||
return s;
|
||||
|
||||
anetSetError(err, "connect: %s\n", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int anetTcpConnect(char *err, char *addr, int port)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE);
|
||||
}
|
||||
|
||||
int anetTcpNonBlockConnect(char *err, char *addr, int port)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK);
|
||||
}
|
||||
|
||||
/* Like read(2) but make sure 'count' is read before to return
|
||||
* (unless error or EOF condition is encountered) */
|
||||
int anetRead(int fd, char *buf, int count)
|
||||
{
|
||||
int nread, totlen = 0;
|
||||
while(totlen != count) {
|
||||
nread = read(fd,buf,count-totlen);
|
||||
if (nread == 0) return totlen;
|
||||
if (nread == -1) return -1;
|
||||
totlen += nread;
|
||||
buf += nread;
|
||||
}
|
||||
return totlen;
|
||||
}
|
||||
|
||||
/* Like write(2) but make sure 'count' is read before to return
|
||||
* (unless error is encountered) */
|
||||
int anetWrite(int fd, char *buf, int count)
|
||||
{
|
||||
int nwritten, totlen = 0;
|
||||
while(totlen != count) {
|
||||
nwritten = write(fd,buf,count-totlen);
|
||||
if (nwritten == 0) return totlen;
|
||||
if (nwritten == -1) return -1;
|
||||
totlen += nwritten;
|
||||
buf += nwritten;
|
||||
}
|
||||
return totlen;
|
||||
}
|
||||
|
||||
int anetTcpServer(char *err, int port, char *bindaddr)
|
||||
{
|
||||
int s, on = 1;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
anetSetError(err, "socket: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
anetSetError(err, "setsockopt SO_REUSEADDR: %s\n", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
memset(&sa,0,sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bindaddr) {
|
||||
if (inet_aton(bindaddr, &sa.sin_addr) == 0) {
|
||||
anetSetError(err, "Invalid bind address\n");
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
}
|
||||
if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
||||
anetSetError(err, "bind: %s\n", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
if (listen(s, 511) == -1) { /* the magic 511 constant is from nginx */
|
||||
anetSetError(err, "listen: %s\n", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int anetAccept(char *err, int serversock, char *ip, int *port)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_in sa;
|
||||
unsigned int saLen;
|
||||
|
||||
while(1) {
|
||||
saLen = sizeof(sa);
|
||||
fd = accept(serversock, (struct sockaddr*)&sa, &saLen);
|
||||
if (fd == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else {
|
||||
anetSetError(err, "accept: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ip) strcpy(ip,inet_ntoa(sa.sin_addr));
|
||||
if (port) *port = ntohs(sa.sin_port);
|
||||
return fd;
|
||||
}
|
||||
+18
-20
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
/* anet.c -- Basic TCP socket stuff made a bit less boring
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,25 +28,22 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __BIO_H
|
||||
#define __BIO_H
|
||||
#ifndef ANET_H
|
||||
#define ANET_H
|
||||
|
||||
typedef void lazy_free_fn(void *args[]);
|
||||
#define ANET_OK 0
|
||||
#define ANET_ERR -1
|
||||
#define ANET_ERR_LEN 256
|
||||
|
||||
/* Exported API */
|
||||
void bioInit(void);
|
||||
unsigned long long bioPendingJobsOfType(int type);
|
||||
unsigned long long bioWaitStepOfType(int type);
|
||||
time_t bioOlderJobOfType(int type);
|
||||
void bioKillThreads(void);
|
||||
void bioCreateCloseJob(int fd);
|
||||
void bioCreateFsyncJob(int fd);
|
||||
void bioCreateLazyFreeJob(lazy_free_fn free_fn, int arg_count, ...);
|
||||
|
||||
/* Background job opcodes */
|
||||
#define BIO_CLOSE_FILE 0 /* Deferred close(2) syscall. */
|
||||
#define BIO_AOF_FSYNC 1 /* Deferred AOF fsync. */
|
||||
#define BIO_LAZY_FREE 2 /* Deferred objects freeing. */
|
||||
#define BIO_NUM_OPS 3
|
||||
int anetTcpConnect(char *err, char *addr, int port);
|
||||
int anetTcpNonBlockConnect(char *err, char *addr, int port);
|
||||
int anetRead(int fd, char *buf, int count);
|
||||
int anetResolve(char *err, char *host, char *ipbuf);
|
||||
int anetTcpServer(char *err, int port, char *bindaddr);
|
||||
int anetAccept(char *err, int serversock, char *ip, int *port);
|
||||
int anetWrite(int fd, char *buf, int count);
|
||||
int anetNonBlock(char *err, int fd);
|
||||
int anetTcpNoDelay(char *err, int fd);
|
||||
int anetTcpKeepAlive(char *err, int fd);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
An updated list of client libraries for Redis can be found here:
|
||||
|
||||
http://code.google.com/p/redis
|
||||
|
||||
All the links are in the front page.
|
||||
|
||||
Most mainstream (and less mainstream) languages are supported.
|
||||
|
||||
If you plan to write a new client library for a missing language, please
|
||||
get in touch with the other developers writing a message to the Redis google
|
||||
group that is located here: http://groups.google.com/group/redis-db
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef __CONFIG_H
|
||||
#define __CONFIG_H
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
/* test for malloc_size() */
|
||||
#ifdef __APPLE__
|
||||
#include <malloc/malloc.h>
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define redis_malloc_size(p) malloc_size(p)
|
||||
#endif
|
||||
|
||||
/* define redis_fstat to fstat or fstat64() */
|
||||
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
|
||||
#define redis_fstat fstat64
|
||||
#define redis_stat stat64
|
||||
#else
|
||||
#define redis_fstat fstat
|
||||
#define redis_stat stat
|
||||
#endif
|
||||
|
||||
/* test for backtrace() */
|
||||
#if defined(__APPLE__) || defined(__linux__)
|
||||
#define HAVE_BACKTRACE 1
|
||||
#endif
|
||||
|
||||
/* test for polling API */
|
||||
#ifdef __linux__
|
||||
#define HAVE_EPOLL 1
|
||||
#endif
|
||||
|
||||
#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
|
||||
#define HAVE_KQUEUE 1
|
||||
#endif
|
||||
|
||||
/* define aof_fsync to fdatasync() in Linux and fsync() for all the rest */
|
||||
#ifdef __linux__
|
||||
#define aof_fsync fdatasync
|
||||
#else
|
||||
#define aof_fsync fsync
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
-94
@@ -1,94 +0,0 @@
|
||||
# Redis dependency Makefile
|
||||
|
||||
uname_S:= $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
|
||||
CCCOLOR="\033[34m"
|
||||
LINKCOLOR="\033[34;1m"
|
||||
SRCCOLOR="\033[33m"
|
||||
BINCOLOR="\033[37;1m"
|
||||
MAKECOLOR="\033[32;1m"
|
||||
ENDCOLOR="\033[0m"
|
||||
|
||||
default:
|
||||
@echo "Explicit target required"
|
||||
|
||||
.PHONY: default
|
||||
|
||||
# Prerequisites target
|
||||
.make-prerequisites:
|
||||
@touch $@
|
||||
|
||||
# Clean everything when CFLAGS is different
|
||||
ifneq ($(shell sh -c '[ -f .make-cflags ] && cat .make-cflags || echo none'), $(CFLAGS))
|
||||
.make-cflags: distclean
|
||||
-(echo "$(CFLAGS)" > .make-cflags)
|
||||
.make-prerequisites: .make-cflags
|
||||
endif
|
||||
|
||||
# Clean everything when LDFLAGS is different
|
||||
ifneq ($(shell sh -c '[ -f .make-ldflags ] && cat .make-ldflags || echo none'), $(LDFLAGS))
|
||||
.make-ldflags: distclean
|
||||
-(echo "$(LDFLAGS)" > .make-ldflags)
|
||||
.make-prerequisites: .make-ldflags
|
||||
endif
|
||||
|
||||
distclean:
|
||||
-(cd hiredis && $(MAKE) clean) > /dev/null || true
|
||||
-(cd linenoise && $(MAKE) clean) > /dev/null || true
|
||||
-(cd lua && $(MAKE) clean) > /dev/null || true
|
||||
-(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true
|
||||
-(cd hdr_histogram && $(MAKE) clean) > /dev/null || true
|
||||
-(rm -f .make-*)
|
||||
|
||||
.PHONY: distclean
|
||||
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
HIREDIS_MAKE_FLAGS = USE_SSL=1
|
||||
endif
|
||||
|
||||
hiredis: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd hiredis && $(MAKE) static $(HIREDIS_MAKE_FLAGS)
|
||||
|
||||
.PHONY: hiredis
|
||||
|
||||
linenoise: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd linenoise && $(MAKE)
|
||||
|
||||
.PHONY: linenoise
|
||||
|
||||
hdr_histogram: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd hdr_histogram && $(MAKE)
|
||||
|
||||
.PHONY: hdr_histogram
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
# Make isinf() available
|
||||
LUA_CFLAGS= -D__C99FEATURES__=1
|
||||
endif
|
||||
|
||||
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI -DENABLE_CJSON_GLOBAL -DREDIS_STATIC='' -DLUA_USE_MKSTEMP $(CFLAGS)
|
||||
LUA_LDFLAGS+= $(LDFLAGS)
|
||||
# lua's Makefile defines AR="ar rcu", which is unusual, and makes it more
|
||||
# challenging to cross-compile lua (and redis). These defines make it easier
|
||||
# to fit redis into cross-compilation environments, which typically set AR.
|
||||
AR=ar
|
||||
ARFLAGS=rc
|
||||
|
||||
lua: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd lua/src && $(MAKE) all CFLAGS="$(LUA_CFLAGS)" MYLDFLAGS="$(LUA_LDFLAGS)" AR="$(AR) $(ARFLAGS)"
|
||||
|
||||
.PHONY: lua
|
||||
|
||||
JEMALLOC_CFLAGS= -std=gnu99 -Wall -pipe -g3 -O3 -funroll-loops $(CFLAGS)
|
||||
JEMALLOC_LDFLAGS= $(LDFLAGS)
|
||||
|
||||
jemalloc: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd jemalloc && ./configure --with-version=5.1.0-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
|
||||
cd jemalloc && $(MAKE) CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" lib/libjemalloc.a
|
||||
|
||||
.PHONY: jemalloc
|
||||
Vendored
-82
@@ -1,82 +0,0 @@
|
||||
This directory contains all Redis dependencies, except for the libc that
|
||||
should be provided by the operating system.
|
||||
|
||||
* **Jemalloc** is our memory allocator, used as replacement for libc malloc on Linux by default. It has good performances and excellent fragmentation behavior. This component is upgraded from time to time.
|
||||
* **hiredis** is the official C client library for Redis. It is used by redis-cli, redis-benchmark and Redis Sentinel. It is part of the Redis official ecosystem but is developed externally from the Redis repository, so we just upgrade it as needed.
|
||||
* **linenoise** is a readline replacement. It is developed by the same authors of Redis but is managed as a separated project and updated as needed.
|
||||
* **lua** is Lua 5.1 with minor changes for security and additional libraries.
|
||||
|
||||
How to upgrade the above dependencies
|
||||
===
|
||||
|
||||
Jemalloc
|
||||
---
|
||||
|
||||
Jemalloc is modified with changes that allow us to implement the Redis
|
||||
active defragmentation logic. However this feature of Redis is not mandatory
|
||||
and Redis is able to understand if the Jemalloc version it is compiled
|
||||
against supports such Redis-specific modifications. So in theory, if you
|
||||
are not interested in the active defragmentation, you can replace Jemalloc
|
||||
just following these steps:
|
||||
|
||||
1. Remove the jemalloc directory.
|
||||
2. Substitute it with the new jemalloc source tree.
|
||||
3. Edit the Makefile located in the same directory as the README you are
|
||||
reading, and change the --with-version in the Jemalloc configure script
|
||||
options with the version you are using. This is required because otherwise
|
||||
Jemalloc configuration script is broken and will not work nested in another
|
||||
git repository.
|
||||
|
||||
However note that we change Jemalloc settings via the `configure` script of Jemalloc using the `--with-lg-quantum` option, setting it to the value of 3 instead of 4. This provides us with more size classes that better suit the Redis data structures, in order to gain memory efficiency.
|
||||
|
||||
If you want to upgrade Jemalloc while also providing support for
|
||||
active defragmentation, in addition to the above steps you need to perform
|
||||
the following additional steps:
|
||||
|
||||
5. In Jemalloc tree, file `include/jemalloc/jemalloc_macros.h.in`, make sure
|
||||
to add `#define JEMALLOC_FRAG_HINT`.
|
||||
6. Implement the function `je_get_defrag_hint()` inside `src/jemalloc.c`. You
|
||||
can see how it is implemented in the current Jemalloc source tree shipped
|
||||
with Redis, and rewrite it according to the new Jemalloc internals, if they
|
||||
changed, otherwise you could just copy the old implementation if you are
|
||||
upgrading just to a similar version of Jemalloc.
|
||||
|
||||
Hiredis
|
||||
---
|
||||
|
||||
Hiredis uses the SDS string library, that must be the same version used inside Redis itself. Hiredis is also very critical for Sentinel. Historically Redis often used forked versions of hiredis in a way or the other. In order to upgrade it is advised to take a lot of care:
|
||||
|
||||
1. Check with diff if hiredis API changed and what impact it could have in Redis.
|
||||
2. Make sure that the SDS library inside Hiredis and inside Redis are compatible.
|
||||
3. After the upgrade, run the Redis Sentinel test.
|
||||
4. Check manually that redis-cli and redis-benchmark behave as expected, since we have no tests for CLI utilities currently.
|
||||
|
||||
Linenoise
|
||||
---
|
||||
|
||||
Linenoise is rarely upgraded as needed. The upgrade process is trivial since
|
||||
Redis uses a non modified version of linenoise, so to upgrade just do the
|
||||
following:
|
||||
|
||||
1. Remove the linenoise directory.
|
||||
2. Substitute it with the new linenoise source tree.
|
||||
|
||||
Lua
|
||||
---
|
||||
|
||||
We use Lua 5.1 and no upgrade is planned currently, since we don't want to break
|
||||
Lua scripts for new Lua features: in the context of Redis Lua scripts the
|
||||
capabilities of 5.1 are usually more than enough, the release is rock solid,
|
||||
and we definitely don't want to break old scripts.
|
||||
|
||||
So upgrading of Lua is up to the Redis project maintainers and should be a
|
||||
manual procedure performed by taking a diff between the different versions.
|
||||
|
||||
Currently we have at least the following differences between official Lua 5.1
|
||||
and our version:
|
||||
|
||||
1. Makefile is modified to allow a different compiler than GCC.
|
||||
2. We have the implementation source code, and directly link to the following external libraries: `lua_cjson.o`, `lua_struct.o`, `lua_cmsgpack.o` and `lua_bit.o`.
|
||||
3. There is a security fix in `ldo.c`, line 498: The check for `LUA_SIGNATURE[0]` is removed in order to avoid direct bytecode execution.
|
||||
|
||||
|
||||
Vendored
-121
@@ -1,121 +0,0 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
Vendored
-41
@@ -1,41 +0,0 @@
|
||||
The code in this repository code was Written by Gil Tene, Michael Barker,
|
||||
and Matt Warren, and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
For users of this code who wish to consume it under the "BSD" license
|
||||
rather than under the public domain or CC0 contribution text mentioned
|
||||
above, the code found under this directory is *also* provided under the
|
||||
following license (commonly referred to as the BSD 2-Clause License). This
|
||||
license does not detract from the above stated release of the code into
|
||||
the public domain, and simply represents an additional license granted by
|
||||
the Author.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
** Beginning of "BSD 2-Clause License" text. **
|
||||
|
||||
Copyright (c) 2012, 2013, 2014 Gil Tene
|
||||
Copyright (c) 2014 Michael Barker
|
||||
Copyright (c) 2014 Matt Warren
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
STD=
|
||||
WARN= -Wall
|
||||
OPT= -Os
|
||||
|
||||
R_CFLAGS= $(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS)
|
||||
R_LDFLAGS= $(LDFLAGS)
|
||||
DEBUG= -g
|
||||
|
||||
R_CC=$(CC) $(R_CFLAGS)
|
||||
R_LD=$(CC) $(R_LDFLAGS)
|
||||
|
||||
hdr_histogram.o: hdr_histogram.h hdr_histogram.c
|
||||
|
||||
.c.o:
|
||||
$(R_CC) -c $<
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
|
||||
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
HdrHistogram_c v0.11.0
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
This port contains a subset of the 'C' version of High Dynamic Range (HDR) Histogram available at [github.com/HdrHistogram/HdrHistogram_c](https://github.com/HdrHistogram/HdrHistogram_c).
|
||||
|
||||
|
||||
The code present on `hdr_histogram.c`, `hdr_histogram.h`, and `hdr_atomic.c` was Written by Gil Tene, Michael Barker,
|
||||
and Matt Warren, and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/.
|
||||
Vendored
-146
@@ -1,146 +0,0 @@
|
||||
/**
|
||||
* hdr_atomic.h
|
||||
* Written by Philip Orwig and released to the public domain,
|
||||
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#ifndef HDR_ATOMIC_H__
|
||||
#define HDR_ATOMIC_H__
|
||||
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <intrin.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static void __inline * hdr_atomic_load_pointer(void** pointer)
|
||||
{
|
||||
_ReadBarrier();
|
||||
return *pointer;
|
||||
}
|
||||
|
||||
static void hdr_atomic_store_pointer(void** pointer, void* value)
|
||||
{
|
||||
_WriteBarrier();
|
||||
*pointer = value;
|
||||
}
|
||||
|
||||
static int64_t __inline hdr_atomic_load_64(int64_t* field)
|
||||
{
|
||||
_ReadBarrier();
|
||||
return *field;
|
||||
}
|
||||
|
||||
static void __inline hdr_atomic_store_64(int64_t* field, int64_t value)
|
||||
{
|
||||
_WriteBarrier();
|
||||
*field = value;
|
||||
}
|
||||
|
||||
static int64_t __inline hdr_atomic_exchange_64(volatile int64_t* field, int64_t value)
|
||||
{
|
||||
#if defined(_WIN64)
|
||||
return _InterlockedExchange64(field, value);
|
||||
#else
|
||||
int64_t comparand;
|
||||
int64_t initial_value = *field;
|
||||
do
|
||||
{
|
||||
comparand = initial_value;
|
||||
initial_value = _InterlockedCompareExchange64(field, value, comparand);
|
||||
}
|
||||
while (comparand != initial_value);
|
||||
|
||||
return initial_value;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int64_t __inline hdr_atomic_add_fetch_64(volatile int64_t* field, int64_t value)
|
||||
{
|
||||
#if defined(_WIN64)
|
||||
return _InterlockedExchangeAdd64(field, value) + value;
|
||||
#else
|
||||
int64_t comparand;
|
||||
int64_t initial_value = *field;
|
||||
do
|
||||
{
|
||||
comparand = initial_value;
|
||||
initial_value = _InterlockedCompareExchange64(field, comparand + value, comparand);
|
||||
}
|
||||
while (comparand != initial_value);
|
||||
|
||||
return initial_value + value;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool __inline hdr_atomic_compare_exchange_64(volatile int64_t* field, int64_t* expected, int64_t desired)
|
||||
{
|
||||
return *expected == _InterlockedCompareExchange64(field, desired, *expected);
|
||||
}
|
||||
|
||||
#elif defined(__ATOMIC_SEQ_CST)
|
||||
|
||||
#define hdr_atomic_load_pointer(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_store_pointer(f,v) __atomic_store_n(f,v, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_load_64(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_store_64(f,v) __atomic_store_n(f,v, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_exchange_64(f,i) __atomic_exchange_n(f,i, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_add_fetch_64(field, value) __atomic_add_fetch(field, value, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_compare_exchange_64(field, expected, desired) __atomic_compare_exchange_n(field, expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static inline void* hdr_atomic_load_pointer(void** pointer)
|
||||
{
|
||||
void* p = *pointer;
|
||||
asm volatile ("" ::: "memory");
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline void hdr_atomic_store_pointer(void** pointer, void* value)
|
||||
{
|
||||
asm volatile ("lock; xchgq %0, %1" : "+q" (value), "+m" (*pointer));
|
||||
}
|
||||
|
||||
static inline int64_t hdr_atomic_load_64(int64_t* field)
|
||||
{
|
||||
int64_t i = *field;
|
||||
asm volatile ("" ::: "memory");
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline void hdr_atomic_store_64(int64_t* field, int64_t value)
|
||||
{
|
||||
asm volatile ("lock; xchgq %0, %1" : "+q" (value), "+m" (*field));
|
||||
}
|
||||
|
||||
static inline int64_t hdr_atomic_exchange_64(volatile int64_t* field, int64_t value)
|
||||
{
|
||||
int64_t result = 0;
|
||||
asm volatile ("lock; xchgq %1, %2" : "=r" (result), "+q" (value), "+m" (*field));
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int64_t hdr_atomic_add_fetch_64(volatile int64_t* field, int64_t value)
|
||||
{
|
||||
return __sync_add_and_fetch(field, value);
|
||||
}
|
||||
|
||||
static inline bool hdr_atomic_compare_exchange_64(volatile int64_t* field, int64_t* expected, int64_t desired)
|
||||
{
|
||||
int64_t original;
|
||||
asm volatile( "lock; cmpxchgq %2, %1" : "=a"(original), "+m"(*field) : "q"(desired), "0"(*expected));
|
||||
return original == *expected;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error "Unable to determine atomic operations for your platform"
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HDR_ATOMIC_H__ */
|
||||
Vendored
-1155
File diff suppressed because it is too large
Load Diff
Vendored
-509
@@ -1,509 +0,0 @@
|
||||
/**
|
||||
* hdr_histogram.h
|
||||
* Written by Michael Barker and released to the public domain,
|
||||
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* The source for the hdr_histogram utilises a few C99 constructs, specifically
|
||||
* the use of stdint/stdbool and inline variable declaration.
|
||||
*/
|
||||
|
||||
#ifndef HDR_HISTOGRAM_H
|
||||
#define HDR_HISTOGRAM_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct hdr_histogram
|
||||
{
|
||||
int64_t lowest_trackable_value;
|
||||
int64_t highest_trackable_value;
|
||||
int32_t unit_magnitude;
|
||||
int32_t significant_figures;
|
||||
int32_t sub_bucket_half_count_magnitude;
|
||||
int32_t sub_bucket_half_count;
|
||||
int64_t sub_bucket_mask;
|
||||
int32_t sub_bucket_count;
|
||||
int32_t bucket_count;
|
||||
int64_t min_value;
|
||||
int64_t max_value;
|
||||
int32_t normalizing_index_offset;
|
||||
double conversion_ratio;
|
||||
int32_t counts_len;
|
||||
int64_t total_count;
|
||||
int64_t* counts;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Allocate the memory and initialise the hdr_histogram.
|
||||
*
|
||||
* Due to the size of the histogram being the result of some reasonably
|
||||
* involved math on the input parameters this function it is tricky to stack allocate.
|
||||
* The histogram should be released with hdr_close
|
||||
*
|
||||
* @param lowest_trackable_value The smallest possible value to be put into the
|
||||
* histogram.
|
||||
* @param highest_trackable_value The largest possible value to be put into the
|
||||
* histogram.
|
||||
* @param significant_figures The level of precision for this histogram, i.e. the number
|
||||
* of figures in a decimal number that will be maintained. E.g. a value of 3 will mean
|
||||
* the results from the histogram will be accurate up to the first three digits. Must
|
||||
* be a value between 1 and 5 (inclusive).
|
||||
* @param result Output parameter to capture allocated histogram.
|
||||
* @return 0 on success, EINVAL if lowest_trackable_value is < 1 or the
|
||||
* significant_figure value is outside of the allowed range, ENOMEM if malloc
|
||||
* failed.
|
||||
*/
|
||||
int hdr_init(
|
||||
int64_t lowest_trackable_value,
|
||||
int64_t highest_trackable_value,
|
||||
int significant_figures,
|
||||
struct hdr_histogram** result);
|
||||
|
||||
/**
|
||||
* Free the memory and close the hdr_histogram.
|
||||
*
|
||||
* @param h The histogram you want to close.
|
||||
*/
|
||||
void hdr_close(struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Allocate the memory and initialise the hdr_histogram. This is the equivalent of calling
|
||||
* hdr_init(1, highest_trackable_value, significant_figures, result);
|
||||
*
|
||||
* @deprecated use hdr_init.
|
||||
*/
|
||||
int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result);
|
||||
|
||||
|
||||
/**
|
||||
* Reset a histogram to zero - empty out a histogram and re-initialise it
|
||||
*
|
||||
* If you want to re-use an existing histogram, but reset everything back to zero, this
|
||||
* is the routine to use.
|
||||
*
|
||||
* @param h The histogram you want to reset to empty.
|
||||
*
|
||||
*/
|
||||
void hdr_reset(struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Get the memory size of the hdr_histogram.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @return The amount of memory used by the hdr_histogram in bytes
|
||||
*/
|
||||
size_t hdr_get_memory_size(struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at construction time.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_value(struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at construction time.
|
||||
*
|
||||
* Will record this value atomically, however the whole structure may appear inconsistent
|
||||
* when read concurrently with this update. Do NOT mix calls to this method with calls
|
||||
* to non-atomic updates.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_value_atomic(struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Records count values in the histogram, will round this value of to a
|
||||
* precision at or better than the significant_figure specified at construction
|
||||
* time.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @return false if any value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count);
|
||||
|
||||
/**
|
||||
* Records count values in the histogram, will round this value of to a
|
||||
* precision at or better than the significant_figure specified at construction
|
||||
* time.
|
||||
*
|
||||
* Will record this value atomically, however the whole structure may appear inconsistent
|
||||
* when read concurrently with this update. Do NOT mix calls to this method with calls
|
||||
* to non-atomic updates.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @return false if any value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count);
|
||||
|
||||
/**
|
||||
* Record a value in the histogram and backfill based on an expected interval.
|
||||
*
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at contruction time. This is specifically used
|
||||
* for recording latency. If the value is larger than the expected_interval then the
|
||||
* latency recording system has experienced co-ordinated omission. This method fills in the
|
||||
* values that would have occured had the client providing the load not been blocked.
|
||||
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval);
|
||||
|
||||
/**
|
||||
* Record a value in the histogram and backfill based on an expected interval.
|
||||
*
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at contruction time. This is specifically used
|
||||
* for recording latency. If the value is larger than the expected_interval then the
|
||||
* latency recording system has experienced co-ordinated omission. This method fills in the
|
||||
* values that would have occured had the client providing the load not been blocked.
|
||||
*
|
||||
* Will record this value atomically, however the whole structure may appear inconsistent
|
||||
* when read concurrently with this update. Do NOT mix calls to this method with calls
|
||||
* to non-atomic updates.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_value_atomic(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval);
|
||||
|
||||
/**
|
||||
* Record a value in the histogram 'count' times. Applies the same correcting logic
|
||||
* as 'hdr_record_corrected_value'.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval);
|
||||
|
||||
/**
|
||||
* Record a value in the histogram 'count' times. Applies the same correcting logic
|
||||
* as 'hdr_record_corrected_value'.
|
||||
*
|
||||
* Will record this value atomically, however the whole structure may appear inconsistent
|
||||
* when read concurrently with this update. Do NOT mix calls to this method with calls
|
||||
* to non-atomic updates.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval);
|
||||
|
||||
/**
|
||||
* Adds all of the values from 'from' to 'this' histogram. Will return the
|
||||
* number of values that are dropped when copying. Values will be dropped
|
||||
* if they around outside of h.lowest_trackable_value and
|
||||
* h.highest_trackable_value.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param from Histogram to copy values from.
|
||||
* @return The number of values dropped when copying.
|
||||
*/
|
||||
int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from);
|
||||
|
||||
/**
|
||||
* Adds all of the values from 'from' to 'this' histogram. Will return the
|
||||
* number of values that are dropped when copying. Values will be dropped
|
||||
* if they around outside of h.lowest_trackable_value and
|
||||
* h.highest_trackable_value.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param from Histogram to copy values from.
|
||||
* @return The number of values dropped when copying.
|
||||
*/
|
||||
int64_t hdr_add_while_correcting_for_coordinated_omission(
|
||||
struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval);
|
||||
|
||||
/**
|
||||
* Get minimum value from the histogram. Will return 2^63-1 if the histogram
|
||||
* is empty.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
*/
|
||||
int64_t hdr_min(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Get maximum value from the histogram. Will return 0 if the histogram
|
||||
* is empty.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
*/
|
||||
int64_t hdr_max(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Get the value at a specific percentile.
|
||||
*
|
||||
* @param h "This" pointer.
|
||||
* @param percentile The percentile to get the value for
|
||||
*/
|
||||
int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile);
|
||||
|
||||
/**
|
||||
* Gets the standard deviation for the values in the histogram.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @return The standard deviation
|
||||
*/
|
||||
double hdr_stddev(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Gets the mean for the values in the histogram.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @return The mean
|
||||
*/
|
||||
double hdr_mean(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Determine if two values are equivalent with the histogram's resolution.
|
||||
* Where "equivalent" means that value samples recorded for any two
|
||||
* equivalent values are counted in a common total count.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param a first value to compare
|
||||
* @param b second value to compare
|
||||
* @return 'true' if values are equivalent with the histogram's resolution.
|
||||
*/
|
||||
bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b);
|
||||
|
||||
/**
|
||||
* Get the lowest value that is equivalent to the given value within the histogram's resolution.
|
||||
* Where "equivalent" means that value samples recorded for any two
|
||||
* equivalent values are counted in a common total count.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value The given value
|
||||
* @return The lowest value that is equivalent to the given value within the histogram's resolution.
|
||||
*/
|
||||
int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Get the count of recorded values at a specific value
|
||||
* (to within the histogram resolution at the value level).
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value The value for which to provide the recorded count
|
||||
* @return The total count of values recorded in the histogram within the value range that is
|
||||
* {@literal >=} lowestEquivalentValue(<i>value</i>) and {@literal <=} highestEquivalentValue(<i>value</i>)
|
||||
*/
|
||||
int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index);
|
||||
|
||||
int64_t hdr_value_at_index(const struct hdr_histogram* h, int32_t index);
|
||||
|
||||
struct hdr_iter_percentiles
|
||||
{
|
||||
bool seen_last_value;
|
||||
int32_t ticks_per_half_distance;
|
||||
double percentile_to_iterate_to;
|
||||
double percentile;
|
||||
};
|
||||
|
||||
struct hdr_iter_recorded
|
||||
{
|
||||
int64_t count_added_in_this_iteration_step;
|
||||
};
|
||||
|
||||
struct hdr_iter_linear
|
||||
{
|
||||
int64_t value_units_per_bucket;
|
||||
int64_t count_added_in_this_iteration_step;
|
||||
int64_t next_value_reporting_level;
|
||||
int64_t next_value_reporting_level_lowest_equivalent;
|
||||
};
|
||||
|
||||
struct hdr_iter_log
|
||||
{
|
||||
double log_base;
|
||||
int64_t count_added_in_this_iteration_step;
|
||||
int64_t next_value_reporting_level;
|
||||
int64_t next_value_reporting_level_lowest_equivalent;
|
||||
};
|
||||
|
||||
/**
|
||||
* The basic iterator. This is a generic structure
|
||||
* that supports all of the types of iteration. Use
|
||||
* the appropriate initialiser to get the desired
|
||||
* iteration.
|
||||
*
|
||||
* @
|
||||
*/
|
||||
struct hdr_iter
|
||||
{
|
||||
const struct hdr_histogram* h;
|
||||
/** raw index into the counts array */
|
||||
int32_t counts_index;
|
||||
/** snapshot of the length at the time the iterator is created */
|
||||
int64_t total_count;
|
||||
/** value directly from array for the current counts_index */
|
||||
int64_t count;
|
||||
/** sum of all of the counts up to and including the count at this index */
|
||||
int64_t cumulative_count;
|
||||
/** The current value based on counts_index */
|
||||
int64_t value;
|
||||
int64_t highest_equivalent_value;
|
||||
int64_t lowest_equivalent_value;
|
||||
int64_t median_equivalent_value;
|
||||
int64_t value_iterated_from;
|
||||
int64_t value_iterated_to;
|
||||
|
||||
union
|
||||
{
|
||||
struct hdr_iter_percentiles percentiles;
|
||||
struct hdr_iter_recorded recorded;
|
||||
struct hdr_iter_linear linear;
|
||||
struct hdr_iter_log log;
|
||||
} specifics;
|
||||
|
||||
bool (* _next_fp)(struct hdr_iter* iter);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Initalises the basic iterator.
|
||||
*
|
||||
* @param itr 'This' pointer
|
||||
* @param h The histogram to iterate over
|
||||
*/
|
||||
void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with percentiles.
|
||||
*/
|
||||
void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with recorded values.
|
||||
*/
|
||||
void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with linear values.
|
||||
*/
|
||||
void hdr_iter_linear_init(
|
||||
struct hdr_iter* iter,
|
||||
const struct hdr_histogram* h,
|
||||
int64_t value_units_per_bucket);
|
||||
|
||||
/**
|
||||
* Update the iterator value units per bucket
|
||||
*/
|
||||
void hdr_iter_linear_set_value_units_per_bucket(struct hdr_iter* iter, int64_t value_units_per_bucket);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with logarithmic values
|
||||
*/
|
||||
void hdr_iter_log_init(
|
||||
struct hdr_iter* iter,
|
||||
const struct hdr_histogram* h,
|
||||
int64_t value_units_first_bucket,
|
||||
double log_base);
|
||||
|
||||
/**
|
||||
* Iterate to the next value for the iterator. If there are no more values
|
||||
* available return faluse.
|
||||
*
|
||||
* @param itr 'This' pointer
|
||||
* @return 'false' if there are no values remaining for this iterator.
|
||||
*/
|
||||
bool hdr_iter_next(struct hdr_iter* iter);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CLASSIC,
|
||||
CSV
|
||||
} format_type;
|
||||
|
||||
/**
|
||||
* Print out a percentile based histogram to the supplied stream. Note that
|
||||
* this call will not flush the FILE, this is left up to the user.
|
||||
*
|
||||
* @param h 'This' pointer
|
||||
* @param stream The FILE to write the output to
|
||||
* @param ticks_per_half_distance The number of iteration steps per half-distance to 100%
|
||||
* @param value_scale Scale the output values by this amount
|
||||
* @param format_type Format to use, e.g. CSV.
|
||||
* @return 0 on success, error code on failure. EIO if an error occurs writing
|
||||
* the output.
|
||||
*/
|
||||
int hdr_percentiles_print(
|
||||
struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance,
|
||||
double value_scale, format_type format);
|
||||
|
||||
/**
|
||||
* Internal allocation methods, used by hdr_dbl_histogram.
|
||||
*/
|
||||
struct hdr_histogram_bucket_config
|
||||
{
|
||||
int64_t lowest_trackable_value;
|
||||
int64_t highest_trackable_value;
|
||||
int64_t unit_magnitude;
|
||||
int64_t significant_figures;
|
||||
int32_t sub_bucket_half_count_magnitude;
|
||||
int32_t sub_bucket_half_count;
|
||||
int64_t sub_bucket_mask;
|
||||
int32_t sub_bucket_count;
|
||||
int32_t bucket_count;
|
||||
int32_t counts_len;
|
||||
};
|
||||
|
||||
int hdr_calculate_bucket_config(
|
||||
int64_t lowest_trackable_value,
|
||||
int64_t highest_trackable_value,
|
||||
int significant_figures,
|
||||
struct hdr_histogram_bucket_config* cfg);
|
||||
|
||||
void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg);
|
||||
|
||||
int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
int64_t hdr_next_non_equivalent_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
int64_t hdr_median_equivalent_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Used to reset counters after importing data manuallying into the histogram, used by the logging code
|
||||
* and other custom serialisation tools.
|
||||
*/
|
||||
void hdr_reset_internal_counters(struct hdr_histogram* h);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,9 +0,0 @@
|
||||
/hiredis-test
|
||||
/examples/hiredis-example*
|
||||
/*.o
|
||||
/*.so
|
||||
/*.dylib
|
||||
/*.a
|
||||
/*.pc
|
||||
*.dSYM
|
||||
tags
|
||||
Vendored
-131
@@ -1,131 +0,0 @@
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
dist: bionic
|
||||
|
||||
branches:
|
||||
only:
|
||||
- staging
|
||||
- trying
|
||||
- master
|
||||
- /^release\/.*$/
|
||||
|
||||
install:
|
||||
- if [ "$BITS" == "64" ]; then
|
||||
wget https://github.com/redis/redis/archive/6.0.6.tar.gz;
|
||||
tar -xzvf 6.0.6.tar.gz;
|
||||
pushd redis-6.0.6 && BUILD_TLS=yes make && export PATH=$PWD/src:$PATH && popd;
|
||||
fi
|
||||
|
||||
before_script:
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.6.2-10.13-HighSierra.pkg;
|
||||
sudo installer -pkg MacPorts-2.6.2-10.13-HighSierra.pkg -target /;
|
||||
export PATH=$PATH:/opt/local/bin && sudo port -v selfupdate;
|
||||
sudo port -N install openssl redis;
|
||||
fi;
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:chris-lea/redis-server'
|
||||
packages:
|
||||
- libc6-dbg
|
||||
- libc6-dev
|
||||
- libc6:i386
|
||||
- libc6-dev-i386
|
||||
- libc6-dbg:i386
|
||||
- gcc-multilib
|
||||
- g++-multilib
|
||||
- libssl-dev
|
||||
- libssl-dev:i386
|
||||
- valgrind
|
||||
- redis
|
||||
|
||||
env:
|
||||
- BITS="32"
|
||||
- BITS="64"
|
||||
|
||||
script:
|
||||
- EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON";
|
||||
if [ "$BITS" == "64" ]; then
|
||||
EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DENABLE_SSL_TESTS:BOOL=ON";
|
||||
fi;
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
if [ "$BITS" == "32" ]; then
|
||||
CFLAGS="-m32 -Werror";
|
||||
CXXFLAGS="-m32 -Werror";
|
||||
LDFLAGS="-m32";
|
||||
EXTRA_CMAKE_OPTS=;
|
||||
else
|
||||
CFLAGS="-Werror";
|
||||
CXXFLAGS="-Werror";
|
||||
fi;
|
||||
else
|
||||
TEST_PREFIX="valgrind --track-origins=yes --leak-check=full";
|
||||
if [ "$BITS" == "32" ]; then
|
||||
CFLAGS="-m32 -Werror";
|
||||
CXXFLAGS="-m32 -Werror";
|
||||
LDFLAGS="-m32";
|
||||
EXTRA_CMAKE_OPTS=;
|
||||
else
|
||||
CFLAGS="-Werror";
|
||||
CXXFLAGS="-Werror";
|
||||
fi;
|
||||
fi;
|
||||
export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS
|
||||
- make && make clean;
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
if [ "$BITS" == "64" ]; then
|
||||
OPENSSL_PREFIX="$(ls -d /usr/local/Cellar/openssl@1.1/*)" USE_SSL=1 make;
|
||||
fi;
|
||||
else
|
||||
USE_SSL=1 make;
|
||||
fi;
|
||||
- mkdir build/ && cd build/
|
||||
- cmake .. ${EXTRA_CMAKE_OPTS}
|
||||
- make VERBOSE=1
|
||||
- if [ "$BITS" == "64" ]; then
|
||||
TEST_SSL=1 SKIPS_AS_FAILS=1 ctest -V;
|
||||
else
|
||||
SKIPS_AS_FAILS=1 ctest -V;
|
||||
fi;
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# Windows MinGW cross compile on Linux
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: mingw
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- ninja-build
|
||||
- gcc-mingw-w64-x86-64
|
||||
- g++-mingw-w64-x86-64
|
||||
script:
|
||||
- mkdir build && cd build
|
||||
- CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_WITH_INSTALL_RPATH=on
|
||||
- ninja -v
|
||||
|
||||
# Windows MSVC 2017
|
||||
- os: windows
|
||||
compiler: msvc
|
||||
env:
|
||||
- MATRIX_EVAL="CC=cl.exe && CXX=cl.exe"
|
||||
before_install:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
install:
|
||||
- choco install ninja
|
||||
- choco install -y memurai-developer
|
||||
script:
|
||||
- mkdir build && cd build
|
||||
- cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat' amd64 '&&'
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_EXAMPLES=ON '&&' ninja -v
|
||||
- ./hiredis-test.exe
|
||||
Vendored
-345
@@ -1,345 +0,0 @@
|
||||
## [1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) - (2020-08-03)
|
||||
|
||||
Announcing Hiredis v1.0.0, which adds support for RESP3, SSL connections, allocator injection, and better Windows support! :tada:
|
||||
|
||||
_A big thanks to everyone who helped with this release. The following list includes everyone who contributed at least five lines, sorted by lines contributed._ :sparkling_heart:
|
||||
|
||||
[Michael Grunder](https://github.com/michael-grunder), [Yossi Gottlieb](https://github.com/yossigo),
|
||||
[Mark Nunberg](https://github.com/mnunberg), [Marcus Geelnard](https://github.com/mbitsnbites),
|
||||
[Justin Brewer](https://github.com/justinbrewer), [Valentino Geron](https://github.com/valentinogeron),
|
||||
[Minun Dragonation](https://github.com/dragonation), [Omri Steiner](https://github.com/OmriSteiner),
|
||||
[Sangmoon Yi](https://github.com/jman-krafton), [Jinjiazh](https://github.com/jinjiazhang),
|
||||
[Odin Hultgren Van Der Horst](https://github.com/Miniwoffer), [Muhammad Zahalqa](https://github.com/tryfinally),
|
||||
[Nick Rivera](https://github.com/heronr), [Qi Yang](https://github.com/movebean),
|
||||
[kevin1018](https://github.com/kevin1018)
|
||||
|
||||
[Full Changelog](https://github.com/redis/hiredis/compare/v0.14.1...v1.0.0)
|
||||
|
||||
**BREAKING CHANGES**:
|
||||
|
||||
* `redisOptions` now has two timeout fields. One for connecting, and one for commands. If you're presently using `options->timeout` you will need to change it to use `options->connect_timeout`. (See [example](https://github.com/redis/hiredis/commit/38b5ae543f5c99eb4ccabbe277770fc6bc81226f#diff-86ba39d37aa829c8c82624cce4f049fbL36))
|
||||
|
||||
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now protocol errors. This is consistent
|
||||
with the RESP specification. On 32-bit platforms, the upper bound is lowered to `SIZE_MAX`.
|
||||
|
||||
* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter.
|
||||
|
||||
**New features:**
|
||||
- Support for RESP3
|
||||
[\#697](https://github.com/redis/hiredis/pull/697),
|
||||
[\#805](https://github.com/redis/hiredis/pull/805),
|
||||
[\#819](https://github.com/redis/hiredis/pull/819),
|
||||
[\#841](https://github.com/redis/hiredis/pull/841)
|
||||
([Yossi Gottlieb](https://github.com/yossigo), [Michael Grunder](https://github.com/michael-grunder))
|
||||
- Support for SSL connections
|
||||
[\#645](https://github.com/redis/hiredis/pull/645),
|
||||
[\#699](https://github.com/redis/hiredis/pull/699),
|
||||
[\#702](https://github.com/redis/hiredis/pull/702),
|
||||
[\#708](https://github.com/redis/hiredis/pull/708),
|
||||
[\#711](https://github.com/redis/hiredis/pull/711),
|
||||
[\#821](https://github.com/redis/hiredis/pull/821),
|
||||
[more](https://github.com/redis/hiredis/pulls?q=is%3Apr+is%3Amerged+SSL)
|
||||
([Mark Nunberg](https://github.com/mnunberg), [Yossi Gottlieb](https://github.com/yossigo))
|
||||
- Run-time allocator injection
|
||||
[\#800](https://github.com/redis/hiredis/pull/800)
|
||||
([Michael Grunder](https://github.com/michael-grunder))
|
||||
- Improved Windows support (including MinGW and Windows CI)
|
||||
[\#652](https://github.com/redis/hiredis/pull/652),
|
||||
[\#663](https://github.com/redis/hiredis/pull/663)
|
||||
([Marcus Geelnard](https://www.bitsnbites.eu/author/m/))
|
||||
- Adds support for distinct connect and command timeouts
|
||||
[\#839](https://github.com/redis/hiredis/pull/839),
|
||||
[\#829](https://github.com/redis/hiredis/pull/829)
|
||||
([Valentino Geron](https://github.com/valentinogeron))
|
||||
- Add generic pointer and destructor to `redisContext` that users can use for context.
|
||||
[\#855](https://github.com/redis/hiredis/pull/855)
|
||||
([Michael Grunder](https://github.com/michael-grunder))
|
||||
|
||||
**Closed issues (that involved code changes):**
|
||||
|
||||
- Makefile does not install TLS libraries [\#809](https://github.com/redis/hiredis/issues/809)
|
||||
- redisConnectWithOptions should not set command timeout [\#722](https://github.com/redis/hiredis/issues/722), [\#829](https://github.com/redis/hiredis/pull/829) ([valentinogeron](https://github.com/valentinogeron))
|
||||
- Fix integer overflow in `sdsrange` [\#827](https://github.com/redis/hiredis/issues/827)
|
||||
- INFO & CLUSTER commands failed when using RESP3 [\#802](https://github.com/redis/hiredis/issues/802)
|
||||
- Windows compatibility patches [\#687](https://github.com/redis/hiredis/issues/687), [\#838](https://github.com/redis/hiredis/issues/838), [\#842](https://github.com/redis/hiredis/issues/842)
|
||||
- RESP3 PUSH messages incorrectly use pending callback [\#825](https://github.com/redis/hiredis/issues/825)
|
||||
- Asynchronous PSUBSCRIBE command fails when using RESP3 [\#815](https://github.com/redis/hiredis/issues/815)
|
||||
- New SSL API [\#804](https://github.com/redis/hiredis/issues/804), [\#813](https://github.com/redis/hiredis/issues/813)
|
||||
- Hard-coded limit of nested reply depth [\#794](https://github.com/redis/hiredis/issues/794)
|
||||
- Fix TCP_NODELAY in Windows/OSX [\#679](https://github.com/redis/hiredis/issues/679), [\#690](https://github.com/redis/hiredis/issues/690), [\#779](https://github.com/redis/hiredis/issues/779), [\#785](https://github.com/redis/hiredis/issues/785),
|
||||
- Added timers to libev adapter. [\#778](https://github.com/redis/hiredis/issues/778), [\#795](https://github.com/redis/hiredis/pull/795)
|
||||
- Initialization discards const qualifier [\#777](https://github.com/redis/hiredis/issues/777)
|
||||
- \[BUG\]\[MinGW64\] Error setting socket timeout [\#775](https://github.com/redis/hiredis/issues/775)
|
||||
- undefined reference to hi_malloc [\#769](https://github.com/redis/hiredis/issues/769)
|
||||
- hiredis pkg-config file incorrectly ignores multiarch libdir spec'n [\#767](https://github.com/redis/hiredis/issues/767)
|
||||
- Don't use -G to build shared object on Solaris [\#757](https://github.com/redis/hiredis/issues/757)
|
||||
- error when make USE\_SSL=1 [\#748](https://github.com/redis/hiredis/issues/748)
|
||||
- Allow to change SSL Mode [\#646](https://github.com/redis/hiredis/issues/646)
|
||||
- hiredis/adapters/libevent.h memleak [\#618](https://github.com/redis/hiredis/issues/618)
|
||||
- redisLibuvPoll crash when server closes the connetion [\#545](https://github.com/redis/hiredis/issues/545)
|
||||
- about redisAsyncDisconnect question [\#518](https://github.com/redis/hiredis/issues/518)
|
||||
- hiredis adapters libuv error for help [\#508](https://github.com/redis/hiredis/issues/508)
|
||||
- API/ABI changes analysis [\#506](https://github.com/redis/hiredis/issues/506)
|
||||
- Memory leak patch in Redis [\#502](https://github.com/redis/hiredis/issues/502)
|
||||
- Remove the depth limitation [\#421](https://github.com/redis/hiredis/issues/421)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Move SSL management to a distinct private pointer [\#855](https://github.com/redis/hiredis/pull/855) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Move include to sockcompat.h to maintain style [\#850](https://github.com/redis/hiredis/pull/850) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Remove erroneous tag and add license to push example [\#849](https://github.com/redis/hiredis/pull/849) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- fix windows compiling with mingw [\#848](https://github.com/redis/hiredis/pull/848) ([rmalizia44](https://github.com/rmalizia44))
|
||||
- Some Windows quality of life improvements. [\#846](https://github.com/redis/hiredis/pull/846) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use \_WIN32 define instead of WIN32 [\#845](https://github.com/redis/hiredis/pull/845) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Non Linux CI fixes [\#844](https://github.com/redis/hiredis/pull/844) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Resp3 oob push support [\#841](https://github.com/redis/hiredis/pull/841) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- fix \#785: defer TCP\_NODELAY in async tcp connections [\#836](https://github.com/redis/hiredis/pull/836) ([OmriSteiner](https://github.com/OmriSteiner))
|
||||
- sdsrange overflow fix [\#830](https://github.com/redis/hiredis/pull/830) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use explicit pointer casting for c++ compatibility [\#826](https://github.com/redis/hiredis/pull/826) ([aureus1](https://github.com/aureus1))
|
||||
- Document allocator injection and completeness fix in test.c [\#824](https://github.com/redis/hiredis/pull/824) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use unique names for allocator struct members [\#823](https://github.com/redis/hiredis/pull/823) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- New SSL API to replace redisSecureConnection\(\). [\#821](https://github.com/redis/hiredis/pull/821) ([yossigo](https://github.com/yossigo))
|
||||
- Add logic to handle RESP3 push messages [\#819](https://github.com/redis/hiredis/pull/819) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use standrad isxdigit instead of custom helper function. [\#814](https://github.com/redis/hiredis/pull/814) ([tryfinally](https://github.com/tryfinally))
|
||||
- Fix missing SSL build/install options. [\#812](https://github.com/redis/hiredis/pull/812) ([yossigo](https://github.com/yossigo))
|
||||
- Add link to ABI tracker [\#808](https://github.com/redis/hiredis/pull/808) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Resp3 verbatim string support [\#805](https://github.com/redis/hiredis/pull/805) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Allow users to replace allocator and handle OOM everywhere. [\#800](https://github.com/redis/hiredis/pull/800) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Remove nested depth limitation. [\#797](https://github.com/redis/hiredis/pull/797) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Attempt to fix compilation on Solaris [\#796](https://github.com/redis/hiredis/pull/796) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Support timeouts in libev adapater [\#795](https://github.com/redis/hiredis/pull/795) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Fix pkgconfig when installing to a custom lib dir [\#793](https://github.com/redis/hiredis/pull/793) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Fix USE\_SSL=1 make/cmake on OSX and CMake tests [\#789](https://github.com/redis/hiredis/pull/789) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use correct libuv call on Windows [\#784](https://github.com/redis/hiredis/pull/784) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Added CMake package config and fixed hiredis\_ssl on Windows [\#783](https://github.com/redis/hiredis/pull/783) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- CMake: Set hiredis\_ssl shared object version. [\#780](https://github.com/redis/hiredis/pull/780) ([yossigo](https://github.com/yossigo))
|
||||
- Win32 tests and timeout fix [\#776](https://github.com/redis/hiredis/pull/776) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Provides an optional cleanup callback for async data. [\#768](https://github.com/redis/hiredis/pull/768) ([heronr](https://github.com/heronr))
|
||||
- Housekeeping fixes [\#764](https://github.com/redis/hiredis/pull/764) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- install alloc.h [\#756](https://github.com/redis/hiredis/pull/756) ([ch1aki](https://github.com/ch1aki))
|
||||
- fix spelling mistakes [\#746](https://github.com/redis/hiredis/pull/746) ([ShooterIT](https://github.com/ShooterIT))
|
||||
- Free the reply in redisGetReply when passed NULL [\#741](https://github.com/redis/hiredis/pull/741) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Fix dead code in sslLogCallback relating to should\_log variable. [\#737](https://github.com/redis/hiredis/pull/737) ([natoscott](https://github.com/natoscott))
|
||||
- Fix typo in dict.c. [\#731](https://github.com/redis/hiredis/pull/731) ([Kevin-Xi](https://github.com/Kevin-Xi))
|
||||
- Adding an option to DISABLE\_TESTS [\#727](https://github.com/redis/hiredis/pull/727) ([pbotros](https://github.com/pbotros))
|
||||
- Update README with SSL support. [\#720](https://github.com/redis/hiredis/pull/720) ([yossigo](https://github.com/yossigo))
|
||||
- Fixes leaks in unit tests [\#715](https://github.com/redis/hiredis/pull/715) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- SSL Tests [\#711](https://github.com/redis/hiredis/pull/711) ([yossigo](https://github.com/yossigo))
|
||||
- SSL Reorganization [\#708](https://github.com/redis/hiredis/pull/708) ([yossigo](https://github.com/yossigo))
|
||||
- Fix MSVC build. [\#706](https://github.com/redis/hiredis/pull/706) ([yossigo](https://github.com/yossigo))
|
||||
- SSL: Properly report SSL\_connect\(\) errors. [\#702](https://github.com/redis/hiredis/pull/702) ([yossigo](https://github.com/yossigo))
|
||||
- Silent SSL trace to stdout by default. [\#699](https://github.com/redis/hiredis/pull/699) ([yossigo](https://github.com/yossigo))
|
||||
- Port RESP3 support from Redis. [\#697](https://github.com/redis/hiredis/pull/697) ([yossigo](https://github.com/yossigo))
|
||||
- Removed whitespace before newline [\#691](https://github.com/redis/hiredis/pull/691) ([Miniwoffer](https://github.com/Miniwoffer))
|
||||
- Add install adapters header files [\#688](https://github.com/redis/hiredis/pull/688) ([kevin1018](https://github.com/kevin1018))
|
||||
- Remove unnecessary null check before free [\#684](https://github.com/redis/hiredis/pull/684) ([qlyoung](https://github.com/qlyoung))
|
||||
- redisReaderGetReply leak memory [\#671](https://github.com/redis/hiredis/pull/671) ([movebean](https://github.com/movebean))
|
||||
- fix timeout code in windows [\#670](https://github.com/redis/hiredis/pull/670) ([jman-krafton](https://github.com/jman-krafton))
|
||||
- test: fix errstr matching for musl libc [\#665](https://github.com/redis/hiredis/pull/665) ([ghost](https://github.com/ghost))
|
||||
- Windows: MinGW fixes and Windows Travis builders [\#663](https://github.com/redis/hiredis/pull/663) ([mbitsnbites](https://github.com/mbitsnbites))
|
||||
- The setsockopt and getsockopt API diffs from BSD socket and WSA one [\#662](https://github.com/redis/hiredis/pull/662) ([dragonation](https://github.com/dragonation))
|
||||
- Fix Compile Error On Windows \(Visual Studio\) [\#658](https://github.com/redis/hiredis/pull/658) ([jinjiazhang](https://github.com/jinjiazhang))
|
||||
- Fix NXDOMAIN test case [\#653](https://github.com/redis/hiredis/pull/653) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Add MinGW support [\#652](https://github.com/redis/hiredis/pull/652) ([mbitsnbites](https://github.com/mbitsnbites))
|
||||
- SSL Support [\#645](https://github.com/redis/hiredis/pull/645) ([mnunberg](https://github.com/mnunberg))
|
||||
- Fix Invalid argument after redisAsyncConnectUnix [\#644](https://github.com/redis/hiredis/pull/644) ([codehz](https://github.com/codehz))
|
||||
- Makefile: use predefined AR [\#632](https://github.com/redis/hiredis/pull/632) ([Mic92](https://github.com/Mic92))
|
||||
- FreeBSD build fix [\#628](https://github.com/redis/hiredis/pull/628) ([devnexen](https://github.com/devnexen))
|
||||
- Fix errors not propagating properly with libuv.h. [\#624](https://github.com/redis/hiredis/pull/624) ([yossigo](https://github.com/yossigo))
|
||||
- Update README.md [\#621](https://github.com/redis/hiredis/pull/621) ([Crunsher](https://github.com/Crunsher))
|
||||
- Fix redisBufferRead documentation [\#620](https://github.com/redis/hiredis/pull/620) ([hacst](https://github.com/hacst))
|
||||
- Add CPPFLAGS to REAL\_CFLAGS [\#614](https://github.com/redis/hiredis/pull/614) ([thomaslee](https://github.com/thomaslee))
|
||||
- Update createArray to take size\_t [\#597](https://github.com/redis/hiredis/pull/597) ([justinbrewer](https://github.com/justinbrewer))
|
||||
- fix common realloc mistake and add null check more [\#580](https://github.com/redis/hiredis/pull/580) ([charsyam](https://github.com/charsyam))
|
||||
- Proper error reporting for connect failures [\#578](https://github.com/redis/hiredis/pull/578) ([mnunberg](https://github.com/mnunberg))
|
||||
|
||||
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
||||
|
||||
## [1.0.0-rc1](https://github.com/redis/hiredis/tree/v1.0.0-rc1) - (2020-07-29)
|
||||
|
||||
_Note: There were no changes to code between v1.0.0-rc1 and v1.0.0 so see v1.0.0 for changelog_
|
||||
|
||||
### 0.14.1 (2020-03-13)
|
||||
|
||||
* Adds safe allocation wrappers (CVE-2020-7105, #747, #752) (Michael Grunder)
|
||||
|
||||
### 0.14.0 (2018-09-25)
|
||||
**BREAKING CHANGES**:
|
||||
|
||||
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
||||
|
||||
User code should compare this to `size_t` values as well.
|
||||
If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
|
||||
|
||||
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
|
||||
* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])
|
||||
* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622])
|
||||
* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8])
|
||||
* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8])
|
||||
* Fix bulk and multi-bulk length truncation (Justin Brewer [109197])
|
||||
* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94])
|
||||
* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6])
|
||||
* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1])
|
||||
* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b])
|
||||
* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96])
|
||||
* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234])
|
||||
* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129])
|
||||
* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c])
|
||||
* Fix libevent leak (zfz [515228])
|
||||
* Clean up GCC warning (Ichito Nagata [2ec774])
|
||||
* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88])
|
||||
* Solaris compilation fix (Donald Whyte [41b07d])
|
||||
* Reorder linker arguments when building examples (Tustfarm-heart [06eedd])
|
||||
* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999])
|
||||
* libuv use after free fix (Paul Scott [cbb956])
|
||||
* Properly close socket fd on reconnect attempt (WSL [64d1ec])
|
||||
* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78])
|
||||
* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5])
|
||||
* Update libevent (Chris Xin [386802])
|
||||
* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e])
|
||||
* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6])
|
||||
* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3])
|
||||
* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb])
|
||||
* Compatibility fix for strerror_r (Tom Lee [bb1747])
|
||||
* Properly detect integer parse/overflow errors (Justin Brewer [93421f])
|
||||
* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40])
|
||||
* Catch a buffer overflow when formatting the error message
|
||||
* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13
|
||||
* Fix warnings, when compiled with -Wshadow
|
||||
* Make hiredis compile in Cygwin on Windows, now CI-tested
|
||||
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
|
||||
protocol errors. This is consistent with the RESP specification. On 32-bit
|
||||
platforms, the upper bound is lowered to `SIZE_MAX`.
|
||||
|
||||
* Remove backwards compatibility macro's
|
||||
|
||||
This removes the following old function aliases, use the new name now:
|
||||
|
||||
| Old | New |
|
||||
| --------------------------- | ---------------------- |
|
||||
| redisReplyReaderCreate | redisReaderCreate |
|
||||
| redisReplyReaderCreate | redisReaderCreate |
|
||||
| redisReplyReaderFree | redisReaderFree |
|
||||
| redisReplyReaderFeed | redisReaderFeed |
|
||||
| redisReplyReaderGetReply | redisReaderGetReply |
|
||||
| redisReplyReaderSetPrivdata | redisReaderSetPrivdata |
|
||||
| redisReplyReaderGetObject | redisReaderGetObject |
|
||||
| redisReplyReaderGetError | redisReaderGetError |
|
||||
|
||||
* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS`
|
||||
|
||||
Previously it broke some builds for people that had `DEBUG` set to some arbitrary value,
|
||||
due to debugging other software.
|
||||
By renaming we avoid unintentional name clashes.
|
||||
|
||||
Simply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again.
|
||||
|
||||
### 0.13.3 (2015-09-16)
|
||||
|
||||
* Revert "Clear `REDIS_CONNECTED` flag when connection is closed".
|
||||
* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni)
|
||||
|
||||
|
||||
If the `REDIS_CONNECTED` flag is cleared,
|
||||
the async onDisconnect callback function will never be called.
|
||||
This causes problems as the disconnect is never reported back to the user.
|
||||
|
||||
### 0.13.2 (2015-08-25)
|
||||
|
||||
* Prevent crash on pending replies in async code (Thanks, @switch-st)
|
||||
* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs)
|
||||
* Add MacOS X addapter (Thanks, @dizzus)
|
||||
* Add Qt adapter (Thanks, Pietro Cerutti)
|
||||
* Add Ivykis adapter (Thanks, Gergely Nagy)
|
||||
|
||||
All adapters are provided as is and are only tested where possible.
|
||||
|
||||
### 0.13.1 (2015-05-03)
|
||||
|
||||
This is a bug fix release.
|
||||
The new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code.
|
||||
Another commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects.
|
||||
Other non-C99 code can now use hiredis as usual again.
|
||||
Sorry for the inconvenience.
|
||||
|
||||
* Fix memory leak in async reply handling (Salvatore Sanfilippo)
|
||||
* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa)
|
||||
|
||||
### 0.13.0 (2015-04-16)
|
||||
|
||||
This release adds a minimal Windows compatibility layer.
|
||||
The parser, standalone since v0.12.0, can now be compiled on Windows
|
||||
(and thus used in other client libraries as well)
|
||||
|
||||
* Windows compatibility layer for parser code (tzickel)
|
||||
* Properly escape data printed to PKGCONF file (Dan Skorupski)
|
||||
* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff)
|
||||
* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra)
|
||||
|
||||
### 0.12.1 (2015-01-26)
|
||||
|
||||
* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location
|
||||
* Fix `make test` as 32 bit build on 64 bit platform
|
||||
|
||||
### 0.12.0 (2015-01-22)
|
||||
|
||||
* Add optional KeepAlive support
|
||||
|
||||
* Try again on EINTR errors
|
||||
|
||||
* Add libuv adapter
|
||||
|
||||
* Add IPv6 support
|
||||
|
||||
* Remove possibility of multiple close on same fd
|
||||
|
||||
* Add ability to bind source address on connect
|
||||
|
||||
* Add redisConnectFd() and redisFreeKeepFd()
|
||||
|
||||
* Fix getaddrinfo() memory leak
|
||||
|
||||
* Free string if it is unused (fixes memory leak)
|
||||
|
||||
* Improve redisAppendCommandArgv performance 2.5x
|
||||
|
||||
* Add support for SO_REUSEADDR
|
||||
|
||||
* Fix redisvFormatCommand format parsing
|
||||
|
||||
* Add GLib 2.0 adapter
|
||||
|
||||
* Refactor reading code into read.c
|
||||
|
||||
* Fix errno error buffers to not clobber errors
|
||||
|
||||
* Generate pkgconf during build
|
||||
|
||||
* Silence _BSD_SOURCE warnings
|
||||
|
||||
* Improve digit counting for multibulk creation
|
||||
|
||||
|
||||
### 0.11.0
|
||||
|
||||
* Increase the maximum multi-bulk reply depth to 7.
|
||||
|
||||
* Increase the read buffer size from 2k to 16k.
|
||||
|
||||
* Use poll(2) instead of select(2) to support large fds (>= 1024).
|
||||
|
||||
### 0.10.1
|
||||
|
||||
* Makefile overhaul. Important to check out if you override one or more
|
||||
variables using environment variables or via arguments to the "make" tool.
|
||||
|
||||
* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements
|
||||
being created by the default reply object functions.
|
||||
|
||||
* Issue #43: Don't crash in an asynchronous context when Redis returns an error
|
||||
reply after the connection has been made (this happens when the maximum
|
||||
number of connections is reached).
|
||||
|
||||
### 0.10.0
|
||||
|
||||
* See commit log.
|
||||
Vendored
-165
@@ -1,165 +0,0 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0)
|
||||
INCLUDE(GNUInstallDirs)
|
||||
PROJECT(hiredis)
|
||||
|
||||
OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF)
|
||||
OPTION(DISABLE_TESTS "If tests should be compiled or not" OFF)
|
||||
OPTION(ENABLE_SSL_TESTS, "Should we test SSL connections" OFF)
|
||||
|
||||
MACRO(getVersionBit name)
|
||||
SET(VERSION_REGEX "^#define ${name} (.+)$")
|
||||
FILE(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/hiredis.h"
|
||||
VERSION_BIT REGEX ${VERSION_REGEX})
|
||||
STRING(REGEX REPLACE ${VERSION_REGEX} "\\1" ${name} "${VERSION_BIT}")
|
||||
ENDMACRO(getVersionBit)
|
||||
|
||||
getVersionBit(HIREDIS_MAJOR)
|
||||
getVersionBit(HIREDIS_MINOR)
|
||||
getVersionBit(HIREDIS_PATCH)
|
||||
getVersionBit(HIREDIS_SONAME)
|
||||
SET(VERSION "${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}")
|
||||
MESSAGE("Detected version: ${VERSION}")
|
||||
|
||||
PROJECT(hiredis VERSION "${VERSION}")
|
||||
|
||||
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
|
||||
|
||||
SET(hiredis_sources
|
||||
alloc.c
|
||||
async.c
|
||||
dict.c
|
||||
hiredis.c
|
||||
net.c
|
||||
read.c
|
||||
sds.c
|
||||
sockcompat.c)
|
||||
|
||||
SET(hiredis_sources ${hiredis_sources})
|
||||
|
||||
IF(WIN32)
|
||||
ADD_COMPILE_DEFINITIONS(_CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN)
|
||||
ENDIF()
|
||||
|
||||
ADD_LIBRARY(hiredis SHARED ${hiredis_sources})
|
||||
|
||||
SET_TARGET_PROPERTIES(hiredis
|
||||
PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE
|
||||
VERSION "${HIREDIS_SONAME}")
|
||||
IF(WIN32 OR MINGW)
|
||||
TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32)
|
||||
ENDIF()
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $<INSTALL_INTERFACE:.> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
|
||||
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)
|
||||
|
||||
INSTALL(TARGETS hiredis
|
||||
EXPORT hiredis-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(DIRECTORY adapters
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
export(EXPORT hiredis-targets
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis-targets.cmake"
|
||||
NAMESPACE hiredis::)
|
||||
|
||||
SET(CMAKE_CONF_INSTALL_DIR share/hiredis)
|
||||
SET(INCLUDE_INSTALL_DIR include)
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(hiredis-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR}
|
||||
PATH_VARS INCLUDE_INSTALL_DIR)
|
||||
|
||||
INSTALL(EXPORT hiredis-targets
|
||||
FILE hiredis-targets.cmake
|
||||
NAMESPACE hiredis::
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
|
||||
|
||||
IF(ENABLE_SSL)
|
||||
IF (NOT OPENSSL_ROOT_DIR)
|
||||
IF (APPLE)
|
||||
SET(OPENSSL_ROOT_DIR "/usr/local/opt/openssl")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
FIND_PACKAGE(OpenSSL REQUIRED)
|
||||
SET(hiredis_ssl_sources
|
||||
ssl.c)
|
||||
ADD_LIBRARY(hiredis_ssl SHARED
|
||||
${hiredis_ssl_sources})
|
||||
|
||||
IF (APPLE)
|
||||
SET_PROPERTY(TARGET hiredis_ssl PROPERTY LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup")
|
||||
ENDIF()
|
||||
|
||||
SET_TARGET_PROPERTIES(hiredis_ssl
|
||||
PROPERTIES
|
||||
WINDOWS_EXPORT_ALL_SYMBOLS TRUE
|
||||
VERSION "${HIREDIS_SONAME}")
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}")
|
||||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})
|
||||
IF (WIN32 OR MINGW)
|
||||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE hiredis)
|
||||
ENDIF()
|
||||
CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)
|
||||
|
||||
INSTALL(TARGETS hiredis_ssl
|
||||
EXPORT hiredis_ssl-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
INSTALL(FILES hiredis_ssl.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
export(EXPORT hiredis_ssl-targets
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-targets.cmake"
|
||||
NAMESPACE hiredis::)
|
||||
|
||||
SET(CMAKE_CONF_INSTALL_DIR share/hiredis_ssl)
|
||||
configure_package_config_file(hiredis_ssl-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR}
|
||||
PATH_VARS INCLUDE_INSTALL_DIR)
|
||||
|
||||
INSTALL(EXPORT hiredis_ssl-targets
|
||||
FILE hiredis_ssl-targets.cmake
|
||||
NAMESPACE hiredis::
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
ENDIF()
|
||||
|
||||
IF(NOT DISABLE_TESTS)
|
||||
ENABLE_TESTING()
|
||||
ADD_EXECUTABLE(hiredis-test test.c)
|
||||
IF(ENABLE_SSL_TESTS)
|
||||
ADD_DEFINITIONS(-DHIREDIS_TEST_SSL=1)
|
||||
TARGET_LINK_LIBRARIES(hiredis-test hiredis hiredis_ssl)
|
||||
ELSE()
|
||||
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
|
||||
ENDIF()
|
||||
ADD_TEST(NAME hiredis-test
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)
|
||||
ENDIF()
|
||||
|
||||
# Add examples
|
||||
IF(ENABLE_EXAMPLES)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
ENDIF(ENABLE_EXAMPLES)
|
||||
Vendored
-29
@@ -1,29 +0,0 @@
|
||||
Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Redis nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Vendored
-308
@@ -1,308 +0,0 @@
|
||||
# Hiredis Makefile
|
||||
# Copyright (C) 2010-2011 Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
# This file is released under the BSD license, see the COPYING file
|
||||
|
||||
OBJ=alloc.o net.o hiredis.o sds.o async.o read.o sockcompat.o
|
||||
SSL_OBJ=ssl.o
|
||||
EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib hiredis-example-push
|
||||
ifeq ($(USE_SSL),1)
|
||||
EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl
|
||||
endif
|
||||
TESTS=hiredis-test
|
||||
LIBNAME=libhiredis
|
||||
PKGCONFNAME=hiredis.pc
|
||||
SSL_LIBNAME=libhiredis_ssl
|
||||
SSL_PKGCONFNAME=hiredis_ssl.pc
|
||||
|
||||
HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')
|
||||
HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}')
|
||||
HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}')
|
||||
HIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}')
|
||||
|
||||
# Installation related variables and target
|
||||
PREFIX?=/usr/local
|
||||
INCLUDE_PATH?=include/hiredis
|
||||
LIBRARY_PATH?=lib
|
||||
PKGCONF_PATH?=pkgconfig
|
||||
INSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH)
|
||||
INSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH)
|
||||
INSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH)
|
||||
|
||||
# redis-server configuration used for testing
|
||||
REDIS_PORT=56379
|
||||
REDIS_SERVER=redis-server
|
||||
define REDIS_TEST_CONFIG
|
||||
daemonize yes
|
||||
pidfile /tmp/hiredis-test-redis.pid
|
||||
port $(REDIS_PORT)
|
||||
bind 127.0.0.1
|
||||
unixsocket /tmp/hiredis-test-redis.sock
|
||||
endef
|
||||
export REDIS_TEST_CONFIG
|
||||
|
||||
# Fallback to gcc when $CC is not in $PATH.
|
||||
CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
|
||||
CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
|
||||
OPTIMIZATION?=-O3
|
||||
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers
|
||||
DEBUG_FLAGS?= -g -ggdb
|
||||
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS)
|
||||
REAL_LDFLAGS=$(LDFLAGS)
|
||||
|
||||
DYLIBSUFFIX=so
|
||||
STLIBSUFFIX=a
|
||||
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
|
||||
DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
|
||||
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
|
||||
|
||||
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)
|
||||
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
|
||||
STLIB_MAKE_CMD=$(AR) rcs
|
||||
|
||||
SSL_DYLIB_MINOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
|
||||
SSL_DYLIB_MAJOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
|
||||
SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)
|
||||
SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)
|
||||
SSL_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(SSL_DYLIB_MINOR_NAME)
|
||||
|
||||
# Platform-specific overrides
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
|
||||
USE_SSL?=0
|
||||
|
||||
# This is required for test.c only
|
||||
ifeq ($(USE_SSL),1)
|
||||
CFLAGS+=-DHIREDIS_TEST_SSL
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),Linux)
|
||||
SSL_LDFLAGS=-lssl -lcrypto
|
||||
else
|
||||
OPENSSL_PREFIX?=/usr/local/opt/openssl
|
||||
CFLAGS+=-I$(OPENSSL_PREFIX)/include
|
||||
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
IS_SUN_CC=$(shell sh -c '$(CC) -V 2>&1 |egrep -i -c "sun|studio"')
|
||||
ifeq ($(IS_SUN_CC),1)
|
||||
SUN_SHARED_FLAG=-G
|
||||
else
|
||||
SUN_SHARED_FLAG=-shared
|
||||
endif
|
||||
REAL_LDFLAGS+= -ldl -lnsl -lsocket
|
||||
DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
|
||||
SSL_DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(SSL_DYLIBNAME) -h $(SSL_DYLIB_MINOR_NAME) $(LDFLAGS) $(SSL_LDFLAGS)
|
||||
endif
|
||||
ifeq ($(uname_S),Darwin)
|
||||
DYLIBSUFFIX=dylib
|
||||
DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX)
|
||||
DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
|
||||
SSL_DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME) -o $(SSL_DYLIBNAME) $(LDFLAGS) $(SSL_LDFLAGS)
|
||||
DYLIB_PLUGIN=-Wl,-undefined -Wl,dynamic_lookup
|
||||
endif
|
||||
|
||||
all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)
|
||||
ifeq ($(USE_SSL),1)
|
||||
all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
|
||||
endif
|
||||
|
||||
# Deps (use make dep to generate this)
|
||||
alloc.o: alloc.c fmacros.h alloc.h
|
||||
async.o: async.c fmacros.h alloc.h async.h hiredis.h read.h sds.h net.h dict.c dict.h win32.h async_private.h
|
||||
dict.o: dict.c fmacros.h alloc.h dict.h
|
||||
hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h alloc.h net.h async.h win32.h
|
||||
net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h sockcompat.h win32.h
|
||||
read.o: read.c fmacros.h alloc.h read.h sds.h win32.h
|
||||
sds.o: sds.c sds.h sdsalloc.h alloc.h
|
||||
sockcompat.o: sockcompat.c sockcompat.h
|
||||
ssl.o: ssl.c hiredis.h read.h sds.h alloc.h async.h win32.h async_private.h
|
||||
test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h sockcompat.h win32.h
|
||||
|
||||
$(DYLIBNAME): $(OBJ)
|
||||
$(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS)
|
||||
|
||||
$(STLIBNAME): $(OBJ)
|
||||
$(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ)
|
||||
|
||||
$(SSL_DYLIBNAME): $(SSL_OBJ)
|
||||
$(SSL_DYLIB_MAKE_CMD) $(DYLIB_PLUGIN) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(LDFLAGS) $(SSL_LDFLAGS)
|
||||
|
||||
$(SSL_STLIBNAME): $(SSL_OBJ)
|
||||
$(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ)
|
||||
|
||||
dynamic: $(DYLIBNAME)
|
||||
static: $(STLIBNAME)
|
||||
ifeq ($(USE_SSL),1)
|
||||
dynamic: $(SSL_DYLIBNAME)
|
||||
static: $(SSL_STLIBNAME)
|
||||
endif
|
||||
|
||||
# Binaries:
|
||||
hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-libevent-ssl: examples/example-libevent-ssl.c adapters/libevent.h $(STLIBNAME) $(SSL_STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
||||
|
||||
hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lev $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -livykis $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
||||
|
||||
|
||||
ifndef AE_DIR
|
||||
hiredis-example-ae:
|
||||
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
|
||||
@false
|
||||
else
|
||||
hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME)
|
||||
endif
|
||||
|
||||
ifndef LIBUV_DIR
|
||||
hiredis-example-libuv:
|
||||
@echo "Please specify LIBUV_DIR (e.g. ../libuv/)"
|
||||
@false
|
||||
else
|
||||
hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),)
|
||||
hiredis-example-qt:
|
||||
@echo "Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR"
|
||||
@false
|
||||
else
|
||||
hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME)
|
||||
$(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \
|
||||
$(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore
|
||||
$(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \
|
||||
$(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore
|
||||
$(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore
|
||||
endif
|
||||
|
||||
hiredis-example: examples/example.c $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-push: examples/example-push.c $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
examples: $(EXAMPLES)
|
||||
|
||||
TEST_LIBS = $(STLIBNAME)
|
||||
ifeq ($(USE_SSL),1)
|
||||
TEST_LIBS += $(SSL_STLIBNAME)
|
||||
TEST_LDFLAGS = $(SSL_LDFLAGS) -lssl -lcrypto -lpthread
|
||||
endif
|
||||
|
||||
hiredis-test: test.o $(TEST_LIBS)
|
||||
$(CC) -o $@ $(REAL_CFLAGS) -I. $^ $(REAL_LDFLAGS) $(TEST_LDFLAGS)
|
||||
|
||||
hiredis-%: %.o $(STLIBNAME)
|
||||
$(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS)
|
||||
|
||||
test: hiredis-test
|
||||
./hiredis-test
|
||||
|
||||
check: hiredis-test
|
||||
TEST_SSL=$(USE_SSL) ./test.sh
|
||||
|
||||
.c.o:
|
||||
$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
|
||||
|
||||
clean:
|
||||
rm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov
|
||||
|
||||
dep:
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -MM *.c
|
||||
|
||||
INSTALL?= cp -pPR
|
||||
|
||||
$(PKGCONFNAME): hiredis.h
|
||||
@echo "Generating $@ for pkgconfig..."
|
||||
@echo prefix=$(PREFIX) > $@
|
||||
@echo exec_prefix=\$${prefix} >> $@
|
||||
@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@
|
||||
@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
||||
@echo >> $@
|
||||
@echo Name: hiredis >> $@
|
||||
@echo Description: Minimalistic C client library for Redis. >> $@
|
||||
@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@
|
||||
@echo Libs: -L\$${libdir} -lhiredis >> $@
|
||||
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
|
||||
|
||||
$(SSL_PKGCONFNAME): hiredis_ssl.h
|
||||
@echo "Generating $@ for pkgconfig..."
|
||||
@echo prefix=$(PREFIX) > $@
|
||||
@echo exec_prefix=\$${prefix} >> $@
|
||||
@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@
|
||||
@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
||||
@echo >> $@
|
||||
@echo Name: hiredis_ssl >> $@
|
||||
@echo Description: SSL Support for hiredis. >> $@
|
||||
@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@
|
||||
@echo Requires: hiredis >> $@
|
||||
@echo Libs: -L\$${libdir} -lhiredis_ssl >> $@
|
||||
@echo Libs.private: -lssl -lcrypto >> $@
|
||||
|
||||
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
|
||||
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)
|
||||
$(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH)
|
||||
$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters
|
||||
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)
|
||||
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
|
||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
||||
$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
||||
|
||||
ifeq ($(USE_SSL),1)
|
||||
install: install-ssl
|
||||
|
||||
install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
|
||||
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
|
||||
$(INSTALL) hiredis_ssl.h $(INSTALL_INCLUDE_PATH)
|
||||
$(INSTALL) $(SSL_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIBNAME)
|
||||
$(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH)
|
||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
||||
$(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
||||
endif
|
||||
|
||||
32bit:
|
||||
@echo ""
|
||||
@echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
|
||||
@echo ""
|
||||
$(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
|
||||
|
||||
32bit-vars:
|
||||
$(eval CFLAGS=-m32)
|
||||
$(eval LDFLAGS=-m32)
|
||||
|
||||
gprof:
|
||||
$(MAKE) CFLAGS="-pg" LDFLAGS="-pg"
|
||||
|
||||
gcov:
|
||||
$(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs"
|
||||
|
||||
coverage: gcov
|
||||
make check
|
||||
mkdir -p tmp/lcov
|
||||
lcov -d . -c -o tmp/lcov/hiredis.info
|
||||
genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info
|
||||
|
||||
noopt:
|
||||
$(MAKE) OPTIMIZATION=""
|
||||
|
||||
.PHONY: all test check clean dep install 32bit 32bit-vars gprof gcov noopt
|
||||
Vendored
-658
@@ -1,658 +0,0 @@
|
||||
[](https://travis-ci.org/redis/hiredis)
|
||||
|
||||
**This Readme reflects the latest changed in the master branch. See [v1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) for the Readme and documentation for the latest release ([API/ABI history](https://abi-laboratory.pro/?view=timeline&l=hiredis)).**
|
||||
|
||||
# HIREDIS
|
||||
|
||||
Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database.
|
||||
|
||||
It is minimalistic because it just adds minimal support for the protocol, but
|
||||
at the same time it uses a high level printf-alike API in order to make it
|
||||
much higher level than otherwise suggested by its minimal code base and the
|
||||
lack of explicit bindings for every Redis command.
|
||||
|
||||
Apart from supporting sending commands and receiving replies, it comes with
|
||||
a reply parser that is decoupled from the I/O layer. It
|
||||
is a stream parser designed for easy reusability, which can for instance be used
|
||||
in higher level language bindings for efficient reply parsing.
|
||||
|
||||
Hiredis only supports the binary-safe Redis protocol, so you can use it with any
|
||||
Redis version >= 1.2.0.
|
||||
|
||||
The library comes with multiple APIs. There is the
|
||||
*synchronous API*, the *asynchronous API* and the *reply parsing API*.
|
||||
|
||||
## Upgrading to `1.0.0`
|
||||
|
||||
Version 1.0.0 marks the first stable release of Hiredis.
|
||||
It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.
|
||||
It also bundles the updated `sds` library, to sync up with upstream and Redis.
|
||||
For code changes see the [Changelog](CHANGELOG.md).
|
||||
|
||||
_Note: As described below, a few member names have been changed but most applications should be able to upgrade with minor code changes and recompiling._
|
||||
|
||||
## IMPORTANT: Breaking changes from `0.14.1` -> `1.0.0`
|
||||
|
||||
* `redisContext` has two additional members (`free_privdata`, and `privctx`).
|
||||
* `redisOptions.timeout` has been renamed to `redisOptions.connect_timeout`, and we've added `redisOptions.command_timeout`.
|
||||
* `redisReplyObjectFunctions.createArray` now takes `size_t` instead of `int` for its length parameter.
|
||||
|
||||
## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x
|
||||
|
||||
Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
|
||||
protocol errors. This is consistent with the RESP specification. On 32-bit
|
||||
platforms, the upper bound is lowered to `SIZE_MAX`.
|
||||
|
||||
Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
||||
|
||||
User code should compare this to `size_t` values as well. If it was used to
|
||||
compare to other values, casting might be necessary or can be removed, if
|
||||
casting was applied before.
|
||||
|
||||
## Upgrading from `<0.9.0`
|
||||
|
||||
Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
|
||||
code using hiredis should not be a big pain. The key thing to keep in mind when
|
||||
upgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to
|
||||
the stateless 0.0.1 that only has a file descriptor to work with.
|
||||
|
||||
## Synchronous API
|
||||
|
||||
To consume the synchronous API, there are only a few function calls that need to be introduced:
|
||||
|
||||
```c
|
||||
redisContext *redisConnect(const char *ip, int port);
|
||||
void *redisCommand(redisContext *c, const char *format, ...);
|
||||
void freeReplyObject(void *reply);
|
||||
```
|
||||
|
||||
### Connecting
|
||||
|
||||
The function `redisConnect` is used to create a so-called `redisContext`. The
|
||||
context is where Hiredis holds state for a connection. The `redisContext`
|
||||
struct has an integer `err` field that is non-zero when the connection is in
|
||||
an error state. The field `errstr` will contain a string with a description of
|
||||
the error. More information on errors can be found in the **Errors** section.
|
||||
After trying to connect to Redis using `redisConnect` you should
|
||||
check the `err` field to see if establishing the connection was successful:
|
||||
```c
|
||||
redisContext *c = redisConnect("127.0.0.1", 6379);
|
||||
if (c == NULL || c->err) {
|
||||
if (c) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
// handle error
|
||||
} else {
|
||||
printf("Can't allocate redis context\n");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Note: A `redisContext` is not thread-safe.*
|
||||
|
||||
### Sending commands
|
||||
|
||||
There are several ways to issue commands to Redis. The first that will be introduced is
|
||||
`redisCommand`. This function takes a format similar to printf. In the simplest form,
|
||||
it is used like this:
|
||||
```c
|
||||
reply = redisCommand(context, "SET foo bar");
|
||||
```
|
||||
|
||||
The specifier `%s` interpolates a string in the command, and uses `strlen` to
|
||||
determine the length of the string:
|
||||
```c
|
||||
reply = redisCommand(context, "SET foo %s", value);
|
||||
```
|
||||
When you need to pass binary safe strings in a command, the `%b` specifier can be
|
||||
used. Together with a pointer to the string, it requires a `size_t` length argument
|
||||
of the string:
|
||||
```c
|
||||
reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);
|
||||
```
|
||||
Internally, Hiredis splits the command in different arguments and will
|
||||
convert it to the protocol used to communicate with Redis.
|
||||
One or more spaces separates arguments, so you can use the specifiers
|
||||
anywhere in an argument:
|
||||
```c
|
||||
reply = redisCommand(context, "SET key:%s %s", myid, value);
|
||||
```
|
||||
|
||||
### Using replies
|
||||
|
||||
The return value of `redisCommand` holds a reply when the command was
|
||||
successfully executed. When an error occurs, the return value is `NULL` and
|
||||
the `err` field in the context will be set (see section on **Errors**).
|
||||
Once an error is returned the context cannot be reused and you should set up
|
||||
a new connection.
|
||||
|
||||
The standard replies that `redisCommand` are of the type `redisReply`. The
|
||||
`type` field in the `redisReply` should be used to test what kind of reply
|
||||
was received:
|
||||
|
||||
### RESP2
|
||||
|
||||
* **`REDIS_REPLY_STATUS`**:
|
||||
* The command replied with a status reply. The status string can be accessed using `reply->str`.
|
||||
The length of this string can be accessed using `reply->len`.
|
||||
|
||||
* **`REDIS_REPLY_ERROR`**:
|
||||
* The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`.
|
||||
|
||||
* **`REDIS_REPLY_INTEGER`**:
|
||||
* The command replied with an integer. The integer value can be accessed using the
|
||||
`reply->integer` field of type `long long`.
|
||||
|
||||
* **`REDIS_REPLY_NIL`**:
|
||||
* The command replied with a **nil** object. There is no data to access.
|
||||
|
||||
* **`REDIS_REPLY_STRING`**:
|
||||
* A bulk (string) reply. The value of the reply can be accessed using `reply->str`.
|
||||
The length of this string can be accessed using `reply->len`.
|
||||
|
||||
* **`REDIS_REPLY_ARRAY`**:
|
||||
* A multi bulk reply. The number of elements in the multi bulk reply is stored in
|
||||
`reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well
|
||||
and can be accessed via `reply->element[..index..]`.
|
||||
Redis may reply with nested arrays but this is fully supported.
|
||||
|
||||
### RESP3
|
||||
|
||||
Hiredis also supports every new `RESP3` data type which are as follows. For more information about the protocol see the `RESP3` [specification.](https://github.com/antirez/RESP3/blob/master/spec.md)
|
||||
|
||||
* **`REDIS_REPLY_DOUBLE`**:
|
||||
* The command replied with a double-precision floating point number.
|
||||
The value is stored as a string in the `str` member, and can be converted with `strtod` or similar.
|
||||
|
||||
* **`REDIS_REPLY_BOOL`**:
|
||||
* A boolean true/false reply.
|
||||
The value is stored in the `integer` member and will be either `0` or `1`.
|
||||
|
||||
* **`REDIS_REPLY_MAP`**:
|
||||
* An array with the added invariant that there will always be an even number of elements.
|
||||
The MAP is functionally equivelant to `REDIS_REPLY_ARRAY` except for the previously mentioned invariant.
|
||||
|
||||
* **`REDIS_REPLY_SET`**:
|
||||
* An array response where each entry is unique.
|
||||
Like the MAP type, the data is identical to an array response except there are no duplicate values.
|
||||
|
||||
* **`REDIS_REPLY_PUSH`**:
|
||||
* An array that can be generated spontaneously by Redis.
|
||||
This array response will always contain at least two subelements. The first contains the type of `PUSH` message (e.g. `message`, or `invalidate`), and the second being a sub-array with the `PUSH` payload itself.
|
||||
|
||||
* **`REDIS_REPLY_ATTR`**:
|
||||
* An array structurally identical to a `MAP` but intended as meta-data about a reply.
|
||||
_As of Redis 6.0.6 this reply type is not used in Redis_
|
||||
|
||||
* **`REDIS_REPLY_BIGNUM`**:
|
||||
* A string representing an arbitrarily large signed or unsigned integer value.
|
||||
The number will be encoded as a string in the `str` member of `redisReply`.
|
||||
|
||||
* **`REDIS_REPLY_VERB`**:
|
||||
* A verbatim string, intended to be presented to the user without modification.
|
||||
The string payload is stored in the `str` memeber, and type data is stored in the `vtype` member (e.g. `txt` for raw text or `md` for markdown).
|
||||
|
||||
Replies should be freed using the `freeReplyObject()` function.
|
||||
Note that this function will take care of freeing sub-reply objects
|
||||
contained in arrays and nested arrays, so there is no need for the user to
|
||||
free the sub replies (it is actually harmful and will corrupt the memory).
|
||||
|
||||
**Important:** the current version of hiredis (1.0.0) frees replies when the
|
||||
asynchronous API is used. This means you should not call `freeReplyObject` when
|
||||
you use this API. The reply is cleaned up by hiredis _after_ the callback
|
||||
returns. We may introduce a flag to make this configurable in future versions of the library.
|
||||
|
||||
### Cleaning up
|
||||
|
||||
To disconnect and free the context the following function can be used:
|
||||
```c
|
||||
void redisFree(redisContext *c);
|
||||
```
|
||||
This function immediately closes the socket and then frees the allocations done in
|
||||
creating the context.
|
||||
|
||||
### Sending commands (cont'd)
|
||||
|
||||
Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands.
|
||||
It has the following prototype:
|
||||
```c
|
||||
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
||||
```
|
||||
It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the
|
||||
arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will
|
||||
use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments
|
||||
need to be binary safe, the entire array of lengths `argvlen` should be provided.
|
||||
|
||||
The return value has the same semantic as `redisCommand`.
|
||||
|
||||
### Pipelining
|
||||
|
||||
To explain how Hiredis supports pipelining in a blocking connection, there needs to be
|
||||
understanding of the internal execution flow.
|
||||
|
||||
When any of the functions in the `redisCommand` family is called, Hiredis first formats the
|
||||
command according to the Redis protocol. The formatted command is then put in the output buffer
|
||||
of the context. This output buffer is dynamic, so it can hold any number of commands.
|
||||
After the command is put in the output buffer, `redisGetReply` is called. This function has the
|
||||
following two execution paths:
|
||||
|
||||
1. The input buffer is non-empty:
|
||||
* Try to parse a single reply from the input buffer and return it
|
||||
* If no reply could be parsed, continue at *2*
|
||||
2. The input buffer is empty:
|
||||
* Write the **entire** output buffer to the socket
|
||||
* Read from the socket until a single reply could be parsed
|
||||
|
||||
The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply
|
||||
is expected on the socket. To pipeline commands, the only things that needs to be done is
|
||||
filling up the output buffer. For this cause, two commands can be used that are identical
|
||||
to the `redisCommand` family, apart from not returning a reply:
|
||||
```c
|
||||
void redisAppendCommand(redisContext *c, const char *format, ...);
|
||||
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
||||
```
|
||||
After calling either function one or more times, `redisGetReply` can be used to receive the
|
||||
subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where
|
||||
the latter means an error occurred while reading a reply. Just as with the other commands,
|
||||
the `err` field in the context can be used to find out what the cause of this error is.
|
||||
|
||||
The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and
|
||||
a single call to `read(2)`):
|
||||
```c
|
||||
redisReply *reply;
|
||||
redisAppendCommand(context,"SET foo bar");
|
||||
redisAppendCommand(context,"GET foo");
|
||||
redisGetReply(context,(void *)&reply); // reply for SET
|
||||
freeReplyObject(reply);
|
||||
redisGetReply(context,(void *)&reply); // reply for GET
|
||||
freeReplyObject(reply);
|
||||
```
|
||||
This API can also be used to implement a blocking subscriber:
|
||||
```c
|
||||
reply = redisCommand(context,"SUBSCRIBE foo");
|
||||
freeReplyObject(reply);
|
||||
while(redisGetReply(context,(void *)&reply) == REDIS_OK) {
|
||||
// consume message
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
```
|
||||
### Errors
|
||||
|
||||
When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is
|
||||
returned. The `err` field inside the context will be non-zero and set to one of the
|
||||
following constants:
|
||||
|
||||
* **`REDIS_ERR_IO`**:
|
||||
There was an I/O error while creating the connection, trying to write
|
||||
to the socket or read from the socket. If you included `errno.h` in your
|
||||
application, you can use the global `errno` variable to find out what is
|
||||
wrong.
|
||||
|
||||
* **`REDIS_ERR_EOF`**:
|
||||
The server closed the connection which resulted in an empty read.
|
||||
|
||||
* **`REDIS_ERR_PROTOCOL`**:
|
||||
There was an error while parsing the protocol.
|
||||
|
||||
* **`REDIS_ERR_OTHER`**:
|
||||
Any other error. Currently, it is only used when a specified hostname to connect
|
||||
to cannot be resolved.
|
||||
|
||||
In every case, the `errstr` field in the context will be set to hold a string representation
|
||||
of the error.
|
||||
|
||||
## Asynchronous API
|
||||
|
||||
Hiredis comes with an asynchronous API that works easily with any event library.
|
||||
Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html)
|
||||
and [libevent](http://monkey.org/~provos/libevent/).
|
||||
|
||||
### Connecting
|
||||
|
||||
The function `redisAsyncConnect` can be used to establish a non-blocking connection to
|
||||
Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field
|
||||
should be checked after creation to see if there were errors creating the connection.
|
||||
Because the connection that will be created is non-blocking, the kernel is not able to
|
||||
instantly return if the specified host and port is able to accept a connection.
|
||||
|
||||
*Note: A `redisAsyncContext` is not thread-safe.*
|
||||
|
||||
```c
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
// handle error
|
||||
}
|
||||
```
|
||||
|
||||
The asynchronous context can hold a disconnect callback function that is called when the
|
||||
connection is disconnected (either because of an error or per user request). This function should
|
||||
have the following prototype:
|
||||
```c
|
||||
void(const redisAsyncContext *c, int status);
|
||||
```
|
||||
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the
|
||||
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`
|
||||
field in the context can be accessed to find out the cause of the error.
|
||||
|
||||
The context object is always freed after the disconnect callback fired. When a reconnect is needed,
|
||||
the disconnect callback is a good point to do so.
|
||||
|
||||
Setting the disconnect callback can only be done once per context. For subsequent calls it will
|
||||
return `REDIS_ERR`. The function to set the disconnect callback has the following prototype:
|
||||
```c
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
||||
```
|
||||
`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback.
|
||||
### Sending commands and their callbacks
|
||||
|
||||
In an asynchronous context, commands are automatically pipelined due to the nature of an event loop.
|
||||
Therefore, unlike the synchronous API, there is only a single way to send commands.
|
||||
Because commands are sent to Redis asynchronously, issuing a command requires a callback function
|
||||
that is called when the reply is received. Reply callbacks should have the following prototype:
|
||||
```c
|
||||
void(redisAsyncContext *c, void *reply, void *privdata);
|
||||
```
|
||||
The `privdata` argument can be used to curry arbitrary data to the callback from the point where
|
||||
the command is initially queued for execution.
|
||||
|
||||
The functions that can be used to issue commands in an asynchronous context are:
|
||||
```c
|
||||
int redisAsyncCommand(
|
||||
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
|
||||
const char *format, ...);
|
||||
int redisAsyncCommandArgv(
|
||||
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
|
||||
int argc, const char **argv, const size_t *argvlen);
|
||||
```
|
||||
Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command
|
||||
was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection
|
||||
is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is
|
||||
returned on calls to the `redisAsyncCommand` family.
|
||||
|
||||
If the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback
|
||||
for a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only
|
||||
valid for the duration of the callback.
|
||||
|
||||
All pending callbacks are called with a `NULL` reply when the context encountered an error.
|
||||
|
||||
### Disconnecting
|
||||
|
||||
An asynchronous connection can be terminated using:
|
||||
```c
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac);
|
||||
```
|
||||
When this function is called, the connection is **not** immediately terminated. Instead, new
|
||||
commands are no longer accepted and the connection is only terminated when all pending commands
|
||||
have been written to the socket, their respective replies have been read and their respective
|
||||
callbacks have been executed. After this, the disconnection callback is executed with the
|
||||
`REDIS_OK` status and the context object is freed.
|
||||
|
||||
### Hooking it up to event library *X*
|
||||
|
||||
There are a few hooks that need to be set on the context object after it is created.
|
||||
See the `adapters/` directory for bindings to *libev* and *libevent*.
|
||||
|
||||
## Reply parsing API
|
||||
|
||||
Hiredis comes with a reply parsing API that makes it easy for writing higher
|
||||
level language bindings.
|
||||
|
||||
The reply parsing API consists of the following functions:
|
||||
```c
|
||||
redisReader *redisReaderCreate(void);
|
||||
void redisReaderFree(redisReader *reader);
|
||||
int redisReaderFeed(redisReader *reader, const char *buf, size_t len);
|
||||
int redisReaderGetReply(redisReader *reader, void **reply);
|
||||
```
|
||||
The same set of functions are used internally by hiredis when creating a
|
||||
normal Redis context, the above API just exposes it to the user for a direct
|
||||
usage.
|
||||
|
||||
### Usage
|
||||
|
||||
The function `redisReaderCreate` creates a `redisReader` structure that holds a
|
||||
buffer with unparsed data and state for the protocol parser.
|
||||
|
||||
Incoming data -- most likely from a socket -- can be placed in the internal
|
||||
buffer of the `redisReader` using `redisReaderFeed`. This function will make a
|
||||
copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed
|
||||
when `redisReaderGetReply` is called. This function returns an integer status
|
||||
and a reply object (as described above) via `void **reply`. The returned status
|
||||
can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went
|
||||
wrong (either a protocol error, or an out of memory error).
|
||||
|
||||
The parser limits the level of nesting for multi bulk payloads to 7. If the
|
||||
multi bulk nesting level is higher than this, the parser returns an error.
|
||||
|
||||
### Customizing replies
|
||||
|
||||
The function `redisReaderGetReply` creates `redisReply` and makes the function
|
||||
argument `reply` point to the created `redisReply` variable. For instance, if
|
||||
the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply`
|
||||
will hold the status as a vanilla C string. However, the functions that are
|
||||
responsible for creating instances of the `redisReply` can be customized by
|
||||
setting the `fn` field on the `redisReader` struct. This should be done
|
||||
immediately after creating the `redisReader`.
|
||||
|
||||
For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c)
|
||||
uses customized reply object functions to create Ruby objects.
|
||||
|
||||
### Reader max buffer
|
||||
|
||||
Both when using the Reader API directly or when using it indirectly via a
|
||||
normal Redis context, the redisReader structure uses a buffer in order to
|
||||
accumulate data from the server.
|
||||
Usually this buffer is destroyed when it is empty and is larger than 16
|
||||
KiB in order to avoid wasting memory in unused buffers
|
||||
|
||||
However when working with very big payloads destroying the buffer may slow
|
||||
down performances considerably, so it is possible to modify the max size of
|
||||
an idle buffer changing the value of the `maxbuf` field of the reader structure
|
||||
to the desired value. The special value of 0 means that there is no maximum
|
||||
value for an idle buffer, so the buffer will never get freed.
|
||||
|
||||
For instance if you have a normal Redis context you can set the maximum idle
|
||||
buffer to zero (unlimited) just with:
|
||||
```c
|
||||
context->reader->maxbuf = 0;
|
||||
```
|
||||
This should be done only in order to maximize performances when working with
|
||||
large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again
|
||||
as soon as possible in order to prevent allocation of useless memory.
|
||||
|
||||
### Reader max array elements
|
||||
|
||||
By default the hiredis reply parser sets the maximum number of multi-bulk elements
|
||||
to 2^32 - 1 or 4,294,967,295 entries. If you need to process multi-bulk replies
|
||||
with more than this many elements you can set the value higher or to zero, meaning
|
||||
unlimited with:
|
||||
```c
|
||||
context->reader->maxelements = 0;
|
||||
```
|
||||
|
||||
## SSL/TLS Support
|
||||
|
||||
### Building
|
||||
|
||||
SSL/TLS support is not built by default and requires an explicit flag:
|
||||
|
||||
make USE_SSL=1
|
||||
|
||||
This requires OpenSSL development package (e.g. including header files to be
|
||||
available.
|
||||
|
||||
When enabled, SSL/TLS support is built into extra `libhiredis_ssl.a` and
|
||||
`libhiredis_ssl.so` static/dynamic libraries. This leaves the original libraries
|
||||
unaffected so no additional dependencies are introduced.
|
||||
|
||||
### Using it
|
||||
|
||||
First, you'll need to make sure you include the SSL header file:
|
||||
|
||||
```c
|
||||
#include "hiredis.h"
|
||||
#include "hiredis_ssl.h"
|
||||
```
|
||||
|
||||
You will also need to link against `libhiredis_ssl`, **in addition** to
|
||||
`libhiredis` and add `-lssl -lcrypto` to satisfy its dependencies.
|
||||
|
||||
Hiredis implements SSL/TLS on top of its normal `redisContext` or
|
||||
`redisAsyncContext`, so you will need to establish a connection first and then
|
||||
initiate an SSL/TLS handshake.
|
||||
|
||||
#### Hiredis OpenSSL Wrappers
|
||||
|
||||
Before Hiredis can negotiate an SSL/TLS connection, it is necessary to
|
||||
initialize OpenSSL and create a context. You can do that in two ways:
|
||||
|
||||
1. Work directly with the OpenSSL API to initialize the library's global context
|
||||
and create `SSL_CTX *` and `SSL *` contexts. With an `SSL *` object you can
|
||||
call `redisInitiateSSL()`.
|
||||
2. Work with a set of Hiredis-provided wrappers around OpenSSL, create a
|
||||
`redisSSLContext` object to hold configuration and use
|
||||
`redisInitiateSSLWithContext()` to initiate the SSL/TLS handshake.
|
||||
|
||||
```c
|
||||
/* An Hiredis SSL context. It holds SSL configuration and can be reused across
|
||||
* many contexts.
|
||||
*/
|
||||
redisSSLContext *ssl;
|
||||
|
||||
/* An error variable to indicate what went wrong, if the context fails to
|
||||
* initialize.
|
||||
*/
|
||||
redisSSLContextError ssl_error;
|
||||
|
||||
/* Initialize global OpenSSL state.
|
||||
*
|
||||
* You should call this only once when your app initializes, and only if
|
||||
* you don't explicitly or implicitly initialize OpenSSL it elsewhere.
|
||||
*/
|
||||
redisInitOpenSSL();
|
||||
|
||||
/* Create SSL context */
|
||||
ssl = redisCreateSSLContext(
|
||||
"cacertbundle.crt", /* File name of trusted CA/ca bundle file, optional */
|
||||
"/path/to/certs", /* Path of trusted certificates, optional */
|
||||
"client_cert.pem", /* File name of client certificate file, optional */
|
||||
"client_key.pem", /* File name of client private key, optional */
|
||||
"redis.mydomain.com", /* Server name to request (SNI), optional */
|
||||
&ssl_error
|
||||
) != REDIS_OK) {
|
||||
printf("SSL error: %s\n", redisSSLContextGetError(ssl_error);
|
||||
/* Abort... */
|
||||
}
|
||||
|
||||
/* Create Redis context and establish connection */
|
||||
c = redisConnect("localhost", 6443);
|
||||
if (c == NULL || c->err) {
|
||||
/* Handle error and abort... */
|
||||
}
|
||||
|
||||
/* Negotiate SSL/TLS */
|
||||
if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
|
||||
/* Handle error, in c->err / c->errstr */
|
||||
}
|
||||
```
|
||||
|
||||
## RESP3 PUSH replies
|
||||
Redis 6.0 introduced PUSH replies with the reply-type `>`. These messages are generated spontaneously and can arrive at any time, so must be handled using callbacks.
|
||||
|
||||
### Default behavior
|
||||
Hiredis installs handlers on `redisContext` and `redisAsyncContext` by default, which will intercept and free any PUSH replies detected. This means existing code will work as-is after upgrading to Redis 6 and switching to `RESP3`.
|
||||
|
||||
### Custom PUSH handler prototypes
|
||||
The callback prototypes differ between `redisContext` and `redisAsyncContext`.
|
||||
|
||||
#### redisContext
|
||||
```c
|
||||
void my_push_handler(void *privdata, void *reply) {
|
||||
/* Handle the reply */
|
||||
|
||||
/* Note: We need to free the reply in our custom handler for
|
||||
blocking contexts. This lets us keep the reply if
|
||||
we want. */
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
```
|
||||
|
||||
#### redisAsyncContext
|
||||
```c
|
||||
void my_async_push_handler(redisAsyncContext *ac, void *reply) {
|
||||
/* Handle the reply */
|
||||
|
||||
/* Note: Because async hiredis always frees replies, you should
|
||||
not call freeReplyObject in an async push callback. */
|
||||
}
|
||||
```
|
||||
|
||||
### Installing a custom handler
|
||||
There are two ways to set your own PUSH handlers.
|
||||
|
||||
1. Set `push_cb` or `async_push_cb` in the `redisOptions` struct and connect with `redisConnectWithOptions` or `redisAsyncConnectWithOptions`.
|
||||
```c
|
||||
redisOptions = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
||||
options->push_cb = my_push_handler;
|
||||
redisContext *context = redisConnectWithOptions(&options);
|
||||
```
|
||||
2. Call `redisSetPushCallback` or `redisAsyncSetPushCallback` on a connected context.
|
||||
```c
|
||||
redisContext *context = redisConnect("127.0.0.1", 6379);
|
||||
redisSetPushCallback(context, my_push_handler);
|
||||
```
|
||||
|
||||
_Note `redisSetPushCallback` and `redisAsyncSetPushCallback` both return any currently configured handler, making it easy to override and then return to the old value._
|
||||
|
||||
### Specifying no handler
|
||||
If you have a unique use-case where you don't want hiredis to automatically intercept and free PUSH replies, you will want to configure no handler at all. This can be done in two ways.
|
||||
1. Set the `REDIS_OPT_NO_PUSH_AUTOFREE` flag in `redisOptions` and leave the callback function pointer `NULL`.
|
||||
```c
|
||||
redisOptions = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
||||
options->options |= REDIS_OPT_NO_PUSH_AUTOFREE;
|
||||
redisContext *context = redisConnectWithOptions(&options);
|
||||
```
|
||||
3. Call `redisSetPushCallback` with `NULL` once connected.
|
||||
```c
|
||||
redisContext *context = redisConnect("127.0.0.1", 6379);
|
||||
redisSetPushCallback(context, NULL);
|
||||
```
|
||||
|
||||
_Note: With no handler configured, calls to `redisCommand` may generate more than one reply, so this strategy is only applicable when there's some kind of blocking`redisGetReply()` loop (e.g. `MONITOR` or `SUBSCRIBE` workloads)._
|
||||
|
||||
## Allocator injection
|
||||
|
||||
Hiredis uses a pass-thru structure of function pointers defined in [alloc.h](https://github.com/redis/hiredis/blob/f5d25850/alloc.h#L41) that contain the currently configured allocation and deallocation functions. By default they just point to libc (`malloc`, `calloc`, `realloc`, etc).
|
||||
|
||||
### Overriding
|
||||
|
||||
One can override the allocators like so:
|
||||
|
||||
```c
|
||||
hiredisAllocFuncs myfuncs = {
|
||||
.mallocFn = my_malloc,
|
||||
.callocFn = my_calloc,
|
||||
.reallocFn = my_realloc,
|
||||
.strdupFn = my_strdup,
|
||||
.freeFn = my_free,
|
||||
};
|
||||
|
||||
// Override allocators (function returns current allocators if needed)
|
||||
hiredisAllocFuncs orig = hiredisSetAllocators(&myfuncs);
|
||||
```
|
||||
|
||||
To reset the allocators to their default libc function simply call:
|
||||
|
||||
```c
|
||||
hiredisResetAllocators();
|
||||
```
|
||||
|
||||
## AUTHORS
|
||||
|
||||
Salvatore Sanfilippo (antirez at gmail),\
|
||||
Pieter Noordhuis (pcnoordhuis at gmail)\
|
||||
Michael Grunder (michael dot grunder at gmail)
|
||||
|
||||
_Hiredis is released under the BSD license._
|
||||
Vendored
-130
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_AE_H__
|
||||
#define __HIREDIS_AE_H__
|
||||
#include <sys/types.h>
|
||||
#include <ae.h>
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
typedef struct redisAeEvents {
|
||||
redisAsyncContext *context;
|
||||
aeEventLoop *loop;
|
||||
int fd;
|
||||
int reading, writing;
|
||||
} redisAeEvents;
|
||||
|
||||
static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
((void)el); ((void)fd); ((void)mask);
|
||||
|
||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
||||
redisAsyncHandleRead(e->context);
|
||||
}
|
||||
|
||||
static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
((void)el); ((void)fd); ((void)mask);
|
||||
|
||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
||||
redisAsyncHandleWrite(e->context);
|
||||
}
|
||||
|
||||
static void redisAeAddRead(void *privdata) {
|
||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
||||
aeEventLoop *loop = e->loop;
|
||||
if (!e->reading) {
|
||||
e->reading = 1;
|
||||
aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisAeDelRead(void *privdata) {
|
||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
||||
aeEventLoop *loop = e->loop;
|
||||
if (e->reading) {
|
||||
e->reading = 0;
|
||||
aeDeleteFileEvent(loop,e->fd,AE_READABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisAeAddWrite(void *privdata) {
|
||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
||||
aeEventLoop *loop = e->loop;
|
||||
if (!e->writing) {
|
||||
e->writing = 1;
|
||||
aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisAeDelWrite(void *privdata) {
|
||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
||||
aeEventLoop *loop = e->loop;
|
||||
if (e->writing) {
|
||||
e->writing = 0;
|
||||
aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisAeCleanup(void *privdata) {
|
||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
||||
redisAeDelRead(privdata);
|
||||
redisAeDelWrite(privdata);
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisAeEvents *e;
|
||||
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if (ac->ev.data != NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisAeEvents*)hi_malloc(sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e->context = ac;
|
||||
e->loop = loop;
|
||||
e->fd = c->fd;
|
||||
e->reading = e->writing = 0;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
ac->ev.addRead = redisAeAddRead;
|
||||
ac->ev.delRead = redisAeDelRead;
|
||||
ac->ev.addWrite = redisAeAddWrite;
|
||||
ac->ev.delWrite = redisAeDelWrite;
|
||||
ac->ev.cleanup = redisAeCleanup;
|
||||
ac->ev.data = e;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
Vendored
-156
@@ -1,156 +0,0 @@
|
||||
#ifndef __HIREDIS_GLIB_H__
|
||||
#define __HIREDIS_GLIB_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GSource source;
|
||||
redisAsyncContext *ac;
|
||||
GPollFD poll_fd;
|
||||
} RedisSource;
|
||||
|
||||
static void
|
||||
redis_source_add_read (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
g_return_if_fail(source);
|
||||
source->poll_fd.events |= G_IO_IN;
|
||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_del_read (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
g_return_if_fail(source);
|
||||
source->poll_fd.events &= ~G_IO_IN;
|
||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_add_write (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
g_return_if_fail(source);
|
||||
source->poll_fd.events |= G_IO_OUT;
|
||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_del_write (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
g_return_if_fail(source);
|
||||
source->poll_fd.events &= ~G_IO_OUT;
|
||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_cleanup (gpointer data)
|
||||
{
|
||||
RedisSource *source = (RedisSource *)data;
|
||||
|
||||
g_return_if_fail(source);
|
||||
|
||||
redis_source_del_read(source);
|
||||
redis_source_del_write(source);
|
||||
/*
|
||||
* It is not our responsibility to remove ourself from the
|
||||
* current main loop. However, we will remove the GPollFD.
|
||||
*/
|
||||
if (source->poll_fd.fd >= 0) {
|
||||
g_source_remove_poll((GSource *)data, &source->poll_fd);
|
||||
source->poll_fd.fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
redis_source_prepare (GSource *source,
|
||||
gint *timeout_)
|
||||
{
|
||||
RedisSource *redis = (RedisSource *)source;
|
||||
*timeout_ = -1;
|
||||
return !!(redis->poll_fd.events & redis->poll_fd.revents);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
redis_source_check (GSource *source)
|
||||
{
|
||||
RedisSource *redis = (RedisSource *)source;
|
||||
return !!(redis->poll_fd.events & redis->poll_fd.revents);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
redis_source_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
RedisSource *redis = (RedisSource *)source;
|
||||
|
||||
if ((redis->poll_fd.revents & G_IO_OUT)) {
|
||||
redisAsyncHandleWrite(redis->ac);
|
||||
redis->poll_fd.revents &= ~G_IO_OUT;
|
||||
}
|
||||
|
||||
if ((redis->poll_fd.revents & G_IO_IN)) {
|
||||
redisAsyncHandleRead(redis->ac);
|
||||
redis->poll_fd.revents &= ~G_IO_IN;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
return callback(user_data);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
redis_source_finalize (GSource *source)
|
||||
{
|
||||
RedisSource *redis = (RedisSource *)source;
|
||||
|
||||
if (redis->poll_fd.fd >= 0) {
|
||||
g_source_remove_poll(source, &redis->poll_fd);
|
||||
redis->poll_fd.fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static GSource *
|
||||
redis_source_new (redisAsyncContext *ac)
|
||||
{
|
||||
static GSourceFuncs source_funcs = {
|
||||
.prepare = redis_source_prepare,
|
||||
.check = redis_source_check,
|
||||
.dispatch = redis_source_dispatch,
|
||||
.finalize = redis_source_finalize,
|
||||
};
|
||||
redisContext *c = &ac->c;
|
||||
RedisSource *source;
|
||||
|
||||
g_return_val_if_fail(ac != NULL, NULL);
|
||||
|
||||
source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);
|
||||
if (source == NULL)
|
||||
return NULL;
|
||||
|
||||
source->ac = ac;
|
||||
source->poll_fd.fd = c->fd;
|
||||
source->poll_fd.events = 0;
|
||||
source->poll_fd.revents = 0;
|
||||
g_source_add_poll((GSource *)source, &source->poll_fd);
|
||||
|
||||
ac->ev.addRead = redis_source_add_read;
|
||||
ac->ev.delRead = redis_source_del_read;
|
||||
ac->ev.addWrite = redis_source_add_write;
|
||||
ac->ev.delWrite = redis_source_del_write;
|
||||
ac->ev.cleanup = redis_source_cleanup;
|
||||
ac->ev.data = source;
|
||||
|
||||
return (GSource *)source;
|
||||
}
|
||||
|
||||
#endif /* __HIREDIS_GLIB_H__ */
|
||||
Vendored
-84
@@ -1,84 +0,0 @@
|
||||
#ifndef __HIREDIS_IVYKIS_H__
|
||||
#define __HIREDIS_IVYKIS_H__
|
||||
#include <iv.h>
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
typedef struct redisIvykisEvents {
|
||||
redisAsyncContext *context;
|
||||
struct iv_fd fd;
|
||||
} redisIvykisEvents;
|
||||
|
||||
static void redisIvykisReadEvent(void *arg) {
|
||||
redisAsyncContext *context = (redisAsyncContext *)arg;
|
||||
redisAsyncHandleRead(context);
|
||||
}
|
||||
|
||||
static void redisIvykisWriteEvent(void *arg) {
|
||||
redisAsyncContext *context = (redisAsyncContext *)arg;
|
||||
redisAsyncHandleWrite(context);
|
||||
}
|
||||
|
||||
static void redisIvykisAddRead(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent);
|
||||
}
|
||||
|
||||
static void redisIvykisDelRead(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
iv_fd_set_handler_in(&e->fd, NULL);
|
||||
}
|
||||
|
||||
static void redisIvykisAddWrite(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent);
|
||||
}
|
||||
|
||||
static void redisIvykisDelWrite(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
iv_fd_set_handler_out(&e->fd, NULL);
|
||||
}
|
||||
|
||||
static void redisIvykisCleanup(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
|
||||
iv_fd_unregister(&e->fd);
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static int redisIvykisAttach(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisIvykisEvents *e;
|
||||
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if (ac->ev.data != NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisIvykisEvents*)hi_malloc(sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e->context = ac;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
ac->ev.addRead = redisIvykisAddRead;
|
||||
ac->ev.delRead = redisIvykisDelRead;
|
||||
ac->ev.addWrite = redisIvykisAddWrite;
|
||||
ac->ev.delWrite = redisIvykisDelWrite;
|
||||
ac->ev.cleanup = redisIvykisCleanup;
|
||||
ac->ev.data = e;
|
||||
|
||||
/* Initialize and install read/write events */
|
||||
IV_FD_INIT(&e->fd);
|
||||
e->fd.fd = c->fd;
|
||||
e->fd.handler_in = redisIvykisReadEvent;
|
||||
e->fd.handler_out = redisIvykisWriteEvent;
|
||||
e->fd.handler_err = NULL;
|
||||
e->fd.cookie = e->context;
|
||||
|
||||
iv_fd_register(&e->fd);
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
Vendored
-179
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_LIBEV_H__
|
||||
#define __HIREDIS_LIBEV_H__
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <ev.h>
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
typedef struct redisLibevEvents {
|
||||
redisAsyncContext *context;
|
||||
struct ev_loop *loop;
|
||||
int reading, writing;
|
||||
ev_io rev, wev;
|
||||
ev_timer timer;
|
||||
} redisLibevEvents;
|
||||
|
||||
static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
|
||||
#if EV_MULTIPLICITY
|
||||
((void)loop);
|
||||
#endif
|
||||
((void)revents);
|
||||
|
||||
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
|
||||
redisAsyncHandleRead(e->context);
|
||||
}
|
||||
|
||||
static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
|
||||
#if EV_MULTIPLICITY
|
||||
((void)loop);
|
||||
#endif
|
||||
((void)revents);
|
||||
|
||||
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
|
||||
redisAsyncHandleWrite(e->context);
|
||||
}
|
||||
|
||||
static void redisLibevAddRead(void *privdata) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
struct ev_loop *loop = e->loop;
|
||||
((void)loop);
|
||||
if (!e->reading) {
|
||||
e->reading = 1;
|
||||
ev_io_start(EV_A_ &e->rev);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibevDelRead(void *privdata) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
struct ev_loop *loop = e->loop;
|
||||
((void)loop);
|
||||
if (e->reading) {
|
||||
e->reading = 0;
|
||||
ev_io_stop(EV_A_ &e->rev);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibevAddWrite(void *privdata) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
struct ev_loop *loop = e->loop;
|
||||
((void)loop);
|
||||
if (!e->writing) {
|
||||
e->writing = 1;
|
||||
ev_io_start(EV_A_ &e->wev);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibevDelWrite(void *privdata) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
struct ev_loop *loop = e->loop;
|
||||
((void)loop);
|
||||
if (e->writing) {
|
||||
e->writing = 0;
|
||||
ev_io_stop(EV_A_ &e->wev);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibevStopTimer(void *privdata) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
struct ev_loop *loop = e->loop;
|
||||
((void)loop);
|
||||
ev_timer_stop(EV_A_ &e->timer);
|
||||
}
|
||||
|
||||
static void redisLibevCleanup(void *privdata) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
redisLibevDelRead(privdata);
|
||||
redisLibevDelWrite(privdata);
|
||||
redisLibevStopTimer(privdata);
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) {
|
||||
((void)revents);
|
||||
redisLibevEvents *e = (redisLibevEvents*)timer->data;
|
||||
redisAsyncHandleTimeout(e->context);
|
||||
}
|
||||
|
||||
static void redisLibevSetTimeout(void *privdata, struct timeval tv) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
struct ev_loop *loop = e->loop;
|
||||
((void)loop);
|
||||
|
||||
if (!ev_is_active(&e->timer)) {
|
||||
ev_init(&e->timer, redisLibevTimeout);
|
||||
e->timer.data = e;
|
||||
}
|
||||
|
||||
e->timer.repeat = tv.tv_sec + tv.tv_usec / 1000000.00;
|
||||
ev_timer_again(EV_A_ &e->timer);
|
||||
}
|
||||
|
||||
static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisLibevEvents *e;
|
||||
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if (ac->ev.data != NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisLibevEvents*)hi_calloc(1, sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e->context = ac;
|
||||
#if EV_MULTIPLICITY
|
||||
e->loop = loop;
|
||||
#else
|
||||
e->loop = NULL;
|
||||
#endif
|
||||
e->rev.data = e;
|
||||
e->wev.data = e;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
ac->ev.addRead = redisLibevAddRead;
|
||||
ac->ev.delRead = redisLibevDelRead;
|
||||
ac->ev.addWrite = redisLibevAddWrite;
|
||||
ac->ev.delWrite = redisLibevDelWrite;
|
||||
ac->ev.cleanup = redisLibevCleanup;
|
||||
ac->ev.scheduleTimer = redisLibevSetTimeout;
|
||||
ac->ev.data = e;
|
||||
|
||||
/* Initialize read/write events */
|
||||
ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
|
||||
ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
-175
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_LIBEVENT_H__
|
||||
#define __HIREDIS_LIBEVENT_H__
|
||||
#include <event2/event.h>
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
#define REDIS_LIBEVENT_DELETED 0x01
|
||||
#define REDIS_LIBEVENT_ENTERED 0x02
|
||||
|
||||
typedef struct redisLibeventEvents {
|
||||
redisAsyncContext *context;
|
||||
struct event *ev;
|
||||
struct event_base *base;
|
||||
struct timeval tv;
|
||||
short flags;
|
||||
short state;
|
||||
} redisLibeventEvents;
|
||||
|
||||
static void redisLibeventDestroy(redisLibeventEvents *e) {
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static void redisLibeventHandler(int fd, short event, void *arg) {
|
||||
((void)fd);
|
||||
redisLibeventEvents *e = (redisLibeventEvents*)arg;
|
||||
e->state |= REDIS_LIBEVENT_ENTERED;
|
||||
|
||||
#define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\
|
||||
redisLibeventDestroy(e);\
|
||||
return; \
|
||||
}
|
||||
|
||||
if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
|
||||
redisAsyncHandleTimeout(e->context);
|
||||
CHECK_DELETED();
|
||||
}
|
||||
|
||||
if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
|
||||
redisAsyncHandleRead(e->context);
|
||||
CHECK_DELETED();
|
||||
}
|
||||
|
||||
if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
|
||||
redisAsyncHandleWrite(e->context);
|
||||
CHECK_DELETED();
|
||||
}
|
||||
|
||||
e->state &= ~REDIS_LIBEVENT_ENTERED;
|
||||
#undef CHECK_DELETED
|
||||
}
|
||||
|
||||
static void redisLibeventUpdate(void *privdata, short flag, int isRemove) {
|
||||
redisLibeventEvents *e = (redisLibeventEvents *)privdata;
|
||||
const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL;
|
||||
|
||||
if (isRemove) {
|
||||
if ((e->flags & flag) == 0) {
|
||||
return;
|
||||
} else {
|
||||
e->flags &= ~flag;
|
||||
}
|
||||
} else {
|
||||
if (e->flags & flag) {
|
||||
return;
|
||||
} else {
|
||||
e->flags |= flag;
|
||||
}
|
||||
}
|
||||
|
||||
event_del(e->ev);
|
||||
event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST,
|
||||
redisLibeventHandler, privdata);
|
||||
event_add(e->ev, tv);
|
||||
}
|
||||
|
||||
static void redisLibeventAddRead(void *privdata) {
|
||||
redisLibeventUpdate(privdata, EV_READ, 0);
|
||||
}
|
||||
|
||||
static void redisLibeventDelRead(void *privdata) {
|
||||
redisLibeventUpdate(privdata, EV_READ, 1);
|
||||
}
|
||||
|
||||
static void redisLibeventAddWrite(void *privdata) {
|
||||
redisLibeventUpdate(privdata, EV_WRITE, 0);
|
||||
}
|
||||
|
||||
static void redisLibeventDelWrite(void *privdata) {
|
||||
redisLibeventUpdate(privdata, EV_WRITE, 1);
|
||||
}
|
||||
|
||||
static void redisLibeventCleanup(void *privdata) {
|
||||
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
event_del(e->ev);
|
||||
event_free(e->ev);
|
||||
e->ev = NULL;
|
||||
|
||||
if (e->state & REDIS_LIBEVENT_ENTERED) {
|
||||
e->state |= REDIS_LIBEVENT_DELETED;
|
||||
} else {
|
||||
redisLibeventDestroy(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibeventSetTimeout(void *privdata, struct timeval tv) {
|
||||
redisLibeventEvents *e = (redisLibeventEvents *)privdata;
|
||||
short flags = e->flags;
|
||||
e->flags = 0;
|
||||
e->tv = tv;
|
||||
redisLibeventUpdate(e, flags, 0);
|
||||
}
|
||||
|
||||
static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisLibeventEvents *e;
|
||||
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if (ac->ev.data != NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e->context = ac;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
ac->ev.addRead = redisLibeventAddRead;
|
||||
ac->ev.delRead = redisLibeventDelRead;
|
||||
ac->ev.addWrite = redisLibeventAddWrite;
|
||||
ac->ev.delWrite = redisLibeventDelWrite;
|
||||
ac->ev.cleanup = redisLibeventCleanup;
|
||||
ac->ev.scheduleTimer = redisLibeventSetTimeout;
|
||||
ac->ev.data = e;
|
||||
|
||||
/* Initialize and install read/write events */
|
||||
e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e);
|
||||
e->base = base;
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
Vendored
-117
@@ -1,117 +0,0 @@
|
||||
#ifndef __HIREDIS_LIBUV_H__
|
||||
#define __HIREDIS_LIBUV_H__
|
||||
#include <stdlib.h>
|
||||
#include <uv.h>
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct redisLibuvEvents {
|
||||
redisAsyncContext* context;
|
||||
uv_poll_t handle;
|
||||
int events;
|
||||
} redisLibuvEvents;
|
||||
|
||||
|
||||
static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
|
||||
int ev = (status ? p->events : events);
|
||||
|
||||
if (p->context != NULL && (ev & UV_READABLE)) {
|
||||
redisAsyncHandleRead(p->context);
|
||||
}
|
||||
if (p->context != NULL && (ev & UV_WRITABLE)) {
|
||||
redisAsyncHandleWrite(p->context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvAddRead(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->events |= UV_READABLE;
|
||||
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvDelRead(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->events &= ~UV_READABLE;
|
||||
|
||||
if (p->events) {
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
} else {
|
||||
uv_poll_stop(&p->handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvAddWrite(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->events |= UV_WRITABLE;
|
||||
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvDelWrite(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->events &= ~UV_WRITABLE;
|
||||
|
||||
if (p->events) {
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
} else {
|
||||
uv_poll_stop(&p->handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void on_close(uv_handle_t* handle) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
|
||||
|
||||
hi_free(p);
|
||||
}
|
||||
|
||||
|
||||
static void redisLibuvCleanup(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
p->context = NULL; // indicate that context might no longer exist
|
||||
uv_close((uv_handle_t*)&p->handle, on_close);
|
||||
}
|
||||
|
||||
|
||||
static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
if (ac->ev.data != NULL) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
ac->ev.addRead = redisLibuvAddRead;
|
||||
ac->ev.delRead = redisLibuvDelRead;
|
||||
ac->ev.addWrite = redisLibuvAddWrite;
|
||||
ac->ev.delWrite = redisLibuvDelWrite;
|
||||
ac->ev.cleanup = redisLibuvCleanup;
|
||||
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p));
|
||||
if (p == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
ac->ev.data = p;
|
||||
p->handle.data = p;
|
||||
p->context = ac;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
Vendored
-115
@@ -1,115 +0,0 @@
|
||||
//
|
||||
// Created by Дмитрий Бахвалов on 13.07.15.
|
||||
// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __HIREDIS_MACOSX_H__
|
||||
#define __HIREDIS_MACOSX_H__
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
typedef struct {
|
||||
redisAsyncContext *context;
|
||||
CFSocketRef socketRef;
|
||||
CFRunLoopSourceRef sourceRef;
|
||||
} RedisRunLoop;
|
||||
|
||||
static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
|
||||
if( redisRunLoop != NULL ) {
|
||||
if( redisRunLoop->sourceRef != NULL ) {
|
||||
CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);
|
||||
CFRelease(redisRunLoop->sourceRef);
|
||||
}
|
||||
if( redisRunLoop->socketRef != NULL ) {
|
||||
CFSocketInvalidate(redisRunLoop->socketRef);
|
||||
CFRelease(redisRunLoop->socketRef);
|
||||
}
|
||||
hi_free(redisRunLoop);
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static void redisMacOSAddRead(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
|
||||
}
|
||||
|
||||
static void redisMacOSDelRead(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
|
||||
}
|
||||
|
||||
static void redisMacOSAddWrite(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
|
||||
}
|
||||
|
||||
static void redisMacOSDelWrite(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
|
||||
}
|
||||
|
||||
static void redisMacOSCleanup(void *privdata) {
|
||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||
freeRedisRunLoop(redisRunLoop);
|
||||
}
|
||||
|
||||
static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {
|
||||
redisAsyncContext* context = (redisAsyncContext*) info;
|
||||
|
||||
switch (callbackType) {
|
||||
case kCFSocketReadCallBack:
|
||||
redisAsyncHandleRead(context);
|
||||
break;
|
||||
|
||||
case kCFSocketWriteCallBack:
|
||||
redisAsyncHandleWrite(context);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {
|
||||
redisContext *redisCtx = &(redisAsyncCtx->c);
|
||||
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
|
||||
|
||||
RedisRunLoop* redisRunLoop = (RedisRunLoop*) hi_calloc(1, sizeof(RedisRunLoop));
|
||||
if (redisRunLoop == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Setup redis stuff */
|
||||
redisRunLoop->context = redisAsyncCtx;
|
||||
|
||||
redisAsyncCtx->ev.addRead = redisMacOSAddRead;
|
||||
redisAsyncCtx->ev.delRead = redisMacOSDelRead;
|
||||
redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;
|
||||
redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;
|
||||
redisAsyncCtx->ev.cleanup = redisMacOSCleanup;
|
||||
redisAsyncCtx->ev.data = redisRunLoop;
|
||||
|
||||
/* Initialize and install read/write events */
|
||||
CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };
|
||||
|
||||
redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,
|
||||
kCFSocketReadCallBack | kCFSocketWriteCallBack,
|
||||
redisMacOSAsyncCallback,
|
||||
&socketCtx);
|
||||
if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);
|
||||
|
||||
redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);
|
||||
if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);
|
||||
|
||||
CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
-135
@@ -1,135 +0,0 @@
|
||||
/*-
|
||||
* Copyright (C) 2014 Pietro Cerutti <gahr@gahr.ch>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_QT_H__
|
||||
#define __HIREDIS_QT_H__
|
||||
#include <QSocketNotifier>
|
||||
#include "../async.h"
|
||||
|
||||
static void RedisQtAddRead(void *);
|
||||
static void RedisQtDelRead(void *);
|
||||
static void RedisQtAddWrite(void *);
|
||||
static void RedisQtDelWrite(void *);
|
||||
static void RedisQtCleanup(void *);
|
||||
|
||||
class RedisQtAdapter : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
friend
|
||||
void RedisQtAddRead(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->addRead();
|
||||
}
|
||||
|
||||
friend
|
||||
void RedisQtDelRead(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->delRead();
|
||||
}
|
||||
|
||||
friend
|
||||
void RedisQtAddWrite(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->addWrite();
|
||||
}
|
||||
|
||||
friend
|
||||
void RedisQtDelWrite(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->delWrite();
|
||||
}
|
||||
|
||||
friend
|
||||
void RedisQtCleanup(void * adapter) {
|
||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||
a->cleanup();
|
||||
}
|
||||
|
||||
public:
|
||||
RedisQtAdapter(QObject * parent = 0)
|
||||
: QObject(parent), m_ctx(0), m_read(0), m_write(0) { }
|
||||
|
||||
~RedisQtAdapter() {
|
||||
if (m_ctx != 0) {
|
||||
m_ctx->ev.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int setContext(redisAsyncContext * ac) {
|
||||
if (ac->ev.data != NULL) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
m_ctx = ac;
|
||||
m_ctx->ev.data = this;
|
||||
m_ctx->ev.addRead = RedisQtAddRead;
|
||||
m_ctx->ev.delRead = RedisQtDelRead;
|
||||
m_ctx->ev.addWrite = RedisQtAddWrite;
|
||||
m_ctx->ev.delWrite = RedisQtDelWrite;
|
||||
m_ctx->ev.cleanup = RedisQtCleanup;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
void addRead() {
|
||||
if (m_read) return;
|
||||
m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0);
|
||||
connect(m_read, SIGNAL(activated(int)), this, SLOT(read()));
|
||||
}
|
||||
|
||||
void delRead() {
|
||||
if (!m_read) return;
|
||||
delete m_read;
|
||||
m_read = 0;
|
||||
}
|
||||
|
||||
void addWrite() {
|
||||
if (m_write) return;
|
||||
m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0);
|
||||
connect(m_write, SIGNAL(activated(int)), this, SLOT(write()));
|
||||
}
|
||||
|
||||
void delWrite() {
|
||||
if (!m_write) return;
|
||||
delete m_write;
|
||||
m_write = 0;
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
delRead();
|
||||
delWrite();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void read() { redisAsyncHandleRead(m_ctx); }
|
||||
void write() { redisAsyncHandleWrite(m_ctx); }
|
||||
|
||||
private:
|
||||
redisAsyncContext * m_ctx;
|
||||
QSocketNotifier * m_read;
|
||||
QSocketNotifier * m_write;
|
||||
};
|
||||
|
||||
#endif /* !__HIREDIS_QT_H__ */
|
||||
Vendored
-86
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include "alloc.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
hiredisAllocFuncs hiredisAllocFns = {
|
||||
.mallocFn = malloc,
|
||||
.callocFn = calloc,
|
||||
.reallocFn = realloc,
|
||||
.strdupFn = strdup,
|
||||
.freeFn = free,
|
||||
};
|
||||
|
||||
/* Override hiredis' allocators with ones supplied by the user */
|
||||
hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) {
|
||||
hiredisAllocFuncs orig = hiredisAllocFns;
|
||||
|
||||
hiredisAllocFns = *override;
|
||||
|
||||
return orig;
|
||||
}
|
||||
|
||||
/* Reset allocators to use libc defaults */
|
||||
void hiredisResetAllocators(void) {
|
||||
hiredisAllocFns = (hiredisAllocFuncs) {
|
||||
.mallocFn = malloc,
|
||||
.callocFn = calloc,
|
||||
.reallocFn = realloc,
|
||||
.strdupFn = strdup,
|
||||
.freeFn = free,
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
void *hi_malloc(size_t size) {
|
||||
return hiredisAllocFns.mallocFn(size);
|
||||
}
|
||||
|
||||
void *hi_calloc(size_t nmemb, size_t size) {
|
||||
return hiredisAllocFns.callocFn(nmemb, size);
|
||||
}
|
||||
|
||||
void *hi_realloc(void *ptr, size_t size) {
|
||||
return hiredisAllocFns.reallocFn(ptr, size);
|
||||
}
|
||||
|
||||
char *hi_strdup(const char *str) {
|
||||
return hiredisAllocFns.strdupFn(str);
|
||||
}
|
||||
|
||||
void hi_free(void *ptr) {
|
||||
hiredisAllocFns.freeFn(ptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
-91
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef HIREDIS_ALLOC_H
|
||||
#define HIREDIS_ALLOC_H
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Structure pointing to our actually configured allocators */
|
||||
typedef struct hiredisAllocFuncs {
|
||||
void *(*mallocFn)(size_t);
|
||||
void *(*callocFn)(size_t,size_t);
|
||||
void *(*reallocFn)(void*,size_t);
|
||||
char *(*strdupFn)(const char*);
|
||||
void (*freeFn)(void*);
|
||||
} hiredisAllocFuncs;
|
||||
|
||||
hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha);
|
||||
void hiredisResetAllocators(void);
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
/* Hiredis' configured allocator function pointer struct */
|
||||
extern hiredisAllocFuncs hiredisAllocFns;
|
||||
|
||||
static inline void *hi_malloc(size_t size) {
|
||||
return hiredisAllocFns.mallocFn(size);
|
||||
}
|
||||
|
||||
static inline void *hi_calloc(size_t nmemb, size_t size) {
|
||||
return hiredisAllocFns.callocFn(nmemb, size);
|
||||
}
|
||||
|
||||
static inline void *hi_realloc(void *ptr, size_t size) {
|
||||
return hiredisAllocFns.reallocFn(ptr, size);
|
||||
}
|
||||
|
||||
static inline char *hi_strdup(const char *str) {
|
||||
return hiredisAllocFns.strdupFn(str);
|
||||
}
|
||||
|
||||
static inline void hi_free(void *ptr) {
|
||||
hiredisAllocFns.freeFn(ptr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *hi_malloc(size_t size);
|
||||
void *hi_calloc(size_t nmemb, size_t size);
|
||||
void *hi_realloc(void *ptr, size_t size);
|
||||
char *hi_strdup(const char *str);
|
||||
void hi_free(void *ptr);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HIREDIS_ALLOC_H */
|
||||
Vendored
-24
@@ -1,24 +0,0 @@
|
||||
# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin)
|
||||
environment:
|
||||
matrix:
|
||||
- CYG_BASH: C:\cygwin64\bin\bash
|
||||
CC: gcc
|
||||
- CYG_BASH: C:\cygwin\bin\bash
|
||||
CC: gcc
|
||||
CFLAGS: -m32
|
||||
CXXFLAGS: -m32
|
||||
LDFLAGS: -m32
|
||||
|
||||
clone_depth: 1
|
||||
|
||||
# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
|
||||
# Install needed build dependencies
|
||||
install:
|
||||
- '%CYG_BASH% -lc "cygcheck -dc cygwin"'
|
||||
|
||||
build_script:
|
||||
- 'echo building...'
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; mkdir build && cd build && cmake .. -G \"Unix Makefiles\" && make VERBOSE=1"'
|
||||
Vendored
-887
@@ -1,887 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include "alloc.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include "async.h"
|
||||
#include "net.h"
|
||||
#include "dict.c"
|
||||
#include "sds.h"
|
||||
#include "win32.h"
|
||||
|
||||
#include "async_private.h"
|
||||
|
||||
/* Forward declarations of hiredis.c functions */
|
||||
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
/* Functions managing dictionary of callbacks for pub/sub. */
|
||||
static unsigned int callbackHash(const void *key) {
|
||||
return dictGenHashFunction((const unsigned char *)key,
|
||||
hi_sdslen((const hisds)key));
|
||||
}
|
||||
|
||||
static void *callbackValDup(void *privdata, const void *src) {
|
||||
((void) privdata);
|
||||
redisCallback *dup;
|
||||
|
||||
dup = hi_malloc(sizeof(*dup));
|
||||
if (dup == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(dup,src,sizeof(*dup));
|
||||
return dup;
|
||||
}
|
||||
|
||||
static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) {
|
||||
int l1, l2;
|
||||
((void) privdata);
|
||||
|
||||
l1 = hi_sdslen((const hisds)key1);
|
||||
l2 = hi_sdslen((const hisds)key2);
|
||||
if (l1 != l2) return 0;
|
||||
return memcmp(key1,key2,l1) == 0;
|
||||
}
|
||||
|
||||
static void callbackKeyDestructor(void *privdata, void *key) {
|
||||
((void) privdata);
|
||||
hi_sdsfree((hisds)key);
|
||||
}
|
||||
|
||||
static void callbackValDestructor(void *privdata, void *val) {
|
||||
((void) privdata);
|
||||
hi_free(val);
|
||||
}
|
||||
|
||||
static dictType callbackDict = {
|
||||
callbackHash,
|
||||
NULL,
|
||||
callbackValDup,
|
||||
callbackKeyCompare,
|
||||
callbackKeyDestructor,
|
||||
callbackValDestructor
|
||||
};
|
||||
|
||||
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
redisAsyncContext *ac;
|
||||
dict *channels = NULL, *patterns = NULL;
|
||||
|
||||
channels = dictCreate(&callbackDict,NULL);
|
||||
if (channels == NULL)
|
||||
goto oom;
|
||||
|
||||
patterns = dictCreate(&callbackDict,NULL);
|
||||
if (patterns == NULL)
|
||||
goto oom;
|
||||
|
||||
ac = hi_realloc(c,sizeof(redisAsyncContext));
|
||||
if (ac == NULL)
|
||||
goto oom;
|
||||
|
||||
c = &(ac->c);
|
||||
|
||||
/* The regular connect functions will always set the flag REDIS_CONNECTED.
|
||||
* For the async API, we want to wait until the first write event is
|
||||
* received up before setting this flag, so reset it here. */
|
||||
c->flags &= ~REDIS_CONNECTED;
|
||||
|
||||
ac->err = 0;
|
||||
ac->errstr = NULL;
|
||||
ac->data = NULL;
|
||||
ac->dataCleanup = NULL;
|
||||
|
||||
ac->ev.data = NULL;
|
||||
ac->ev.addRead = NULL;
|
||||
ac->ev.delRead = NULL;
|
||||
ac->ev.addWrite = NULL;
|
||||
ac->ev.delWrite = NULL;
|
||||
ac->ev.cleanup = NULL;
|
||||
ac->ev.scheduleTimer = NULL;
|
||||
|
||||
ac->onConnect = NULL;
|
||||
ac->onDisconnect = NULL;
|
||||
|
||||
ac->replies.head = NULL;
|
||||
ac->replies.tail = NULL;
|
||||
ac->sub.invalid.head = NULL;
|
||||
ac->sub.invalid.tail = NULL;
|
||||
ac->sub.channels = channels;
|
||||
ac->sub.patterns = patterns;
|
||||
|
||||
return ac;
|
||||
oom:
|
||||
if (channels) dictRelease(channels);
|
||||
if (patterns) dictRelease(patterns);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We want the error field to be accessible directly instead of requiring
|
||||
* an indirection to the redisContext struct. */
|
||||
static void __redisAsyncCopyError(redisAsyncContext *ac) {
|
||||
if (!ac)
|
||||
return;
|
||||
|
||||
redisContext *c = &(ac->c);
|
||||
ac->err = c->err;
|
||||
ac->errstr = c->errstr;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) {
|
||||
redisOptions myOptions = *options;
|
||||
redisContext *c;
|
||||
redisAsyncContext *ac;
|
||||
|
||||
/* Clear any erroneously set sync callback and flag that we don't want to
|
||||
* use freeReplyObject by default. */
|
||||
myOptions.push_cb = NULL;
|
||||
myOptions.options |= REDIS_OPT_NO_PUSH_AUTOFREE;
|
||||
|
||||
myOptions.options |= REDIS_OPT_NONBLOCK;
|
||||
c = redisConnectWithOptions(&myOptions);
|
||||
if (c == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ac = redisAsyncInitialize(c);
|
||||
if (ac == NULL) {
|
||||
redisFree(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set any configured async push handler */
|
||||
redisAsyncSetPushCallback(ac, myOptions.async_push_cb);
|
||||
|
||||
__redisAsyncCopyError(ac);
|
||||
return ac;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
return redisAsyncConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
|
||||
const char *source_addr) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
options.endpoint.tcp.source_addr = source_addr;
|
||||
return redisAsyncConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
||||
const char *source_addr) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
options.options |= REDIS_OPT_REUSEADDR;
|
||||
options.endpoint.tcp.source_addr = source_addr;
|
||||
return redisAsyncConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_UNIX(&options, path);
|
||||
return redisAsyncConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
|
||||
if (ac->onConnect == NULL) {
|
||||
ac->onConnect = fn;
|
||||
|
||||
/* The common way to detect an established connection is to wait for
|
||||
* the first write event to be fired. This assumes the related event
|
||||
* library functions are already set. */
|
||||
_EL_ADD_WRITE(ac);
|
||||
return REDIS_OK;
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
|
||||
if (ac->onDisconnect == NULL) {
|
||||
ac->onDisconnect = fn;
|
||||
return REDIS_OK;
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Helper functions to push/shift callbacks */
|
||||
static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
|
||||
redisCallback *cb;
|
||||
|
||||
/* Copy callback from stack to heap */
|
||||
cb = hi_malloc(sizeof(*cb));
|
||||
if (cb == NULL)
|
||||
return REDIS_ERR_OOM;
|
||||
|
||||
if (source != NULL) {
|
||||
memcpy(cb,source,sizeof(*cb));
|
||||
cb->next = NULL;
|
||||
}
|
||||
|
||||
/* Store callback in list */
|
||||
if (list->head == NULL)
|
||||
list->head = cb;
|
||||
if (list->tail != NULL)
|
||||
list->tail->next = cb;
|
||||
list->tail = cb;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) {
|
||||
redisCallback *cb = list->head;
|
||||
if (cb != NULL) {
|
||||
list->head = cb->next;
|
||||
if (cb == list->tail)
|
||||
list->tail = NULL;
|
||||
|
||||
/* Copy callback from heap to stack */
|
||||
if (target != NULL)
|
||||
memcpy(target,cb,sizeof(*cb));
|
||||
hi_free(cb);
|
||||
return REDIS_OK;
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) {
|
||||
redisContext *c = &(ac->c);
|
||||
if (cb->fn != NULL) {
|
||||
c->flags |= REDIS_IN_CALLBACK;
|
||||
cb->fn(ac,reply,cb->privdata);
|
||||
c->flags &= ~REDIS_IN_CALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) {
|
||||
if (ac->push_cb != NULL) {
|
||||
ac->c.flags |= REDIS_IN_CALLBACK;
|
||||
ac->push_cb(ac, reply);
|
||||
ac->c.flags &= ~REDIS_IN_CALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to free the context. */
|
||||
static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
dictIterator *it;
|
||||
dictEntry *de;
|
||||
|
||||
/* Execute pending callbacks with NULL reply. */
|
||||
while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)
|
||||
__redisRunCallback(ac,&cb,NULL);
|
||||
|
||||
/* Execute callbacks for invalid commands */
|
||||
while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
|
||||
__redisRunCallback(ac,&cb,NULL);
|
||||
|
||||
/* Run subscription callbacks with NULL reply */
|
||||
if (ac->sub.channels) {
|
||||
it = dictGetIterator(ac->sub.channels);
|
||||
if (it != NULL) {
|
||||
while ((de = dictNext(it)) != NULL)
|
||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
||||
dictReleaseIterator(it);
|
||||
}
|
||||
|
||||
dictRelease(ac->sub.channels);
|
||||
}
|
||||
|
||||
if (ac->sub.patterns) {
|
||||
it = dictGetIterator(ac->sub.patterns);
|
||||
if (it != NULL) {
|
||||
while ((de = dictNext(it)) != NULL)
|
||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
||||
dictReleaseIterator(it);
|
||||
}
|
||||
|
||||
dictRelease(ac->sub.patterns);
|
||||
}
|
||||
|
||||
/* Signal event lib to clean up */
|
||||
_EL_CLEANUP(ac);
|
||||
|
||||
/* Execute disconnect callback. When redisAsyncFree() initiated destroying
|
||||
* this context, the status will always be REDIS_OK. */
|
||||
if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {
|
||||
if (c->flags & REDIS_FREEING) {
|
||||
ac->onDisconnect(ac,REDIS_OK);
|
||||
} else {
|
||||
ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
if (ac->dataCleanup) {
|
||||
ac->dataCleanup(ac->data);
|
||||
}
|
||||
|
||||
/* Cleanup self */
|
||||
redisFree(c);
|
||||
}
|
||||
|
||||
/* Free the async context. When this function is called from a callback,
|
||||
* control needs to be returned to redisProcessCallbacks() before actual
|
||||
* free'ing. To do so, a flag is set on the context which is picked up by
|
||||
* redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
|
||||
void redisAsyncFree(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
c->flags |= REDIS_FREEING;
|
||||
if (!(c->flags & REDIS_IN_CALLBACK))
|
||||
__redisAsyncFree(ac);
|
||||
}
|
||||
|
||||
/* Helper function to make the disconnect happen and clean up. */
|
||||
void __redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
/* Make sure error is accessible if there is any */
|
||||
__redisAsyncCopyError(ac);
|
||||
|
||||
if (ac->err == 0) {
|
||||
/* For clean disconnects, there should be no pending callbacks. */
|
||||
int ret = __redisShiftCallback(&ac->replies,NULL);
|
||||
assert(ret == REDIS_ERR);
|
||||
} else {
|
||||
/* Disconnection is caused by an error, make sure that pending
|
||||
* callbacks cannot call new commands. */
|
||||
c->flags |= REDIS_DISCONNECTING;
|
||||
}
|
||||
|
||||
/* cleanup event library on disconnect.
|
||||
* this is safe to call multiple times */
|
||||
_EL_CLEANUP(ac);
|
||||
|
||||
/* For non-clean disconnects, __redisAsyncFree() will execute pending
|
||||
* callbacks with a NULL-reply. */
|
||||
if (!(c->flags & REDIS_NO_AUTO_FREE)) {
|
||||
__redisAsyncFree(ac);
|
||||
}
|
||||
}
|
||||
|
||||
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
|
||||
* from being issued, but tries to flush the output buffer and execute
|
||||
* callbacks for all remaining replies. When this function is called from a
|
||||
* callback, there might be more replies and we can safely defer disconnecting
|
||||
* to redisProcessCallbacks(). Otherwise, we can only disconnect immediately
|
||||
* when there are no pending callbacks. */
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
c->flags |= REDIS_DISCONNECTING;
|
||||
|
||||
/** unset the auto-free flag here, because disconnect undoes this */
|
||||
c->flags &= ~REDIS_NO_AUTO_FREE;
|
||||
if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
|
||||
redisContext *c = &(ac->c);
|
||||
dict *callbacks;
|
||||
redisCallback *cb;
|
||||
dictEntry *de;
|
||||
int pvariant;
|
||||
char *stype;
|
||||
hisds sname;
|
||||
|
||||
/* Custom reply functions are not supported for pub/sub. This will fail
|
||||
* very hard when they are used... */
|
||||
if (reply->type == REDIS_REPLY_ARRAY || reply->type == REDIS_REPLY_PUSH) {
|
||||
assert(reply->elements >= 2);
|
||||
assert(reply->element[0]->type == REDIS_REPLY_STRING);
|
||||
stype = reply->element[0]->str;
|
||||
pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;
|
||||
|
||||
if (pvariant)
|
||||
callbacks = ac->sub.patterns;
|
||||
else
|
||||
callbacks = ac->sub.channels;
|
||||
|
||||
/* Locate the right callback */
|
||||
assert(reply->element[1]->type == REDIS_REPLY_STRING);
|
||||
sname = hi_sdsnewlen(reply->element[1]->str,reply->element[1]->len);
|
||||
if (sname == NULL)
|
||||
goto oom;
|
||||
|
||||
de = dictFind(callbacks,sname);
|
||||
if (de != NULL) {
|
||||
cb = dictGetEntryVal(de);
|
||||
|
||||
/* If this is an subscribe reply decrease pending counter. */
|
||||
if (strcasecmp(stype+pvariant,"subscribe") == 0) {
|
||||
cb->pending_subs -= 1;
|
||||
}
|
||||
|
||||
memcpy(dstcb,cb,sizeof(*dstcb));
|
||||
|
||||
/* If this is an unsubscribe message, remove it. */
|
||||
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
|
||||
if (cb->pending_subs == 0)
|
||||
dictDelete(callbacks,sname);
|
||||
|
||||
/* If this was the last unsubscribe message, revert to
|
||||
* non-subscribe mode. */
|
||||
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
|
||||
|
||||
/* Unset subscribed flag only when no pipelined pending subscribe. */
|
||||
if (reply->element[2]->integer == 0
|
||||
&& dictSize(ac->sub.channels) == 0
|
||||
&& dictSize(ac->sub.patterns) == 0)
|
||||
c->flags &= ~REDIS_SUBSCRIBED;
|
||||
}
|
||||
}
|
||||
hi_sdsfree(sname);
|
||||
} else {
|
||||
/* Shift callback for invalid commands. */
|
||||
__redisShiftCallback(&ac->sub.invalid,dstcb);
|
||||
}
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
#define redisIsSpontaneousPushReply(r) \
|
||||
(redisIsPushReply(r) && !redisIsSubscribeReply(r))
|
||||
|
||||
static int redisIsSubscribeReply(redisReply *reply) {
|
||||
char *str;
|
||||
size_t len, off;
|
||||
|
||||
/* We will always have at least one string with the subscribe/message type */
|
||||
if (reply->elements < 1 || reply->element[0]->type != REDIS_REPLY_STRING ||
|
||||
reply->element[0]->len < sizeof("message") - 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the string/len moving past 'p' if needed */
|
||||
off = tolower(reply->element[0]->str[0]) == 'p';
|
||||
str = reply->element[0]->str + off;
|
||||
len = reply->element[0]->len - off;
|
||||
|
||||
return !strncasecmp(str, "subscribe", len) ||
|
||||
!strncasecmp(str, "message", len);
|
||||
|
||||
}
|
||||
|
||||
void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb = {NULL, NULL, 0, NULL};
|
||||
void *reply = NULL;
|
||||
int status;
|
||||
|
||||
while((status = redisGetReply(c,&reply)) == REDIS_OK) {
|
||||
if (reply == NULL) {
|
||||
/* When the connection is being disconnected and there are
|
||||
* no more replies, this is the cue to really disconnect. */
|
||||
if (c->flags & REDIS_DISCONNECTING && hi_sdslen(c->obuf) == 0
|
||||
&& ac->replies.head == NULL) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If monitor mode, repush callback */
|
||||
if(c->flags & REDIS_MONITORING) {
|
||||
__redisPushCallback(&ac->replies,&cb);
|
||||
}
|
||||
|
||||
/* When the connection is not being disconnected, simply stop
|
||||
* trying to get replies and wait for the next loop tick. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Send any non-subscribe related PUSH messages to our PUSH handler
|
||||
* while allowing subscribe related PUSH messages to pass through.
|
||||
* This allows existing code to be backward compatible and work in
|
||||
* either RESP2 or RESP3 mode. */
|
||||
if (redisIsSpontaneousPushReply(reply)) {
|
||||
__redisRunPushCallback(ac, reply);
|
||||
c->reader->fn->freeObject(reply);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Even if the context is subscribed, pending regular
|
||||
* callbacks will get a reply before pub/sub messages arrive. */
|
||||
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
|
||||
/*
|
||||
* A spontaneous reply in a not-subscribed context can be the error
|
||||
* reply that is sent when a new connection exceeds the maximum
|
||||
* number of allowed connections on the server side.
|
||||
*
|
||||
* This is seen as an error instead of a regular reply because the
|
||||
* server closes the connection after sending it.
|
||||
*
|
||||
* To prevent the error from being overwritten by an EOF error the
|
||||
* connection is closed here. See issue #43.
|
||||
*
|
||||
* Another possibility is that the server is loading its dataset.
|
||||
* In this case we also want to close the connection, and have the
|
||||
* user wait until the server is ready to take our request.
|
||||
*/
|
||||
if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
|
||||
c->err = REDIS_ERR_OTHER;
|
||||
snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
|
||||
c->reader->fn->freeObject(reply);
|
||||
__redisAsyncDisconnect(ac);
|
||||
return;
|
||||
}
|
||||
/* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */
|
||||
assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING));
|
||||
if(c->flags & REDIS_SUBSCRIBED)
|
||||
__redisGetSubscribeCallback(ac,reply,&cb);
|
||||
}
|
||||
|
||||
if (cb.fn != NULL) {
|
||||
__redisRunCallback(ac,&cb,reply);
|
||||
c->reader->fn->freeObject(reply);
|
||||
|
||||
/* Proceed with free'ing when redisAsyncFree() was called. */
|
||||
if (c->flags & REDIS_FREEING) {
|
||||
__redisAsyncFree(ac);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* No callback for this reply. This can either be a NULL callback,
|
||||
* or there were no callbacks to begin with. Either way, don't
|
||||
* abort with an error, but simply ignore it because the client
|
||||
* doesn't know what the server will spit out over the wire. */
|
||||
c->reader->fn->freeObject(reply);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disconnect when there was an error reading the reply */
|
||||
if (status != REDIS_OK)
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
static void __redisAsyncHandleConnectFailure(redisAsyncContext *ac) {
|
||||
if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
/* Internal helper function to detect socket status the first time a read or
|
||||
* write event fires. When connecting was not successful, the connect callback
|
||||
* is called with a REDIS_ERR status and the context is free'd. */
|
||||
static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
|
||||
int completed = 0;
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {
|
||||
/* Error! */
|
||||
redisCheckSocketError(c);
|
||||
__redisAsyncHandleConnectFailure(ac);
|
||||
return REDIS_ERR;
|
||||
} else if (completed == 1) {
|
||||
/* connected! */
|
||||
if (c->connection_type == REDIS_CONN_TCP &&
|
||||
redisSetTcpNoDelay(c) == REDIS_ERR) {
|
||||
__redisAsyncHandleConnectFailure(ac);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (ac->onConnect) ac->onConnect(ac, REDIS_OK);
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
} else {
|
||||
return REDIS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void redisAsyncRead(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
if (redisBufferRead(c) == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
} else {
|
||||
/* Always re-schedule reads */
|
||||
_EL_ADD_READ(ac);
|
||||
redisProcessCallbacks(ac);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function should be called when the socket is readable.
|
||||
* It processes all replies that can be read and executes their callbacks.
|
||||
*/
|
||||
void redisAsyncHandleRead(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
if (!(c->flags & REDIS_CONNECTED)) {
|
||||
/* Abort connect was not successful. */
|
||||
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
|
||||
return;
|
||||
/* Try again later when the context is still not connected. */
|
||||
if (!(c->flags & REDIS_CONNECTED))
|
||||
return;
|
||||
}
|
||||
|
||||
c->funcs->async_read(ac);
|
||||
}
|
||||
|
||||
void redisAsyncWrite(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
int done = 0;
|
||||
|
||||
if (redisBufferWrite(c,&done) == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
} else {
|
||||
/* Continue writing when not done, stop writing otherwise */
|
||||
if (!done)
|
||||
_EL_ADD_WRITE(ac);
|
||||
else
|
||||
_EL_DEL_WRITE(ac);
|
||||
|
||||
/* Always schedule reads after writes */
|
||||
_EL_ADD_READ(ac);
|
||||
}
|
||||
}
|
||||
|
||||
void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
if (!(c->flags & REDIS_CONNECTED)) {
|
||||
/* Abort connect was not successful. */
|
||||
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
|
||||
return;
|
||||
/* Try again later when the context is still not connected. */
|
||||
if (!(c->flags & REDIS_CONNECTED))
|
||||
return;
|
||||
}
|
||||
|
||||
c->funcs->async_write(ac);
|
||||
}
|
||||
|
||||
void redisAsyncHandleTimeout(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
|
||||
if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) {
|
||||
/* Nothing to do - just an idle timeout */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!c->err) {
|
||||
__redisSetError(c, REDIS_ERR_TIMEOUT, "Timeout");
|
||||
}
|
||||
|
||||
if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) {
|
||||
ac->onConnect(ac, REDIS_ERR);
|
||||
}
|
||||
|
||||
while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) {
|
||||
__redisRunCallback(ac, &cb, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Don't automatically sever the connection,
|
||||
* rather, allow to ignore <x> responses before the queue is clear
|
||||
*/
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
/* Sets a pointer to the first argument and its length starting at p. Returns
|
||||
* the number of bytes to skip to get to the following argument. */
|
||||
static const char *nextArgument(const char *start, const char **str, size_t *len) {
|
||||
const char *p = start;
|
||||
if (p[0] != '$') {
|
||||
p = strchr(p,'$');
|
||||
if (p == NULL) return NULL;
|
||||
}
|
||||
|
||||
*len = (int)strtol(p+1,NULL,10);
|
||||
p = strchr(p,'\r');
|
||||
assert(p);
|
||||
*str = p+2;
|
||||
return p+2+(*len)+2;
|
||||
}
|
||||
|
||||
/* Helper function for the redisAsyncCommand* family of functions. Writes a
|
||||
* formatted command to the output buffer and registers the provided callback
|
||||
* function with the context. */
|
||||
static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
struct dict *cbdict;
|
||||
dictEntry *de;
|
||||
redisCallback *existcb;
|
||||
int pvariant, hasnext;
|
||||
const char *cstr, *astr;
|
||||
size_t clen, alen;
|
||||
const char *p;
|
||||
hisds sname;
|
||||
int ret;
|
||||
|
||||
/* Don't accept new commands when the connection is about to be closed. */
|
||||
if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
|
||||
|
||||
/* Setup callback */
|
||||
cb.fn = fn;
|
||||
cb.privdata = privdata;
|
||||
cb.pending_subs = 1;
|
||||
|
||||
/* Find out which command will be appended. */
|
||||
p = nextArgument(cmd,&cstr,&clen);
|
||||
assert(p != NULL);
|
||||
hasnext = (p[0] == '$');
|
||||
pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0;
|
||||
cstr += pvariant;
|
||||
clen -= pvariant;
|
||||
|
||||
if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) {
|
||||
c->flags |= REDIS_SUBSCRIBED;
|
||||
|
||||
/* Add every channel/pattern to the list of subscription callbacks. */
|
||||
while ((p = nextArgument(p,&astr,&alen)) != NULL) {
|
||||
sname = hi_sdsnewlen(astr,alen);
|
||||
if (sname == NULL)
|
||||
goto oom;
|
||||
|
||||
if (pvariant)
|
||||
cbdict = ac->sub.patterns;
|
||||
else
|
||||
cbdict = ac->sub.channels;
|
||||
|
||||
de = dictFind(cbdict,sname);
|
||||
|
||||
if (de != NULL) {
|
||||
existcb = dictGetEntryVal(de);
|
||||
cb.pending_subs = existcb->pending_subs + 1;
|
||||
}
|
||||
|
||||
ret = dictReplace(cbdict,sname,&cb);
|
||||
|
||||
if (ret == 0) hi_sdsfree(sname);
|
||||
}
|
||||
} else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
|
||||
/* It is only useful to call (P)UNSUBSCRIBE when the context is
|
||||
* subscribed to one or more channels or patterns. */
|
||||
if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;
|
||||
|
||||
/* (P)UNSUBSCRIBE does not have its own response: every channel or
|
||||
* pattern that is unsubscribed will receive a message. This means we
|
||||
* should not append a callback function for this command. */
|
||||
} else if(strncasecmp(cstr,"monitor\r\n",9) == 0) {
|
||||
/* Set monitor flag and push callback */
|
||||
c->flags |= REDIS_MONITORING;
|
||||
__redisPushCallback(&ac->replies,&cb);
|
||||
} else {
|
||||
if (c->flags & REDIS_SUBSCRIBED)
|
||||
/* This will likely result in an error reply, but it needs to be
|
||||
* received and passed to the callback. */
|
||||
__redisPushCallback(&ac->sub.invalid,&cb);
|
||||
else
|
||||
__redisPushCallback(&ac->replies,&cb);
|
||||
}
|
||||
|
||||
__redisAppendCommand(c,cmd,len);
|
||||
|
||||
/* Always schedule a write when the write buffer is non-empty */
|
||||
_EL_ADD_WRITE(ac);
|
||||
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
|
||||
char *cmd;
|
||||
int len;
|
||||
int status;
|
||||
len = redisvFormatCommand(&cmd,format,ap);
|
||||
|
||||
/* We don't want to pass -1 or -2 to future functions as a length. */
|
||||
if (len < 0)
|
||||
return REDIS_ERR;
|
||||
|
||||
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
hi_free(cmd);
|
||||
return status;
|
||||
}
|
||||
|
||||
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) {
|
||||
va_list ap;
|
||||
int status;
|
||||
va_start(ap,format);
|
||||
status = redisvAsyncCommand(ac,fn,privdata,format,ap);
|
||||
va_end(ap);
|
||||
return status;
|
||||
}
|
||||
|
||||
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
|
||||
hisds cmd;
|
||||
int len;
|
||||
int status;
|
||||
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
|
||||
if (len < 0)
|
||||
return REDIS_ERR;
|
||||
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
hi_sdsfree(cmd);
|
||||
return status;
|
||||
}
|
||||
|
||||
int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
|
||||
int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
return status;
|
||||
}
|
||||
|
||||
redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn) {
|
||||
redisAsyncPushFn *old = ac->push_cb;
|
||||
ac->push_cb = fn;
|
||||
return old;
|
||||
}
|
||||
|
||||
int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
|
||||
if (!ac->c.command_timeout) {
|
||||
ac->c.command_timeout = hi_calloc(1, sizeof(tv));
|
||||
if (ac->c.command_timeout == NULL) {
|
||||
__redisSetError(&ac->c, REDIS_ERR_OOM, "Out of memory");
|
||||
__redisAsyncCopyError(ac);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (tv.tv_sec != ac->c.command_timeout->tv_sec ||
|
||||
tv.tv_usec != ac->c.command_timeout->tv_usec)
|
||||
{
|
||||
*ac->c.command_timeout = tv;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
Vendored
-147
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_ASYNC_H
|
||||
#define __HIREDIS_ASYNC_H
|
||||
#include "hiredis.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
|
||||
struct dict; /* dictionary header is included in async.c */
|
||||
|
||||
/* Reply callback prototype and container */
|
||||
typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
|
||||
typedef struct redisCallback {
|
||||
struct redisCallback *next; /* simple singly linked list */
|
||||
redisCallbackFn *fn;
|
||||
int pending_subs;
|
||||
void *privdata;
|
||||
} redisCallback;
|
||||
|
||||
/* List of callbacks for either regular replies or pub/sub */
|
||||
typedef struct redisCallbackList {
|
||||
redisCallback *head, *tail;
|
||||
} redisCallbackList;
|
||||
|
||||
/* Connection callback prototypes */
|
||||
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
|
||||
typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
|
||||
typedef void(redisTimerCallback)(void *timer, void *privdata);
|
||||
|
||||
/* Context for an async connection to Redis */
|
||||
typedef struct redisAsyncContext {
|
||||
/* Hold the regular context, so it can be realloc'ed. */
|
||||
redisContext c;
|
||||
|
||||
/* Setup error flags so they can be used directly. */
|
||||
int err;
|
||||
char *errstr;
|
||||
|
||||
/* Not used by hiredis */
|
||||
void *data;
|
||||
void (*dataCleanup)(void *privdata);
|
||||
|
||||
/* Event library data and hooks */
|
||||
struct {
|
||||
void *data;
|
||||
|
||||
/* Hooks that are called when the library expects to start
|
||||
* reading/writing. These functions should be idempotent. */
|
||||
void (*addRead)(void *privdata);
|
||||
void (*delRead)(void *privdata);
|
||||
void (*addWrite)(void *privdata);
|
||||
void (*delWrite)(void *privdata);
|
||||
void (*cleanup)(void *privdata);
|
||||
void (*scheduleTimer)(void *privdata, struct timeval tv);
|
||||
} ev;
|
||||
|
||||
/* Called when either the connection is terminated due to an error or per
|
||||
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
|
||||
redisDisconnectCallback *onDisconnect;
|
||||
|
||||
/* Called when the first write event was received. */
|
||||
redisConnectCallback *onConnect;
|
||||
|
||||
/* Regular command callbacks */
|
||||
redisCallbackList replies;
|
||||
|
||||
/* Address used for connect() */
|
||||
struct sockaddr *saddr;
|
||||
size_t addrlen;
|
||||
|
||||
/* Subscription callbacks */
|
||||
struct {
|
||||
redisCallbackList invalid;
|
||||
struct dict *channels;
|
||||
struct dict *patterns;
|
||||
} sub;
|
||||
|
||||
/* Any configured RESP3 PUSH handler */
|
||||
redisAsyncPushFn *push_cb;
|
||||
} redisAsyncContext;
|
||||
|
||||
/* Functions that proxy to hiredis */
|
||||
redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options);
|
||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
|
||||
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
|
||||
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
||||
const char *source_addr);
|
||||
redisAsyncContext *redisAsyncConnectUnix(const char *path);
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
||||
|
||||
redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn);
|
||||
int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac);
|
||||
void redisAsyncFree(redisAsyncContext *ac);
|
||||
|
||||
/* Handle read/write events */
|
||||
void redisAsyncHandleRead(redisAsyncContext *ac);
|
||||
void redisAsyncHandleWrite(redisAsyncContext *ac);
|
||||
void redisAsyncHandleTimeout(redisAsyncContext *ac);
|
||||
void redisAsyncRead(redisAsyncContext *ac);
|
||||
void redisAsyncWrite(redisAsyncContext *ac);
|
||||
|
||||
/* Command functions for an async context. Write the command to the
|
||||
* output buffer and register the provided callback. */
|
||||
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);
|
||||
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
|
||||
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
|
||||
int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
-75
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_ASYNC_PRIVATE_H
|
||||
#define __HIREDIS_ASYNC_PRIVATE_H
|
||||
|
||||
#define _EL_ADD_READ(ctx) \
|
||||
do { \
|
||||
refreshTimeout(ctx); \
|
||||
if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
|
||||
} while (0)
|
||||
#define _EL_DEL_READ(ctx) do { \
|
||||
if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
|
||||
} while(0)
|
||||
#define _EL_ADD_WRITE(ctx) \
|
||||
do { \
|
||||
refreshTimeout(ctx); \
|
||||
if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
|
||||
} while (0)
|
||||
#define _EL_DEL_WRITE(ctx) do { \
|
||||
if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
|
||||
} while(0)
|
||||
#define _EL_CLEANUP(ctx) do { \
|
||||
if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
|
||||
ctx->ev.cleanup = NULL; \
|
||||
} while(0)
|
||||
|
||||
static inline void refreshTimeout(redisAsyncContext *ctx) {
|
||||
#define REDIS_TIMER_ISSET(tvp) \
|
||||
(tvp && ((tvp)->tv_sec || (tvp)->tv_usec))
|
||||
|
||||
#define REDIS_EL_TIMER(ac, tvp) \
|
||||
if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \
|
||||
(ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \
|
||||
}
|
||||
|
||||
if (ctx->c.flags & REDIS_CONNECTED) {
|
||||
REDIS_EL_TIMER(ctx, ctx->c.command_timeout);
|
||||
} else {
|
||||
REDIS_EL_TIMER(ctx, ctx->c.connect_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
void __redisAsyncDisconnect(redisAsyncContext *ac);
|
||||
void redisProcessCallbacks(redisAsyncContext *ac);
|
||||
|
||||
#endif /* __HIREDIS_ASYNC_PRIVATE_H */
|
||||
Vendored
-352
@@ -1,352 +0,0 @@
|
||||
/* Hash table implementation.
|
||||
*
|
||||
* This file implements in memory hash tables with insert/del/replace/find/
|
||||
* get-random-element operations. Hash tables will auto resize if needed
|
||||
* tables of power of two in size are used, collisions are handled by
|
||||
* chaining. See the source code for more information... :)
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include "alloc.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include "dict.h"
|
||||
|
||||
/* -------------------------- private prototypes ---------------------------- */
|
||||
|
||||
static int _dictExpandIfNeeded(dict *ht);
|
||||
static unsigned long _dictNextPower(unsigned long size);
|
||||
static int _dictKeyIndex(dict *ht, const void *key);
|
||||
static int _dictInit(dict *ht, dictType *type, void *privDataPtr);
|
||||
|
||||
/* -------------------------- hash functions -------------------------------- */
|
||||
|
||||
/* Generic hash function (a popular one from Bernstein).
|
||||
* I tested a few and this was the best. */
|
||||
static unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
|
||||
unsigned int hash = 5381;
|
||||
|
||||
while (len--)
|
||||
hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* ----------------------------- API implementation ------------------------- */
|
||||
|
||||
/* Reset an hashtable already initialized with ht_init().
|
||||
* NOTE: This function should only called by ht_destroy(). */
|
||||
static void _dictReset(dict *ht) {
|
||||
ht->table = NULL;
|
||||
ht->size = 0;
|
||||
ht->sizemask = 0;
|
||||
ht->used = 0;
|
||||
}
|
||||
|
||||
/* Create a new hash table */
|
||||
static dict *dictCreate(dictType *type, void *privDataPtr) {
|
||||
dict *ht = hi_malloc(sizeof(*ht));
|
||||
if (ht == NULL)
|
||||
return NULL;
|
||||
|
||||
_dictInit(ht,type,privDataPtr);
|
||||
return ht;
|
||||
}
|
||||
|
||||
/* Initialize the hash table */
|
||||
static int _dictInit(dict *ht, dictType *type, void *privDataPtr) {
|
||||
_dictReset(ht);
|
||||
ht->type = type;
|
||||
ht->privdata = privDataPtr;
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
/* Expand or create the hashtable */
|
||||
static int dictExpand(dict *ht, unsigned long size) {
|
||||
dict n; /* the new hashtable */
|
||||
unsigned long realsize = _dictNextPower(size), i;
|
||||
|
||||
/* the size is invalid if it is smaller than the number of
|
||||
* elements already inside the hashtable */
|
||||
if (ht->used > size)
|
||||
return DICT_ERR;
|
||||
|
||||
_dictInit(&n, ht->type, ht->privdata);
|
||||
n.size = realsize;
|
||||
n.sizemask = realsize-1;
|
||||
n.table = hi_calloc(realsize,sizeof(dictEntry*));
|
||||
if (n.table == NULL)
|
||||
return DICT_ERR;
|
||||
|
||||
/* Copy all the elements from the old to the new table:
|
||||
* note that if the old hash table is empty ht->size is zero,
|
||||
* so dictExpand just creates an hash table. */
|
||||
n.used = ht->used;
|
||||
for (i = 0; i < ht->size && ht->used > 0; i++) {
|
||||
dictEntry *he, *nextHe;
|
||||
|
||||
if (ht->table[i] == NULL) continue;
|
||||
|
||||
/* For each hash entry on this slot... */
|
||||
he = ht->table[i];
|
||||
while(he) {
|
||||
unsigned int h;
|
||||
|
||||
nextHe = he->next;
|
||||
/* Get the new element index */
|
||||
h = dictHashKey(ht, he->key) & n.sizemask;
|
||||
he->next = n.table[h];
|
||||
n.table[h] = he;
|
||||
ht->used--;
|
||||
/* Pass to the next element */
|
||||
he = nextHe;
|
||||
}
|
||||
}
|
||||
assert(ht->used == 0);
|
||||
hi_free(ht->table);
|
||||
|
||||
/* Remap the new hashtable in the old */
|
||||
*ht = n;
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
/* Add an element to the target hash table */
|
||||
static int dictAdd(dict *ht, void *key, void *val) {
|
||||
int index;
|
||||
dictEntry *entry;
|
||||
|
||||
/* Get the index of the new element, or -1 if
|
||||
* the element already exists. */
|
||||
if ((index = _dictKeyIndex(ht, key)) == -1)
|
||||
return DICT_ERR;
|
||||
|
||||
/* Allocates the memory and stores key */
|
||||
entry = hi_malloc(sizeof(*entry));
|
||||
if (entry == NULL)
|
||||
return DICT_ERR;
|
||||
|
||||
entry->next = ht->table[index];
|
||||
ht->table[index] = entry;
|
||||
|
||||
/* Set the hash entry fields. */
|
||||
dictSetHashKey(ht, entry, key);
|
||||
dictSetHashVal(ht, entry, val);
|
||||
ht->used++;
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
/* Add an element, discarding the old if the key already exists.
|
||||
* Return 1 if the key was added from scratch, 0 if there was already an
|
||||
* element with such key and dictReplace() just performed a value update
|
||||
* operation. */
|
||||
static int dictReplace(dict *ht, void *key, void *val) {
|
||||
dictEntry *entry, auxentry;
|
||||
|
||||
/* Try to add the element. If the key
|
||||
* does not exists dictAdd will succeed. */
|
||||
if (dictAdd(ht, key, val) == DICT_OK)
|
||||
return 1;
|
||||
/* It already exists, get the entry */
|
||||
entry = dictFind(ht, key);
|
||||
if (entry == NULL)
|
||||
return 0;
|
||||
|
||||
/* Free the old value and set the new one */
|
||||
/* Set the new value and free the old one. Note that it is important
|
||||
* to do that in this order, as the value may just be exactly the same
|
||||
* as the previous one. In this context, think to reference counting,
|
||||
* you want to increment (set), and then decrement (free), and not the
|
||||
* reverse. */
|
||||
auxentry = *entry;
|
||||
dictSetHashVal(ht, entry, val);
|
||||
dictFreeEntryVal(ht, &auxentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search and remove an element */
|
||||
static int dictDelete(dict *ht, const void *key) {
|
||||
unsigned int h;
|
||||
dictEntry *de, *prevde;
|
||||
|
||||
if (ht->size == 0)
|
||||
return DICT_ERR;
|
||||
h = dictHashKey(ht, key) & ht->sizemask;
|
||||
de = ht->table[h];
|
||||
|
||||
prevde = NULL;
|
||||
while(de) {
|
||||
if (dictCompareHashKeys(ht,key,de->key)) {
|
||||
/* Unlink the element from the list */
|
||||
if (prevde)
|
||||
prevde->next = de->next;
|
||||
else
|
||||
ht->table[h] = de->next;
|
||||
|
||||
dictFreeEntryKey(ht,de);
|
||||
dictFreeEntryVal(ht,de);
|
||||
hi_free(de);
|
||||
ht->used--;
|
||||
return DICT_OK;
|
||||
}
|
||||
prevde = de;
|
||||
de = de->next;
|
||||
}
|
||||
return DICT_ERR; /* not found */
|
||||
}
|
||||
|
||||
/* Destroy an entire hash table */
|
||||
static int _dictClear(dict *ht) {
|
||||
unsigned long i;
|
||||
|
||||
/* Free all the elements */
|
||||
for (i = 0; i < ht->size && ht->used > 0; i++) {
|
||||
dictEntry *he, *nextHe;
|
||||
|
||||
if ((he = ht->table[i]) == NULL) continue;
|
||||
while(he) {
|
||||
nextHe = he->next;
|
||||
dictFreeEntryKey(ht, he);
|
||||
dictFreeEntryVal(ht, he);
|
||||
hi_free(he);
|
||||
ht->used--;
|
||||
he = nextHe;
|
||||
}
|
||||
}
|
||||
/* Free the table and the allocated cache structure */
|
||||
hi_free(ht->table);
|
||||
/* Re-initialize the table */
|
||||
_dictReset(ht);
|
||||
return DICT_OK; /* never fails */
|
||||
}
|
||||
|
||||
/* Clear & Release the hash table */
|
||||
static void dictRelease(dict *ht) {
|
||||
_dictClear(ht);
|
||||
hi_free(ht);
|
||||
}
|
||||
|
||||
static dictEntry *dictFind(dict *ht, const void *key) {
|
||||
dictEntry *he;
|
||||
unsigned int h;
|
||||
|
||||
if (ht->size == 0) return NULL;
|
||||
h = dictHashKey(ht, key) & ht->sizemask;
|
||||
he = ht->table[h];
|
||||
while(he) {
|
||||
if (dictCompareHashKeys(ht, key, he->key))
|
||||
return he;
|
||||
he = he->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static dictIterator *dictGetIterator(dict *ht) {
|
||||
dictIterator *iter = hi_malloc(sizeof(*iter));
|
||||
if (iter == NULL)
|
||||
return NULL;
|
||||
|
||||
iter->ht = ht;
|
||||
iter->index = -1;
|
||||
iter->entry = NULL;
|
||||
iter->nextEntry = NULL;
|
||||
return iter;
|
||||
}
|
||||
|
||||
static dictEntry *dictNext(dictIterator *iter) {
|
||||
while (1) {
|
||||
if (iter->entry == NULL) {
|
||||
iter->index++;
|
||||
if (iter->index >=
|
||||
(signed)iter->ht->size) break;
|
||||
iter->entry = iter->ht->table[iter->index];
|
||||
} else {
|
||||
iter->entry = iter->nextEntry;
|
||||
}
|
||||
if (iter->entry) {
|
||||
/* We need to save the 'next' here, the iterator user
|
||||
* may delete the entry we are returning. */
|
||||
iter->nextEntry = iter->entry->next;
|
||||
return iter->entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dictReleaseIterator(dictIterator *iter) {
|
||||
hi_free(iter);
|
||||
}
|
||||
|
||||
/* ------------------------- private functions ------------------------------ */
|
||||
|
||||
/* Expand the hash table if needed */
|
||||
static int _dictExpandIfNeeded(dict *ht) {
|
||||
/* If the hash table is empty expand it to the initial size,
|
||||
* if the table is "full" double its size. */
|
||||
if (ht->size == 0)
|
||||
return dictExpand(ht, DICT_HT_INITIAL_SIZE);
|
||||
if (ht->used == ht->size)
|
||||
return dictExpand(ht, ht->size*2);
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
/* Our hash table capability is a power of two */
|
||||
static unsigned long _dictNextPower(unsigned long size) {
|
||||
unsigned long i = DICT_HT_INITIAL_SIZE;
|
||||
|
||||
if (size >= LONG_MAX) return LONG_MAX;
|
||||
while(1) {
|
||||
if (i >= size)
|
||||
return i;
|
||||
i *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the index of a free slot that can be populated with
|
||||
* an hash entry for the given 'key'.
|
||||
* If the key already exists, -1 is returned. */
|
||||
static int _dictKeyIndex(dict *ht, const void *key) {
|
||||
unsigned int h;
|
||||
dictEntry *he;
|
||||
|
||||
/* Expand the hashtable if needed */
|
||||
if (_dictExpandIfNeeded(ht) == DICT_ERR)
|
||||
return -1;
|
||||
/* Compute the key hash value */
|
||||
h = dictHashKey(ht, key) & ht->sizemask;
|
||||
/* Search if this slot does not already contain the given key */
|
||||
he = ht->table[h];
|
||||
while(he) {
|
||||
if (dictCompareHashKeys(ht, key, he->key))
|
||||
return -1;
|
||||
he = he->next;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
Vendored
-49
@@ -1,49 +0,0 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
# Check for GLib
|
||||
|
||||
PKG_CHECK_MODULES(GLIB2 glib-2.0)
|
||||
if (GLIB2_FOUND)
|
||||
INCLUDE_DIRECTORIES(${GLIB2_INCLUDE_DIRS})
|
||||
LINK_DIRECTORIES(${GLIB2_LIBRARY_DIRS})
|
||||
ADD_EXECUTABLE(example-glib example-glib.c)
|
||||
TARGET_LINK_LIBRARIES(example-glib hiredis ${GLIB2_LIBRARIES})
|
||||
ENDIF(GLIB2_FOUND)
|
||||
|
||||
FIND_PATH(LIBEV ev.h
|
||||
HINTS /usr/local /usr/opt/local
|
||||
ENV LIBEV_INCLUDE_DIR)
|
||||
|
||||
if (LIBEV)
|
||||
# Just compile and link with libev
|
||||
ADD_EXECUTABLE(example-libev example-libev.c)
|
||||
TARGET_LINK_LIBRARIES(example-libev hiredis ev)
|
||||
ENDIF()
|
||||
|
||||
FIND_PATH(LIBEVENT event.h)
|
||||
if (LIBEVENT)
|
||||
ADD_EXECUTABLE(example-libevent example-libevent)
|
||||
TARGET_LINK_LIBRARIES(example-libevent hiredis event)
|
||||
ENDIF()
|
||||
|
||||
FIND_PATH(LIBUV uv.h)
|
||||
IF (LIBUV)
|
||||
ADD_EXECUTABLE(example-libuv example-libuv.c)
|
||||
TARGET_LINK_LIBRARIES(example-libuv hiredis uv)
|
||||
ENDIF()
|
||||
|
||||
IF (APPLE)
|
||||
FIND_LIBRARY(CF CoreFoundation)
|
||||
ADD_EXECUTABLE(example-macosx example-macosx.c)
|
||||
TARGET_LINK_LIBRARIES(example-macosx hiredis ${CF})
|
||||
ENDIF()
|
||||
|
||||
IF (ENABLE_SSL)
|
||||
ADD_EXECUTABLE(example-ssl example-ssl.c)
|
||||
TARGET_LINK_LIBRARIES(example-ssl hiredis hiredis_ssl)
|
||||
ENDIF()
|
||||
|
||||
ADD_EXECUTABLE(example example.c)
|
||||
TARGET_LINK_LIBRARIES(example hiredis)
|
||||
|
||||
ADD_EXECUTABLE(example-push example-push.c)
|
||||
TARGET_LINK_LIBRARIES(example-push hiredis)
|
||||
Vendored
-62
@@ -1,62 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/ae.h>
|
||||
|
||||
/* Put event loop in the global scope, so it can be explicitly stopped */
|
||||
static aeEventLoop *loop;
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
aeStop(loop);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
aeStop(loop);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Disconnected...\n");
|
||||
aeStop(loop);
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
loop = aeCreateEventLoop(64);
|
||||
redisAeAttach(loop, c);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
aeMain(loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
-73
@@ -1,73 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/glib.h>
|
||||
|
||||
static GMainLoop *mainloop;
|
||||
|
||||
static void
|
||||
connect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,
|
||||
int status)
|
||||
{
|
||||
if (status != REDIS_OK) {
|
||||
g_printerr("Failed to connect: %s\n", ac->errstr);
|
||||
g_main_loop_quit(mainloop);
|
||||
} else {
|
||||
g_printerr("Connected...\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
disconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,
|
||||
int status)
|
||||
{
|
||||
if (status != REDIS_OK) {
|
||||
g_error("Failed to disconnect: %s", ac->errstr);
|
||||
} else {
|
||||
g_printerr("Disconnected...\n");
|
||||
g_main_loop_quit(mainloop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
command_cb(redisAsyncContext *ac,
|
||||
gpointer r,
|
||||
gpointer user_data G_GNUC_UNUSED)
|
||||
{
|
||||
redisReply *reply = r;
|
||||
|
||||
if (reply) {
|
||||
g_print("REPLY: %s\n", reply->str);
|
||||
}
|
||||
|
||||
redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
gint
|
||||
main (gint argc G_GNUC_UNUSED,
|
||||
gchar *argv[] G_GNUC_UNUSED)
|
||||
{
|
||||
redisAsyncContext *ac;
|
||||
GMainContext *context = NULL;
|
||||
GSource *source;
|
||||
|
||||
ac = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (ac->err) {
|
||||
g_printerr("%s\n", ac->errstr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
source = redis_source_new(ac);
|
||||
mainloop = g_main_loop_new(context, FALSE);
|
||||
g_source_attach(source, context);
|
||||
|
||||
redisAsyncSetConnectCallback(ac, connect_cb);
|
||||
redisAsyncSetDisconnectCallback(ac, disconnect_cb);
|
||||
redisAsyncCommand(ac, command_cb, NULL, "SET key 1234");
|
||||
redisAsyncCommand(ac, command_cb, NULL, "GET key");
|
||||
|
||||
g_main_loop_run(mainloop);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/ivykis.h>
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
iv_init();
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisIvykisAttach(c);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
|
||||
iv_main();
|
||||
|
||||
iv_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/libev.h>
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisLibevAttach(EV_DEFAULT_ c);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
ev_loop(EV_DEFAULT_ 0);
|
||||
return 0;
|
||||
}
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <hiredis_ssl.h>
|
||||
#include <async.h>
|
||||
#include <adapters/libevent.h>
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
struct event_base *base = event_base_new();
|
||||
if (argc < 5) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <key> <host> <port> <cert> <certKey> [ca]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char *value = argv[1];
|
||||
size_t nvalue = strlen(value);
|
||||
|
||||
const char *hostname = argv[2];
|
||||
int port = atoi(argv[3]);
|
||||
|
||||
const char *cert = argv[4];
|
||||
const char *certKey = argv[5];
|
||||
const char *caCert = argc > 5 ? argv[6] : NULL;
|
||||
|
||||
redisSSLContext *ssl;
|
||||
redisSSLContextError ssl_error;
|
||||
|
||||
redisInitOpenSSL();
|
||||
|
||||
ssl = redisCreateSSLContext(caCert, NULL,
|
||||
cert, certKey, NULL, &ssl_error);
|
||||
if (!ssl) {
|
||||
printf("Error: %s\n", redisSSLContextGetError(ssl_error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect(hostname, port);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
if (redisInitiateSSLWithContext(&c->c, ssl) != REDIS_OK) {
|
||||
printf("SSL Error!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
redisLibeventAttach(c,base);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", value, nvalue);
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
event_base_dispatch(base);
|
||||
|
||||
redisFreeSSLContext(ssl);
|
||||
return 0;
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/libevent.h>
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) {
|
||||
if (c->errstr) {
|
||||
printf("errstr: %s\n", c->errstr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
struct event_base *base = event_base_new();
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
||||
struct timeval tv = {0};
|
||||
tv.tv_sec = 1;
|
||||
options.connect_timeout = &tv;
|
||||
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnectWithOptions(&options);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisLibeventAttach(c,base);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
event_base_dispatch(base);
|
||||
return 0;
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/libuv.h>
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
uv_loop_t* loop = uv_default_loop();
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisLibuvAttach(c,loop);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
return 0;
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
//
|
||||
// Created by Дмитрий Бахвалов on 13.07.15.
|
||||
// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/macosx.h>
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
CFRunLoopRef loop = CFRunLoopGetCurrent();
|
||||
if( !loop ) {
|
||||
printf("Error: Cannot get current run loop\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisMacOSAttach(c, loop);
|
||||
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
|
||||
CFRunLoopRun();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
-160
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <hiredis.h>
|
||||
#include <win32.h>
|
||||
|
||||
#define KEY_COUNT 5
|
||||
|
||||
#define panicAbort(fmt, ...) \
|
||||
do { \
|
||||
fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, __VA_ARGS__); \
|
||||
exit(-1); \
|
||||
} while (0)
|
||||
|
||||
static void assertReplyAndFree(redisContext *context, redisReply *reply, int type) {
|
||||
if (reply == NULL)
|
||||
panicAbort("NULL reply from server (error: %s)", context->errstr);
|
||||
|
||||
if (reply->type != type) {
|
||||
if (reply->type == REDIS_REPLY_ERROR)
|
||||
fprintf(stderr, "Redis Error: %s\n", reply->str);
|
||||
|
||||
panicAbort("Expected reply type %d but got type %d", type, reply->type);
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
/* Switch to the RESP3 protocol and enable client tracking */
|
||||
static void enableClientTracking(redisContext *c) {
|
||||
redisReply *reply = redisCommand(c, "HELLO 3");
|
||||
if (reply == NULL || c->err) {
|
||||
panicAbort("NULL reply or server error (error: %s)", c->errstr);
|
||||
}
|
||||
|
||||
if (reply->type != REDIS_REPLY_MAP) {
|
||||
fprintf(stderr, "Error: Can't send HELLO 3 command. Are you sure you're ");
|
||||
fprintf(stderr, "connected to redis-server >= 6.0.0?\nRedis error: %s\n",
|
||||
reply->type == REDIS_REPLY_ERROR ? reply->str : "(unknown)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Enable client tracking */
|
||||
reply = redisCommand(c, "CLIENT TRACKING ON");
|
||||
assertReplyAndFree(c, reply, REDIS_REPLY_STATUS);
|
||||
}
|
||||
|
||||
void pushReplyHandler(void *privdata, void *r) {
|
||||
redisReply *reply = r;
|
||||
int *invalidations = privdata;
|
||||
|
||||
/* Sanity check on the invalidation reply */
|
||||
if (reply->type != REDIS_REPLY_PUSH || reply->elements != 2 ||
|
||||
reply->element[1]->type != REDIS_REPLY_ARRAY ||
|
||||
reply->element[1]->element[0]->type != REDIS_REPLY_STRING)
|
||||
{
|
||||
panicAbort("%s", "Can't parse PUSH message!");
|
||||
}
|
||||
|
||||
/* Increment our invalidation count */
|
||||
*invalidations += 1;
|
||||
|
||||
printf("pushReplyHandler(): INVALIDATE '%s' (invalidation count: %d)\n",
|
||||
reply->element[1]->element[0]->str, *invalidations);
|
||||
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
/* We aren't actually freeing anything here, but it is included to show that we can
|
||||
* have hiredis call our data destructor when freeing the context */
|
||||
void privdata_dtor(void *privdata) {
|
||||
unsigned int *icount = privdata;
|
||||
printf("privdata_dtor(): In context privdata dtor (invalidations: %u)\n", *icount);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j, invalidations = 0;
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
|
||||
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
|
||||
int port = (argc > 2) ? atoi(argv[2]) : 6379;
|
||||
|
||||
redisOptions o = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&o, hostname, port);
|
||||
|
||||
/* Set our context privdata to the address of our invalidation counter. Each
|
||||
* time our PUSH handler is called, hiredis will pass the privdata for context.
|
||||
*
|
||||
* This could also be done after we create the context like so:
|
||||
*
|
||||
* c->privdata = &invalidations;
|
||||
* c->free_privdata = privdata_dtor;
|
||||
*/
|
||||
REDIS_OPTIONS_SET_PRIVDATA(&o, &invalidations, privdata_dtor);
|
||||
|
||||
/* Set our custom PUSH message handler */
|
||||
o.push_cb = pushReplyHandler;
|
||||
|
||||
c = redisConnectWithOptions(&o);
|
||||
if (c == NULL || c->err)
|
||||
panicAbort("Connection error: %s", c ? c->errstr : "OOM");
|
||||
|
||||
/* Enable RESP3 and turn on client tracking */
|
||||
enableClientTracking(c);
|
||||
|
||||
/* Set some keys and then read them back. Once we do that, Redis will deliver
|
||||
* invalidation push messages whenever the key is modified */
|
||||
for (j = 0; j < KEY_COUNT; j++) {
|
||||
reply = redisCommand(c, "SET key:%d initial:%d", j, j);
|
||||
assertReplyAndFree(c, reply, REDIS_REPLY_STATUS);
|
||||
|
||||
reply = redisCommand(c, "GET key:%d", j);
|
||||
assertReplyAndFree(c, reply, REDIS_REPLY_STRING);
|
||||
}
|
||||
|
||||
/* Trigger invalidation messages by updating keys we just read */
|
||||
for (j = 0; j < KEY_COUNT; j++) {
|
||||
printf(" main(): SET key:%d update:%d\n", j, j);
|
||||
reply = redisCommand(c, "SET key:%d update:%d", j, j);
|
||||
assertReplyAndFree(c, reply, REDIS_REPLY_STATUS);
|
||||
printf(" main(): SET REPLY OK\n");
|
||||
}
|
||||
|
||||
printf("\nTotal detected invalidations: %d, expected: %d\n", invalidations, KEY_COUNT);
|
||||
|
||||
/* PING server */
|
||||
redisFree(c);
|
||||
}
|
||||
Vendored
-46
@@ -1,46 +0,0 @@
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTimer>
|
||||
|
||||
#include "example-qt.h"
|
||||
|
||||
void getCallback(redisAsyncContext *, void * r, void * privdata) {
|
||||
|
||||
redisReply * reply = static_cast<redisReply *>(r);
|
||||
ExampleQt * ex = static_cast<ExampleQt *>(privdata);
|
||||
if (reply == nullptr || ex == nullptr) return;
|
||||
|
||||
cout << "key: " << reply->str << endl;
|
||||
|
||||
ex->finish();
|
||||
}
|
||||
|
||||
void ExampleQt::run() {
|
||||
|
||||
m_ctx = redisAsyncConnect("localhost", 6379);
|
||||
|
||||
if (m_ctx->err) {
|
||||
cerr << "Error: " << m_ctx->errstr << endl;
|
||||
redisAsyncFree(m_ctx);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
m_adapter.setContext(m_ctx);
|
||||
|
||||
redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value);
|
||||
redisAsyncCommand(m_ctx, getCallback, this, "GET key");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
ExampleQt example(argv[argc-1]);
|
||||
|
||||
QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit()));
|
||||
QTimer::singleShot(0, &example, SLOT(run()));
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
Vendored
-32
@@ -1,32 +0,0 @@
|
||||
#ifndef __HIREDIS_EXAMPLE_QT_H
|
||||
#define __HIREDIS_EXAMPLE_QT_H
|
||||
|
||||
#include <adapters/qt.h>
|
||||
|
||||
class ExampleQt : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ExampleQt(const char * value, QObject * parent = 0)
|
||||
: QObject(parent), m_value(value) {}
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
|
||||
private:
|
||||
void finish() { emit finished(); }
|
||||
|
||||
private:
|
||||
const char * m_value;
|
||||
redisAsyncContext * m_ctx;
|
||||
RedisQtAdapter m_adapter;
|
||||
|
||||
friend
|
||||
void getCallback(redisAsyncContext *, void *, void *);
|
||||
};
|
||||
|
||||
#endif /* !__HIREDIS_EXAMPLE_QT_H */
|
||||
Vendored
-110
@@ -1,110 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <hiredis_ssl.h>
|
||||
#include <win32.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j;
|
||||
redisSSLContext *ssl;
|
||||
redisSSLContextError ssl_error;
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
if (argc < 4) {
|
||||
printf("Usage: %s <host> <port> <cert> <key> [ca]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
|
||||
int port = atoi(argv[2]);
|
||||
const char *cert = argv[3];
|
||||
const char *key = argv[4];
|
||||
const char *ca = argc > 4 ? argv[5] : NULL;
|
||||
|
||||
redisInitOpenSSL();
|
||||
ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error);
|
||||
if (!ssl) {
|
||||
printf("SSL Context error: %s\n",
|
||||
redisSSLContextGetError(ssl_error));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct timeval tv = { 1, 500000 }; // 1.5 seconds
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, hostname, port);
|
||||
options.connect_timeout = &tv;
|
||||
c = redisConnectWithOptions(&options);
|
||||
|
||||
if (c == NULL || c->err) {
|
||||
if (c) {
|
||||
printf("Connection error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
} else {
|
||||
printf("Connection error: can't allocate redis context\n");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
|
||||
printf("Couldn't initialize SSL!\n");
|
||||
printf("Error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* PING server */
|
||||
reply = redisCommand(c,"PING");
|
||||
printf("PING: %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Set a key */
|
||||
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
|
||||
printf("SET: %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Set a key using binary safe API */
|
||||
reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
|
||||
printf("SET (binary API): %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Try a GET and two INCR */
|
||||
reply = redisCommand(c,"GET foo");
|
||||
printf("GET foo: %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
reply = redisCommand(c,"INCR counter");
|
||||
printf("INCR counter: %lld\n", reply->integer);
|
||||
freeReplyObject(reply);
|
||||
/* again ... */
|
||||
reply = redisCommand(c,"INCR counter");
|
||||
printf("INCR counter: %lld\n", reply->integer);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Create a list of numbers, from 0 to 9 */
|
||||
reply = redisCommand(c,"DEL mylist");
|
||||
freeReplyObject(reply);
|
||||
for (j = 0; j < 10; j++) {
|
||||
char buf[64];
|
||||
|
||||
snprintf(buf,64,"%u",j);
|
||||
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
/* Let's check what we have inside the list */
|
||||
reply = redisCommand(c,"LRANGE mylist 0 -1");
|
||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
||||
for (j = 0; j < reply->elements; j++) {
|
||||
printf("%u) %s\n", j, reply->element[j]->str);
|
||||
}
|
||||
}
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Disconnects and frees the context */
|
||||
redisFree(c);
|
||||
|
||||
redisFreeSSLContext(ssl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
-91
@@ -1,91 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <hiredis.h>
|
||||
#include <win32.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j, isunix = 0;
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
|
||||
|
||||
if (argc > 2) {
|
||||
if (*argv[2] == 'u' || *argv[2] == 'U') {
|
||||
isunix = 1;
|
||||
/* in this case, host is the path to the unix socket */
|
||||
printf("Will connect to unix socket @%s\n", hostname);
|
||||
}
|
||||
}
|
||||
|
||||
int port = (argc > 2) ? atoi(argv[2]) : 6379;
|
||||
|
||||
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
|
||||
if (isunix) {
|
||||
c = redisConnectUnixWithTimeout(hostname, timeout);
|
||||
} else {
|
||||
c = redisConnectWithTimeout(hostname, port, timeout);
|
||||
}
|
||||
if (c == NULL || c->err) {
|
||||
if (c) {
|
||||
printf("Connection error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
} else {
|
||||
printf("Connection error: can't allocate redis context\n");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* PING server */
|
||||
reply = redisCommand(c,"PING");
|
||||
printf("PING: %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Set a key */
|
||||
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
|
||||
printf("SET: %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Set a key using binary safe API */
|
||||
reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
|
||||
printf("SET (binary API): %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Try a GET and two INCR */
|
||||
reply = redisCommand(c,"GET foo");
|
||||
printf("GET foo: %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
reply = redisCommand(c,"INCR counter");
|
||||
printf("INCR counter: %lld\n", reply->integer);
|
||||
freeReplyObject(reply);
|
||||
/* again ... */
|
||||
reply = redisCommand(c,"INCR counter");
|
||||
printf("INCR counter: %lld\n", reply->integer);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Create a list of numbers, from 0 to 9 */
|
||||
reply = redisCommand(c,"DEL mylist");
|
||||
freeReplyObject(reply);
|
||||
for (j = 0; j < 10; j++) {
|
||||
char buf[64];
|
||||
|
||||
snprintf(buf,64,"%u",j);
|
||||
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
/* Let's check what we have inside the list */
|
||||
reply = redisCommand(c,"LRANGE mylist 0 -1");
|
||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
||||
for (j = 0; j < reply->elements; j++) {
|
||||
printf("%u) %s\n", j, reply->element[j]->str);
|
||||
}
|
||||
}
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Disconnects and frees the context */
|
||||
redisFree(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
#ifndef __HIREDIS_FMACRO_H
|
||||
#define __HIREDIS_FMACRO_H
|
||||
|
||||
#define _XOPEN_SOURCE 600
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
/* Enable TCP_KEEPALIVE */
|
||||
#define _DARWIN_C_SOURCE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set_and_check(hiredis_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||
|
||||
IF (NOT TARGET hiredis::hiredis)
|
||||
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake)
|
||||
ENDIF()
|
||||
|
||||
SET(hiredis_LIBRARIES hiredis::hiredis)
|
||||
SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR})
|
||||
|
||||
check_required_components(hiredis)
|
||||
|
||||
Vendored
-1174
File diff suppressed because it is too large
Load Diff
Vendored
-336
@@ -1,336 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
||||
* Jan-Erik Rediger <janerik at fnordig dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_H
|
||||
#define __HIREDIS_H
|
||||
#include "read.h"
|
||||
#include <stdarg.h> /* for va_list */
|
||||
#ifndef _MSC_VER
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
#else
|
||||
struct timeval; /* forward declaration */
|
||||
typedef long long ssize_t;
|
||||
#endif
|
||||
#include <stdint.h> /* uintXX_t, etc */
|
||||
#include "sds.h" /* for hisds */
|
||||
#include "alloc.h" /* for allocation wrappers */
|
||||
|
||||
#define HIREDIS_MAJOR 1
|
||||
#define HIREDIS_MINOR 0
|
||||
#define HIREDIS_PATCH 0
|
||||
#define HIREDIS_SONAME 1.0.0
|
||||
|
||||
/* Connection type can be blocking or non-blocking and is set in the
|
||||
* least significant bit of the flags field in redisContext. */
|
||||
#define REDIS_BLOCK 0x1
|
||||
|
||||
/* Connection may be disconnected before being free'd. The second bit
|
||||
* in the flags field is set when the context is connected. */
|
||||
#define REDIS_CONNECTED 0x2
|
||||
|
||||
/* The async API might try to disconnect cleanly and flush the output
|
||||
* buffer and read all subsequent replies before disconnecting.
|
||||
* This flag means no new commands can come in and the connection
|
||||
* should be terminated once all replies have been read. */
|
||||
#define REDIS_DISCONNECTING 0x4
|
||||
|
||||
/* Flag specific to the async API which means that the context should be clean
|
||||
* up as soon as possible. */
|
||||
#define REDIS_FREEING 0x8
|
||||
|
||||
/* Flag that is set when an async callback is executed. */
|
||||
#define REDIS_IN_CALLBACK 0x10
|
||||
|
||||
/* Flag that is set when the async context has one or more subscriptions. */
|
||||
#define REDIS_SUBSCRIBED 0x20
|
||||
|
||||
/* Flag that is set when monitor mode is active */
|
||||
#define REDIS_MONITORING 0x40
|
||||
|
||||
/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
|
||||
#define REDIS_REUSEADDR 0x80
|
||||
|
||||
/**
|
||||
* Flag that indicates the user does not want the context to
|
||||
* be automatically freed upon error
|
||||
*/
|
||||
#define REDIS_NO_AUTO_FREE 0x200
|
||||
|
||||
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
|
||||
|
||||
/* number of times we retry to connect in the case of EADDRNOTAVAIL and
|
||||
* SO_REUSEADDR is being used. */
|
||||
#define REDIS_CONNECT_RETRIES 10
|
||||
|
||||
/* Forward declarations for structs defined elsewhere */
|
||||
struct redisAsyncContext;
|
||||
struct redisContext;
|
||||
|
||||
/* RESP3 push helpers and callback prototypes */
|
||||
#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH)
|
||||
typedef void (redisPushFn)(void *, void *);
|
||||
typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This is the reply object returned by redisCommand() */
|
||||
typedef struct redisReply {
|
||||
int type; /* REDIS_REPLY_* */
|
||||
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
|
||||
double dval; /* The double when type is REDIS_REPLY_DOUBLE */
|
||||
size_t len; /* Length of string */
|
||||
char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
|
||||
REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */
|
||||
char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null
|
||||
terminated 3 character content type, such as "txt". */
|
||||
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
|
||||
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
|
||||
} redisReply;
|
||||
|
||||
redisReader *redisReaderCreate(void);
|
||||
|
||||
/* Function to free the reply objects hiredis returns by default. */
|
||||
void freeReplyObject(void *reply);
|
||||
|
||||
/* Functions to format a command according to the protocol. */
|
||||
int redisvFormatCommand(char **target, const char *format, va_list ap);
|
||||
int redisFormatCommand(char **target, const char *format, ...);
|
||||
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
|
||||
int redisFormatSdsCommandArgv(hisds *target, int argc, const char ** argv, const size_t *argvlen);
|
||||
void redisFreeCommand(char *cmd);
|
||||
void redisFreeSdsCommand(hisds cmd);
|
||||
|
||||
enum redisConnectionType {
|
||||
REDIS_CONN_TCP,
|
||||
REDIS_CONN_UNIX,
|
||||
REDIS_CONN_USERFD
|
||||
};
|
||||
|
||||
struct redisSsl;
|
||||
|
||||
#define REDIS_OPT_NONBLOCK 0x01
|
||||
#define REDIS_OPT_REUSEADDR 0x02
|
||||
|
||||
/**
|
||||
* Don't automatically free the async object on a connection failure,
|
||||
* or other implicit conditions. Only free on an explicit call to disconnect() or free()
|
||||
*/
|
||||
#define REDIS_OPT_NOAUTOFREE 0x04
|
||||
|
||||
/* Don't automatically intercept and free RESP3 PUSH replies. */
|
||||
#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08
|
||||
|
||||
/* In Unix systems a file descriptor is a regular signed int, with -1
|
||||
* representing an invalid descriptor. In Windows it is a SOCKET
|
||||
* (32- or 64-bit unsigned integer depending on the architecture), where
|
||||
* all bits set (~0) is INVALID_SOCKET. */
|
||||
#ifndef _WIN32
|
||||
typedef int redisFD;
|
||||
#define REDIS_INVALID_FD -1
|
||||
#else
|
||||
#ifdef _WIN64
|
||||
typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */
|
||||
#else
|
||||
typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */
|
||||
#endif
|
||||
#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
/*
|
||||
* the type of connection to use. This also indicates which
|
||||
* `endpoint` member field to use
|
||||
*/
|
||||
int type;
|
||||
/* bit field of REDIS_OPT_xxx */
|
||||
int options;
|
||||
/* timeout value for connect operation. If NULL, no timeout is used */
|
||||
const struct timeval *connect_timeout;
|
||||
/* timeout value for commands. If NULL, no timeout is used. This can be
|
||||
* updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */
|
||||
const struct timeval *command_timeout;
|
||||
union {
|
||||
/** use this field for tcp/ip connections */
|
||||
struct {
|
||||
const char *source_addr;
|
||||
const char *ip;
|
||||
int port;
|
||||
} tcp;
|
||||
/** use this field for unix domain sockets */
|
||||
const char *unix_socket;
|
||||
/**
|
||||
* use this field to have hiredis operate an already-open
|
||||
* file descriptor */
|
||||
redisFD fd;
|
||||
} endpoint;
|
||||
|
||||
/* Optional user defined data/destructor */
|
||||
void *privdata;
|
||||
void (*free_privdata)(void *);
|
||||
|
||||
/* A user defined PUSH message callback */
|
||||
redisPushFn *push_cb;
|
||||
redisAsyncPushFn *async_push_cb;
|
||||
} redisOptions;
|
||||
|
||||
/**
|
||||
* Helper macros to initialize options to their specified fields.
|
||||
*/
|
||||
#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \
|
||||
(opts)->type = REDIS_CONN_TCP; \
|
||||
(opts)->endpoint.tcp.ip = ip_; \
|
||||
(opts)->endpoint.tcp.port = port_;
|
||||
|
||||
#define REDIS_OPTIONS_SET_UNIX(opts, path) \
|
||||
(opts)->type = REDIS_CONN_UNIX; \
|
||||
(opts)->endpoint.unix_socket = path;
|
||||
|
||||
#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \
|
||||
(opts)->privdata = data; \
|
||||
(opts)->free_privdata = dtor; \
|
||||
|
||||
typedef struct redisContextFuncs {
|
||||
void (*free_privctx)(void *);
|
||||
void (*async_read)(struct redisAsyncContext *);
|
||||
void (*async_write)(struct redisAsyncContext *);
|
||||
ssize_t (*read)(struct redisContext *, char *, size_t);
|
||||
ssize_t (*write)(struct redisContext *);
|
||||
} redisContextFuncs;
|
||||
|
||||
/* Context for a connection to Redis */
|
||||
typedef struct redisContext {
|
||||
const redisContextFuncs *funcs; /* Function table */
|
||||
|
||||
int err; /* Error flags, 0 when there is no error */
|
||||
char errstr[128]; /* String representation of error when applicable */
|
||||
redisFD fd;
|
||||
int flags;
|
||||
char *obuf; /* Write buffer */
|
||||
redisReader *reader; /* Protocol reader */
|
||||
|
||||
enum redisConnectionType connection_type;
|
||||
struct timeval *connect_timeout;
|
||||
struct timeval *command_timeout;
|
||||
|
||||
struct {
|
||||
char *host;
|
||||
char *source_addr;
|
||||
int port;
|
||||
} tcp;
|
||||
|
||||
struct {
|
||||
char *path;
|
||||
} unix_sock;
|
||||
|
||||
/* For non-blocking connect */
|
||||
struct sockadr *saddr;
|
||||
size_t addrlen;
|
||||
|
||||
/* Optional data and corresponding destructor users can use to provide
|
||||
* context to a given redisContext. Not used by hiredis. */
|
||||
void *privdata;
|
||||
void (*free_privdata)(void *);
|
||||
|
||||
/* Internal context pointer presently used by hiredis to manage
|
||||
* SSL connections. */
|
||||
void *privctx;
|
||||
|
||||
/* An optional RESP3 PUSH handler */
|
||||
redisPushFn *push_cb;
|
||||
} redisContext;
|
||||
|
||||
redisContext *redisConnectWithOptions(const redisOptions *options);
|
||||
redisContext *redisConnect(const char *ip, int port);
|
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
|
||||
redisContext *redisConnectNonBlock(const char *ip, int port);
|
||||
redisContext *redisConnectBindNonBlock(const char *ip, int port,
|
||||
const char *source_addr);
|
||||
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
|
||||
const char *source_addr);
|
||||
redisContext *redisConnectUnix(const char *path);
|
||||
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
|
||||
redisContext *redisConnectUnixNonBlock(const char *path);
|
||||
redisContext *redisConnectFd(redisFD fd);
|
||||
|
||||
/**
|
||||
* Reconnect the given context using the saved information.
|
||||
*
|
||||
* This re-uses the exact same connect options as in the initial connection.
|
||||
* host, ip (or path), timeout and bind address are reused,
|
||||
* flags are used unmodified from the existing context.
|
||||
*
|
||||
* Returns REDIS_OK on successful connect or REDIS_ERR otherwise.
|
||||
*/
|
||||
int redisReconnect(redisContext *c);
|
||||
|
||||
redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn);
|
||||
int redisSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisEnableKeepAlive(redisContext *c);
|
||||
void redisFree(redisContext *c);
|
||||
redisFD redisFreeKeepFd(redisContext *c);
|
||||
int redisBufferRead(redisContext *c);
|
||||
int redisBufferWrite(redisContext *c, int *done);
|
||||
|
||||
/* In a blocking context, this function first checks if there are unconsumed
|
||||
* replies to return and returns one if so. Otherwise, it flushes the output
|
||||
* buffer to the socket and reads until it has a reply. In a non-blocking
|
||||
* context, it will return unconsumed replies until there are no more. */
|
||||
int redisGetReply(redisContext *c, void **reply);
|
||||
int redisGetReplyFromReader(redisContext *c, void **reply);
|
||||
|
||||
/* Write a formatted command to the output buffer. Use these functions in blocking mode
|
||||
* to get a pipeline of commands. */
|
||||
int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
|
||||
|
||||
/* Write a command to the output buffer. Use these functions in blocking mode
|
||||
* to get a pipeline of commands. */
|
||||
int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
|
||||
int redisAppendCommand(redisContext *c, const char *format, ...);
|
||||
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
||||
|
||||
/* Issue a command to Redis. In a blocking context, it is identical to calling
|
||||
* redisAppendCommand, followed by redisGetReply. The function will return
|
||||
* NULL if there was an error in performing the request, otherwise it will
|
||||
* return the reply. In a non-blocking context, it is identical to calling
|
||||
* only redisAppendCommand and will always return NULL. */
|
||||
void *redisvCommand(redisContext *c, const char *format, va_list ap);
|
||||
void *redisCommand(redisContext *c, const char *format, ...);
|
||||
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
install_libdir=@CMAKE_INSTALL_LIBDIR@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/${install_libdir}
|
||||
includedir=${prefix}/include
|
||||
pkgincludedir=${includedir}/hiredis
|
||||
|
||||
Name: hiredis
|
||||
Description: Minimalistic C client library for Redis.
|
||||
Version: @PROJECT_VERSION@
|
||||
Libs: -L${libdir} -lhiredis
|
||||
Cflags: -I${pkgincludedir} -D_FILE_OFFSET_BITS=64
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set_and_check(hiredis_ssl_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||
|
||||
IF (NOT TARGET hiredis::hiredis_ssl)
|
||||
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis_ssl-targets.cmake)
|
||||
ENDIF()
|
||||
|
||||
SET(hiredis_ssl_LIBRARIES hiredis::hiredis_ssl)
|
||||
SET(hiredis_ssl_INCLUDE_DIRS ${hiredis_ssl_INCLUDEDIR})
|
||||
|
||||
check_required_components(hiredis_ssl)
|
||||
|
||||
Vendored
-127
@@ -1,127 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019, Redis Labs
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_SSL_H
|
||||
#define __HIREDIS_SSL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This is the underlying struct for SSL in ssl.h, which is not included to
|
||||
* keep build dependencies short here.
|
||||
*/
|
||||
struct ssl_st;
|
||||
|
||||
/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly
|
||||
* calling OpenSSL.
|
||||
*/
|
||||
typedef struct redisSSLContext redisSSLContext;
|
||||
|
||||
/**
|
||||
* Initialization errors that redisCreateSSLContext() may return.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
REDIS_SSL_CTX_NONE = 0, /* No Error */
|
||||
REDIS_SSL_CTX_CREATE_FAILED, /* Failed to create OpenSSL SSL_CTX */
|
||||
REDIS_SSL_CTX_CERT_KEY_REQUIRED, /* Client cert and key must both be specified or skipped */
|
||||
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */
|
||||
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */
|
||||
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED /* Failed to load private key */
|
||||
} redisSSLContextError;
|
||||
|
||||
/**
|
||||
* Return the error message corresponding with the specified error code.
|
||||
*/
|
||||
|
||||
const char *redisSSLContextGetError(redisSSLContextError error);
|
||||
|
||||
/**
|
||||
* Helper function to initialize the OpenSSL library.
|
||||
*
|
||||
* OpenSSL requires one-time initialization before it can be used. Callers should
|
||||
* call this function only once, and only if OpenSSL is not directly initialized
|
||||
* elsewhere.
|
||||
*/
|
||||
int redisInitOpenSSL(void);
|
||||
|
||||
/**
|
||||
* Helper function to initialize an OpenSSL context that can be used
|
||||
* to initiate SSL connections.
|
||||
*
|
||||
* cacert_filename is an optional name of a CA certificate/bundle file to load
|
||||
* and use for validation.
|
||||
*
|
||||
* capath is an optional directory path where trusted CA certificate files are
|
||||
* stored in an OpenSSL-compatible structure.
|
||||
*
|
||||
* cert_filename and private_key_filename are optional names of a client side
|
||||
* certificate and private key files to use for authentication. They need to
|
||||
* be both specified or omitted.
|
||||
*
|
||||
* server_name is an optional and will be used as a server name indication
|
||||
* (SNI) TLS extension.
|
||||
*
|
||||
* If error is non-null, it will be populated in case the context creation fails
|
||||
* (returning a NULL).
|
||||
*/
|
||||
|
||||
redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
|
||||
const char *cert_filename, const char *private_key_filename,
|
||||
const char *server_name, redisSSLContextError *error);
|
||||
|
||||
/**
|
||||
* Free a previously created OpenSSL context.
|
||||
*/
|
||||
void redisFreeSSLContext(redisSSLContext *redis_ssl_ctx);
|
||||
|
||||
/**
|
||||
* Initiate SSL on an existing redisContext.
|
||||
*
|
||||
* This is similar to redisInitiateSSL() but does not require the caller
|
||||
* to directly interact with OpenSSL, and instead uses a redisSSLContext
|
||||
* previously created using redisCreateSSLContext().
|
||||
*/
|
||||
|
||||
int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx);
|
||||
|
||||
/**
|
||||
* Initiate SSL/TLS negotiation on a provided OpenSSL SSL object.
|
||||
*/
|
||||
|
||||
int redisInitiateSSL(redisContext *c, struct ssl_st *ssl);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __HIREDIS_SSL_H */
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
pkgincludedir=${includedir}/hiredis
|
||||
|
||||
Name: hiredis_ssl
|
||||
Description: SSL Support for hiredis.
|
||||
Version: @PROJECT_VERSION@
|
||||
Requires: hiredis
|
||||
Libs: -L${libdir} -lhiredis_ssl
|
||||
Libs.private: -lssl -lcrypto
|
||||
Vendored
-612
@@ -1,612 +0,0 @@
|
||||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
||||
* Jan-Erik Rediger <janerik at fnordig dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "sds.h"
|
||||
#include "sockcompat.h"
|
||||
#include "win32.h"
|
||||
|
||||
/* Defined in hiredis.c */
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
void redisNetClose(redisContext *c) {
|
||||
if (c && c->fd != REDIS_INVALID_FD) {
|
||||
close(c->fd);
|
||||
c->fd = REDIS_INVALID_FD;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
ssize_t nread = recv(c->fd, buf, bufcap, 0);
|
||||
if (nread == -1) {
|
||||
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
||||
/* Try again later */
|
||||
return 0;
|
||||
} else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) {
|
||||
/* especially in windows */
|
||||
__redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout");
|
||||
return -1;
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
return -1;
|
||||
}
|
||||
} else if (nread == 0) {
|
||||
__redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
|
||||
return -1;
|
||||
} else {
|
||||
return nread;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t redisNetWrite(redisContext *c) {
|
||||
ssize_t nwritten = send(c->fd, c->obuf, hi_sdslen(c->obuf), 0);
|
||||
if (nwritten < 0) {
|
||||
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
||||
/* Try again later */
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
|
||||
int errorno = errno; /* snprintf() may change errno */
|
||||
char buf[128] = { 0 };
|
||||
size_t len = 0;
|
||||
|
||||
if (prefix != NULL)
|
||||
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
|
||||
strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
|
||||
__redisSetError(c,type,buf);
|
||||
}
|
||||
|
||||
static int redisSetReuseAddr(redisContext *c) {
|
||||
int on = 1;
|
||||
if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisCreateSocket(redisContext *c, int type) {
|
||||
redisFD s;
|
||||
if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
c->fd = s;
|
||||
if (type == AF_INET) {
|
||||
if (redisSetReuseAddr(c) == REDIS_ERR) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisSetBlocking(redisContext *c, int blocking) {
|
||||
#ifndef _WIN32
|
||||
int flags;
|
||||
|
||||
/* Set the socket nonblocking.
|
||||
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
||||
* interrupted by a signal. */
|
||||
if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (blocking)
|
||||
flags &= ~O_NONBLOCK;
|
||||
else
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if (fcntl(c->fd, F_SETFL, flags) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#else
|
||||
u_long mode = blocking ? 0 : 1;
|
||||
if (ioctl(c->fd, FIONBIO, &mode) == -1) {
|
||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)");
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisKeepAlive(redisContext *c, int interval) {
|
||||
int val = 1;
|
||||
redisFD fd = c->fd;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = interval;
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#else
|
||||
#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = interval/3;
|
||||
if (val == 0) val = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisSetTcpNoDelay(redisContext *c) {
|
||||
int yes = 1;
|
||||
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
|
||||
|
||||
static int redisContextTimeoutMsec(redisContext *c, long *result)
|
||||
{
|
||||
const struct timeval *timeout = c->connect_timeout;
|
||||
long msec = -1;
|
||||
|
||||
/* Only use timeout when not NULL. */
|
||||
if (timeout != NULL) {
|
||||
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
|
||||
*result = msec;
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
|
||||
|
||||
if (msec < 0 || msec > INT_MAX) {
|
||||
msec = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
*result = msec;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisContextWaitReady(redisContext *c, long msec) {
|
||||
struct pollfd wfd[1];
|
||||
|
||||
wfd[0].fd = c->fd;
|
||||
wfd[0].events = POLLOUT;
|
||||
|
||||
if (errno == EINPROGRESS) {
|
||||
int res;
|
||||
|
||||
if ((res = poll(wfd, 1, msec)) == -1) {
|
||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
} else if (res == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {
|
||||
redisCheckSocketError(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisCheckConnectDone(redisContext *c, int *completed) {
|
||||
int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen);
|
||||
if (rc == 0) {
|
||||
*completed = 1;
|
||||
return REDIS_OK;
|
||||
}
|
||||
switch (errno) {
|
||||
case EISCONN:
|
||||
*completed = 1;
|
||||
return REDIS_OK;
|
||||
case EALREADY:
|
||||
case EINPROGRESS:
|
||||
case EWOULDBLOCK:
|
||||
*completed = 0;
|
||||
return REDIS_OK;
|
||||
default:
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
int redisCheckSocketError(redisContext *c) {
|
||||
int err = 0, errno_saved = errno;
|
||||
socklen_t errlen = sizeof(err);
|
||||
|
||||
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
err = errno_saved;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
errno = err;
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
||||
const void *to_ptr = &tv;
|
||||
size_t to_sz = sizeof(tv);
|
||||
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) {
|
||||
/* Same timeval struct, short circuit */
|
||||
if (c->connect_timeout == timeout)
|
||||
return REDIS_OK;
|
||||
|
||||
/* Allocate context timeval if we need to */
|
||||
if (c->connect_timeout == NULL) {
|
||||
c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout));
|
||||
if (c->connect_timeout == NULL)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout));
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) {
|
||||
/* Same timeval struct, short circuit */
|
||||
if (c->command_timeout == timeout)
|
||||
return REDIS_OK;
|
||||
|
||||
/* Allocate context timeval if we need to */
|
||||
if (c->command_timeout == NULL) {
|
||||
c->command_timeout = hi_malloc(sizeof(*c->command_timeout));
|
||||
if (c->command_timeout == NULL)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout));
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout,
|
||||
const char *source_addr) {
|
||||
redisFD s;
|
||||
int rv, n;
|
||||
char _port[6]; /* strlen("65535"); */
|
||||
struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
int reuseaddr = (c->flags & REDIS_REUSEADDR);
|
||||
int reuses = 0;
|
||||
long timeout_msec = -1;
|
||||
|
||||
servinfo = NULL;
|
||||
c->connection_type = REDIS_CONN_TCP;
|
||||
c->tcp.port = port;
|
||||
|
||||
/* We need to take possession of the passed parameters
|
||||
* to make them reusable for a reconnect.
|
||||
* We also carefully check we don't free data we already own,
|
||||
* as in the case of the reconnect method.
|
||||
*
|
||||
* This is a bit ugly, but atleast it works and doesn't leak memory.
|
||||
**/
|
||||
if (c->tcp.host != addr) {
|
||||
hi_free(c->tcp.host);
|
||||
|
||||
c->tcp.host = hi_strdup(addr);
|
||||
if (c->tcp.host == NULL)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
|
||||
goto oom;
|
||||
} else {
|
||||
hi_free(c->connect_timeout);
|
||||
c->connect_timeout = NULL;
|
||||
}
|
||||
|
||||
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
|
||||
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (source_addr == NULL) {
|
||||
hi_free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = NULL;
|
||||
} else if (c->tcp.source_addr != source_addr) {
|
||||
hi_free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = hi_strdup(source_addr);
|
||||
}
|
||||
|
||||
snprintf(_port, 6, "%d", port);
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
/* Try with IPv6 if no IPv4 address was found. We do it in this order since
|
||||
* in a Redis client you can't afford to test if you have IPv6 connectivity
|
||||
* as this would add latency to every connect. Otherwise a more sensible
|
||||
* route could be: Use IPv6 if both addresses are available and there is IPv6
|
||||
* connectivity. */
|
||||
if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
|
||||
hints.ai_family = AF_INET6;
|
||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||
addrretry:
|
||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD)
|
||||
continue;
|
||||
|
||||
c->fd = s;
|
||||
if (redisSetBlocking(c,0) != REDIS_OK)
|
||||
goto error;
|
||||
if (c->tcp.source_addr) {
|
||||
int bound = 0;
|
||||
/* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
|
||||
if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
|
||||
char buf[128];
|
||||
snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
|
||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (reuseaddr) {
|
||||
n = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
|
||||
sizeof(n)) < 0) {
|
||||
freeaddrinfo(bservinfo);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
for (b = bservinfo; b != NULL; b = b->ai_next) {
|
||||
if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
|
||||
bound = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(bservinfo);
|
||||
if (!bound) {
|
||||
char buf[128];
|
||||
snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
|
||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* For repeat connection */
|
||||
hi_free(c->saddr);
|
||||
c->saddr = hi_malloc(p->ai_addrlen);
|
||||
if (c->saddr == NULL)
|
||||
goto oom;
|
||||
|
||||
memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
|
||||
c->addrlen = p->ai_addrlen;
|
||||
|
||||
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
||||
if (errno == EHOSTUNREACH) {
|
||||
redisNetClose(c);
|
||||
continue;
|
||||
} else if (errno == EINPROGRESS) {
|
||||
if (blocking) {
|
||||
goto wait_for_ready;
|
||||
}
|
||||
/* This is ok.
|
||||
* Note that even when it's in blocking mode, we unset blocking
|
||||
* for `connect()`
|
||||
*/
|
||||
} else if (errno == EADDRNOTAVAIL && reuseaddr) {
|
||||
if (++reuses >= REDIS_CONNECT_RETRIES) {
|
||||
goto error;
|
||||
} else {
|
||||
redisNetClose(c);
|
||||
goto addrretry;
|
||||
}
|
||||
} else {
|
||||
wait_for_ready:
|
||||
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
||||
goto error;
|
||||
if (redisSetTcpNoDelay(c) != REDIS_OK)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
||||
goto error;
|
||||
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
rv = REDIS_OK;
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
char buf[128];
|
||||
snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
|
||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
oom:
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
error:
|
||||
rv = REDIS_ERR;
|
||||
end:
|
||||
if(servinfo) {
|
||||
freeaddrinfo(servinfo);
|
||||
}
|
||||
|
||||
return rv; // Need to return REDIS_OK if alright
|
||||
}
|
||||
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout) {
|
||||
return _redisContextConnectTcp(c, addr, port, timeout, NULL);
|
||||
}
|
||||
|
||||
int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout,
|
||||
const char *source_addr) {
|
||||
return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
|
||||
}
|
||||
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
|
||||
#ifndef _WIN32
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
struct sockaddr_un *sa;
|
||||
long timeout_msec = -1;
|
||||
|
||||
if (redisCreateSocket(c,AF_UNIX) < 0)
|
||||
return REDIS_ERR;
|
||||
if (redisSetBlocking(c,0) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
c->connection_type = REDIS_CONN_UNIX;
|
||||
if (c->unix_sock.path != path) {
|
||||
hi_free(c->unix_sock.path);
|
||||
|
||||
c->unix_sock.path = hi_strdup(path);
|
||||
if (c->unix_sock.path == NULL)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
|
||||
goto oom;
|
||||
} else {
|
||||
hi_free(c->connect_timeout);
|
||||
c->connect_timeout = NULL;
|
||||
}
|
||||
|
||||
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Don't leak sockaddr if we're reconnecting */
|
||||
if (c->saddr) hi_free(c->saddr);
|
||||
|
||||
sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un)));
|
||||
if (sa == NULL)
|
||||
goto oom;
|
||||
|
||||
c->addrlen = sizeof(struct sockaddr_un);
|
||||
sa->sun_family = AF_UNIX;
|
||||
strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);
|
||||
if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) {
|
||||
if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset socket to be blocking after connect(2). */
|
||||
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
#else
|
||||
/* We currently do not support Unix sockets for Windows. */
|
||||
/* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */
|
||||
errno = EPROTONOSUPPORT;
|
||||
return REDIS_ERR;
|
||||
#endif /* _WIN32 */
|
||||
oom:
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
Vendored
-56
@@ -1,56 +0,0 @@
|
||||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
||||
* Jan-Erik Rediger <janerik at fnordig dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __NET_H
|
||||
#define __NET_H
|
||||
|
||||
#include "hiredis.h"
|
||||
|
||||
void redisNetClose(redisContext *c);
|
||||
ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap);
|
||||
ssize_t redisNetWrite(redisContext *c);
|
||||
|
||||
int redisCheckSocketError(redisContext *c);
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
|
||||
int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout,
|
||||
const char *source_addr);
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
|
||||
int redisKeepAlive(redisContext *c, int interval);
|
||||
int redisCheckConnectDone(redisContext *c, int *completed);
|
||||
|
||||
int redisSetTcpNoDelay(redisContext *c);
|
||||
|
||||
#endif
|
||||
Vendored
-739
@@ -1,739 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "alloc.h"
|
||||
#include "read.h"
|
||||
#include "sds.h"
|
||||
#include "win32.h"
|
||||
|
||||
/* Initial size of our nested reply stack and how much we grow it when needd */
|
||||
#define REDIS_READER_STACK_SIZE 9
|
||||
|
||||
static void __redisReaderSetError(redisReader *r, int type, const char *str) {
|
||||
size_t len;
|
||||
|
||||
if (r->reply != NULL && r->fn && r->fn->freeObject) {
|
||||
r->fn->freeObject(r->reply);
|
||||
r->reply = NULL;
|
||||
}
|
||||
|
||||
/* Clear input buffer on errors. */
|
||||
hi_sdsfree(r->buf);
|
||||
r->buf = NULL;
|
||||
r->pos = r->len = 0;
|
||||
|
||||
/* Reset task stack. */
|
||||
r->ridx = -1;
|
||||
|
||||
/* Set error. */
|
||||
r->err = type;
|
||||
len = strlen(str);
|
||||
len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
|
||||
memcpy(r->errstr,str,len);
|
||||
r->errstr[len] = '\0';
|
||||
}
|
||||
|
||||
static size_t chrtos(char *buf, size_t size, char byte) {
|
||||
size_t len = 0;
|
||||
|
||||
switch(byte) {
|
||||
case '\\':
|
||||
case '"':
|
||||
len = snprintf(buf,size,"\"\\%c\"",byte);
|
||||
break;
|
||||
case '\n': len = snprintf(buf,size,"\"\\n\""); break;
|
||||
case '\r': len = snprintf(buf,size,"\"\\r\""); break;
|
||||
case '\t': len = snprintf(buf,size,"\"\\t\""); break;
|
||||
case '\a': len = snprintf(buf,size,"\"\\a\""); break;
|
||||
case '\b': len = snprintf(buf,size,"\"\\b\""); break;
|
||||
default:
|
||||
if (isprint(byte))
|
||||
len = snprintf(buf,size,"\"%c\"",byte);
|
||||
else
|
||||
len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
|
||||
break;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
|
||||
char cbuf[8], sbuf[128];
|
||||
|
||||
chrtos(cbuf,sizeof(cbuf),byte);
|
||||
snprintf(sbuf,sizeof(sbuf),
|
||||
"Protocol error, got %s as reply type byte", cbuf);
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
|
||||
}
|
||||
|
||||
static void __redisReaderSetErrorOOM(redisReader *r) {
|
||||
__redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
|
||||
}
|
||||
|
||||
static char *readBytes(redisReader *r, unsigned int bytes) {
|
||||
char *p;
|
||||
if (r->len-r->pos >= bytes) {
|
||||
p = r->buf+r->pos;
|
||||
r->pos += bytes;
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find pointer to \r\n. */
|
||||
static char *seekNewline(char *s, size_t len) {
|
||||
int pos = 0;
|
||||
int _len = len-1;
|
||||
|
||||
/* Position should be < len-1 because the character at "pos" should be
|
||||
* followed by a \n. Note that strchr cannot be used because it doesn't
|
||||
* allow to search a limited length and the buffer that is being searched
|
||||
* might not have a trailing NULL character. */
|
||||
while (pos < _len) {
|
||||
while(pos < _len && s[pos] != '\r') pos++;
|
||||
if (pos==_len) {
|
||||
/* Not found. */
|
||||
return NULL;
|
||||
} else {
|
||||
if (s[pos+1] == '\n') {
|
||||
/* Found. */
|
||||
return s+pos;
|
||||
} else {
|
||||
/* Continue searching. */
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert a string into a long long. Returns REDIS_OK if the string could be
|
||||
* parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value
|
||||
* will be set to the parsed value when appropriate.
|
||||
*
|
||||
* Note that this function demands that the string strictly represents
|
||||
* a long long: no spaces or other characters before or after the string
|
||||
* representing the number are accepted, nor zeroes at the start if not
|
||||
* for the string "0" representing the zero number.
|
||||
*
|
||||
* Because of its strictness, it is safe to use this function to check if
|
||||
* you can convert a string into a long long, and obtain back the string
|
||||
* from the number without any loss in the string representation. */
|
||||
static int string2ll(const char *s, size_t slen, long long *value) {
|
||||
const char *p = s;
|
||||
size_t plen = 0;
|
||||
int negative = 0;
|
||||
unsigned long long v;
|
||||
|
||||
if (plen == slen)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Special case: first and only digit is 0. */
|
||||
if (slen == 1 && p[0] == '0') {
|
||||
if (value != NULL) *value = 0;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
if (p[0] == '-') {
|
||||
negative = 1;
|
||||
p++; plen++;
|
||||
|
||||
/* Abort on only a negative sign. */
|
||||
if (plen == slen)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* First digit should be 1-9, otherwise the string should just be 0. */
|
||||
if (p[0] >= '1' && p[0] <= '9') {
|
||||
v = p[0]-'0';
|
||||
p++; plen++;
|
||||
} else if (p[0] == '0' && slen == 1) {
|
||||
*value = 0;
|
||||
return REDIS_OK;
|
||||
} else {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
while (plen < slen && p[0] >= '0' && p[0] <= '9') {
|
||||
if (v > (ULLONG_MAX / 10)) /* Overflow. */
|
||||
return REDIS_ERR;
|
||||
v *= 10;
|
||||
|
||||
if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
|
||||
return REDIS_ERR;
|
||||
v += p[0]-'0';
|
||||
|
||||
p++; plen++;
|
||||
}
|
||||
|
||||
/* Return if not all bytes were used. */
|
||||
if (plen < slen)
|
||||
return REDIS_ERR;
|
||||
|
||||
if (negative) {
|
||||
if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
|
||||
return REDIS_ERR;
|
||||
if (value != NULL) *value = -v;
|
||||
} else {
|
||||
if (v > LLONG_MAX) /* Overflow. */
|
||||
return REDIS_ERR;
|
||||
if (value != NULL) *value = v;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static char *readLine(redisReader *r, int *_len) {
|
||||
char *p, *s;
|
||||
int len;
|
||||
|
||||
p = r->buf+r->pos;
|
||||
s = seekNewline(p,(r->len-r->pos));
|
||||
if (s != NULL) {
|
||||
len = s-(r->buf+r->pos);
|
||||
r->pos += len+2; /* skip \r\n */
|
||||
if (_len) *_len = len;
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void moveToNextTask(redisReader *r) {
|
||||
redisReadTask *cur, *prv;
|
||||
while (r->ridx >= 0) {
|
||||
/* Return a.s.a.p. when the stack is now empty. */
|
||||
if (r->ridx == 0) {
|
||||
r->ridx--;
|
||||
return;
|
||||
}
|
||||
|
||||
cur = r->task[r->ridx];
|
||||
prv = r->task[r->ridx-1];
|
||||
assert(prv->type == REDIS_REPLY_ARRAY ||
|
||||
prv->type == REDIS_REPLY_MAP ||
|
||||
prv->type == REDIS_REPLY_SET ||
|
||||
prv->type == REDIS_REPLY_PUSH);
|
||||
if (cur->idx == prv->elements-1) {
|
||||
r->ridx--;
|
||||
} else {
|
||||
/* Reset the type because the next item can be anything */
|
||||
assert(cur->idx < prv->elements);
|
||||
cur->type = -1;
|
||||
cur->elements = -1;
|
||||
cur->idx++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int processLineItem(redisReader *r) {
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
void *obj;
|
||||
char *p;
|
||||
int len;
|
||||
|
||||
if ((p = readLine(r,&len)) != NULL) {
|
||||
if (cur->type == REDIS_REPLY_INTEGER) {
|
||||
if (r->fn && r->fn->createInteger) {
|
||||
long long v;
|
||||
if (string2ll(p, len, &v) == REDIS_ERR) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Bad integer value");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
obj = r->fn->createInteger(cur,v);
|
||||
} else {
|
||||
obj = (void*)REDIS_REPLY_INTEGER;
|
||||
}
|
||||
} else if (cur->type == REDIS_REPLY_DOUBLE) {
|
||||
if (r->fn && r->fn->createDouble) {
|
||||
char buf[326], *eptr;
|
||||
double d;
|
||||
|
||||
if ((size_t)len >= sizeof(buf)) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Double value is too large");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
memcpy(buf,p,len);
|
||||
buf[len] = '\0';
|
||||
|
||||
if (strcasecmp(buf,",inf") == 0) {
|
||||
d = INFINITY; /* Positive infinite. */
|
||||
} else if (strcasecmp(buf,",-inf") == 0) {
|
||||
d = -INFINITY; /* Negative infinite. */
|
||||
} else {
|
||||
d = strtod((char*)buf,&eptr);
|
||||
if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Bad double value");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
obj = r->fn->createDouble(cur,d,buf,len);
|
||||
} else {
|
||||
obj = (void*)REDIS_REPLY_DOUBLE;
|
||||
}
|
||||
} else if (cur->type == REDIS_REPLY_NIL) {
|
||||
if (r->fn && r->fn->createNil)
|
||||
obj = r->fn->createNil(cur);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_NIL;
|
||||
} else if (cur->type == REDIS_REPLY_BOOL) {
|
||||
int bval = p[0] == 't' || p[0] == 'T';
|
||||
if (r->fn && r->fn->createBool)
|
||||
obj = r->fn->createBool(cur,bval);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_BOOL;
|
||||
} else {
|
||||
/* Type will be error or status. */
|
||||
if (r->fn && r->fn->createString)
|
||||
obj = r->fn->createString(cur,p,len);
|
||||
else
|
||||
obj = (void*)(size_t)(cur->type);
|
||||
}
|
||||
|
||||
if (obj == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Set reply if this is the root object. */
|
||||
if (r->ridx == 0) r->reply = obj;
|
||||
moveToNextTask(r);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int processBulkItem(redisReader *r) {
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
void *obj = NULL;
|
||||
char *p, *s;
|
||||
long long len;
|
||||
unsigned long bytelen;
|
||||
int success = 0;
|
||||
|
||||
p = r->buf+r->pos;
|
||||
s = seekNewline(p,r->len-r->pos);
|
||||
if (s != NULL) {
|
||||
p = r->buf+r->pos;
|
||||
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
|
||||
|
||||
if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Bad bulk string length");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Bulk string length out of range");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (len == -1) {
|
||||
/* The nil object can always be created. */
|
||||
if (r->fn && r->fn->createNil)
|
||||
obj = r->fn->createNil(cur);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_NIL;
|
||||
success = 1;
|
||||
} else {
|
||||
/* Only continue when the buffer contains the entire bulk item. */
|
||||
bytelen += len+2; /* include \r\n */
|
||||
if (r->pos+bytelen <= r->len) {
|
||||
if ((cur->type == REDIS_REPLY_VERB && len < 4) ||
|
||||
(cur->type == REDIS_REPLY_VERB && s[5] != ':'))
|
||||
{
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Verbatim string 4 bytes of content type are "
|
||||
"missing or incorrectly encoded.");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (r->fn && r->fn->createString)
|
||||
obj = r->fn->createString(cur,s+2,len);
|
||||
else
|
||||
obj = (void*)(long)cur->type;
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Proceed when obj was created. */
|
||||
if (success) {
|
||||
if (obj == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
r->pos += bytelen;
|
||||
|
||||
/* Set reply if this is the root object. */
|
||||
if (r->ridx == 0) r->reply = obj;
|
||||
moveToNextTask(r);
|
||||
return REDIS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int redisReaderGrow(redisReader *r) {
|
||||
redisReadTask **aux;
|
||||
int newlen;
|
||||
|
||||
/* Grow our stack size */
|
||||
newlen = r->tasks + REDIS_READER_STACK_SIZE;
|
||||
aux = hi_realloc(r->task, sizeof(*r->task) * newlen);
|
||||
if (aux == NULL)
|
||||
goto oom;
|
||||
|
||||
r->task = aux;
|
||||
|
||||
/* Allocate new tasks */
|
||||
for (; r->tasks < newlen; r->tasks++) {
|
||||
r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
|
||||
if (r->task[r->tasks] == NULL)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Process the array, map and set types. */
|
||||
static int processAggregateItem(redisReader *r) {
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
void *obj;
|
||||
char *p;
|
||||
long long elements;
|
||||
int root = 0, len;
|
||||
|
||||
/* Set error for nested multi bulks with depth > 7 */
|
||||
if (r->ridx == r->tasks - 1) {
|
||||
if (redisReaderGrow(r) == REDIS_ERR)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if ((p = readLine(r,&len)) != NULL) {
|
||||
if (string2ll(p, len, &elements) == REDIS_ERR) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Bad multi-bulk length");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
root = (r->ridx == 0);
|
||||
|
||||
if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) ||
|
||||
(r->maxelements > 0 && elements > r->maxelements))
|
||||
{
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Multi-bulk length out of range");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (elements == -1) {
|
||||
if (r->fn && r->fn->createNil)
|
||||
obj = r->fn->createNil(cur);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_NIL;
|
||||
|
||||
if (obj == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
moveToNextTask(r);
|
||||
} else {
|
||||
if (cur->type == REDIS_REPLY_MAP) elements *= 2;
|
||||
|
||||
if (r->fn && r->fn->createArray)
|
||||
obj = r->fn->createArray(cur,elements);
|
||||
else
|
||||
obj = (void*)(long)cur->type;
|
||||
|
||||
if (obj == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Modify task stack when there are more than 0 elements. */
|
||||
if (elements > 0) {
|
||||
cur->elements = elements;
|
||||
cur->obj = obj;
|
||||
r->ridx++;
|
||||
r->task[r->ridx]->type = -1;
|
||||
r->task[r->ridx]->elements = -1;
|
||||
r->task[r->ridx]->idx = 0;
|
||||
r->task[r->ridx]->obj = NULL;
|
||||
r->task[r->ridx]->parent = cur;
|
||||
r->task[r->ridx]->privdata = r->privdata;
|
||||
} else {
|
||||
moveToNextTask(r);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set reply if this is the root object. */
|
||||
if (root) r->reply = obj;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int processItem(redisReader *r) {
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
char *p;
|
||||
|
||||
/* check if we need to read type */
|
||||
if (cur->type < 0) {
|
||||
if ((p = readBytes(r,1)) != NULL) {
|
||||
switch (p[0]) {
|
||||
case '-':
|
||||
cur->type = REDIS_REPLY_ERROR;
|
||||
break;
|
||||
case '+':
|
||||
cur->type = REDIS_REPLY_STATUS;
|
||||
break;
|
||||
case ':':
|
||||
cur->type = REDIS_REPLY_INTEGER;
|
||||
break;
|
||||
case ',':
|
||||
cur->type = REDIS_REPLY_DOUBLE;
|
||||
break;
|
||||
case '_':
|
||||
cur->type = REDIS_REPLY_NIL;
|
||||
break;
|
||||
case '$':
|
||||
cur->type = REDIS_REPLY_STRING;
|
||||
break;
|
||||
case '*':
|
||||
cur->type = REDIS_REPLY_ARRAY;
|
||||
break;
|
||||
case '%':
|
||||
cur->type = REDIS_REPLY_MAP;
|
||||
break;
|
||||
case '~':
|
||||
cur->type = REDIS_REPLY_SET;
|
||||
break;
|
||||
case '#':
|
||||
cur->type = REDIS_REPLY_BOOL;
|
||||
break;
|
||||
case '=':
|
||||
cur->type = REDIS_REPLY_VERB;
|
||||
break;
|
||||
case '>':
|
||||
cur->type = REDIS_REPLY_PUSH;
|
||||
break;
|
||||
default:
|
||||
__redisReaderSetErrorProtocolByte(r,*p);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
} else {
|
||||
/* could not consume 1 byte */
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* process typed item */
|
||||
switch(cur->type) {
|
||||
case REDIS_REPLY_ERROR:
|
||||
case REDIS_REPLY_STATUS:
|
||||
case REDIS_REPLY_INTEGER:
|
||||
case REDIS_REPLY_DOUBLE:
|
||||
case REDIS_REPLY_NIL:
|
||||
case REDIS_REPLY_BOOL:
|
||||
return processLineItem(r);
|
||||
case REDIS_REPLY_STRING:
|
||||
case REDIS_REPLY_VERB:
|
||||
return processBulkItem(r);
|
||||
case REDIS_REPLY_ARRAY:
|
||||
case REDIS_REPLY_MAP:
|
||||
case REDIS_REPLY_SET:
|
||||
case REDIS_REPLY_PUSH:
|
||||
return processAggregateItem(r);
|
||||
default:
|
||||
assert(NULL);
|
||||
return REDIS_ERR; /* Avoid warning. */
|
||||
}
|
||||
}
|
||||
|
||||
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
|
||||
redisReader *r;
|
||||
|
||||
r = hi_calloc(1,sizeof(redisReader));
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
r->buf = hi_sdsempty();
|
||||
if (r->buf == NULL)
|
||||
goto oom;
|
||||
|
||||
r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task));
|
||||
if (r->task == NULL)
|
||||
goto oom;
|
||||
|
||||
for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) {
|
||||
r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
|
||||
if (r->task[r->tasks] == NULL)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
r->fn = fn;
|
||||
r->maxbuf = REDIS_READER_MAX_BUF;
|
||||
r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS;
|
||||
r->ridx = -1;
|
||||
|
||||
return r;
|
||||
oom:
|
||||
redisReaderFree(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void redisReaderFree(redisReader *r) {
|
||||
if (r == NULL)
|
||||
return;
|
||||
|
||||
if (r->reply != NULL && r->fn && r->fn->freeObject)
|
||||
r->fn->freeObject(r->reply);
|
||||
|
||||
if (r->task) {
|
||||
/* We know r->task[i] is allocated if i < r->tasks */
|
||||
for (int i = 0; i < r->tasks; i++) {
|
||||
hi_free(r->task[i]);
|
||||
}
|
||||
|
||||
hi_free(r->task);
|
||||
}
|
||||
|
||||
hi_sdsfree(r->buf);
|
||||
hi_free(r);
|
||||
}
|
||||
|
||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
|
||||
hisds newbuf;
|
||||
|
||||
/* Return early when this reader is in an erroneous state. */
|
||||
if (r->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Copy the provided buffer. */
|
||||
if (buf != NULL && len >= 1) {
|
||||
/* Destroy internal buffer when it is empty and is quite large. */
|
||||
if (r->len == 0 && r->maxbuf != 0 && hi_sdsavail(r->buf) > r->maxbuf) {
|
||||
hi_sdsfree(r->buf);
|
||||
r->buf = hi_sdsempty();
|
||||
if (r->buf == 0) goto oom;
|
||||
|
||||
r->pos = 0;
|
||||
}
|
||||
|
||||
newbuf = hi_sdscatlen(r->buf,buf,len);
|
||||
if (newbuf == NULL) goto oom;
|
||||
|
||||
r->buf = newbuf;
|
||||
r->len = hi_sdslen(r->buf);
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisReaderGetReply(redisReader *r, void **reply) {
|
||||
/* Default target pointer to NULL. */
|
||||
if (reply != NULL)
|
||||
*reply = NULL;
|
||||
|
||||
/* Return early when this reader is in an erroneous state. */
|
||||
if (r->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* When the buffer is empty, there will never be a reply. */
|
||||
if (r->len == 0)
|
||||
return REDIS_OK;
|
||||
|
||||
/* Set first item to process when the stack is empty. */
|
||||
if (r->ridx == -1) {
|
||||
r->task[0]->type = -1;
|
||||
r->task[0]->elements = -1;
|
||||
r->task[0]->idx = -1;
|
||||
r->task[0]->obj = NULL;
|
||||
r->task[0]->parent = NULL;
|
||||
r->task[0]->privdata = r->privdata;
|
||||
r->ridx = 0;
|
||||
}
|
||||
|
||||
/* Process items in reply. */
|
||||
while (r->ridx >= 0)
|
||||
if (processItem(r) != REDIS_OK)
|
||||
break;
|
||||
|
||||
/* Return ASAP when an error occurred. */
|
||||
if (r->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Discard part of the buffer when we've consumed at least 1k, to avoid
|
||||
* doing unnecessary calls to memmove() in sds.c. */
|
||||
if (r->pos >= 1024) {
|
||||
if (hi_sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR;
|
||||
r->pos = 0;
|
||||
r->len = hi_sdslen(r->buf);
|
||||
}
|
||||
|
||||
/* Emit a reply when there is one. */
|
||||
if (r->ridx == -1) {
|
||||
if (reply != NULL) {
|
||||
*reply = r->reply;
|
||||
} else if (r->reply != NULL && r->fn && r->fn->freeObject) {
|
||||
r->fn->freeObject(r->reply);
|
||||
}
|
||||
r->reply = NULL;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
Vendored
-129
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __HIREDIS_READ_H
|
||||
#define __HIREDIS_READ_H
|
||||
#include <stdio.h> /* for size_t */
|
||||
|
||||
#define REDIS_ERR -1
|
||||
#define REDIS_OK 0
|
||||
|
||||
/* When an error occurs, the err flag in a context is set to hold the type of
|
||||
* error that occurred. REDIS_ERR_IO means there was an I/O error and you
|
||||
* should use the "errno" variable to find out what is wrong.
|
||||
* For other values, the "errstr" field will hold a description. */
|
||||
#define REDIS_ERR_IO 1 /* Error in read or write */
|
||||
#define REDIS_ERR_EOF 3 /* End of file */
|
||||
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
|
||||
#define REDIS_ERR_OOM 5 /* Out of memory */
|
||||
#define REDIS_ERR_TIMEOUT 6 /* Timed out */
|
||||
#define REDIS_ERR_OTHER 2 /* Everything else... */
|
||||
|
||||
#define REDIS_REPLY_STRING 1
|
||||
#define REDIS_REPLY_ARRAY 2
|
||||
#define REDIS_REPLY_INTEGER 3
|
||||
#define REDIS_REPLY_NIL 4
|
||||
#define REDIS_REPLY_STATUS 5
|
||||
#define REDIS_REPLY_ERROR 6
|
||||
#define REDIS_REPLY_DOUBLE 7
|
||||
#define REDIS_REPLY_BOOL 8
|
||||
#define REDIS_REPLY_MAP 9
|
||||
#define REDIS_REPLY_SET 10
|
||||
#define REDIS_REPLY_ATTR 11
|
||||
#define REDIS_REPLY_PUSH 12
|
||||
#define REDIS_REPLY_BIGNUM 13
|
||||
#define REDIS_REPLY_VERB 14
|
||||
|
||||
/* Default max unused reader buffer. */
|
||||
#define REDIS_READER_MAX_BUF (1024*16)
|
||||
|
||||
/* Default multi-bulk element limit */
|
||||
#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct redisReadTask {
|
||||
int type;
|
||||
long long elements; /* number of elements in multibulk container */
|
||||
int idx; /* index in parent (array) object */
|
||||
void *obj; /* holds user-generated value for a read task */
|
||||
struct redisReadTask *parent; /* parent task */
|
||||
void *privdata; /* user-settable arbitrary field */
|
||||
} redisReadTask;
|
||||
|
||||
typedef struct redisReplyObjectFunctions {
|
||||
void *(*createString)(const redisReadTask*, char*, size_t);
|
||||
void *(*createArray)(const redisReadTask*, size_t);
|
||||
void *(*createInteger)(const redisReadTask*, long long);
|
||||
void *(*createDouble)(const redisReadTask*, double, char*, size_t);
|
||||
void *(*createNil)(const redisReadTask*);
|
||||
void *(*createBool)(const redisReadTask*, int);
|
||||
void (*freeObject)(void*);
|
||||
} redisReplyObjectFunctions;
|
||||
|
||||
typedef struct redisReader {
|
||||
int err; /* Error flags, 0 when there is no error */
|
||||
char errstr[128]; /* String representation of error when applicable */
|
||||
|
||||
char *buf; /* Read buffer */
|
||||
size_t pos; /* Buffer cursor */
|
||||
size_t len; /* Buffer length */
|
||||
size_t maxbuf; /* Max length of unused buffer */
|
||||
long long maxelements; /* Max multi-bulk elements */
|
||||
|
||||
redisReadTask **task;
|
||||
int tasks;
|
||||
|
||||
int ridx; /* Index of current read task */
|
||||
void *reply; /* Temporary reply pointer */
|
||||
|
||||
redisReplyObjectFunctions *fn;
|
||||
void *privdata;
|
||||
} redisReader;
|
||||
|
||||
/* Public API for the protocol parser. */
|
||||
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);
|
||||
void redisReaderFree(redisReader *r);
|
||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
|
||||
int redisReaderGetReply(redisReader *r, void **reply);
|
||||
|
||||
#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
|
||||
#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)
|
||||
#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
-1289
File diff suppressed because it is too large
Load Diff
Vendored
-278
@@ -1,278 +0,0 @@
|
||||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2015, Oran Agra
|
||||
* Copyright (c) 2015, Redis Labs, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef HIREDIS_SDS_H
|
||||
#define HIREDIS_SDS_H
|
||||
|
||||
#define HI_SDS_MAX_PREALLOC (1024*1024)
|
||||
#ifdef _MSC_VER
|
||||
#define __attribute__(x)
|
||||
typedef long long ssize_t;
|
||||
#define SSIZE_MAX (LLONG_MAX >> 1)
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef char *hisds;
|
||||
|
||||
/* Note: sdshdr5 is never used, we just access the flags byte directly.
|
||||
* However is here to document the layout of type 5 SDS strings. */
|
||||
struct __attribute__ ((__packed__)) hisdshdr5 {
|
||||
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) hisdshdr8 {
|
||||
uint8_t len; /* used */
|
||||
uint8_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) hisdshdr16 {
|
||||
uint16_t len; /* used */
|
||||
uint16_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) hisdshdr32 {
|
||||
uint32_t len; /* used */
|
||||
uint32_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) hisdshdr64 {
|
||||
uint64_t len; /* used */
|
||||
uint64_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
|
||||
#define HI_SDS_TYPE_5 0
|
||||
#define HI_SDS_TYPE_8 1
|
||||
#define HI_SDS_TYPE_16 2
|
||||
#define HI_SDS_TYPE_32 3
|
||||
#define HI_SDS_TYPE_64 4
|
||||
#define HI_SDS_TYPE_MASK 7
|
||||
#define HI_SDS_TYPE_BITS 3
|
||||
#define HI_SDS_HDR_VAR(T,s) struct hisdshdr##T *sh = (struct hisdshdr##T *)((s)-(sizeof(struct hisdshdr##T)));
|
||||
#define HI_SDS_HDR(T,s) ((struct hisdshdr##T *)((s)-(sizeof(struct hisdshdr##T))))
|
||||
#define HI_SDS_TYPE_5_LEN(f) ((f)>>HI_SDS_TYPE_BITS)
|
||||
|
||||
static inline size_t hi_sdslen(const hisds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags & HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
return HI_SDS_TYPE_5_LEN(flags);
|
||||
case HI_SDS_TYPE_8:
|
||||
return HI_SDS_HDR(8,s)->len;
|
||||
case HI_SDS_TYPE_16:
|
||||
return HI_SDS_HDR(16,s)->len;
|
||||
case HI_SDS_TYPE_32:
|
||||
return HI_SDS_HDR(32,s)->len;
|
||||
case HI_SDS_TYPE_64:
|
||||
return HI_SDS_HDR(64,s)->len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t hi_sdsavail(const hisds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5: {
|
||||
return 0;
|
||||
}
|
||||
case HI_SDS_TYPE_8: {
|
||||
HI_SDS_HDR_VAR(8,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case HI_SDS_TYPE_16: {
|
||||
HI_SDS_HDR_VAR(16,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case HI_SDS_TYPE_32: {
|
||||
HI_SDS_HDR_VAR(32,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case HI_SDS_TYPE_64: {
|
||||
HI_SDS_HDR_VAR(64,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hi_sdssetlen(hisds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
*fp = (unsigned char)(HI_SDS_TYPE_5 | (newlen << HI_SDS_TYPE_BITS));
|
||||
}
|
||||
break;
|
||||
case HI_SDS_TYPE_8:
|
||||
HI_SDS_HDR(8,s)->len = (uint8_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_16:
|
||||
HI_SDS_HDR(16,s)->len = (uint16_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_32:
|
||||
HI_SDS_HDR(32,s)->len = (uint32_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_64:
|
||||
HI_SDS_HDR(64,s)->len = (uint64_t)newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hi_sdsinclen(hisds s, size_t inc) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
unsigned char newlen = HI_SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
|
||||
*fp = HI_SDS_TYPE_5 | (newlen << HI_SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case HI_SDS_TYPE_8:
|
||||
HI_SDS_HDR(8,s)->len += (uint8_t)inc;
|
||||
break;
|
||||
case HI_SDS_TYPE_16:
|
||||
HI_SDS_HDR(16,s)->len += (uint16_t)inc;
|
||||
break;
|
||||
case HI_SDS_TYPE_32:
|
||||
HI_SDS_HDR(32,s)->len += (uint32_t)inc;
|
||||
break;
|
||||
case HI_SDS_TYPE_64:
|
||||
HI_SDS_HDR(64,s)->len += (uint64_t)inc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* hi_sdsalloc() = hi_sdsavail() + hi_sdslen() */
|
||||
static inline size_t hi_sdsalloc(const hisds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags & HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
return HI_SDS_TYPE_5_LEN(flags);
|
||||
case HI_SDS_TYPE_8:
|
||||
return HI_SDS_HDR(8,s)->alloc;
|
||||
case HI_SDS_TYPE_16:
|
||||
return HI_SDS_HDR(16,s)->alloc;
|
||||
case HI_SDS_TYPE_32:
|
||||
return HI_SDS_HDR(32,s)->alloc;
|
||||
case HI_SDS_TYPE_64:
|
||||
return HI_SDS_HDR(64,s)->alloc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hi_sdssetalloc(hisds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
/* Nothing to do, this type has no total allocation info. */
|
||||
break;
|
||||
case HI_SDS_TYPE_8:
|
||||
HI_SDS_HDR(8,s)->alloc = (uint8_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_16:
|
||||
HI_SDS_HDR(16,s)->alloc = (uint16_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_32:
|
||||
HI_SDS_HDR(32,s)->alloc = (uint32_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_64:
|
||||
HI_SDS_HDR(64,s)->alloc = (uint64_t)newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hisds hi_sdsnewlen(const void *init, size_t initlen);
|
||||
hisds hi_sdsnew(const char *init);
|
||||
hisds hi_sdsempty(void);
|
||||
hisds hi_sdsdup(const hisds s);
|
||||
void hi_sdsfree(hisds s);
|
||||
hisds hi_sdsgrowzero(hisds s, size_t len);
|
||||
hisds hi_sdscatlen(hisds s, const void *t, size_t len);
|
||||
hisds hi_sdscat(hisds s, const char *t);
|
||||
hisds hi_sdscatsds(hisds s, const hisds t);
|
||||
hisds hi_sdscpylen(hisds s, const char *t, size_t len);
|
||||
hisds hi_sdscpy(hisds s, const char *t);
|
||||
|
||||
hisds hi_sdscatvprintf(hisds s, const char *fmt, va_list ap);
|
||||
#ifdef __GNUC__
|
||||
hisds hi_sdscatprintf(hisds s, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
hisds hi_sdscatprintf(hisds s, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
hisds hi_sdscatfmt(hisds s, char const *fmt, ...);
|
||||
hisds hi_sdstrim(hisds s, const char *cset);
|
||||
int hi_sdsrange(hisds s, ssize_t start, ssize_t end);
|
||||
void hi_sdsupdatelen(hisds s);
|
||||
void hi_sdsclear(hisds s);
|
||||
int hi_sdscmp(const hisds s1, const hisds s2);
|
||||
hisds *hi_sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
|
||||
void hi_sdsfreesplitres(hisds *tokens, int count);
|
||||
void hi_sdstolower(hisds s);
|
||||
void hi_sdstoupper(hisds s);
|
||||
hisds hi_sdsfromlonglong(long long value);
|
||||
hisds hi_sdscatrepr(hisds s, const char *p, size_t len);
|
||||
hisds *hi_sdssplitargs(const char *line, int *argc);
|
||||
hisds hi_sdsmapchars(hisds s, const char *from, const char *to, size_t setlen);
|
||||
hisds hi_sdsjoin(char **argv, int argc, char *sep);
|
||||
hisds hi_sdsjoinsds(hisds *argv, int argc, const char *sep, size_t seplen);
|
||||
|
||||
/* Low level functions exposed to the user API */
|
||||
hisds hi_sdsMakeRoomFor(hisds s, size_t addlen);
|
||||
void hi_sdsIncrLen(hisds s, int incr);
|
||||
hisds hi_sdsRemoveFreeSpace(hisds s);
|
||||
size_t hi_sdsAllocSize(hisds s);
|
||||
void *hi_sdsAllocPtr(hisds s);
|
||||
|
||||
/* Export the allocator used by SDS to the program using SDS.
|
||||
* Sometimes the program SDS is linked to, may use a different set of
|
||||
* allocators, but may want to allocate or free things that SDS will
|
||||
* respectively free or allocate. */
|
||||
void *hi_sds_malloc(size_t size);
|
||||
void *hi_sds_realloc(void *ptr, size_t size);
|
||||
void hi_sds_free(void *ptr);
|
||||
|
||||
#ifdef REDIS_TEST
|
||||
int hi_sdsTest(int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
#endif /* HIREDIS_SDS_H */
|
||||
Vendored
-44
@@ -1,44 +0,0 @@
|
||||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2015, Oran Agra
|
||||
* Copyright (c) 2015, Redis Labs, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* SDS allocator selection.
|
||||
*
|
||||
* This file is used in order to change the SDS allocator at compile time.
|
||||
* Just define the following defines to what you want to use. Also add
|
||||
* the include of your alternate allocator if needed (not needed in order
|
||||
* to use the default libc allocator). */
|
||||
|
||||
#include "alloc.h"
|
||||
|
||||
#define hi_s_malloc hi_malloc
|
||||
#define hi_s_realloc hi_realloc
|
||||
#define hi_s_free hi_free
|
||||
Vendored
-94
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SDS compatibility header.
|
||||
*
|
||||
* This simple file maps sds types and calls to their unique hiredis symbol names.
|
||||
* It's useful when we build Hiredis as a dependency of Redis and want to call
|
||||
* Hiredis' sds symbols rather than the ones built into Redis, as the libraries
|
||||
* have slightly diverged and could cause hard to track down ABI incompatibility
|
||||
* bugs.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HIREDIS_SDS_COMPAT
|
||||
#define HIREDIS_SDS_COMPAT
|
||||
|
||||
#define sds hisds
|
||||
|
||||
#define sdslen hi_sdslen
|
||||
#define sdsavail hi_sdsavail
|
||||
#define sdssetlen hi_sdssetlen
|
||||
#define sdsinclen hi_sdsinclen
|
||||
#define sdsalloc hi_sdsalloc
|
||||
#define sdssetalloc hi_sdssetalloc
|
||||
|
||||
#define sdsAllocPtr hi_sdsAllocPtr
|
||||
#define sdsAllocSize hi_sdsAllocSize
|
||||
#define sdscat hi_sdscat
|
||||
#define sdscatfmt hi_sdscatfmt
|
||||
#define sdscatlen hi_sdscatlen
|
||||
#define sdscatprintf hi_sdscatprintf
|
||||
#define sdscatrepr hi_sdscatrepr
|
||||
#define sdscatsds hi_sdscatsds
|
||||
#define sdscatvprintf hi_sdscatvprintf
|
||||
#define sdsclear hi_sdsclear
|
||||
#define sdscmp hi_sdscmp
|
||||
#define sdscpy hi_sdscpy
|
||||
#define sdscpylen hi_sdscpylen
|
||||
#define sdsdup hi_sdsdup
|
||||
#define sdsempty hi_sdsempty
|
||||
#define sds_free hi_sds_free
|
||||
#define sdsfree hi_sdsfree
|
||||
#define sdsfreesplitres hi_sdsfreesplitres
|
||||
#define sdsfromlonglong hi_sdsfromlonglong
|
||||
#define sdsgrowzero hi_sdsgrowzero
|
||||
#define sdsIncrLen hi_sdsIncrLen
|
||||
#define sdsjoin hi_sdsjoin
|
||||
#define sdsjoinsds hi_sdsjoinsds
|
||||
#define sdsll2str hi_sdsll2str
|
||||
#define sdsMakeRoomFor hi_sdsMakeRoomFor
|
||||
#define sds_malloc hi_sds_malloc
|
||||
#define sdsmapchars hi_sdsmapchars
|
||||
#define sdsnew hi_sdsnew
|
||||
#define sdsnewlen hi_sdsnewlen
|
||||
#define sdsrange hi_sdsrange
|
||||
#define sds_realloc hi_sds_realloc
|
||||
#define sdsRemoveFreeSpace hi_sdsRemoveFreeSpace
|
||||
#define sdssplitargs hi_sdssplitargs
|
||||
#define sdssplitlen hi_sdssplitlen
|
||||
#define sdstolower hi_sdstolower
|
||||
#define sdstoupper hi_sdstoupper
|
||||
#define sdstrim hi_sdstrim
|
||||
#define sdsull2str hi_sdsull2str
|
||||
#define sdsupdatelen hi_sdsupdatelen
|
||||
|
||||
#endif /* HIREDIS_SDS_COMPAT */
|
||||
Vendored
-248
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDIS_SOCKCOMPAT_IMPLEMENTATION
|
||||
#include "sockcompat.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
static int _wsaErrorToErrno(int err) {
|
||||
switch (err) {
|
||||
case WSAEWOULDBLOCK:
|
||||
return EWOULDBLOCK;
|
||||
case WSAEINPROGRESS:
|
||||
return EINPROGRESS;
|
||||
case WSAEALREADY:
|
||||
return EALREADY;
|
||||
case WSAENOTSOCK:
|
||||
return ENOTSOCK;
|
||||
case WSAEDESTADDRREQ:
|
||||
return EDESTADDRREQ;
|
||||
case WSAEMSGSIZE:
|
||||
return EMSGSIZE;
|
||||
case WSAEPROTOTYPE:
|
||||
return EPROTOTYPE;
|
||||
case WSAENOPROTOOPT:
|
||||
return ENOPROTOOPT;
|
||||
case WSAEPROTONOSUPPORT:
|
||||
return EPROTONOSUPPORT;
|
||||
case WSAEOPNOTSUPP:
|
||||
return EOPNOTSUPP;
|
||||
case WSAEAFNOSUPPORT:
|
||||
return EAFNOSUPPORT;
|
||||
case WSAEADDRINUSE:
|
||||
return EADDRINUSE;
|
||||
case WSAEADDRNOTAVAIL:
|
||||
return EADDRNOTAVAIL;
|
||||
case WSAENETDOWN:
|
||||
return ENETDOWN;
|
||||
case WSAENETUNREACH:
|
||||
return ENETUNREACH;
|
||||
case WSAENETRESET:
|
||||
return ENETRESET;
|
||||
case WSAECONNABORTED:
|
||||
return ECONNABORTED;
|
||||
case WSAECONNRESET:
|
||||
return ECONNRESET;
|
||||
case WSAENOBUFS:
|
||||
return ENOBUFS;
|
||||
case WSAEISCONN:
|
||||
return EISCONN;
|
||||
case WSAENOTCONN:
|
||||
return ENOTCONN;
|
||||
case WSAETIMEDOUT:
|
||||
return ETIMEDOUT;
|
||||
case WSAECONNREFUSED:
|
||||
return ECONNREFUSED;
|
||||
case WSAELOOP:
|
||||
return ELOOP;
|
||||
case WSAENAMETOOLONG:
|
||||
return ENAMETOOLONG;
|
||||
case WSAEHOSTUNREACH:
|
||||
return EHOSTUNREACH;
|
||||
case WSAENOTEMPTY:
|
||||
return ENOTEMPTY;
|
||||
default:
|
||||
/* We just return a generic I/O error if we could not find a relevant error. */
|
||||
return EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static void _updateErrno(int success) {
|
||||
errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError());
|
||||
}
|
||||
|
||||
static int _initWinsock() {
|
||||
static int s_initialized = 0;
|
||||
if (!s_initialized) {
|
||||
static WSADATA wsadata;
|
||||
int err = WSAStartup(MAKEWORD(2,2), &wsadata);
|
||||
if (err != 0) {
|
||||
errno = _wsaErrorToErrno(err);
|
||||
return 0;
|
||||
}
|
||||
s_initialized = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
|
||||
/* Note: This function is likely to be called before other functions, so run init here. */
|
||||
if (!_initWinsock()) {
|
||||
return EAI_FAIL;
|
||||
}
|
||||
|
||||
switch (getaddrinfo(node, service, hints, res)) {
|
||||
case 0: return 0;
|
||||
case WSATRY_AGAIN: return EAI_AGAIN;
|
||||
case WSAEINVAL: return EAI_BADFLAGS;
|
||||
case WSAEAFNOSUPPORT: return EAI_FAMILY;
|
||||
case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY;
|
||||
case WSAHOST_NOT_FOUND: return EAI_NONAME;
|
||||
case WSATYPE_NOT_FOUND: return EAI_SERVICE;
|
||||
case WSAESOCKTNOSUPPORT: return EAI_SOCKTYPE;
|
||||
default: return EAI_FAIL; /* Including WSANO_RECOVERY */
|
||||
}
|
||||
}
|
||||
|
||||
const char *win32_gai_strerror(int errcode) {
|
||||
switch (errcode) {
|
||||
case 0: errcode = 0; break;
|
||||
case EAI_AGAIN: errcode = WSATRY_AGAIN; break;
|
||||
case EAI_BADFLAGS: errcode = WSAEINVAL; break;
|
||||
case EAI_FAMILY: errcode = WSAEAFNOSUPPORT; break;
|
||||
case EAI_MEMORY: errcode = WSA_NOT_ENOUGH_MEMORY; break;
|
||||
case EAI_NONAME: errcode = WSAHOST_NOT_FOUND; break;
|
||||
case EAI_SERVICE: errcode = WSATYPE_NOT_FOUND; break;
|
||||
case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT; break;
|
||||
default: errcode = WSANO_RECOVERY; break; /* Including EAI_FAIL */
|
||||
}
|
||||
return gai_strerror(errcode);
|
||||
}
|
||||
|
||||
void win32_freeaddrinfo(struct addrinfo *res) {
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
SOCKET win32_socket(int domain, int type, int protocol) {
|
||||
SOCKET s;
|
||||
|
||||
/* Note: This function is likely to be called before other functions, so run init here. */
|
||||
if (!_initWinsock()) {
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
_updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET);
|
||||
return s;
|
||||
}
|
||||
|
||||
int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) {
|
||||
int ret = ioctlsocket(fd, (long)request, argp);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
int ret = bind(sockfd, addr, addrlen);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
int ret = connect(sockfd, addr, addrlen);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
|
||||
/* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as
|
||||
* EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX
|
||||
* logic consistent. */
|
||||
if (errno == EWOULDBLOCK) {
|
||||
errno = EINPROGRESS;
|
||||
}
|
||||
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) {
|
||||
int ret = 0;
|
||||
if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
|
||||
if (*optlen >= sizeof (struct timeval)) {
|
||||
struct timeval *tv = optval;
|
||||
DWORD timeout = 0;
|
||||
socklen_t dwlen = 0;
|
||||
ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen);
|
||||
tv->tv_sec = timeout / 1000;
|
||||
tv->tv_usec = (timeout * 1000) % 1000000;
|
||||
} else {
|
||||
ret = WSAEFAULT;
|
||||
}
|
||||
*optlen = sizeof (struct timeval);
|
||||
} else {
|
||||
ret = getsockopt(sockfd, level, optname, (char*)optval, optlen);
|
||||
}
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) {
|
||||
int ret = 0;
|
||||
if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
|
||||
const struct timeval *tv = optval;
|
||||
DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
|
||||
ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD));
|
||||
} else {
|
||||
ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen);
|
||||
}
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_close(SOCKET fd) {
|
||||
int ret = closesocket(fd);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) {
|
||||
int ret = recv(sockfd, (char*)buf, (int)len, flags);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) {
|
||||
int ret = send(sockfd, (const char*)buf, (int)len, flags);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
|
||||
int ret = WSAPoll(fds, nfds, timeout);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
Vendored
-92
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __SOCKCOMPAT_H
|
||||
#define __SOCKCOMPAT_H
|
||||
|
||||
#ifndef _WIN32
|
||||
/* For POSIX systems we use the standard BSD socket API. */
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#else
|
||||
/* For Windows we use winsock. */
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef long long ssize_t;
|
||||
#endif
|
||||
|
||||
/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
|
||||
int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
|
||||
const char *win32_gai_strerror(int errcode);
|
||||
void win32_freeaddrinfo(struct addrinfo *res);
|
||||
SOCKET win32_socket(int domain, int type, int protocol);
|
||||
int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp);
|
||||
int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen);
|
||||
int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);
|
||||
int win32_close(SOCKET fd);
|
||||
ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags);
|
||||
ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
|
||||
typedef ULONG nfds_t;
|
||||
int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||
|
||||
#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
|
||||
#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
|
||||
#undef gai_strerror
|
||||
#define gai_strerror(errcode) win32_gai_strerror(errcode)
|
||||
#define freeaddrinfo(res) win32_freeaddrinfo(res)
|
||||
#define socket(domain, type, protocol) win32_socket(domain, type, protocol)
|
||||
#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp)
|
||||
#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen)
|
||||
#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen)
|
||||
#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen)
|
||||
#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen)
|
||||
#define close(fd) win32_close(fd)
|
||||
#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags)
|
||||
#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags)
|
||||
#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout)
|
||||
#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* __SOCKCOMPAT_H */
|
||||
Vendored
-526
@@ -1,526 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
* Copyright (c) 2019, Redis Labs
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "hiredis.h"
|
||||
#include "async.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "win32.h"
|
||||
#include "async_private.h"
|
||||
#include "hiredis_ssl.h"
|
||||
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
struct redisSSLContext {
|
||||
/* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
|
||||
SSL_CTX *ssl_ctx;
|
||||
|
||||
/* Requested SNI, or NULL */
|
||||
char *server_name;
|
||||
};
|
||||
|
||||
/* The SSL connection context is attached to SSL/TLS connections as a privdata. */
|
||||
typedef struct redisSSL {
|
||||
/**
|
||||
* OpenSSL SSL object.
|
||||
*/
|
||||
SSL *ssl;
|
||||
|
||||
/**
|
||||
* SSL_write() requires to be called again with the same arguments it was
|
||||
* previously called with in the event of an SSL_read/SSL_write situation
|
||||
*/
|
||||
size_t lastLen;
|
||||
|
||||
/** Whether the SSL layer requires read (possibly before a write) */
|
||||
int wantRead;
|
||||
|
||||
/**
|
||||
* Whether a write was requested prior to a read. If set, the write()
|
||||
* should resume whenever a read takes place, if possible
|
||||
*/
|
||||
int pendingWrite;
|
||||
} redisSSL;
|
||||
|
||||
/* Forward declaration */
|
||||
redisContextFuncs redisContextSSLFuncs;
|
||||
|
||||
/**
|
||||
* OpenSSL global initialization and locking handling callbacks.
|
||||
* Note that this is only required for OpenSSL < 1.1.0.
|
||||
*/
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
#define HIREDIS_USE_CRYPTO_LOCKS
|
||||
#endif
|
||||
|
||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||
#ifdef _WIN32
|
||||
typedef CRITICAL_SECTION sslLockType;
|
||||
static void sslLockInit(sslLockType* l) {
|
||||
InitializeCriticalSection(l);
|
||||
}
|
||||
static void sslLockAcquire(sslLockType* l) {
|
||||
EnterCriticalSection(l);
|
||||
}
|
||||
static void sslLockRelease(sslLockType* l) {
|
||||
LeaveCriticalSection(l);
|
||||
}
|
||||
#else
|
||||
typedef pthread_mutex_t sslLockType;
|
||||
static void sslLockInit(sslLockType *l) {
|
||||
pthread_mutex_init(l, NULL);
|
||||
}
|
||||
static void sslLockAcquire(sslLockType *l) {
|
||||
pthread_mutex_lock(l);
|
||||
}
|
||||
static void sslLockRelease(sslLockType *l) {
|
||||
pthread_mutex_unlock(l);
|
||||
}
|
||||
#endif
|
||||
|
||||
static sslLockType* ossl_locks;
|
||||
|
||||
static void opensslDoLock(int mode, int lkid, const char *f, int line) {
|
||||
sslLockType *l = ossl_locks + lkid;
|
||||
|
||||
if (mode & CRYPTO_LOCK) {
|
||||
sslLockAcquire(l);
|
||||
} else {
|
||||
sslLockRelease(l);
|
||||
}
|
||||
|
||||
(void)f;
|
||||
(void)line;
|
||||
}
|
||||
|
||||
static int initOpensslLocks(void) {
|
||||
unsigned ii, nlocks;
|
||||
if (CRYPTO_get_locking_callback() != NULL) {
|
||||
/* Someone already set the callback before us. Don't destroy it! */
|
||||
return REDIS_OK;
|
||||
}
|
||||
nlocks = CRYPTO_num_locks();
|
||||
ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks);
|
||||
if (ossl_locks == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
for (ii = 0; ii < nlocks; ii++) {
|
||||
sslLockInit(ossl_locks + ii);
|
||||
}
|
||||
CRYPTO_set_locking_callback(opensslDoLock);
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif /* HIREDIS_USE_CRYPTO_LOCKS */
|
||||
|
||||
int redisInitOpenSSL(void)
|
||||
{
|
||||
SSL_library_init();
|
||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||
initOpensslLocks();
|
||||
#endif
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* redisSSLContext helper context destruction.
|
||||
*/
|
||||
|
||||
const char *redisSSLContextGetError(redisSSLContextError error)
|
||||
{
|
||||
switch (error) {
|
||||
case REDIS_SSL_CTX_NONE:
|
||||
return "No Error";
|
||||
case REDIS_SSL_CTX_CREATE_FAILED:
|
||||
return "Failed to create OpenSSL SSL_CTX";
|
||||
case REDIS_SSL_CTX_CERT_KEY_REQUIRED:
|
||||
return "Client cert and key must both be specified or skipped";
|
||||
case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED:
|
||||
return "Failed to load CA Certificate or CA Path";
|
||||
case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED:
|
||||
return "Failed to load client certificate";
|
||||
case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED:
|
||||
return "Failed to load private key";
|
||||
default:
|
||||
return "Unknown error code";
|
||||
}
|
||||
}
|
||||
|
||||
void redisFreeSSLContext(redisSSLContext *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
if (ctx->server_name) {
|
||||
hi_free(ctx->server_name);
|
||||
ctx->server_name = NULL;
|
||||
}
|
||||
|
||||
if (ctx->ssl_ctx) {
|
||||
SSL_CTX_free(ctx->ssl_ctx);
|
||||
ctx->ssl_ctx = NULL;
|
||||
}
|
||||
|
||||
hi_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* redisSSLContext helper context initialization.
|
||||
*/
|
||||
|
||||
redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
|
||||
const char *cert_filename, const char *private_key_filename,
|
||||
const char *server_name, redisSSLContextError *error)
|
||||
{
|
||||
redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext));
|
||||
if (ctx == NULL)
|
||||
goto error;
|
||||
|
||||
ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ctx->ssl_ctx) {
|
||||
if (error) *error = REDIS_SSL_CTX_CREATE_FAILED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||
|
||||
if ((cert_filename != NULL && private_key_filename == NULL) ||
|
||||
(private_key_filename != NULL && cert_filename == NULL)) {
|
||||
if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (capath || cacert_filename) {
|
||||
if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) {
|
||||
if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (cert_filename) {
|
||||
if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) {
|
||||
if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED;
|
||||
goto error;
|
||||
}
|
||||
if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) {
|
||||
if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (server_name)
|
||||
ctx->server_name = hi_strdup(server_name);
|
||||
|
||||
return ctx;
|
||||
|
||||
error:
|
||||
redisFreeSSLContext(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSL Connection initialization.
|
||||
*/
|
||||
|
||||
|
||||
static int redisSSLConnect(redisContext *c, SSL *ssl) {
|
||||
if (c->privctx) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
redisSSL *rssl = hi_calloc(1, sizeof(redisSSL));
|
||||
if (rssl == NULL) {
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
c->funcs = &redisContextSSLFuncs;
|
||||
rssl->ssl = ssl;
|
||||
|
||||
SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
SSL_set_fd(rssl->ssl, c->fd);
|
||||
SSL_set_connect_state(rssl->ssl);
|
||||
|
||||
ERR_clear_error();
|
||||
int rv = SSL_connect(rssl->ssl);
|
||||
if (rv == 1) {
|
||||
c->privctx = rssl;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
rv = SSL_get_error(rssl->ssl, rv);
|
||||
if (((c->flags & REDIS_BLOCK) == 0) &&
|
||||
(rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
|
||||
c->privctx = rssl;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
if (c->err == 0) {
|
||||
char err[512];
|
||||
if (rv == SSL_ERROR_SYSCALL)
|
||||
snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno));
|
||||
else {
|
||||
unsigned long e = ERR_peek_last_error();
|
||||
snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",
|
||||
ERR_reason_error_string(e));
|
||||
}
|
||||
__redisSetError(c, REDIS_ERR_IO, err);
|
||||
}
|
||||
|
||||
hi_free(rssl);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around redisSSLConnect() for users who manage their own context and
|
||||
* create their own SSL object.
|
||||
*/
|
||||
|
||||
int redisInitiateSSL(redisContext *c, SSL *ssl) {
|
||||
return redisSSLConnect(c, ssl);
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
|
||||
* manage their own SSL objects.
|
||||
*/
|
||||
|
||||
int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
|
||||
{
|
||||
if (!c || !redis_ssl_ctx)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* We want to verify that redisSSLConnect() won't fail on this, as it will
|
||||
* not own the SSL object in that case and we'll end up leaking.
|
||||
*/
|
||||
if (c->privctx)
|
||||
return REDIS_ERR;
|
||||
|
||||
SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx);
|
||||
if (!ssl) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (redis_ssl_ctx->server_name) {
|
||||
if (!SSL_set_tlsext_host_name(ssl, redis_ssl_ctx->server_name)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Failed to set server_name/SNI");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return redisSSLConnect(c, ssl);
|
||||
|
||||
error:
|
||||
if (ssl)
|
||||
SSL_free(ssl);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int maybeCheckWant(redisSSL *rssl, int rv) {
|
||||
/**
|
||||
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
|
||||
* and true is returned. False is returned otherwise
|
||||
*/
|
||||
if (rv == SSL_ERROR_WANT_READ) {
|
||||
rssl->wantRead = 1;
|
||||
return 1;
|
||||
} else if (rv == SSL_ERROR_WANT_WRITE) {
|
||||
rssl->pendingWrite = 1;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of redisContextFuncs for SSL connections.
|
||||
*/
|
||||
|
||||
static void redisSSLFree(void *privctx){
|
||||
redisSSL *rsc = privctx;
|
||||
|
||||
if (!rsc) return;
|
||||
if (rsc->ssl) {
|
||||
SSL_free(rsc->ssl);
|
||||
rsc->ssl = NULL;
|
||||
}
|
||||
hi_free(rsc);
|
||||
}
|
||||
|
||||
static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
redisSSL *rssl = c->privctx;
|
||||
|
||||
int nread = SSL_read(rssl->ssl, buf, bufcap);
|
||||
if (nread > 0) {
|
||||
return nread;
|
||||
} else if (nread == 0) {
|
||||
__redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
|
||||
return -1;
|
||||
} else {
|
||||
int err = SSL_get_error(rssl->ssl, nread);
|
||||
if (c->flags & REDIS_BLOCK) {
|
||||
/**
|
||||
* In blocking mode, we should never end up in a situation where
|
||||
* we get an error without it being an actual error, except
|
||||
* in the case of EINTR, which can be spuriously received from
|
||||
* debuggers or whatever.
|
||||
*/
|
||||
if (errno == EINTR) {
|
||||
return 0;
|
||||
} else {
|
||||
const char *msg = NULL;
|
||||
if (errno == EAGAIN) {
|
||||
msg = "Resource temporarily unavailable";
|
||||
}
|
||||
__redisSetError(c, REDIS_ERR_IO, msg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We can very well get an EWOULDBLOCK/EAGAIN, however
|
||||
*/
|
||||
if (maybeCheckWant(rssl, err)) {
|
||||
return 0;
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t redisSSLWrite(redisContext *c) {
|
||||
redisSSL *rssl = c->privctx;
|
||||
|
||||
size_t len = rssl->lastLen ? rssl->lastLen : hi_sdslen(c->obuf);
|
||||
int rv = SSL_write(rssl->ssl, c->obuf, len);
|
||||
|
||||
if (rv > 0) {
|
||||
rssl->lastLen = 0;
|
||||
} else if (rv < 0) {
|
||||
rssl->lastLen = len;
|
||||
|
||||
int err = SSL_get_error(rssl->ssl, rv);
|
||||
if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) {
|
||||
return 0;
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void redisSSLAsyncRead(redisAsyncContext *ac) {
|
||||
int rv;
|
||||
redisSSL *rssl = ac->c.privctx;
|
||||
redisContext *c = &ac->c;
|
||||
|
||||
rssl->wantRead = 0;
|
||||
|
||||
if (rssl->pendingWrite) {
|
||||
int done;
|
||||
|
||||
/* This is probably just a write event */
|
||||
rssl->pendingWrite = 0;
|
||||
rv = redisBufferWrite(c, &done);
|
||||
if (rv == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
return;
|
||||
} else if (!done) {
|
||||
_EL_ADD_WRITE(ac);
|
||||
}
|
||||
}
|
||||
|
||||
rv = redisBufferRead(c);
|
||||
if (rv == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
} else {
|
||||
_EL_ADD_READ(ac);
|
||||
redisProcessCallbacks(ac);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisSSLAsyncWrite(redisAsyncContext *ac) {
|
||||
int rv, done = 0;
|
||||
redisSSL *rssl = ac->c.privctx;
|
||||
redisContext *c = &ac->c;
|
||||
|
||||
rssl->pendingWrite = 0;
|
||||
rv = redisBufferWrite(c, &done);
|
||||
if (rv == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
if (rssl->wantRead) {
|
||||
/* Need to read-before-write */
|
||||
rssl->pendingWrite = 1;
|
||||
_EL_DEL_WRITE(ac);
|
||||
} else {
|
||||
/* No extra reads needed, just need to write more */
|
||||
_EL_ADD_WRITE(ac);
|
||||
}
|
||||
} else {
|
||||
/* Already done! */
|
||||
_EL_DEL_WRITE(ac);
|
||||
}
|
||||
|
||||
/* Always reschedule a read */
|
||||
_EL_ADD_READ(ac);
|
||||
}
|
||||
|
||||
redisContextFuncs redisContextSSLFuncs = {
|
||||
.free_privctx = redisSSLFree,
|
||||
.async_read = redisSSLAsyncRead,
|
||||
.async_write = redisSSLAsyncWrite,
|
||||
.read = redisSSLRead,
|
||||
.write = redisSSLWrite
|
||||
};
|
||||
|
||||
Vendored
-1411
File diff suppressed because it is too large
Load Diff
Vendored
-78
@@ -1,78 +0,0 @@
|
||||
#!/bin/sh -ue
|
||||
|
||||
REDIS_SERVER=${REDIS_SERVER:-redis-server}
|
||||
REDIS_PORT=${REDIS_PORT:-56379}
|
||||
REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443}
|
||||
TEST_SSL=${TEST_SSL:-0}
|
||||
SKIPS_AS_FAILS=${SKIPS_AS_FAILS-:0}
|
||||
SSL_TEST_ARGS=
|
||||
SKIPS_ARG=
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
PID_FILE=${tmpdir}/hiredis-test-redis.pid
|
||||
SOCK_FILE=${tmpdir}/hiredis-test-redis.sock
|
||||
|
||||
if [ "$TEST_SSL" = "1" ]; then
|
||||
SSL_CA_CERT=${tmpdir}/ca.crt
|
||||
SSL_CA_KEY=${tmpdir}/ca.key
|
||||
SSL_CERT=${tmpdir}/redis.crt
|
||||
SSL_KEY=${tmpdir}/redis.key
|
||||
|
||||
openssl genrsa -out ${tmpdir}/ca.key 4096
|
||||
openssl req \
|
||||
-x509 -new -nodes -sha256 \
|
||||
-key ${SSL_CA_KEY} \
|
||||
-days 3650 \
|
||||
-subj '/CN=Hiredis Test CA' \
|
||||
-out ${SSL_CA_CERT}
|
||||
openssl genrsa -out ${SSL_KEY} 2048
|
||||
openssl req \
|
||||
-new -sha256 \
|
||||
-key ${SSL_KEY} \
|
||||
-subj '/CN=Hiredis Test Cert' | \
|
||||
openssl x509 \
|
||||
-req -sha256 \
|
||||
-CA ${SSL_CA_CERT} \
|
||||
-CAkey ${SSL_CA_KEY} \
|
||||
-CAserial ${tmpdir}/ca.txt \
|
||||
-CAcreateserial \
|
||||
-days 365 \
|
||||
-out ${SSL_CERT}
|
||||
|
||||
SSL_TEST_ARGS="--ssl-host 127.0.0.1 --ssl-port ${REDIS_SSL_PORT} --ssl-ca-cert ${SSL_CA_CERT} --ssl-cert ${SSL_CERT} --ssl-key ${SSL_KEY}"
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
set +e
|
||||
kill $(cat ${PID_FILE})
|
||||
rm -rf ${tmpdir}
|
||||
}
|
||||
trap cleanup INT TERM EXIT
|
||||
|
||||
cat > ${tmpdir}/redis.conf <<EOF
|
||||
daemonize yes
|
||||
pidfile ${PID_FILE}
|
||||
port ${REDIS_PORT}
|
||||
bind 127.0.0.1
|
||||
unixsocket ${SOCK_FILE}
|
||||
EOF
|
||||
|
||||
if [ "$TEST_SSL" = "1" ]; then
|
||||
cat >> ${tmpdir}/redis.conf <<EOF
|
||||
tls-port ${REDIS_SSL_PORT}
|
||||
tls-ca-cert-file ${SSL_CA_CERT}
|
||||
tls-cert-file ${SSL_CERT}
|
||||
tls-key-file ${SSL_KEY}
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat ${tmpdir}/redis.conf
|
||||
${REDIS_SERVER} ${tmpdir}/redis.conf
|
||||
|
||||
# Wait until we detect the unix socket
|
||||
while [ ! -S "${SOCK_FILE}" ]; do sleep 1; done
|
||||
|
||||
# Treat skips as failures if directed
|
||||
[ "$SKIPS_AS_FAILS" = 1 ] && SKIPS_ARG="--skips-as-fails"
|
||||
|
||||
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS} ${SKIPS_ARG}
|
||||
Vendored
-56
@@ -1,56 +0,0 @@
|
||||
#ifndef _WIN32_HELPER_INCLUDE
|
||||
#define _WIN32_HELPER_INCLUDE
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#include <winsock2.h> /* for struct timeval */
|
||||
|
||||
#ifndef inline
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
#ifndef strcasecmp
|
||||
#define strcasecmp stricmp
|
||||
#endif
|
||||
|
||||
#ifndef strncasecmp
|
||||
#define strncasecmp strnicmp
|
||||
#endif
|
||||
|
||||
#ifndef va_copy
|
||||
#define va_copy(d,s) ((d) = (s))
|
||||
#endif
|
||||
|
||||
#ifndef snprintf
|
||||
#define snprintf c99_snprintf
|
||||
|
||||
__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap)
|
||||
{
|
||||
int count = -1;
|
||||
|
||||
if (size != 0)
|
||||
count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
|
||||
if (count == -1)
|
||||
count = _vscprintf(format, ap);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
__inline int c99_snprintf(char* str, size_t size, const char* format, ...)
|
||||
{
|
||||
int count;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
count = c99_vsnprintf(str, size, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#ifdef _WIN32
|
||||
#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* _WIN32_HELPER_INCLUDE */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user