From 5babd01d407b45d70d31291fec18442d78fe7717 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 12 May 2020 12:58:09 -0700 Subject: [PATCH] centos8 binaries for libhiredis and libredis++ --- ext/hiredis-0.14.1/lib/centos8/libhiredis.a | Bin 0 -> 485314 bytes .../centos8/include/sw/redis++/command.h | 2233 +++++++++++++++++ .../centos8/include/sw/redis++/command_args.h | 180 ++ .../include/sw/redis++/command_options.h | 211 ++ .../centos8/include/sw/redis++/connection.h | 194 ++ .../include/sw/redis++/connection_pool.h | 115 + .../centos8/include/sw/redis++/errors.h | 159 ++ .../centos8/include/sw/redis++/pipeline.h | 49 + .../centos8/include/sw/redis++/queued_redis.h | 1844 ++++++++++++++ .../include/sw/redis++/queued_redis.hpp | 208 ++ .../centos8/include/sw/redis++/redis++.h | 25 + .../centos8/include/sw/redis++/redis.h | 1523 +++++++++++ .../centos8/include/sw/redis++/redis.hpp | 1365 ++++++++++ .../include/sw/redis++/redis_cluster.h | 1395 ++++++++++ .../include/sw/redis++/redis_cluster.hpp | 1415 +++++++++++ .../centos8/include/sw/redis++/reply.h | 363 +++ .../centos8/include/sw/redis++/sentinel.h | 138 + .../centos8/include/sw/redis++/shards.h | 115 + .../centos8/include/sw/redis++/shards_pool.h | 137 + .../centos8/include/sw/redis++/subscriber.h | 231 ++ .../centos8/include/sw/redis++/transaction.h | 77 + .../centos8/include/sw/redis++/utils.h | 269 ++ 22 files changed, 12246 insertions(+) create mode 100644 ext/hiredis-0.14.1/lib/centos8/libhiredis.a create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_args.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_options.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection_pool.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/errors.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/pipeline.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.hpp create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis++.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.hpp create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.hpp create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/reply.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/sentinel.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards_pool.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/subscriber.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/transaction.h create mode 100644 ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/utils.h diff --git a/ext/hiredis-0.14.1/lib/centos8/libhiredis.a b/ext/hiredis-0.14.1/lib/centos8/libhiredis.a new file mode 100644 index 0000000000000000000000000000000000000000..0b9638798529747795a78056068d5a6a980a4f02 GIT binary patch literal 485314 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 z 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

FvN`Rfv+f3HZdHlRkiTKo zp|F)0VpQT8Zb{|Dbui{&*Xwd9`5?8Tae=uW@}W!Ecx(>c-9(SV9RRSxHPt z;}bCC>#s5TQp1#HtbQzHG?ojVoiE@N)-(s_Jh zQpLjn`VcII0b~~c3`}W(;#*|L4N)xpxVo+$Q47|P!ZX483mQh>k!MK!E|=BC-55SC=CVOx1pAKbkfRo9M|cughE_~C6uvbN;EWa0n#zaO(6m^K;o5+M z0UyprAM#{+InMIXGyx;bK2?>Wy!b10%}C>CDHxE&14uQZ8%NOl&f0B|E6cx^21*tz zJR80PVYPDah%5INSI8Q5g$yhXeme}~%-l~{54G6>W72|K)TWDm*4IP47qglsKuoVg z^nO6fuNv0jIVooe?1iF5OmX8#`C+J=qgwNP$YIeaS5Ux61aWJ&M4!{?(?fFz#~--)edh);4-4vclMj zY+AWrRiYMcq@7BinLZ2Bb+-8?&cJNzPrdbUCdf2?4?D~+z&Zmr=bB#s0oPaVANT*q z`HcJ6{_mWd-6!^cS?@ly|5IH?saeKn-{UR?+@rvL@i)){VfKr8T~`lVx;YJ3wDr_{ zdpO6R9qot56}eG|J^tL*hdupIdi+0o`u}3@_4q&A(G>NI=f0+5uq~(XwWhm{;_>aK z`(WjhQ@FS3E;wfSKXd#4j;lQ`9WPB`O0SP{tFoY|<51G^rVgN0`2?Oqy`lDDU{hD` z=ms`qVxd;hU;g){c?8FO897J zHI@8PLjuJ=JJzN8ICgamyraCpS%&4B*ELAWjt+9YJ&mxV0f(0R7P%1iUf}jm90_KQ z8UY{YSxt>i%`keL&*Vm(TmYUv+4LZQKj_6pt}^x1B~Xp$7J7eoHE3g9RR{dq5&U|F z3*M&!gJ-zF)?QXqM=&kR|7KGv%u9dJL~w($F<|1h(uMyg6BmGuU}BXr(F=B-1?~SG z`1I$dmv999k^UGk4%BsB1Rhay9XF@hHH~KurvJcq9lfOseUQJK)%5AN|5rP<`xgBn zC!rlBU4tr+L^7Fl@}Wxle) zvJ#iKqN1!~fNNoy&(){WRZ!`w@RpaXboo}6dtLKa`e6Q0K{-glF=Jd)v$JxhGid@B|KJP+rg=<+sNfpNDSCuSw!7G==r3+md#yR%LNoB6es`B!( z3ZH91S%s_ATj}!_xt3Iw_=&^{Lc>N^8kPl%#VUX&vFAC$4Sa zEf`$USa5^dG92v@jKlR0-fa1xOBxH4b7A0ofckm}&2Q~CKEVHUI-a+RZ5{gx9HX@M7>bz}CG#b`+klg^@ zQtJ@Q)(3|^MC(JWJ|yXbOCQqpLBD=&!*(@PSA9mS&sYm~uPp0>nAF&g@N_8&BQZl_ z(($Mx!`q;tu{ttBMUr%6q>6Oaku()?>BuM*(SOnqbDoN%=#W?bsh+I&eM@`9fv?XnGpW~RAGgVOl*%$UacdO zoR>o6Djk^|n+DoybYzM%2O{fqWLCn{AhupdF6lBIQf||c+1>IXvPnngD03drk-2Re zAq^B3pE1p0ce`N9MuE{G?+bs;rDl zZxDCr%_5rauaqzgi4v%_9KY6td3~LDmd+= zKClDp?^kafn*g+REEc1LP^}h92O&tgnp3b0oia(MY~mCwP^EzQb8z1IBLt0F>P-qq1<0ce=xdKBbblr&c* z!B(#7C#XU7sWmVqgViXh;;~T2eSs?FNBzL(@m)JCg#uMo43ES2^`mt6Y2_H%=ca&$w8Ynf8b+yzLu)usY0wULj8+<2X$@^0WBa@t&~Sd@ z`#}xqDiWOi}8g~2=<+e;el`W3KTdSPdTXf!vUr&HDHf_!}~5AV30wu(;W$?frzWw zNa~3xz2a0S#Z!oMF(N&t7!gwQm7TO99@G>XNm@;*L3&+gM%EdTvu=V&)D4ZRdu=jk z??)lh;~Uj&wISd|2=--m;!AL0cUi{!&#r@poG!bqk=(1Z5$WxMNS{v(Vr#`;v~e9W zsO#TGMC**gcYP`JavBk2JsJ!fNaZbdLIUgTa0gI%bHwel{ZK zP|iScy*e2A&gImPH0);<8#EO+p)uIY>^7Iep7Q@%Hs?Q8*)56=*7y!xvSruV3E%vI zqB;K`D0&?ieTAJc5698}=yA?}s-g!_8P*W!(<`8mf%X&!%+|@wuT^Y@1O6st5VL`{ zibKy{V6-&#?7eook_0gJ3M0u?X+%_iJV_?=&$>mOG_Fp$$B=N?eVu-8)wOET~@HyQc36*XW=xqb-sg6n^|ImMz zPi60*{!{2~wc3hpMk7Y#w<$) zePJj49WJ`A-A0lw_XC6U`o@eLFe4|7$XT&4U16j5>SWAoy?Pmu9_I8*^ZIbR%~-et z>5Fsl|2FpqCrq!MP=z{v--KzlOmHgLgieBBXV?k&x2V6fnF2H5CY2kmo2O{97PC;G zOIi(-)gEmpEXMZueLW;lH0M86(SZSiQ-L1}!M<|&YB?eNMWx^bfaz--;iiQYqea!S z&$2(alP-h14_Ahfq&w0?gY>%Cj4U)Fy(=Kn;;K(`M4X-YB&@amKfLP0wGe{+MVmj+ z;=1wflX2ZZKcjRmrM+{NTL}dw)U&Xlk)q2(&E=^~RM$Z(K11#{DyTDl+G` z>1Je#BlZ|fcR>VY{x6vi%FIL7P?HryTc3r0*o@1!7M)r*8`kbJ{D5FP?M~^~H~z;? z&G~8QAGZ0YJAn@%T7;6Sr=uA}`-T(>`AB^qObOYL}n(c00=yuavLnnF`Ywqye9=XW*`$d4#jgj#?c1v z@i+{_lO(w12=>PwF2ioBWRfHKZFue1m1-pEN}gws;j4@Y+!d%W5AyZ8%t)|?k2hk} zcdfN3mTM$f!!I&oEcO_R%`p;Lh^$ z^`ZJi8pNEiO$z;EB%E6I3B9F?=Q;QR!M^iSNXDLd!amFFnE|uVVAQ0-3Dx^&MEzpV zfNxDjWPfibn)yn!ASlMW@PZ)OTyNaJAh`Yp%@t3f*zu9i`8 zDUP6rTDS{Mu|}g(`&qkfIv$6^%4rN}f7b4U+Q-6U4a{3VHr#8}TdB>|Q+(C!~BDcLM(P_U#FUDDV}%TBS?W{_5$ zY8E=!?tt~u77WoAP@&OOC|*^u&}g&JR%W5CgN4RWp{f$GT3`CuWtN>_pN>_9r^EOG z*J67Q=*5YEM)=2}9WSa#S9u%_g8eq=RYEapCjlmrDlnGGyhIVG23sjzsYJU+=F=)0)o1>960UUC7 z*LNtU4y|L+p}N}3b+z$gn*nw$cf9~f8LC>#&p~@Kafj1hp>*bAc6+7Puk)Aa{5UaU zla#2zqTN<9tWf2L{;k|b4Y(-5#`{*3UNi)qOgulq#(PlY`J;o*&(C@VM0CmX;ywOIGnrh>;o1w0P zkk7zxII#!ip#OE=6rET3z_GA177(=#^TYn>!v4p=GG(!2k=j0-h}W;o%0-r=Gy$co ziFl<_pd#Tq1@L;Qz_Ns!5u~fgs)Rpd1iBV(!nTgZeb?5pxOdt*7WY4O`7?F-uYf3) zKeKPJ{0mk2)}*8jiTpAM{(0z1Yci_E;P|xlq){72{;y9C@y&~|t`XLxo*R0|r-uB( z1it40uS!hnx}mdt(1;Hg7X^)IJNc|UZ4M0A`F7h$y*7kA+vIl~@YMvRw%dj-@_8uq z+ZK8gG7~0s+|VAy%*UwSV9AajTL)e7ajW3@=dA)3B@S^U1S8$yshr);?vvOdJ}U9_ z#CCT3m?W5P9G$JG#8L6*#izyRK&OuCZNFzs{V!3SfGTGP zJ1Q|gJ}3f@9fOvR1c`P*)8QFa2S~;8I)GR#R|v&AcC!0+({^@hrR?sE1WWCr%4iiV zqic&Yz?^O^v;jB`3Js`-2Mkzq{BczK{BB%xEDhb8-u2cDRZRC*I*;;-qsucH;P*3u zq2Eo`qr08jVXD0)qt%Qq7sbzNbzwXj>1t)AcA#86=s)dYd*xjJRkqa*RKk82oB~Z%-C*D~ zz#Uz0z`ONioq!b>q)%b*Dxg-`0!lGCl~urZ^8^IQ9ugT6uk1(Zp=J*wuM9($(_v^g zeDpLNWidNSkKhO_|5`Q?N(34xO`C8_MmMa}sFsr4n19lZc^(vo9l&h3(aF$PHN(bt zsd|kuTOF1;W{|q&5IvY2aEQzvicwjY0*dR1-dWd;SG}OT9Y~Y8mXuzQ4IC7GpR>?8`YB43s zk?HCRjqk}|=^T3$?(>F?b;)du{)jqEr_0`(_ex} ziw8exa0EfX^X%*uG;TUsSFDe|IXc$WJJxk}Y`-(FuzH>KfF0w$Jx~FB8YLXY&^A?(Hr>GVGhV4)FrrU#9CF>3aeM@86mwPn9@Wo`U4$XL!NT0 z*G7&X4a}!`e99gSD&cBgMD_<$n6LxltSi>pt+i2HYkuw*%*D0iWAHf$6Mf{)ZOk%Z zCW|#RS!@WMELw+77M})Dn3;iR%rG-U!6|13o(2X4$2?C1{|KV5{`<_((gS9Ue;y?0 zjk)ZNW9_t{ZOvtGTnw9+7y$9<-Gq{?HP+zXYph;FdedyelWTWau~H^JWfz8KdW}38 z^yu7BkIoJC=vKqFmj_Ub z400&7NR)%BMIt+wk1(G){!Z6S9!RaN11g!F&8^6s^YSsA4p?0`6jadhkwrNhEF zG-pV+aX6$9&g4Fr$qn7=6s?nG#9CL}hSTyHfeyvmf+@d8k6|k-03>rpsl^Olx!8^m zsLt5XztQ<&I!0OL_C}8IZ*Rcp!rNC2o#?3KIKoXAW)wme$6Fyjp33&}(YB^4^~MKU ztOF17wZTgY4-cO8pmll8$O8b7?5Kdad-C)Yrd!zfg^9oCKUlYw=ATw@cZMOtl7o}S zkXU{= zTat<3D^AV`s0y`;(GlJw-R=G~83f z5U&4m+PZ*DXvgBTHv%%D4&^jwpeuyx;j~EsnNU5Pc1u7e)SI04PCzEKEKb8m+L7wc zX)^;dp?WxNPC#a9NEhccqnE6a<27N*3uQSEM4Q^=l?Qy z^V79VcxH?t!WiS^%SbGoshn1GYE_*4&w#3m5D#d#q$*t%fA)Hm5mOwHQD#*!M z0ac+^aoWUFtK#IEfU3}@=d}7$tK#HW0;)o-;jHqJ|u?ruu_v9iPPGVOaxzX za!x>1XazZK%BfXx@-+cfp%vt`n@+8YlXnGFg<2(V^TIiSSN(if7fwdI@b`V5q?rW2 z;}b);PReO(12Um*;IzE~nb7)iTDw#97f$1!1_@^mr(JgnJ)HJVKqj<9aazYyv6$XR@wA7#ak z2&0Sd{Q0^Y*)}N|-Zm)}*0wa-f8#SBRGV9n(wWnY7o z91s7^#5tiR&IvU!@C=6ceZgg+Ce8^pG4NO>Xky3)N~?g4>_OfPcUU7gO8byW1CdE5 zBICzu>@sB1LHNZ+I>^$&o3d~~vTQ)IXh5=LKwJzTVbd^BG&A$a=2TL*inid7Fg9|G zU1VD`*~nL^lW9SY1-sr%HvI%`@@hej#ZZ?6jeOdjkQU@v-=H9$4s9XGv9UoxK80_+ zP(f?tSa2-NWMAIaLXcy@?l+S?Jd1{f*2pn#aj1;xQ#_|_qyT0=vqy11i(UF9xfB_D z&q0%9AvT-WU`oWb2ryrmUHUqC|H-@ zGaYf=+~iZPi)~GjRRJVx0!UT_5bNY4)q|@8m>=fyQ(LGJkKC!jC+#z66N8@!fD}Iz zFdnP(qkzG|$71{d;JYB=_GryZ7i$i0dKwSR`SNEx9p_4jqfwcAXnQwUnYp2rnHyS} zxuKPr8(NvUp_Q2%TA8__m6;n_nUGo8sD-MZtO|}uu1j&8b^$UzznmCsVsrggQKO+4 zp9dQ-PgW4cr0vM0@yJ9IsJTsA44e6EFn;SdhA{j4fj3691blL)gER;7vk8s|yJbGt zG+3+r(3Z@X6}7GW(3Z>(ZOMG;4ag{+0VrU&d15qYE2@0Z?%APs&knVFcBtL6L+zd& zYWM6=yJv^mJ=-)F&*^d>z_LBD-MMYJ*CFF`?tu;~bt6}hyB*5$IX4!{$tt3pv>ur> zA6dXqI)Sn8kTx5AkWCNM2USN4)~UVr$*4=0d2vYQ#l78!#y?n zJr8a`<~Hh6pV6_s6?*H(o|vp#N9ixyM&kTz!9^Z1*8CAp{ccAmJ*4 zgoK+xBXSW0MDEleN(jjTA|Z*%Nw`P>1752>V7*opygj`r6|Fv1w3W8@d9X^gH?{R? z#lBR1tZl8AR&CXHt(o=PXR`J=N`2cu-p@0TvuD1uX3d&4vu9t-p+Jn5BP$sGedAEczQ9mQ^j!-752YJ5;WuksY-mgNLs2=3?mD7N9x0{eRAe4#f zK_2G6bbi+Ra;EgHGTwh1j{9iakXIb`ebkT0s|sbJdXRTlC==~hqt@lWswT~0iACfIe z;qkO5Q%IX0O%ImG22+h$9&OC>Xk(V+fyz{4mPZ@2JldG$(Z(!~HfDLWF_E)&dnct? z{ERJ~5*!xvTG$)&=gaAM%G$_7Uv%Ce?uj~J$!<~gbf|I(qR)hAF?=#kYNOMgs85#Y zPM~{2S-KlA{NZ@i4}L5R3ZbG&k(0ee5P!DuQ|2{i3I}bOmB@u*Sc~@q(=TiZ8uHiY3yDKd$EmqOLfT@ z*{%Pv%qaSi7l#_zraKVqGa7d>oUuH_onCaTCrQETG93V-lIhQjw7xWz#=Lt}rCHvTJrVn3_z?+y znZWlHK3ghHgx4Ayt~& z0I50~Beg6*Es3y}1&E^JEU$TMwB`lTnj<%esph!*Ma6tMA=w<>r(1%kc}oPz)17)m zzb8Mw;9ra_*@M(Cg_0nmFLe+^ieLZ6fdmod7fV^R1ksyA6!|*9%Gys@emJyzF`^#| z%JSBGi1!e!_Q!5Wq0X4M7|~rK$b}JyNw%ywIFKMuF7;) zEr{$Eu4RFA$E>iY=A-joOd&+kiN4ozMGOh_VVt{u@D(wnVKbc*YrE3iIb)-)lio2e zTc zQeqdU5kodwMcUR!#Bd|ItMtQ!RDYn0o=&09?{(K7h^A5Jiu(7>3-JQ(}l>pJg$tH}#nnhnq38 z(Ug?Rwz1-1TqYxb~$hH8f`6)p}YXte;rmcIB_@54?ks6V*4bck)*=ms5 zk`hETT*b#bPjnx{292TP9inInSGaf&Qn!jS5=0b6TS27w3k?n=h-e?D35)k2g$9x! zqG+Afh*YX!i>>O^XO^kSsOQS~^@9kgoYH6+bsm~CQN#!#x->-RA&SnTo{R0dP4XX- zt?zA&?Lp$lVd15S!XZWuiGt8OOAv)W=x7j2pc%qxYabys!n~<1RaZIQC&RyN`M>rE{OIcGwnz|H`ncEIW!mzV+PY<95d95Eluf8 zr!q}v5iDa3h@&4}i_+B&m!zxRS|M%>Z@z31<6>Urmn6DBg$_s*ULYy-dN0vrzQ`*d zl3hM5yR@IZe%pgQIEv1jU@iKaG{IFcnvhf%Zce6{?Y``gaRgT)9Uvw2`BbqSMc2qy z%j*Hbm|ia;k8n(Nwzrjr6g_aUoHQhwA;QPh899n4_@w+ zE2m=l1{u%RY!G+zixxTH$F{xX9_`HDmg7Bpi}09x1^yW|{h3w-5DilX3Wu){p1qP@mSX zpT5gvFZAefs`X+2#J6eiQhmU49 zwWWDg1^Ux`x&bqsvFvHPqC%)+Rh7Dv8l6>*&>d zF6odk*`xUlbuFGJ?*ODm(tCnR)^KB6lC`yoRTL)^j1)!bgs8Qlj&dkY=NGFJ$;PH+ zRZU}a!e-K9nyod-su{E9Oea7`+Ek1R)iv5;rZUt)i#LvHrH=-yp@yrnV%04ziRPq@ zQ)zO5jeEhFUXDFABQyQou~-lkQYfNV!cjDSK5Z*#dzYmB<12&il#^W%6!;D8LCl{} z8RW$MMLP?F|n^8Ah(v#q#!Sz|29P`DH5aY zb-_SK18e?8UWWgQozO6-OzOa_G<&nBbe7laMnX&Oqc ztO&BX31m;9*qY;ak5N1Qm9d~}MbO1Rp`s+nVHMt%3Hr--k~!4AilFO#{298H;GDSs zjjRLItwj{=666dH<}3+v&M=?L$gJ(s;$_BDLA=$d>asM*yO(lX3nlx4pff28;(LNv zyl^L1x`}#=x-VxGb!-9U4U4a%T8#Lxb4l_X8WA*7lEDewpvs^RZ<4`C+GJH4_hi{o z5sZF}l%5_00}cdJ?zGJhj=wO-E+IviiQBU0kl&I)f7Ww*(A%gxE-UE43gi9@ zn=5JAF^Y0$W);SdlEHr0?Bd{rSkSpJI3XTuKQ0!G^l$Jf9e-hI(1Wb&e11@LP0;!N zphtO7R6)G8dyw;;pc`fJc(r5f0@J*lcq>=t|Ho$DM}k5U3F2qRv)?AIRMQXqn19%2 zb@pelNKx2+YCE-?+HBf8+NclW`-7smf7DL~fxmlmrKzpAziAaEb7lv5e%tM&e;=u) zVR?5@^gBkg39awWJ1Es2^N;v3GKngGeDmA1AH*uilmh=CH88eeRk6)FusO+Dvq|ic zpcs)N5+?GT$)ds0n-Z4?{i!8|#xH|r2Pagr2fK^=*jW)sKIPUI_}8;fxj+2e+o{?- z|HqrNpD&@TQ{w*j{X;=eM6rS(JG;oZl}8IT#QG=I!^i)M=uy+zxqj}>ptJv-E$!rv zm%U1g-?25Blk%y+~39yix*Pwb**GW-`|pbDCkR#=o){A?8&2n z*M}5ewUw>mQdHhSKeAKn<``>)u>7n%9A6M3aI=yV#%(Ol}^XbKjxr(XABL7{*1 zR`&2FI}iFBXWt(5`7jtojW6Xfkk3QX&PSc&bIr&spjjmTfT=pyZ>_kGt^Of(ustF? zfl(dApOyXc1=9(gX=><6<`!`;oz9xF&7tAitu*=P`Og@eDXULV#BS@ffNXnmO9iDK zCOc>z;$dAxQ>_0z+q_;dxqVe;lP^nW@=)b1XT%}yg8%Sig@x3;1wo(nLDzGFzW$~I zL7z6tT3r|npkdIbnc{5}y{*W5D*b+#4*V{35c|{ScB;wGrIAUKJ{@Y4wB5&3 zUS)Rjc~)5#qv~l~?5CZP#swc~sX?9TP(0)d6raQ8f;{g38;xludbf-dKW7z-)uH+So^0dP1R0HBegqbetPYpncSXZul>`Nrma1llP*X(X{mM6 zY}5S8pkI5yZjJbVURWYt5zm%FQnMU~`9&aOKd;q7;E$hWyIGc7D|BP5L zJWeM9lzrD08ILc~l*jW_4$YZnG|>p46Sts{^RxY-oU@BG@PPL7cN$*}s0@1YlxIdF zpV^G%Dmb#q43}JgCQp*l@pUwP;~c-Gx;LHQ!e09a2G=n<*irrIHl#H+-B=7Yp51Xz zhz#0L@N|sD17??r2>#<$AG4oMx5Em10*x%*51*$_zjJ#bO*maCmOaUw;L|A|9jQuq z6kV}3<0vwx{}@F#(xk`3$jos(PHv=mswn98g&?mj=vx_dn@NnXR9NgrjJD2v@(~LL z>P?T%_wXvX(j`TEf@jQ7A*-gX#@E5bYnM!-^#S#APdt5FWkVjXR!BSY89_$n4D<#U-&Y12^ud3!9khw}Iiamrfk|KWT^`%=1T zwZmY-M4e918BCu(t98(528bP=kvYoInA(NXRqbf}^r}-D;|Jm|7|->~PZ>O1^Q}cPeVs<=xpnKq}FhgS_4H)WoIw^=hALR9-NXGrD zr++gIyY6Kqv%=Jx>p#7PcN&a^v5~Pr$89=t%5gi>;rP@0B<1R8TQg~t&ZDZ+^>RvM zhaQom+cE3xAQbz{haJ>Z&PA&jBnof4`;=)3u$(tv$_^a?=u(HSE&o@qjaT|=kGC-mNUE@ z_1%J=bpB4G>x@Gr`9E7??d>$g{9n?X6JFvKyx_#oOT}||Dy91?l{EL$rGC89UM|!1 z7me%+dnNrguCH(0e1NZ%shuljTg%tdl)jMDmFAkxZ%kE7lP)#$M!IUEljPrQ&KefW z?v_29b~gW^7+aZh$?e(C)8*dbEwNSRMg`qaGN;!&%|+o4>B^RW~OZl63REwytGiqPjNGJfk_0 zpmbCHMwd>bJLbv6xpm1kGg=#Jl68#@EnZcXQ*2QpIlZ~LvDvF?S<~2@@mdmz zwR02e>6?axDYCw~E}1ZeS{qvGRyQQf3z)pBRb+Z|qNS>yz8|2JDO{zrY5e;xTLyf zZEI82%IX%Xr*YM)mIO6t)}s2x^$GIXsydQuOm)uU>Xx;}CbH1BW>K=au3@!T!_A~} z8*1xT*Rh6q)M3@NRxf`U(%`M!n56Gs*l*;2He|v$*0QEKN$+J_*VvX=+&DL}A!)0q zZK@rQA-961U81NG4XZ2gwnp z>#T!kEvjiqHrI3K)$ur-waEI5R43LoC22RPtKGo3rK!4x{De`))vv4GV4PglR9!pX z_|bYq>>4}9t7=PDudGj?8@U6x6&d!PdGpR;JF1(h_sqbYOKv=C?vmnZ6DJmzmy9hb zD;`r?HnDVk=_$p-7t*+&S)H_e)VPz1N3Bb?HY848ooGlj*VRz6y18b}>GXBos0rhW zN3Ev+t7}&lkD52OcocR2={2Nt)T;Tjrg_bcjpWaUChDA|88S`0lN(2w5o7kxS&K}* zXZIVkm-BniFdBI7I4hA{Xl4%Me$E`drg2?j^lBRG$M`k+1&Ti&Bqu2IBSQq=es%rki>5CUHt(vi9?zF|T=FP3D!qhgkwSKLc z+N!vHGT)gu?4@!@EzJzn-qxljYPfNYolI#o)9WzFuKLz>4Ia&O{N}>8>iSlmJ;+AW zMKnm-Os7?~Hng@RYN-&lm-Z4n??|1-2=A3dl$LIGtGrwR;e)eoy z34qtJ#PTY$SawE!!S<}SALP^X0plpSff)t!j%1@}iq%uf4{OVvze(7cLDf#8wJDrt(B<3F?W#!ZI zOQ+-yCmE8ZGE?)j&z8jsL|b%2E}$>E|NKcbhaWfP+-UI4!=dST^kG4A1AXnD(t}49 zxwIcyhsNskBkQN}@ABwL0^$d)i0Lk4+3X+GkJyJ$;3CnZEou`!4T7@0*le@I{SMjN zOeF9I0?77Kv(1Uf+edM8yva&_m&o(J<%XL~B8T2>)G^pczH_|dj@Y})q;tF#iho^r zf#UZISJ<;pj|bieok3~elZOSJyhkbCjYc%jSsbk2J?}+|>pD6Aj~V!T8F*LHn@-Qi zB+vEA!^GhmwevH`_aM&l`0<;W_ejUE4DzKJ_$1M@SEdv*50ajR8RVB`;B^_eIn>g1 zg>zCFJ@S=|9Y28%L1{C_m8Z`bH*um@GG|&Tf0XgZc>Wm6A7l8VoIfV;$2k5dGqB9$ zmsw&m%S_oaQ@G41Gipz95BX`YtInD;Z|ameRr6-dSTucc)#52r=S;7{^-AijR9&*+ zyhNW*5SP>Tsw(%4#gPk7+ft{19WKU5g}qpz&ZBTQ^#=#;Rv!C*|HOtz9L{q$_+K1; zvct#86I86H#Nj{6z@K%vt3O81fpK6xrSyk-hY9Cm{KzHvMUK2%Z>x+`$UjbHI9%dz zo(rMp3WxLT41SGp-jq4~BS)U?!M=KfOyOKFpNl}xSA=u1u@3*a!(IF7fhQASKl_@) z{lZxfpU*)5vyMDJ0t@~dN8YV>i99FEdd55QTZNbe2NAA-*NQ2{vXUj6kz9WoAgFI+|@t9;e2d_{?mlB{jUDij(mSd zzR{6){rQN)-F`ep4#ceA<;#S#{UgvA;<4(ab^bj*JYl{v&BOB zp9{zQ0r_H4jO!$D{9b_P77pN>*%=gMIE4Ib_#KAL4*79n;1IKeE;&~#Ec?k95 z`E8!7Ie>4Fd57mH4&eBmLb=5upDgoWh2m!lpR72&GuebQ6`vz~rsD9!9L1Aj=X}L0 zMbBcz&lmYh#aD>@`HI&HU!nLK;kAm_3tyvnlkj@Qlfs)6$I8}8#W#!m2F14t->mo! z;oB6yLii5FuNHoV;!WbWs};v{?mHFVA>;6R#oLA7toU`pcPkD(vKyEy$9$3DO`gR0 zin5UY?w0+rtKtbs<4OSXD~_`na@gcEb2Fwz+&f7kEH6I^Focf<9H8froL6D|ga1zS z&$i;B{JFvxDE=bvXcRC$p=X)&OO?$I^?XnGD#f3W^g6{K7Wrny9~FDHDLzs7rHWr7 z?cJq#o#?+!@#CdkUswEYk$+h6nbP4;D_$#lUQoPR?0Hl1`^ElG6#ue}6Ml(22lzQx zcu&RWN_w#37{{k8-dpTHQ}M4$`fSBd5wTuV+-Wly(D0c2q@-K`2`xL)J?0i)5 zTVW@5dE{H9QXxd=NA+oBJwy7g*@Nm;ILK6!_N;X4m%Gjj@6rSJ8-@`OL1|VH`n2O zK8$g4p2JxW&cl`~j`O+I!eI}__eG9;SJHz;INBY~`d^TKzf5r)zrW;gSI_khXFX?1 zf8DJ32;p}qUM2l%x<#4uVzQ`X|yj=KC9q#IR$>FRA zet%i@npO!nE^?#KsqEMmuUBa;zFzorg@L5X!HQ{p|?$&#*!@1s}xu#lgnc@S5*D8L2 zaI85Dd+-K^Hj#%PuwKSCM-SVxU;J>j!`YrIMgLC4FBN`^;y)37o8pIs-|29--g_O+ z^>&qs<9@|E34cWKD}_I<_@%<15)MCL{g0nH@@&sr;)kOSXM3I!J+CYNgz!H&+|~0J zhqInq`D*w>#mj~JGJesIhYF|=g-(ix>&!Zw>&5vQ_5;@A@==aF+f&SwE`>71-<6vO zrz+mgH#;d{|AU>YgwGOr_!;YaEXu%fp9Xra?q&7kJ{dUPFZ;y|dM?kv_Xvlc2Soo} zir+8%KE+=WzF+ZQ3V&4bnB188j^dw){P%@x|Gc2&hl!q-6dxk|h|=@8d>`hC z#~FBEb_@mhXO8F}p!jUz!xe86ezM|8;o}wGEqtQlUlBe@IQ#?q7b*GgiTt^WKOy{l zrRTJMww;ZNUgeyr2+6~$kbdU1Te0QJVN&O?)9P4+T>~L-u>>TfK_U9|ozY`Vz ziSS9n;fHU@e0sLxc$?>1hx4hnnD1Tea6Z+*etN6JSzZpO-n|ZIdAzmrzZ}l;1L=>$ z5r?xp*3o)HIL6gZ>8}qR&VIOU0BiBQj}_k~ypx=#pnpdd8*wjJ@u9+t6u&`uFU7AB z-rwQeU$AqS!`aRcM1F+ge-=JkIQk3s1tuvD|15L3bS>55c^5gH{fTwNE*1`d9ua?D z?QphpwRFr*#g_=b!QtFq=(*kDtY_RH`k-*9;)8_WtN16v?^pa!!XHumfx%Y)`guf>o{fqU`K5^vPpL^vxy1RS{ zhjBZ8nAP7~@iO5jINa59qQkjgE)w~Ximw$uPVpy%mn;65@M#V=<3a3Sl7TOGINQ_z zMBA>FioYlN*DBt9gq6oTK;WN`PO^NH$ivTA$8JXkeuvWk!bq#<9>sql9PbrDy|I(6 z{DVsV6On&R@e@V<3B`vBe^R)%|CdUBj>!L7@!7(EtMuF>#~r-&2KuqC-zQ2QydQTG z1+=$W^cO4MB>W`BcMBh-_*aDE{VLG^ec`7n`GdmG5U%ZCtmI!4`AWrqC;S4X=SOnf ztXKSaHjKhmisQ|qcPoxJi5^hAvs^zvDV){v{xUhv+CA?V4(Dk9Q3iXzR6JMsZyfIG zdClP*eM98&<{|j`Md9x%`Fl%jz3(f2kMNjW-$T#G!f6dN6TsgUUML*>g>?)2JMw1U z5I>YSoK3iBwADLC@pZyaQ~Y7!XDI%F@G~9m)_ab_yHJ&X68Qy+|6cfcil10&?Od+- zP~r8$wIA9XdA6ri?k8UEaJFZW==qZ3^MvnoxU1(DhqIn9i~MbhUn%_G70)fR?ZW*V z^lvBO-*)7=-X^jCsSNz54rhCoik{~cpCbHa#ZTdf3Mu>_#d`~XQ#kyLw+Q_u1MkL0 zQ$V{C{IDm59*Wlp?<-vEpP)F_4?fG`Jj6#B?Vh(-alzhdhqEj9N(QdaP%qXM-Qmdd zd0?r??{c{8S|;aihr9Y863%|$=t1enA35?_#Ia82&ol5pJ9@Z(uM)q#r}!1ZKUVx9 z;aPIsiGF!dc&_5V65d7e7lq@UgYX;dKS{}dBJ!gY|48^4;n33{*B4V1A1~>pisKE7 z7b=dIF>X^F>v3O|fqzvv{4;a{Ih(@0iVqh4km6?xe?;+F!oMRN?S(x*Rq{7w0g`PPMXFc&#=!3$1#s4mP&J_-SE|vRl8ytC#w#)hR4keG@r`+Sn zbG?^IyE|IPJgfNg!hfdtkA%PIaE`uX)p$o8&UV&Mv>C4} zzC!pP9L`ZZ_y1Sn==XzGjdxs;jliFHJ|DF;SQH6%BU)H zIO`dEs;yzX;-iI65)M1@Jixh1ex1lKQ+%!PgwiuyuHUyP`Rhf#UGeLLU#;{^7CrYU z`Nu^5>xw@r{98&-rRX`Nlyds1C8IC;b z$Go@D;jI5I(enkx?+{+4_+jBSia#y9Uh#K?H!1#S;jIp5JCD#Ghb<0gJA0jOu-C45 z58-$Z0sP$RTlt$r9{k5V*;BY%aXhc~fZ}+K+;*!&7 z`qLkW^-3PU)4auzXL}Axy|*d;i17OqKXH;#@9kH7fbfTf!w+~4^=FDd&H@yUDE^`F zKRWukT^Gqo?qGQ!2mZ%#WR!3&&V3T@M@s%9IsRU+_#0G)!ySspW!`&S@z+I9k6yMM z?CCCiu;R~%o-)NdOM99N+iC4_Ax-zpCVKnPTEs`v2yE!u_t?cc`=fH%o}i|=*8KM_C7Q}UZcezoGevaH@M ziqDjBb(P|HUi?PI@utnY6z?wj4=O%R?0iOX)cZTdJf4C7 zO7RWS-rr>4uPWYG#t*(ng`MDUEBTS)|G#A5_`N6e1R{^;1vJl<10wi`;?FJ__yHEA zz?;tWS19*&-RBlu{-iVCGCw5!>duqQUlsm0Xa8kBNZRS{7tE&%e?z`!fIS<8Un=*x z!FLPaF`5cf0M99~>D?LlZR2e|idS)n*8*{nFOqaRXyuk06INz2A{w2BpNlg)O!2C?QueRWBaR&w%6%%VRf zD`^3ylIBKEuy`3Q>QrKcEbEv@3n_?bO$#j*Cz7>^m949*>Kaxxl2Q||Ub(V4(bgfG z*KV_lMZ~O7Ky}+BQl@H)%<^%PO_q5jHI3_dML4gdp)r{#p;d@zRV-ew%&JYSp_RU> z*CoU${|}pDD$9L|aR615C8FQQBf}439`6R+6_Z0^Bdl{OWZb1DrT}W}%kL`3}?wIEyJB#_#^PaPt z@KgJ5GUcT6U%_Lx$U$is`;Yrb`|liL>Fi%I(3YQPH{mDB!~XN7yxhhM<@QVYZ6UD{ z*pF=^Vd!#2O9$ChFPWE-r)4jsjCAdP?z^_caGB4z{XD0^ewn7di-~ESTymzIXFiO1 zf*}PhduB5wl)8s2knQK=fVTg3V%jDyIsbD0Ox;JZ zsi^ENl##CfUhmlwrBaaV??QjN{(Z#K)xS;ZpNs;cN6Q|}Q2yY**#hs%b(R#5)WyR* zo&7V#e)Kcj&tnwkfImZwZ9+RKYQjTbwc9VMi4FE+`vQrjD_{0aTYi+?gr6vf?GegB zJQSkHaw+dJj%!(NCNn*MtU!5H0PSa89Nwb8bpBf*{@Y-rJdgdyU4ioOU%I-v$cp>< zlg(o*Mh0xdLIJh8l-7>5N3D2{#E&=O7*du#t#nz>LAmeJQGjzVaC~U zFZLWyd44wES@DiLF5s0J{cO|K*u+dv`?L@*OCD!8F|#-MSw@oH%8|{dYrdtOOx&t8 zWn-sxYU9EbV!Pj^&m2CpiJjJ;;|M&bd$EdKNOCflKR8s8wXf1PFSf*gC$=QD?49|Q zZajNC<=Lvnc1m&q3bEar$OAdNdSz^EFHp|OH`!x{$dgB$GKUl|HZIqd3#;ef{MM5) zsXi~Zn5*wDVzj(ZP0}%o%!&Fr8*7sc#~ZcJEJ#B zpV)g=;gX({d)+j(cZ~9K0vgKDo7MB%Q>m`w$2oNg%e}HaMrM^2a-qDd+GAc;uU%7V zUhGU69%qmnNmpp2lF-mn=PN!*58kDXim;I-^{C`6m z)A%%*9h6sh$8)?_c5;)co|Xw(aop+GBgqtz8G3mY<|R z2VGj$oZbyf)r9YfwQ>6bTkb1*p=XZmGfMb__~jJzb}o9=+}ZT1f~DsDTe0hdljARP z#>tV45jJB4zrUbEi9t4Fkj|hp2ztp!j$7%_SiCacC-q0>#9|$nO6SGxX|3rsb-Y%* zSs_2Vczvowhjpi^3^}5TUlp@F-!I?*&T&8d zhMZHW@XuS@+MK$k6idtxS0wG2^^dP%-MyoNP(PjSsW6w)W^H2OX6#VBlK#RoV#(vp zf(*PN1Lv{F`ip7H0s7ZdJe~e6#94kaZ8<>x5{jpjzmho1ub?dl$n(CCPCh(pgi|El zhOVzIDNW6g7L++tuq9)d*vZ0zu?%F|s7kwLu$_NWb1%g(tB1Ob+2Q!(3tWDT0Bc?j zwb|yvm+x3V z<|Fun0`zbh4&`udVFu21N7JwJ&P?7a6Wb7u*_~kdEDArsd$UDt4{GE z;Z2HH3*V}EtE9iAIKE5RsrW2O-|BGo4dfnhIN!iQzdo$EB)tO;=l;D}(m!-K>sc!4 zrxafz=^x9Mo5^DretFH|0mb<^%HeNz6Z#qBCN6&Be&Kel5V?L1XM1iCUaUC!eW=5^ zU68|fkF1~dydiq<3<3C2;aD?{IqSJXxVr`+>%m%uGo5;wqx?C-dBgreKdy5)`v?1g zyG9__o1i}qmpS^`Kc$9x9@e~rosGhGJMwJLMoI5g9OL;Ohr52;?{L=hV@W@x_#sI@ zsrW!{GzH|t&se+fu*ma% zUncTR893I+gPye_|3xJaewpIfudd3#zpOacuDmG&$NU8Utf2OBK;MC{vDkbE!BNcz zNH%6X)jQ#5r#W62oo`W9DW7Up4HwF6Z9q#Jq^D+*shdIs+F>gWtWXj`k zo85%%!u7}3GVuM1pDStk$}_B&`o?5P(1UsS zBsPKqIP_!QgC4ZE!jWf&c@Og|^cbb|k#OWWnk^Za#~=@mwN$}-iah2k*dy4x*3s|! z0rNNXz|Kb;d5%K=4;+rRave`A&ga)0Fu!qsqtBW19L@pt+El9lwf)G~^6&>Z>~!_J zcJ>lQ(2u-QhjUYkC5`iK=tsX#mVB*ers8Px3Wu})$&y~9__>m9ayZ)vJsT9qys^{a ztbc{1aXt(C*9+e(Ti6dhUsD{%g@X=f{RoF7qV+%LaQK+fi;6@4l@53Hf9!BqPqA>8 zb>-(f+?B6&ILjAHn!j-6z>MWl2L6Qp$&|+d{((ID<#C&B^l;t^N&np8tQ&s*9M9b0 zNxYKQhWfg-33`94`+VHzeVopDfbRdNXX-GH#l7Ktf;8W=;5leAfNZ}e9dJ<6J!#8h zH(S!ZWIIf_FKKMPjfmQ^4mX@k3D)^}%TJ)PtP}GmMNL5c*l)mf{biJquKjBYY>5?8 zklWAWQn!CLF|Ct}Jk1{z_VOTif|i|5dFkrMT8SH^VO&2y7oqF_0x_+VkhfMGitA)% z9JFj*8pd)Lu~8JThOMxd?_%m4<1+Jg3&vgCQ5Ysvdc{fS@Z*js2TJ{RcOIQCIssD%+;QIO8QrEwinASuG5o4XD^ydbwDT#LnAP@Cp`)$fdSH4Vc z4t~}%Z3m@1^kci+y}1aOM)nuN)Nbajkn%^IvK&SGfuEyDI{$4D{~Z)T_8;GSLV3)8 z+;&|z7n#h1jly%1$?`lG>GFIYspmdIUV+TNU*?1p;CCRtznZRm`2Z{NG6ZF-%l}_A Cl30!a literal 0 HcmV?d00001 diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command.h new file mode 100644 index 00000000..3a4b24c5 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command.h @@ -0,0 +1,2233 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_H + +#include +#include +#include +#include +#include "connection.h" +#include "command_options.h" +#include "command_args.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +namespace cmd { + +// CONNECTION command. +inline void auth(Connection &connection, const StringView &password) { + connection.send("AUTH %b", password.data(), password.size()); +} + +inline void echo(Connection &connection, const StringView &msg) { + connection.send("ECHO %b", msg.data(), msg.size()); +} + +inline void ping(Connection &connection) { + connection.send("PING"); +} + +inline void quit(Connection &connection) { + connection.send("QUIT"); +} + +inline void ping(Connection &connection, const StringView &msg) { + // If *msg* is empty, Redis returns am empty reply of REDIS_REPLY_STRING type. + connection.send("PING %b", msg.data(), msg.size()); +} + +inline void select(Connection &connection, long long idx) { + connection.send("SELECT %lld", idx); +} + +inline void swapdb(Connection &connection, long long idx1, long long idx2) { + connection.send("SWAPDB %lld %lld", idx1, idx2); +} + +// SERVER commands. + +inline void bgrewriteaof(Connection &connection) { + connection.send("BGREWRITEAOF"); +} + +inline void bgsave(Connection &connection) { + connection.send("BGSAVE"); +} + +inline void dbsize(Connection &connection) { + connection.send("DBSIZE"); +} + +inline void flushall(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHALL ASYNC"); + } else { + connection.send("FLUSHALL"); + } +} + +inline void flushdb(Connection &connection, bool async) { + if (async) { + connection.send("FLUSHDB ASYNC"); + } else { + connection.send("FLUSHDB"); + } +} + +inline void info(Connection &connection) { + connection.send("INFO"); +} + +inline void info(Connection &connection, const StringView §ion) { + connection.send("INFO %b", section.data(), section.size()); +} + +inline void lastsave(Connection &connection) { + connection.send("LASTSAVE"); +} + +inline void save(Connection &connection) { + connection.send("SAVE"); +} + +// KEY commands. + +inline void del(Connection &connection, const StringView &key) { + connection.send("DEL %b", key.data(), key.size()); +} + +template +inline void del_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "DEL" << std::make_pair(first, last); + + connection.send(args); +} + +inline void dump(Connection &connection, const StringView &key) { + connection.send("DUMP %b", key.data(), key.size()); +} + +inline void exists(Connection &connection, const StringView &key) { + connection.send("EXISTS %b", key.data(), key.size()); +} + +template +inline void exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void expire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("EXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void expireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("EXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void keys(Connection &connection, const StringView &pattern) { + connection.send("KEYS %b", pattern.data(), pattern.size()); +} + +inline void move(Connection &connection, const StringView &key, long long db) { + connection.send("MOVE %b %lld", + key.data(), key.size(), + db); +} + +inline void persist(Connection &connection, const StringView &key) { + connection.send("PERSIST %b", key.data(), key.size()); +} + +inline void pexpire(Connection &connection, + const StringView &key, + long long timeout) { + connection.send("PEXPIRE %b %lld", + key.data(), key.size(), + timeout); +} + +inline void pexpireat(Connection &connection, + const StringView &key, + long long timestamp) { + connection.send("PEXPIREAT %b %lld", + key.data(), key.size(), + timestamp); +} + +inline void pttl(Connection &connection, const StringView &key) { + connection.send("PTTL %b", key.data(), key.size()); +} + +inline void randomkey(Connection &connection) { + connection.send("RANDOMKEY"); +} + +inline void rename(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAME %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +inline void renamenx(Connection &connection, + const StringView &key, + const StringView &newkey) { + connection.send("RENAMENX %b %b", + key.data(), key.size(), + newkey.data(), newkey.size()); +} + +void restore(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + bool replace); + +inline void scan(Connection &connection, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SCAN %lld MATCH %b COUNT %lld", + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void touch(Connection &connection, const StringView &key) { + connection.send("TOUCH %b", key.data(), key.size()); +} + +template +inline void touch_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "TOUCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void ttl(Connection &connection, const StringView &key) { + connection.send("TTL %b", key.data(), key.size()); +} + +inline void type(Connection &connection, const StringView &key) { + connection.send("TYPE %b", key.data(), key.size()); +} + +inline void unlink(Connection &connection, const StringView &key) { + connection.send("UNLINK %b", key.data(), key.size()); +} + +template +inline void unlink_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "UNLINK" << std::make_pair(first, last); + + connection.send(args); +} + +inline void wait(Connection &connection, long long numslave, long long timeout) { + connection.send("WAIT %lld %lld", numslave, timeout); +} + +// STRING commands. + +inline void append(Connection &connection, const StringView &key, const StringView &str) { + connection.send("APPEND %b %b", + key.data(), key.size(), + str.data(), str.size()); +} + +inline void bitcount(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("BITCOUNT %b %lld %lld", + key.data(), key.size(), + start, end); +} + +void bitop(Connection &connection, + BitOp op, + const StringView &destination, + const StringView &key); + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last); + +inline void bitpos(Connection &connection, + const StringView &key, + long long bit, + long long start, + long long end) { + connection.send("BITPOS %b %lld %lld %lld", + key.data(), key.size(), + bit, + start, + end); +} + +inline void decr(Connection &connection, const StringView &key) { + connection.send("DECR %b", key.data(), key.size()); +} + +inline void decrby(Connection &connection, const StringView &key, long long decrement) { + connection.send("DECRBY %b %lld", + key.data(), key.size(), + decrement); +} + +inline void get(Connection &connection, const StringView &key) { + connection.send("GET %b", + key.data(), key.size()); +} + +inline void getbit(Connection &connection, const StringView &key, long long offset) { + connection.send("GETBIT %b %lld", + key.data(), key.size(), + offset); +} + +inline void getrange(Connection &connection, + const StringView &key, + long long start, + long long end) { + connection.send("GETRANGE %b %lld %lld", + key.data(), key.size(), + start, + end); +} + +inline void getset(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("GETSET %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void incr(Connection &connection, const StringView &key) { + connection.send("INCR %b", key.data(), key.size()); +} + +inline void incrby(Connection &connection, const StringView &key, long long increment) { + connection.send("INCRBY %b %lld", + key.data(), key.size(), + increment); +} + +inline void incrbyfloat(Connection &connection, const StringView &key, double increment) { + connection.send("INCRBYFLOAT %b %f", + key.data(), key.size(), + increment); +} + +template +inline void mget(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MGET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void mset(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSET" << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void msetnx(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "MSETNX" << std::make_pair(first, last); + + connection.send(args); +} + +inline void psetex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("PSETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +void set(Connection &connection, + const StringView &key, + const StringView &val, + long long ttl, + UpdateType type); + +inline void setex(Connection &connection, + const StringView &key, + long long ttl, + const StringView &val) { + connection.send("SETEX %b %lld %b", + key.data(), key.size(), + ttl, + val.data(), val.size()); +} + +inline void setnx(Connection &connection, + const StringView &key, + const StringView &val) { + connection.send("SETNX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void setrange(Connection &connection, + const StringView &key, + long long offset, + const StringView &val) { + connection.send("SETRANGE %b %lld %b", + key.data(), key.size(), + offset, + val.data(), val.size()); +} + +inline void strlen(Connection &connection, const StringView &key) { + connection.send("STRLEN %b", key.data(), key.size()); +} + +// LIST commands. + +inline void blpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BLPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void blpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BLPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpop(Connection &connection, const StringView &key, long long timeout) { + connection.send("BRPOP %b %lld", + key.data(), key.size(), + timeout); +} + +template +inline void brpop_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BRPOP" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void brpoplpush(Connection &connection, + const StringView &source, + const StringView &destination, + long long timeout) { + connection.send("BRPOPLPUSH %b %b %lld", + source.data(), source.size(), + destination.data(), destination.size(), + timeout); +} + +inline void lindex(Connection &connection, const StringView &key, long long index) { + connection.send("LINDEX %b %lld", + key.data(), key.size(), + index); +} + +void linsert(Connection &connection, + const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + +inline void llen(Connection &connection, + const StringView &key) { + connection.send("LLEN %b", key.data(), key.size()); +} + +inline void lpop(Connection &connection, const StringView &key) { + connection.send("LPOP %b", + key.data(), key.size()); +} + +inline void lpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void lpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "LPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void lpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("LPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +inline void lrange(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void lrem(Connection &connection, + const StringView &key, + long long count, + const StringView &val) { + connection.send("LREM %b %lld %b", + key.data(), key.size(), + count, + val.data(), val.size()); +} + +inline void lset(Connection &connection, + const StringView &key, + long long index, + const StringView &val) { + connection.send("LSET %b %lld %b", + key.data(), key.size(), + index, + val.data(), val.size()); +} + +inline void ltrim(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("LTRIM %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +inline void rpop(Connection &connection, const StringView &key) { + connection.send("RPOP %b", key.data(), key.size()); +} + +inline void rpoplpush(Connection &connection, + const StringView &source, + const StringView &destination) { + connection.send("RPOPLPUSH %b %b", + source.data(), source.size(), + destination.data(), destination.size()); +} + +inline void rpush(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSH %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +template +inline void rpush_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "RPUSH" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void rpushx(Connection &connection, const StringView &key, const StringView &val) { + connection.send("RPUSHX %b %b", + key.data(), key.size(), + val.data(), val.size()); +} + +// HASH commands. + +inline void hdel(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HDEL %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +template +inline void hdel_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hexists(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HEXISTS %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hget(Connection &connection, const StringView &key, const StringView &field) { + connection.send("HGET %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hgetall(Connection &connection, const StringView &key) { + connection.send("HGETALL %b", key.data(), key.size()); +} + +inline void hincrby(Connection &connection, + const StringView &key, + const StringView &field, + long long increment) { + connection.send("HINCRBY %b %b %lld", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hincrbyfloat(Connection &connection, + const StringView &key, + const StringView &field, + double increment) { + connection.send("HINCRBYFLOAT %b %b %f", + key.data(), key.size(), + field.data(), field.size(), + increment); +} + +inline void hkeys(Connection &connection, const StringView &key) { + connection.send("HKEYS %b", key.data(), key.size()); +} + +inline void hlen(Connection &connection, const StringView &key) { + connection.send("HLEN %b", key.data(), key.size()); +} + +template +inline void hmget(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMGET" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void hmset(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "HMSET" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void hscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("HSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void hset(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSET %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +inline void hsetnx(Connection &connection, + const StringView &key, + const StringView &field, + const StringView &val) { + connection.send("HSETNX %b %b %b", + key.data(), key.size(), + field.data(), field.size(), + val.data(), val.size()); +} + +inline void hstrlen(Connection &connection, + const StringView &key, + const StringView &field) { + connection.send("HSTRLEN %b %b", + key.data(), key.size(), + field.data(), field.size()); +} + +inline void hvals(Connection &connection, const StringView &key) { + connection.send("HVALS %b", key.data(), key.size()); +} + +// SET commands + +inline void sadd(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SADD %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void sadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void scard(Connection &connection, const StringView &key) { + connection.send("SCARD %b", key.data(), key.size()); +} + +template +inline void sdiff(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFF" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sdiffstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SDIFFSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sdiffstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SDIFFSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void sinter(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTER" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sinterstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SINTERSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SINTERSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +inline void sismember(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SISMEMBER %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void smembers(Connection &connection, const StringView &key) { + connection.send("SMEMBERS %b", key.data(), key.size()); +} + +inline void smove(Connection &connection, + const StringView &source, + const StringView &destination, + const StringView &member) { + connection.send("SMOVE %b %b %b", + source.data(), source.size(), + destination.data(), destination.size(), + member.data(), member.size()); +} + +inline void spop(Connection &connection, const StringView &key) { + connection.send("SPOP %b", key.data(), key.size()); +} + +inline void spop_range(Connection &connection, const StringView &key, long long count) { + connection.send("SPOP %b %lld", + key.data(), key.size(), + count); +} + +inline void srandmember(Connection &connection, const StringView &key) { + connection.send("SRANDMEMBER %b", key.data(), key.size()); +} + +inline void srandmember_range(Connection &connection, + const StringView &key, + long long count) { + connection.send("SRANDMEMBER %b %lld", + key.data(), key.size(), + count); +} + +inline void srem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("SREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void srem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void sscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("SSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +template +inline void sunion(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNION" << std::make_pair(first, last); + + connection.send(args); +} + +inline void sunionstore(Connection &connection, + const StringView &destination, + const StringView &key) { + connection.send("SUNIONSTORE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void sunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "SUNIONSTORE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// Sorted Set commands. + +inline void bzpopmax(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMAX %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmax_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMAX" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +inline void bzpopmin(Connection &connection, const StringView &key, long long timeout) { + connection.send("BZPOPMIN %b %lld", key.data(), key.size(), timeout); +} + +template +void bzpopmin_range(Connection &connection, + Input first, + Input last, + long long timeout) { + assert(first != last); + + CmdArgs args; + args << "BZPOPMIN" << std::make_pair(first, last) << timeout; + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed); + +inline void zadd(Connection &connection, + const StringView &key, + const StringView &member, + double score, + UpdateType type, + bool changed) { + auto tmp = {std::make_pair(member, score)}; + + zadd_range(connection, key, tmp.begin(), tmp.end(), type, changed); +} + +inline void zcard(Connection &connection, const StringView &key) { + connection.send("ZCARD %b", key.data(), key.size()); +} + +template +inline void zcount(Connection &connection, + const StringView &key, + const Interval &interval) { + connection.send("ZCOUNT %b %s %s", + key.data(), key.size(), + interval.min().c_str(), + interval.max().c_str()); +} + +inline void zincrby(Connection &connection, + const StringView &key, + double increment, + const StringView &member) { + connection.send("ZINCRBY %b %f %b", + key.data(), key.size(), + increment, + member.data(), member.size()); +} + +inline void zinterstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZINTERSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +template +inline void zlexcount(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZLEXCOUNT %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zpopmax(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMAX %b %lld", + key.data(), key.size(), + count); +} + +inline void zpopmin(Connection &connection, const StringView &key, long long count) { + connection.send("ZPOPMIN %b %lld", + key.data(), key.size(), + count); +} + +inline void zrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); +} + +template +void zrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } else { + connection.send("ZRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size(), + opts.offset, + opts.count); + } +} + +inline void zrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zrem(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREM %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +template +inline void zrem_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "ZREM" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void zremrangebylex(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYLEX %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zremrangebyrank(Connection &connection, + const StringView &key, + long long start, + long long stop) { + connection.send("zremrangebyrank %b %lld %lld", + key.data(), key.size(), + start, + stop); +} + +template +inline void zremrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREMRANGEBYSCORE %b %b %b", + key.data(), key.size(), + min.data(), min.size(), + max.data(), max.size()); +} + +inline void zrevrange(Connection &connection, + const StringView &key, + long long start, + long long stop, + bool with_scores) { + if (with_scores) { + connection.send("ZREVRANGE %b %lld %lld WITHSCORES", + key.data(), key.size(), + start, + stop); + } else { + connection.send("ZREVRANGE %b %lld %lld", + key.data(), key.size(), + start, + stop); + } +} + +template +inline void zrevrangebylex(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + connection.send("ZREVRANGEBYLEX %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); +} + +template +void zrevrangebyscore(Connection &connection, + const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores) { + const auto &min = interval.min(); + const auto &max = interval.max(); + + if (with_scores) { + connection.send("ZREVRANGEBYSCORE %b %b %b WITHSCORES LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } else { + connection.send("ZREVRANGEBYSCORE %b %b %b LIMIT %lld %lld", + key.data(), key.size(), + max.data(), max.size(), + min.data(), min.size(), + opts.offset, + opts.count); + } +} + +inline void zrevrank(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZREVRANK %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zscan(Connection &connection, + const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + connection.send("ZSCAN %b %lld MATCH %b COUNT %lld", + key.data(), key.size(), + cursor, + pattern.data(), pattern.size(), + count); +} + +inline void zscore(Connection &connection, + const StringView &key, + const StringView &member) { + connection.send("ZSCORE %b %b", + key.data(), key.size(), + member.data(), member.size()); +} + +inline void zunionstore(Connection &connection, + const StringView &destination, + const StringView &key, + double weight) { + connection.send("ZUNIONSTORE %b 1 %b WEIGHTS %f", + destination.data(), destination.size(), + key.data(), key.size(), + weight); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr); + +// HYPERLOGLOG commands. + +inline void pfadd(Connection &connection, + const StringView &key, + const StringView &element) { + connection.send("PFADD %b %b", + key.data(), key.size(), + element.data(), element.size()); +} + +template +inline void pfadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFADD" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfcount(Connection &connection, const StringView &key) { + connection.send("PFCOUNT %b", key.data(), key.size()); +} + +template +inline void pfcount_range(Connection &connection, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFCOUNT" << std::make_pair(first, last); + + connection.send(args); +} + +inline void pfmerge(Connection &connection, const StringView &destination, const StringView &key) { + connection.send("PFMERGE %b %b", + destination.data(), destination.size(), + key.data(), key.size()); +} + +template +inline void pfmerge_range(Connection &connection, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "PFMERGE" << destination << std::make_pair(first, last); + + connection.send(args); +} + +// GEO commands. + +inline void geoadd(Connection &connection, + const StringView &key, + const std::tuple &member) { + const auto &mem = std::get<0>(member); + + connection.send("GEOADD %b %f %f %b", + key.data(), key.size(), + std::get<1>(member), + std::get<2>(member), + mem.data(), mem.size()); +} + +template +inline void geoadd_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOADD" << key; + + while (first != last) { + const auto &member = *first; + args << std::get<1>(member) << std::get<2>(member) << std::get<0>(member); + ++first; + } + + connection.send(args); +} + +void geodist(Connection &connection, + const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit); + +template +inline void geohash_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOHASH" << key << std::make_pair(first, last); + + connection.send(args); +} + +template +inline void geopos_range(Connection &connection, + const StringView &key, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + args << "GEOPOS" << key << std::make_pair(first, last); + + connection.send(args); +} + +void georadius(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadius_store(Connection &connection, + const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void georadiusbymember(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +void georadiusbymember_store(Connection &connection, + const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +// SCRIPTING commands. + +inline void eval(Connection &connection, + const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + CmdArgs cmd_args; + + cmd_args << "EVAL" << script << keys.size() + << std::make_pair(keys.begin(), keys.end()) + << std::make_pair(args.begin(), args.end()); + + connection.send(cmd_args); +} + +inline void evalsha(Connection &connection, + const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + CmdArgs cmd_args; + + cmd_args << "EVALSHA" << script << keys.size() + << std::make_pair(keys.begin(), keys.end()) + << std::make_pair(args.begin(), args.end()); + + connection.send(cmd_args); +} + +inline void script_exists(Connection &connection, const StringView &sha) { + connection.send("SCRIPT EXISTS %b", sha.data(), sha.size()); +} + +template +inline void script_exists_range(Connection &connection, Input first, Input last) { + assert(first != last); + + CmdArgs args; + args << "SCRIPT" << "EXISTS" << std::make_pair(first, last); + + connection.send(args); +} + +inline void script_flush(Connection &connection) { + connection.send("SCRIPT FLUSH"); +} + +inline void script_kill(Connection &connection) { + connection.send("SCRIPT KILL"); +} + +inline void script_load(Connection &connection, const StringView &script) { + connection.send("SCRIPT LOAD %b", script.data(), script.size()); +} + +// PUBSUB commands. + +inline void psubscribe(Connection &connection, const StringView &pattern) { + connection.send("PSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void psubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void publish(Connection &connection, + const StringView &channel, + const StringView &message) { + connection.send("PUBLISH %b %b", + channel.data(), channel.size(), + message.data(), message.size()); +} + +inline void punsubscribe(Connection &connection) { + connection.send("PUNSUBSCRIBE"); +} + +inline void punsubscribe(Connection &connection, const StringView &pattern) { + connection.send("PUNSUBSCRIBE %b", pattern.data(), pattern.size()); +} + +template +inline void punsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("PUNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "PUNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void subscribe(Connection &connection, const StringView &channel) { + connection.send("SUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void subscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("SUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "SUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +inline void unsubscribe(Connection &connection) { + connection.send("UNSUBSCRIBE"); +} + +inline void unsubscribe(Connection &connection, const StringView &channel) { + connection.send("UNSUBSCRIBE %b", channel.data(), channel.size()); +} + +template +inline void unsubscribe_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNSUBSCRIBE: no key specified"); + } + + CmdArgs args; + args << "UNSUBSCRIBE" << std::make_pair(first, last); + + connection.send(args); +} + +// Transaction commands. + +inline void discard(Connection &connection) { + connection.send("DISCARD"); +} + +inline void exec(Connection &connection) { + connection.send("EXEC"); +} + +inline void multi(Connection &connection) { + connection.send("MULTI"); +} + +inline void unwatch(Connection &connection, const StringView &key) { + connection.send("UNWATCH %b", key.data(), key.size()); +} + +template +inline void unwatch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("UNWATCH: no key specified"); + } + + CmdArgs args; + args << "UNWATCH" << std::make_pair(first, last); + + connection.send(args); +} + +inline void watch(Connection &connection, const StringView &key) { + connection.send("WATCH %b", key.data(), key.size()); +} + +template +inline void watch_range(Connection &connection, Input first, Input last) { + if (first == last) { + throw Error("WATCH: no key specified"); + } + + CmdArgs args; + args << "WATCH" << std::make_pair(first, last); + + connection.send(args); +} + +// Stream commands. + +inline void xack(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XACK %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +template +void xack_range(Connection &connection, + const StringView &key, + const StringView &group, + Input first, + Input last) { + CmdArgs args; + args << "XACK" << key << group << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last) { + CmdArgs args; + args << "XADD" << key << id << std::make_pair(first, last); + + connection.send(args); +} + +template +void xadd_maxlen_range(Connection &connection, + const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + CmdArgs args; + args << "XADD" << key << "MAXLEN"; + + if (approx) { + args << "~"; + } + + args << count << id << std::make_pair(first, last); + + connection.send(args); +} + +inline void xclaim(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + const StringView &id) { + connection.send("XCLAIM %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size(), + min_idle_time, + id.data(), id.size()); +} + +template +void xclaim_range(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer, + long long min_idle_time, + Input first, + Input last) { + CmdArgs args; + args << "XCLAIM" << key << group << consumer << min_idle_time << std::make_pair(first, last); + + connection.send(args); +} + +inline void xdel(Connection &connection, const StringView &key, const StringView &id) { + connection.send("XDEL %b %b", key.data(), key.size(), id.data(), id.size()); +} + +template +void xdel_range(Connection &connection, const StringView &key, Input first, Input last) { + CmdArgs args; + args << "XDEL" << key << std::make_pair(first, last); + + connection.send(args); +} + +inline void xgroup_create(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream) { + CmdArgs args; + args << "XGROUP" << "CREATE" << key << group << id; + + if (mkstream) { + args << "MKSTREAM"; + } + + connection.send(args); +} + +inline void xgroup_setid(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &id) { + connection.send("XGROUP SETID %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + id.data(), id.size()); +} + +inline void xgroup_destroy(Connection &connection, + const StringView &key, + const StringView &group) { + connection.send("XGROUP DESTROY %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xgroup_delconsumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &consumer) { + connection.send("XGROUP DELCONSUMER %b %b %b", + key.data(), key.size(), + group.data(), group.size(), + consumer.data(), consumer.size()); +} + +inline void xlen(Connection &connection, const StringView &key) { + connection.send("XLEN %b", key.data(), key.size()); +} + +inline void xpending(Connection &connection, const StringView &key, const StringView &group) { + connection.send("XPENDING %b %b", + key.data(), key.size(), + group.data(), group.size()); +} + +inline void xpending_detail(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XPENDING %b %b %b %b %lld", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xpending_per_consumer(Connection &connection, + const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + connection.send("XPENDING %b %b %b %b %lld %b", + key.data(), key.size(), + group.data(), group.size(), + start.data(), start.size(), + end.data(), end.size(), + count, + consumer.data(), consumer.size()); +} + +inline void xrange(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end) { + connection.send("XRANGE %b %b %b", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size()); +} + +inline void xrange_count(Connection &connection, + const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + connection.send("XRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + start.data(), start.size(), + end.data(), end.size(), + count); +} + +inline void xread(Connection &connection, + const StringView &key, + const StringView &id, + long long count) { + connection.send("XREAD COUNT %lld STREAMS %b %b", + count, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_range(Connection &connection, Input first, Input last, long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xread_block(Connection &connection, + const StringView &key, + const StringView &id, + long long timeout, + long long count) { + connection.send("XREAD COUNT %lld BLOCK %lld STREAMS %b %b", + count, + timeout, + key.data(), key.size(), + id.data(), id.size()); +} + +template +void xread_block_range(Connection &connection, + Input first, + Input last, + long long timeout, + long long count) { + CmdArgs args; + args << "XREAD" << "COUNT" << count << "BLOCK" << timeout << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer << "COUNT" << count; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xreadgroup_block(Connection &connection, + const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS" << key << id; + + connection.send(args); +} + +template +void xreadgroup_block_range(Connection &connection, + const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long timeout, + long long count, + bool noack) { + CmdArgs args; + args << "XREADGROUP" << "GROUP" << group << consumer + << "COUNT" << count << "BLOCK" << timeout; + + if (noack) { + args << "NOACK"; + } + + args << "STREAMS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + connection.send(args); +} + +inline void xrevrange(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start) { + connection.send("XREVRANGE %b %b %b", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size()); +} + +inline void xrevrange_count(Connection &connection, + const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + connection.send("XREVRANGE %b %b %b COUNT %lld", + key.data(), key.size(), + end.data(), end.size(), + start.data(), start.size(), + count); +} + +void xtrim(Connection &connection, const StringView &key, long long count, bool approx); + +namespace detail { + +void set_bitop(CmdArgs &args, BitOp op); + +void set_update_type(CmdArgs &args, UpdateType type); + +void set_aggregation_type(CmdArgs &args, Aggregation type); + +template +void zinterstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zinterstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZINTERSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::false_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last) + << std::make_pair(first, last); + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +template +void zunionstore(std::true_type, + Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + CmdArgs args; + args << "ZUNIONSTORE" << destination << std::distance(first, last); + + for (auto iter = first; iter != last; ++iter) { + args << iter->first; + } + + args << "WEIGHTS"; + + for (auto iter = first; iter != last; ++iter) { + args << iter->second; + } + + set_aggregation_type(args, aggr); + + connection.send(args); +} + +void set_geo_unit(CmdArgs &args, GeoUnit unit); + +void set_georadius_store_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + +void set_georadius_parameters(CmdArgs &args, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash); + +} + +} + +} + +} + +namespace sw { + +namespace redis { + +namespace cmd { + +template +void bitop_range(Connection &connection, + BitOp op, + const StringView &destination, + Input first, + Input last) { + assert(first != last); + + CmdArgs args; + + detail::set_bitop(args, op); + + args << destination << std::make_pair(first, last); + + connection.send(args); +} + +template +void zadd_range(Connection &connection, + const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + assert(first != last); + + CmdArgs args; + + args << "ZADD" << key; + + detail::set_update_type(args, type); + + if (changed) { + args << "CH"; + } + + while (first != last) { + // Swap the pair to pair. + args << first->second << first->first; + ++first; + } + + connection.send(args); +} + +template +void zinterstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zinterstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +template +void zunionstore_range(Connection &connection, + const StringView &destination, + Input first, + Input last, + Aggregation aggr) { + assert(first != last); + + detail::zunionstore(typename IsKvPairIter::type(), + connection, + destination, + first, + last, + aggr); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_args.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_args.h new file mode 100644 index 00000000..0beb71e5 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_args.h @@ -0,0 +1,180 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H + +#include +#include +#include +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +class CmdArgs { +public: + template + CmdArgs& append(Arg &&arg); + + template + CmdArgs& append(Arg &&arg, Args &&...args); + + // All overloads of operator<< are for internal use only. + CmdArgs& operator<<(const StringView &arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& operator<<(T &&arg); + + template + CmdArgs& operator<<(const std::pair &range); + + template + auto operator<<(const std::tuple &) -> + typename std::enable_if::type { + return *this; + } + + template + auto operator<<(const std::tuple &arg) -> + typename std::enable_if::type; + + const char** argv() { + return _argv.data(); + } + + const std::size_t* argv_len() { + return _argv_len.data(); + } + + std::size_t size() const { + return _argv.size(); + } + +private: + // Deep copy. + CmdArgs& _append(std::string arg); + + // Shallow copy. + CmdArgs& _append(const StringView &arg); + + // Shallow copy. + CmdArgs& _append(const char *arg); + + template ::type>::value, + int>::type = 0> + CmdArgs& _append(T &&arg) { + return operator<<(std::forward(arg)); + } + + template + CmdArgs& _append(std::true_type, const std::pair &range); + + template + CmdArgs& _append(std::false_type, const std::pair &range); + + std::vector _argv; + std::vector _argv_len; + + std::list _args; +}; + +template +inline CmdArgs& CmdArgs::append(Arg &&arg) { + return _append(std::forward(arg)); +} + +template +inline CmdArgs& CmdArgs::append(Arg &&arg, Args &&...args) { + _append(std::forward(arg)); + + return append(std::forward(args)...); +} + +inline CmdArgs& CmdArgs::operator<<(const StringView &arg) { + _argv.push_back(arg.data()); + _argv_len.push_back(arg.size()); + + return *this; +} + +template +inline CmdArgs& CmdArgs::operator<<(const std::pair &range) { + return _append(IsKvPair())>::type>(), range); +} + +template ::type>::value, + int>::type> +inline CmdArgs& CmdArgs::operator<<(T &&arg) { + return _append(std::to_string(std::forward(arg))); +} + +template +auto CmdArgs::operator<<(const std::tuple &arg) -> + typename std::enable_if::type { + operator<<(std::get(arg)); + + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(std::string arg) { + _args.push_back(std::move(arg)); + return operator<<(_args.back()); +} + +inline CmdArgs& CmdArgs::_append(const StringView &arg) { + return operator<<(arg); +} + +inline CmdArgs& CmdArgs::_append(const char *arg) { + return operator<<(arg); +} + +template +CmdArgs& CmdArgs::_append(std::false_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << *first; + ++first; + } + + return *this; +} + +template +CmdArgs& CmdArgs::_append(std::true_type, const std::pair &range) { + auto first = range.first; + auto last = range.second; + while (first != last) { + *this << first->first << first->second; + ++first; + } + + return *this; +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_options.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_options.h new file mode 100644 index 00000000..ca766c08 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/command_options.h @@ -0,0 +1,211 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H +#define SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H + +#include +#include "utils.h" + +namespace sw { + +namespace redis { + +enum class UpdateType { + EXIST, + NOT_EXIST, + ALWAYS +}; + +enum class InsertPosition { + BEFORE, + AFTER +}; + +enum class BoundType { + CLOSED, + OPEN, + LEFT_OPEN, + RIGHT_OPEN +}; + +// (-inf, +inf) +template +class UnboundedInterval; + +// [min, max], (min, max), (min, max], [min, max) +template +class BoundedInterval; + +// [min, +inf), (min, +inf) +template +class LeftBoundedInterval; + +// (-inf, max], (-inf, max) +template +class RightBoundedInterval; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(double min, double max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(double min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(double max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +template <> +class UnboundedInterval { +public: + const std::string& min() const; + + const std::string& max() const; +}; + +template <> +class BoundedInterval { +public: + BoundedInterval(const std::string &min, const std::string &max, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const { + return _max; + } + +private: + std::string _min; + std::string _max; +}; + +template <> +class LeftBoundedInterval { +public: + LeftBoundedInterval(const std::string &min, BoundType type); + + const std::string& min() const { + return _min; + } + + const std::string& max() const; + +private: + std::string _min; +}; + +template <> +class RightBoundedInterval { +public: + RightBoundedInterval(const std::string &max, BoundType type); + + const std::string& min() const; + + const std::string& max() const { + return _max; + } + +private: + std::string _max; +}; + +struct LimitOptions { + long long offset = 0; + long long count = -1; +}; + +enum class Aggregation { + SUM, + MIN, + MAX +}; + +enum class BitOp { + AND, + OR, + XOR, + NOT +}; + +enum class GeoUnit { + M, + KM, + MI, + FT +}; + +template +struct WithCoord : TupleWithType, T> {}; + +template +struct WithDist : TupleWithType {}; + +template +struct WithHash : TupleWithType {}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection.h new file mode 100644 index 00000000..5ad41922 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection.h @@ -0,0 +1,194 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "reply.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +enum class ConnectionType { + TCP = 0, + UNIX +}; + +struct ConnectionOptions { +public: + ConnectionOptions() = default; + + explicit ConnectionOptions(const std::string &uri); + + ConnectionOptions(const ConnectionOptions &) = default; + ConnectionOptions& operator=(const ConnectionOptions &) = default; + + ConnectionOptions(ConnectionOptions &&) = default; + ConnectionOptions& operator=(ConnectionOptions &&) = default; + + ~ConnectionOptions() = default; + + ConnectionType type = ConnectionType::TCP; + + std::string host; + + int port = 6379; + + std::string path; + + std::string password; + + int db = 0; + + bool keep_alive = false; + + std::chrono::milliseconds connect_timeout{0}; + + std::chrono::milliseconds socket_timeout{0}; + +private: + ConnectionOptions _parse_options(const std::string &uri) const; + + ConnectionOptions _parse_tcp_options(const std::string &path) const; + + ConnectionOptions _parse_unix_options(const std::string &path) const; + + auto _split_string(const std::string &str, const std::string &delimiter) const -> + std::pair; +}; + +class CmdArgs; + +class Connection { +public: + explicit Connection(const ConnectionOptions &opts); + + Connection(const Connection &) = delete; + Connection& operator=(const Connection &) = delete; + + Connection(Connection &&) = default; + Connection& operator=(Connection &&) = default; + + ~Connection() = default; + + // Check if the connection is broken. Client needs to do this check + // before sending some command to the connection. If it's broken, + // client needs to reconnect it. + bool broken() const noexcept { + return _ctx->err != REDIS_OK; + } + + void reset() noexcept { + _ctx->err = 0; + } + + void reconnect(); + + auto last_active() const + -> std::chrono::time_point { + return _last_active; + } + + template + void send(const char *format, Args &&...args); + + void send(int argc, const char **argv, const std::size_t *argv_len); + + void send(CmdArgs &args); + + ReplyUPtr recv(); + + const ConnectionOptions& options() const { + return _opts; + } + + friend void swap(Connection &lhs, Connection &rhs) noexcept; + +private: + class Connector; + + struct ContextDeleter { + void operator()(redisContext *context) const { + if (context != nullptr) { + redisFree(context); + } + }; + }; + + using ContextUPtr = std::unique_ptr; + + void _set_options(); + + void _auth(); + + void _select_db(); + + redisContext* _context(); + + ContextUPtr _ctx; + + // The time that the connection is created or the time that + // the connection is used, i.e. *context()* is called. + std::chrono::time_point _last_active{}; + + ConnectionOptions _opts; +}; + +using ConnectionSPtr = std::shared_ptr; + +enum class Role { + MASTER, + SLAVE +}; + +// Inline implementaions. + +template +inline void Connection::send(const char *format, Args &&...args) { + auto ctx = _context(); + + assert(ctx != nullptr); + + if (redisAppendCommand(ctx, + format, + std::forward(args)...) != REDIS_OK) { + throw_error(*ctx, "Failed to send command"); + } + + assert(!broken()); +} + +inline redisContext* Connection::_context() { + _last_active = std::chrono::steady_clock::now(); + + return _ctx.get(); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection_pool.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection_pool.h new file mode 100644 index 00000000..6f2663ad --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/connection_pool.h @@ -0,0 +1,115 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H +#define SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +struct ConnectionPoolOptions { + // Max number of connections, including both in-use and idle ones. + std::size_t size = 1; + + // Max time to wait for a connection. 0ms means client waits forever. + std::chrono::milliseconds wait_timeout{0}; + + // Max lifetime of a connection. 0ms means we never expire the connection. + std::chrono::milliseconds connection_lifetime{0}; +}; + +class ConnectionPool { +public: + ConnectionPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool(SimpleSentinel sentinel, + const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + ConnectionPool() = default; + + ConnectionPool(ConnectionPool &&that); + ConnectionPool& operator=(ConnectionPool &&that); + + ConnectionPool(const ConnectionPool &) = delete; + ConnectionPool& operator=(const ConnectionPool &) = delete; + + ~ConnectionPool() = default; + + // Fetch a connection from pool. + Connection fetch(); + + ConnectionOptions connection_options(); + + void release(Connection connection); + + // Create a new connection. + Connection create(); + +private: + void _move(ConnectionPool &&that); + + // NOT thread-safe + Connection _create(); + + Connection _create(SimpleSentinel &sentinel, const ConnectionOptions &opts, bool locked); + + Connection _fetch(); + + void _wait_for_connection(std::unique_lock &lock); + + bool _need_reconnect(const Connection &connection, + const std::chrono::milliseconds &connection_lifetime) const; + + void _update_connection_opts(const std::string &host, int port) { + _opts.host = host; + _opts.port = port; + } + + bool _role_changed(const ConnectionOptions &opts) const { + return opts.port != _opts.port || opts.host != _opts.host; + } + + ConnectionOptions _opts; + + ConnectionPoolOptions _pool_opts; + + std::deque _pool; + + std::size_t _used_connections = 0; + + std::mutex _mutex; + + std::condition_variable _cv; + + SimpleSentinel _sentinel; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/errors.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/errors.h new file mode 100644 index 00000000..44d629e5 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/errors.h @@ -0,0 +1,159 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_ERRORS_H +#define SEWENEW_REDISPLUSPLUS_ERRORS_H + +#include +#include +#include + +namespace sw { + +namespace redis { + +enum ReplyErrorType { + ERR, + MOVED, + ASK +}; + +class Error : public std::exception { +public: + explicit Error(const std::string &msg) : _msg(msg) {} + + Error(const Error &) = default; + Error& operator=(const Error &) = default; + + Error(Error &&) = default; + Error& operator=(Error &&) = default; + + virtual ~Error() = default; + + virtual const char* what() const noexcept { + return _msg.data(); + } + +private: + std::string _msg; +}; + +class IoError : public Error { +public: + explicit IoError(const std::string &msg) : Error(msg) {} + + IoError(const IoError &) = default; + IoError& operator=(const IoError &) = default; + + IoError(IoError &&) = default; + IoError& operator=(IoError &&) = default; + + virtual ~IoError() = default; +}; + +class TimeoutError : public IoError { +public: + explicit TimeoutError(const std::string &msg) : IoError(msg) {} + + TimeoutError(const TimeoutError &) = default; + TimeoutError& operator=(const TimeoutError &) = default; + + TimeoutError(TimeoutError &&) = default; + TimeoutError& operator=(TimeoutError &&) = default; + + virtual ~TimeoutError() = default; +}; + +class ClosedError : public Error { +public: + explicit ClosedError(const std::string &msg) : Error(msg) {} + + ClosedError(const ClosedError &) = default; + ClosedError& operator=(const ClosedError &) = default; + + ClosedError(ClosedError &&) = default; + ClosedError& operator=(ClosedError &&) = default; + + virtual ~ClosedError() = default; +}; + +class ProtoError : public Error { +public: + explicit ProtoError(const std::string &msg) : Error(msg) {} + + ProtoError(const ProtoError &) = default; + ProtoError& operator=(const ProtoError &) = default; + + ProtoError(ProtoError &&) = default; + ProtoError& operator=(ProtoError &&) = default; + + virtual ~ProtoError() = default; +}; + +class OomError : public Error { +public: + explicit OomError(const std::string &msg) : Error(msg) {} + + OomError(const OomError &) = default; + OomError& operator=(const OomError &) = default; + + OomError(OomError &&) = default; + OomError& operator=(OomError &&) = default; + + virtual ~OomError() = default; +}; + +class ReplyError : public Error { +public: + explicit ReplyError(const std::string &msg) : Error(msg) {} + + ReplyError(const ReplyError &) = default; + ReplyError& operator=(const ReplyError &) = default; + + ReplyError(ReplyError &&) = default; + ReplyError& operator=(ReplyError &&) = default; + + virtual ~ReplyError() = default; +}; + +class WatchError : public Error { +public: + explicit WatchError() : Error("Watched key has been modified") {} + + WatchError(const WatchError &) = default; + WatchError& operator=(const WatchError &) = default; + + WatchError(WatchError &&) = default; + WatchError& operator=(WatchError &&) = default; + + virtual ~WatchError() = default; +}; + + +// MovedError and AskError are defined in shards.h +class MovedError; + +class AskError; + +void throw_error(redisContext &context, const std::string &err_info); + +void throw_error(const redisReply &reply); + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_ERRORS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/pipeline.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/pipeline.h new file mode 100644 index 00000000..52b01253 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/pipeline.h @@ -0,0 +1,49 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_PIPELINE_H +#define SEWENEW_REDISPLUSPLUS_PIPELINE_H + +#include +#include +#include "connection.h" + +namespace sw { + +namespace redis { + +class PipelineImpl { +public: + template + void command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + } + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t /*cmd_num*/) { + // Reconnect to Redis to discard all commands. + connection.reconnect(); + } +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_PIPELINE_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.h new file mode 100644 index 00000000..71d975ee --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.h @@ -0,0 +1,1844 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H + +#include +#include +#include +#include +#include "connection.h" +#include "utils.h" +#include "reply.h" +#include "command.h" +#include "redis.h" + +namespace sw { + +namespace redis { + +class QueuedReplies; + +// If any command throws, QueuedRedis resets the connection, and becomes invalid. +// In this case, the only thing we can do is to destory the QueuedRedis object. +template +class QueuedRedis { +public: + QueuedRedis(QueuedRedis &&) = default; + QueuedRedis& operator=(QueuedRedis &&) = default; + + // When it destructs, the underlying *Connection* will be closed, + // and any command that has NOT been executed will be ignored. + ~QueuedRedis() = default; + + Redis redis(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type; + + template + QueuedRedis& command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type; + + QueuedReplies exec(); + + void discard(); + + // CONNECTION commands. + + QueuedRedis& auth(const StringView &password) { + return command(cmd::auth, password); + } + + QueuedRedis& echo(const StringView &msg) { + return command(cmd::echo, msg); + } + + QueuedRedis& ping() { + return command(cmd::ping); + } + + QueuedRedis& ping(const StringView &msg) { + return command(cmd::ping, msg); + } + + // We DO NOT support the QUIT command. See *Redis::quit* doc for details. + // + // QueuedRedis& quit(); + + QueuedRedis& select(long long idx) { + return command(cmd::select, idx); + } + + QueuedRedis& swapdb(long long idx1, long long idx2) { + return command(cmd::swapdb, idx1, idx2); + } + + // SERVER commands. + + QueuedRedis& bgrewriteaof() { + return command(cmd::bgrewriteaof); + } + + QueuedRedis& bgsave() { + return command(cmd::bgsave); + } + + QueuedRedis& dbsize() { + return command(cmd::dbsize); + } + + QueuedRedis& flushall(bool async = false) { + return command(cmd::flushall, async); + } + + QueuedRedis& flushdb(bool async = false) { + return command(cmd::flushdb, async); + } + + QueuedRedis& info() { + return command(cmd::info); + } + + QueuedRedis& info(const StringView §ion) { + return command(cmd::info, section); + } + + QueuedRedis& lastsave() { + return command(cmd::lastsave); + } + + QueuedRedis& save() { + return command(cmd::save); + } + + // KEY commands. + + QueuedRedis& del(const StringView &key) { + return command(cmd::del, key); + } + + template + QueuedRedis& del(Input first, Input last) { + return command(cmd::del_range, first, last); + } + + template + QueuedRedis& del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + QueuedRedis& dump(const StringView &key) { + return command(cmd::dump, key); + } + + QueuedRedis& exists(const StringView &key) { + return command(cmd::exists, key); + } + + template + QueuedRedis& exists(Input first, Input last) { + return command(cmd::exists_range, first, last); + } + + template + QueuedRedis& exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + QueuedRedis& expire(const StringView &key, long long timeout) { + return command(cmd::expire, key, timeout); + } + + QueuedRedis& expire(const StringView &key, + const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); + } + + QueuedRedis& expireat(const StringView &key, long long timestamp) { + return command(cmd::expireat, key, timestamp); + } + + QueuedRedis& expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& keys(const StringView &pattern) { + return command(cmd::keys, pattern); + } + + QueuedRedis& move(const StringView &key, long long db) { + return command(cmd::move, key, db); + } + + QueuedRedis& persist(const StringView &key) { + return command(cmd::persist, key); + } + + QueuedRedis& pexpire(const StringView &key, long long timeout) { + return command(cmd::pexpire, key, timeout); + } + + QueuedRedis& pexpire(const StringView &key, + const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); + } + + QueuedRedis& pexpireat(const StringView &key, long long timestamp) { + return command(cmd::pexpireat, key, timestamp); + } + + QueuedRedis& pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); + } + + QueuedRedis& pttl(const StringView &key) { + return command(cmd::pttl, key); + } + + QueuedRedis& randomkey() { + return command(cmd::randomkey); + } + + QueuedRedis& rename(const StringView &key, const StringView &newkey) { + return command(cmd::rename, key, newkey); + } + + QueuedRedis& renamenx(const StringView &key, const StringView &newkey) { + return command(cmd::renamenx, key, newkey); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false) { + return command(cmd::restore, key, val, ttl, replace); + } + + QueuedRedis& restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false) { + return restore(key, val, ttl.count(), replace); + } + + // TODO: sort + + QueuedRedis& scan(long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::scan, cursor, pattern, count); + } + + QueuedRedis& scan(long long cursor) { + return scan(cursor, "*", 10); + } + + QueuedRedis& scan(long long cursor, + const StringView &pattern) { + return scan(cursor, pattern, 10); + } + + QueuedRedis& scan(long long cursor, + long long count) { + return scan(cursor, "*", count); + } + + QueuedRedis& touch(const StringView &key) { + return command(cmd::touch, key); + } + + template + QueuedRedis& touch(Input first, Input last) { + return command(cmd::touch_range, first, last); + } + + template + QueuedRedis& touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + QueuedRedis& ttl(const StringView &key) { + return command(cmd::ttl, key); + } + + QueuedRedis& type(const StringView &key) { + return command(cmd::type, key); + } + + QueuedRedis& unlink(const StringView &key) { + return command(cmd::unlink, key); + } + + template + QueuedRedis& unlink(Input first, Input last) { + return command(cmd::unlink_range, first, last); + } + + template + QueuedRedis& unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + QueuedRedis& wait(long long numslaves, long long timeout) { + return command(cmd::wait, numslaves, timeout); + } + + QueuedRedis& wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); + } + + // STRING commands. + + QueuedRedis& append(const StringView &key, const StringView &str) { + return command(cmd::append, key, str); + } + + QueuedRedis& bitcount(const StringView &key, + long long start = 0, + long long end = -1) { + return command(cmd::bitcount, key, start, end); + } + + QueuedRedis& bitop(BitOp op, + const StringView &destination, + const StringView &key) { + return command(cmd::bitop, op, destination, key); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + Input first, + Input last) { + return command(cmd::bitop_range, op, destination, first, last); + } + + template + QueuedRedis& bitop(BitOp op, + const StringView &destination, + std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + QueuedRedis& bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1) { + return command(cmd::bitpos, key, bit, start, end); + } + + QueuedRedis& decr(const StringView &key) { + return command(cmd::decr, key); + } + + QueuedRedis& decrby(const StringView &key, long long decrement) { + return command(cmd::decrby, key, decrement); + } + + QueuedRedis& get(const StringView &key) { + return command(cmd::get, key); + } + + QueuedRedis& getbit(const StringView &key, long long offset) { + return command(cmd::getbit, key, offset); + } + + QueuedRedis& getrange(const StringView &key, long long start, long long end) { + return command(cmd::getrange, key, start, end); + } + + QueuedRedis& getset(const StringView &key, const StringView &val) { + return command(cmd::getset, key, val); + } + + QueuedRedis& incr(const StringView &key) { + return command(cmd::incr, key); + } + + QueuedRedis& incrby(const StringView &key, long long increment) { + return command(cmd::incrby, key, increment); + } + + QueuedRedis& incrbyfloat(const StringView &key, double increment) { + return command(cmd::incrbyfloat, key, increment); + } + + template + QueuedRedis& mget(Input first, Input last) { + return command(cmd::mget, first, last); + } + + template + QueuedRedis& mget(std::initializer_list il) { + return mget(il.begin(), il.end()); + } + + template + QueuedRedis& mset(Input first, Input last) { + return command(cmd::mset, first, last); + } + + template + QueuedRedis& mset(std::initializer_list il) { + return mset(il.begin(), il.end()); + } + + template + QueuedRedis& msetnx(Input first, Input last) { + return command(cmd::msetnx, first, last); + } + + template + QueuedRedis& msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + QueuedRedis& psetex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::psetex, key, ttl, val); + } + + QueuedRedis& psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); + } + + QueuedRedis& set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS) { + _set_cmd_indexes.push_back(_cmd_num); + + return command(cmd::set, key, val, ttl.count(), type); + } + + QueuedRedis& setex(const StringView &key, + long long ttl, + const StringView &val) { + return command(cmd::setex, key, ttl, val); + } + + QueuedRedis& setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + return setex(key, ttl.count(), val); + } + + QueuedRedis& setnx(const StringView &key, const StringView &val) { + return command(cmd::setnx, key, val); + } + + QueuedRedis& setrange(const StringView &key, + long long offset, + const StringView &val) { + return command(cmd::setrange, key, offset, val); + } + + QueuedRedis& strlen(const StringView &key) { + return command(cmd::strlen, key); + } + + // LIST commands. + + QueuedRedis& blpop(const StringView &key, long long timeout) { + return command(cmd::blpop, key, timeout); + } + + QueuedRedis& blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(key, timeout.count()); + } + + template + QueuedRedis& blpop(Input first, Input last, long long timeout) { + return command(cmd::blpop_range, first, last, timeout); + } + + template + QueuedRedis& blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(first, last, timeout.count()); + } + + template + QueuedRedis& blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpop(const StringView &key, long long timeout) { + return command(cmd::brpop, key, timeout); + } + + QueuedRedis& brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(key, timeout.count()); + } + + template + QueuedRedis& brpop(Input first, Input last, long long timeout) { + return command(cmd::brpop_range, first, last, timeout); + } + + template + QueuedRedis& brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(first, last, timeout.count()); + } + + template + QueuedRedis& brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + long long timeout) { + return command(cmd::brpoplpush, source, destination, timeout); + } + + QueuedRedis& brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpoplpush(source, destination, timeout.count()); + } + + QueuedRedis& lindex(const StringView &key, long long index) { + return command(cmd::lindex, key, index); + } + + QueuedRedis& linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val) { + return command(cmd::linsert, key, position, pivot, val); + } + + QueuedRedis& llen(const StringView &key) { + return command(cmd::llen, key); + } + + QueuedRedis& lpop(const StringView &key) { + return command(cmd::lpop, key); + } + + QueuedRedis& lpush(const StringView &key, const StringView &val) { + return command(cmd::lpush, key, val); + } + + template + QueuedRedis& lpush(const StringView &key, Input first, Input last) { + return command(cmd::lpush_range, key, first, last); + } + + template + QueuedRedis& lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + QueuedRedis& lpushx(const StringView &key, const StringView &val) { + return command(cmd::lpushx, key, val); + } + + QueuedRedis& lrange(const StringView &key, + long long start, + long long stop) { + return command(cmd::lrange, key, start, stop); + } + + QueuedRedis& lrem(const StringView &key, long long count, const StringView &val) { + return command(cmd::lrem, key, count, val); + } + + QueuedRedis& lset(const StringView &key, long long index, const StringView &val) { + return command(cmd::lset, key, index, val); + } + + QueuedRedis& ltrim(const StringView &key, long long start, long long stop) { + return command(cmd::ltrim, key, start, stop); + } + + QueuedRedis& rpop(const StringView &key) { + return command(cmd::rpop, key); + } + + QueuedRedis& rpoplpush(const StringView &source, const StringView &destination) { + return command(cmd::rpoplpush, source, destination); + } + + QueuedRedis& rpush(const StringView &key, const StringView &val) { + return command(cmd::rpush, key, val); + } + + template + QueuedRedis& rpush(const StringView &key, Input first, Input last) { + return command(cmd::rpush_range, key, first, last); + } + + template + QueuedRedis& rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + QueuedRedis& rpushx(const StringView &key, const StringView &val) { + return command(cmd::rpushx, key, val); + } + + // HASH commands. + + QueuedRedis& hdel(const StringView &key, const StringView &field) { + return command(cmd::hdel, key, field); + } + + template + QueuedRedis& hdel(const StringView &key, Input first, Input last) { + return command(cmd::hdel_range, key, first, last); + } + + template + QueuedRedis& hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + QueuedRedis& hexists(const StringView &key, const StringView &field) { + return command(cmd::hexists, key, field); + } + + QueuedRedis& hget(const StringView &key, const StringView &field) { + return command(cmd::hget, key, field); + } + + QueuedRedis& hgetall(const StringView &key) { + return command(cmd::hgetall, key); + } + + QueuedRedis& hincrby(const StringView &key, + const StringView &field, + long long increment) { + return command(cmd::hincrby, key, field, increment); + } + + QueuedRedis& hincrbyfloat(const StringView &key, + const StringView &field, + double increment) { + return command(cmd::hincrbyfloat, key, field, increment); + } + + QueuedRedis& hkeys(const StringView &key) { + return command(cmd::hkeys, key); + } + + QueuedRedis& hlen(const StringView &key) { + return command(cmd::hlen, key); + } + + template + QueuedRedis& hmget(const StringView &key, Input first, Input last) { + return command(cmd::hmget, key, first, last); + } + + template + QueuedRedis& hmget(const StringView &key, std::initializer_list il) { + return hmget(key, il.begin(), il.end()); + } + + template + QueuedRedis& hmset(const StringView &key, Input first, Input last) { + return command(cmd::hmset, key, first, last); + } + + template + QueuedRedis& hmset(const StringView &key, std::initializer_list il) { + return hmset(key, il.begin(), il.end()); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::hscan, key, cursor, pattern, count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return hscan(key, cursor, pattern, 10); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor, + long long count) { + return hscan(key, cursor, "*", count); + } + + QueuedRedis& hscan(const StringView &key, + long long cursor) { + return hscan(key, cursor, "*", 10); + } + + QueuedRedis& hset(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hset, key, field, val); + } + + QueuedRedis& hset(const StringView &key, const std::pair &item) { + return hset(key, item.first, item.second); + } + + QueuedRedis& hsetnx(const StringView &key, const StringView &field, const StringView &val) { + return command(cmd::hsetnx, key, field, val); + } + + QueuedRedis& hsetnx(const StringView &key, const std::pair &item) { + return hsetnx(key, item.first, item.second); + } + + QueuedRedis& hstrlen(const StringView &key, const StringView &field) { + return command(cmd::hstrlen, key, field); + } + + QueuedRedis& hvals(const StringView &key) { + return command(cmd::hvals, key); + } + + // SET commands. + + QueuedRedis& sadd(const StringView &key, const StringView &member) { + return command(cmd::sadd, key, member); + } + + template + QueuedRedis& sadd(const StringView &key, Input first, Input last) { + return command(cmd::sadd_range, key, first, last); + } + + template + QueuedRedis& sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + QueuedRedis& scard(const StringView &key) { + return command(cmd::scard, key); + } + + template + QueuedRedis& sdiff(Input first, Input last) { + return command(cmd::sdiff, first, last); + } + + template + QueuedRedis& sdiff(std::initializer_list il) { + return sdiff(il.begin(), il.end()); + } + + QueuedRedis& sdiffstore(const StringView &destination, const StringView &key) { + return command(cmd::sdiffstore, destination, key); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, + Input first, + Input last) { + return command(cmd::sdiffstore_range, destination, first, last); + } + + template + QueuedRedis& sdiffstore(const StringView &destination, std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + QueuedRedis& sinter(Input first, Input last) { + return command(cmd::sinter, first, last); + } + + template + QueuedRedis& sinter(std::initializer_list il) { + return sinter(il.begin(), il.end()); + } + + QueuedRedis& sinterstore(const StringView &destination, const StringView &key) { + return command(cmd::sinterstore, destination, key); + } + + template + QueuedRedis& sinterstore(const StringView &destination, + Input first, + Input last) { + return command(cmd::sinterstore_range, destination, first, last); + } + + template + QueuedRedis& sinterstore(const StringView &destination, std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + QueuedRedis& sismember(const StringView &key, const StringView &member) { + return command(cmd::sismember, key, member); + } + + QueuedRedis& smembers(const StringView &key) { + return command(cmd::smembers, key); + } + + QueuedRedis& smove(const StringView &source, + const StringView &destination, + const StringView &member) { + return command(cmd::smove, source, destination, member); + } + + QueuedRedis& spop(const StringView &key) { + return command(cmd::spop, key); + } + + QueuedRedis& spop(const StringView &key, long long count) { + return command(cmd::spop_range, key, count); + } + + QueuedRedis& srandmember(const StringView &key) { + return command(cmd::srandmember, key); + } + + QueuedRedis& srandmember(const StringView &key, long long count) { + return command(cmd::srandmember_range, key, count); + } + + QueuedRedis& srem(const StringView &key, const StringView &member) { + return command(cmd::srem, key, member); + } + + template + QueuedRedis& srem(const StringView &key, Input first, Input last) { + return command(cmd::srem_range, key, first, last); + } + + template + QueuedRedis& srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::sscan, key, cursor, pattern, count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return sscan(key, cursor, pattern, 10); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor, + long long count) { + return sscan(key, cursor, "*", count); + } + + QueuedRedis& sscan(const StringView &key, + long long cursor) { + return sscan(key, cursor, "*", 10); + } + + template + QueuedRedis& sunion(Input first, Input last) { + return command(cmd::sunion, first, last); + } + + template + QueuedRedis& sunion(std::initializer_list il) { + return sunion(il.begin(), il.end()); + } + + QueuedRedis& sunionstore(const StringView &destination, const StringView &key) { + return command(cmd::sunionstore, destination, key); + } + + template + QueuedRedis& sunionstore(const StringView &destination, Input first, Input last) { + return command(cmd::sunionstore_range, destination, first, last); + } + + template + QueuedRedis& sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + QueuedRedis& bzpopmax(const StringView &key, long long timeout) { + return command(cmd::bzpopmax, key, timeout); + } + + QueuedRedis& bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(key, timeout.count()); + } + + template + QueuedRedis& bzpopmax(Input first, Input last, long long timeout) { + return command(cmd::bzpopmax_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, long long timeout) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmax(il.begin(), il.end(), timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, long long timeout) { + return command(cmd::bzpopmin, key, timeout); + } + + QueuedRedis& bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(key, timeout.count()); + } + + template + QueuedRedis& bzpopmin(Input first, Input last, long long timeout) { + return command(cmd::bzpopmin_range, first, last, timeout); + } + + template + QueuedRedis& bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(first, last, timeout.count()); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, long long timeout) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + QueuedRedis& bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + QueuedRedis& zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return command(cmd::zadd, key, member, score, type, changed); + } + + template + QueuedRedis& zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return command(cmd::zadd_range, key, first, last, type, changed); + } + + QueuedRedis& zcard(const StringView &key) { + return command(cmd::zcard, key); + } + + template + QueuedRedis& zcount(const StringView &key, const Interval &interval) { + return command(cmd::zcount, key, interval); + } + + QueuedRedis& zincrby(const StringView &key, double increment, const StringView &member) { + return command(cmd::zincrby, key, increment, member); + } + + QueuedRedis& zinterstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zinterstore, destination, key, weight); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + return command(cmd::zinterstore_range, destination, first, last, type); + } + + template + QueuedRedis& zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + QueuedRedis& zlexcount(const StringView &key, const Interval &interval) { + return command(cmd::zlexcount, key, interval); + } + + QueuedRedis& zpopmax(const StringView &key) { + return command(cmd::zpopmax, key, 1); + } + + QueuedRedis& zpopmax(const StringView &key, long long count) { + return command(cmd::zpopmax, key, count); + } + + QueuedRedis& zpopmin(const StringView &key) { + return command(cmd::zpopmin, key, 1); + } + + QueuedRedis& zpopmin(const StringView &key, long long count) { + return command(cmd::zpopmin, key, count); + } + + // NOTE: *QueuedRedis::zrange*'s parameters are different from *Redis::zrange*. + // *Redis::zrange* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::zrange*. So we have to use an extra parameter: *with_scores*, + // to decide whether we should send *WITHSCORES* option to Redis. This also applies to + // other commands with the *WITHSCORES* option, e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, + // *ZREVRANGEBYSCORE*. + QueuedRedis& zrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrangebylex(const StringView &key, const Interval &interval) { + return zrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrank(const StringView &key, const StringView &member) { + return command(cmd::zrank, key, member); + } + + QueuedRedis& zrem(const StringView &key, const StringView &member) { + return command(cmd::zrem, key, member); + } + + template + QueuedRedis& zrem(const StringView &key, Input first, Input last) { + return command(cmd::zrem_range, key, first, last); + } + + template + QueuedRedis& zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + QueuedRedis& zremrangebylex(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebylex, key, interval); + } + + QueuedRedis& zremrangebyrank(const StringView &key, long long start, long long stop) { + return command(cmd::zremrangebyrank, key, start, stop); + } + + template + QueuedRedis& zremrangebyscore(const StringView &key, const Interval &interval) { + return command(cmd::zremrangebyscore, key, interval); + } + + // See comments on *ZRANGE*. + QueuedRedis& zrevrange(const StringView &key, + long long start, + long long stop, + bool with_scores = false) { + return command(cmd::zrevrange, key, start, stop, with_scores); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts) { + return command(cmd::zrevrangebylex, key, interval, opts); + } + + template + QueuedRedis& zrevrangebylex(const StringView &key, const Interval &interval) { + return zrevrangebylex(key, interval, {}); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + bool with_scores = false) { + return command(cmd::zrevrangebyscore, key, interval, opts, with_scores); + } + + // See comments on *ZRANGE*. + template + QueuedRedis& zrevrangebyscore(const StringView &key, + const Interval &interval, + bool with_scores = false) { + return zrevrangebyscore(key, interval, {}, with_scores); + } + + QueuedRedis& zrevrank(const StringView &key, const StringView &member) { + return command(cmd::zrevrank, key, member); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count) { + return command(cmd::zscan, key, cursor, pattern, count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + const StringView &pattern) { + return zscan(key, cursor, pattern, 10); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor, + long long count) { + return zscan(key, cursor, "*", count); + } + + QueuedRedis& zscan(const StringView &key, + long long cursor) { + return zscan(key, cursor, "*", 10); + } + + QueuedRedis& zscore(const StringView &key, const StringView &member) { + return command(cmd::zscore, key, member); + } + + QueuedRedis& zunionstore(const StringView &destination, + const StringView &key, + double weight) { + return command(cmd::zunionstore, destination, key, weight); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM) { + return command(cmd::zunionstore_range, destination, first, last, type); + } + + template + QueuedRedis& zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + QueuedRedis& pfadd(const StringView &key, const StringView &element) { + return command(cmd::pfadd, key, element); + } + + template + QueuedRedis& pfadd(const StringView &key, Input first, Input last) { + return command(cmd::pfadd_range, key, first, last); + } + + template + QueuedRedis& pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + QueuedRedis& pfcount(const StringView &key) { + return command(cmd::pfcount, key); + } + + template + QueuedRedis& pfcount(Input first, Input last) { + return command(cmd::pfcount_range, first, last); + } + + template + QueuedRedis& pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + QueuedRedis& pfmerge(const StringView &destination, const StringView &key) { + return command(cmd::pfmerge, destination, key); + } + + template + QueuedRedis& pfmerge(const StringView &destination, Input first, Input last) { + return command(cmd::pfmerge_range, destination, first, last); + } + + template + QueuedRedis& pfmerge(const StringView &destination, std::initializer_list il) { + return pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + QueuedRedis& geoadd(const StringView &key, + const std::tuple &member) { + return command(cmd::geoadd, key, member); + } + + template + QueuedRedis& geoadd(const StringView &key, + Input first, + Input last) { + return command(cmd::geoadd_range, key, first, last); + } + + template + QueuedRedis& geoadd(const StringView &key, std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + QueuedRedis& geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M) { + return command(cmd::geodist, key, member1, member2, unit); + } + + template + QueuedRedis& geohash(const StringView &key, Input first, Input last) { + return command(cmd::geohash_range, key, first, last); + } + + template + QueuedRedis& geohash(const StringView &key, std::initializer_list il) { + return geohash(key, il.begin(), il.end()); + } + + template + QueuedRedis& geopos(const StringView &key, Input first, Input last) { + return command(cmd::geopos_range, key, first, last); + } + + template + QueuedRedis& geopos(const StringView &key, std::initializer_list il) { + return geopos(key, il.begin(), il.end()); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _georadius_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadius_store, + key, + loc, + radius, + unit, + destination, + store_dist, + count); + } + + // NOTE: *QueuedRedis::georadius*'s parameters are different from *Redis::georadius*. + // *Redis::georadius* is overloaded by the output iterator, however, there's no such + // iterator in *QueuedRedis::georadius*. So we have to use extra parameters to decide + // whether we should send options to Redis. This also applies to *GEORADIUSBYMEMBER*. + QueuedRedis& georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count) { + _georadius_cmd_indexes.push_back(_cmd_num); + + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + destination, + store_dist, + count); + } + + // See the comments on *GEORADIUS*. + QueuedRedis& georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + bool with_coord, + bool with_dist, + bool with_hash) { + return command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + with_coord, + with_dist, + with_hash); + } + + // SCRIPTING commands. + + QueuedRedis& eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return command(cmd::eval, script, keys, args); + } + + QueuedRedis& evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + return command(cmd::evalsha, script, keys, args); + } + + template + QueuedRedis& script_exists(Input first, Input last) { + return command(cmd::script_exists_range, first, last); + } + + template + QueuedRedis& script_exists(std::initializer_list il) { + return script_exists(il.begin(), il.end()); + } + + QueuedRedis& script_flush() { + return command(cmd::script_flush); + } + + QueuedRedis& script_kill() { + return command(cmd::script_kill); + } + + QueuedRedis& script_load(const StringView &script) { + return command(cmd::script_load, script); + } + + // PUBSUB commands. + + QueuedRedis& publish(const StringView &channel, const StringView &message) { + return command(cmd::publish, channel, message); + } + + // Stream commands. + + QueuedRedis& xack(const StringView &key, const StringView &group, const StringView &id) { + return command(cmd::xack, key, group, id); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, Input first, Input last) { + return command(cmd::xack_range, key, group, first, last); + } + + template + QueuedRedis& xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, Input first, Input last) { + return command(cmd::xadd_range, key, id, first, last); + } + + template + QueuedRedis& xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true) { + return command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + } + + template + QueuedRedis& xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id) { + return command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last) { + return command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + } + + template + QueuedRedis& xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il) { + return xclaim(key, group, consumer, min_idle_time, il.begin(), il.end()); + } + + QueuedRedis& xdel(const StringView &key, const StringView &id) { + return command(cmd::xdel, key, id); + } + + template + QueuedRedis& xdel(const StringView &key, Input first, Input last) { + return command(cmd::xdel_range, key, first, last); + } + + template + QueuedRedis& xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + QueuedRedis& xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false) { + return command(cmd::xgroup_create, key, group, id, mkstream); + } + + QueuedRedis& xgroup_setid(const StringView &key, + const StringView &group, + const StringView &id) { + return command(cmd::xgroup_setid, key, group, id); + } + + QueuedRedis& xgroup_destroy(const StringView &key, const StringView &group) { + return command(cmd::xgroup_destroy, key, group); + } + + QueuedRedis& xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer) { + return command(cmd::xgroup_delconsumer, key, group, consumer); + } + + QueuedRedis& xlen(const StringView &key) { + return command(cmd::xlen, key); + } + + QueuedRedis& xpending(const StringView &key, const StringView &group) { + return command(cmd::xpending, key, group); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xpending_detail, key, group, start, end, count); + } + + QueuedRedis& xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer) { + return command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end) { + return command(cmd::xrange, key, start, end); + } + + QueuedRedis& xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count) { + return command(cmd::xrange, key, start, end, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id, long long count) { + return command(cmd::xread, key, id, count); + } + + QueuedRedis& xread(const StringView &key, const StringView &id) { + return xread(key, id, 0); + } + + template + auto xread(Input first, Input last, long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return command(cmd::xread_range, first, last, count); + } + + template + auto xread(Input first, Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, 0); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return command(cmd::xread_block, key, id, timeout.count(), count); + } + + QueuedRedis& xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xread(key, id, timeout, 0); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return command(cmd::xread_block_range, first, last, timeout.count(), count); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xread(first, last, timeout, 0); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack) { + return command(cmd::xreadgroup, group, consumer, key, id, count, noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count) { + return xreadgroup(group, consumer, key, id, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first ,last, 0, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) { + return command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count) { + return xreadgroup(group, consumer, key, id, timeout, count, false); + } + + QueuedRedis& xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout) { + return xreadgroup(group, consumer, key, id, timeout, 0, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, count, false); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout) + -> typename std::enable_if::value, + QueuedRedis&>::type { + return xreadgroup(group, consumer, first, last, timeout, 0, false); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start) { + return command(cmd::xrevrange, key, end, start); + } + + QueuedRedis& xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count) { + return command(cmd::xrevrange, key, end, start, count); + } + + QueuedRedis& xtrim(const StringView &key, long long count, bool approx = true) { + return command(cmd::xtrim, key, count, approx); + } + +private: + friend class Redis; + + friend class RedisCluster; + + template + QueuedRedis(const ConnectionSPtr &connection, Args &&...args); + + void _sanity_check() const; + + void _reset(); + + void _invalidate(); + + void _rewrite_replies(std::vector &replies) const; + + template + void _rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const; + + ConnectionSPtr _connection; + + Impl _impl; + + std::size_t _cmd_num = 0; + + std::vector _set_cmd_indexes; + + std::vector _georadius_cmd_indexes; + + bool _valid = true; +}; + +class QueuedReplies { +public: + std::size_t size() const; + + redisReply& get(std::size_t idx); + + template + Result get(std::size_t idx); + + template + void get(std::size_t idx, Output output); + +private: + template + friend class QueuedRedis; + + explicit QueuedReplies(std::vector replies) : _replies(std::move(replies)) {} + + void _index_check(std::size_t idx) const; + + std::vector _replies; +}; + +} + +} + +#include "queued_redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.hpp b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.hpp new file mode 100644 index 00000000..409f48ac --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/queued_redis.hpp @@ -0,0 +1,208 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP + +namespace sw { + +namespace redis { + +template +template +QueuedRedis::QueuedRedis(const ConnectionSPtr &connection, Args &&...args) : + _connection(connection), + _impl(std::forward(args)...) { + assert(_connection); +} + +template +Redis QueuedRedis::redis() { + return Redis(_connection); +} + +template +template +auto QueuedRedis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, + QueuedRedis&>::type { + try { + _sanity_check(); + + _impl.command(*_connection, cmd, std::forward(args)...); + + ++_cmd_num; + } catch (const Error &e) { + _invalidate(); + throw; + } + + return *this; +} + +template +template +QueuedRedis& QueuedRedis::command(const StringView &cmd_name, Args &&...args) { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +template +auto QueuedRedis::command(Input first, Input last) + -> typename std::enable_if::value, QueuedRedis&>::type { + if (first == last) { + throw Error("command: empty range"); + } + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +QueuedReplies QueuedRedis::exec() { + try { + _sanity_check(); + + auto replies = _impl.exec(*_connection, _cmd_num); + + _rewrite_replies(replies); + + _reset(); + + return QueuedReplies(std::move(replies)); + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +void QueuedRedis::discard() { + try { + _sanity_check(); + + _impl.discard(*_connection, _cmd_num); + + _reset(); + } catch (const Error &e) { + _invalidate(); + throw; + } +} + +template +void QueuedRedis::_sanity_check() const { + if (!_valid) { + throw Error("Not in valid state"); + } + + if (_connection->broken()) { + throw Error("Connection is broken"); + } +} + +template +inline void QueuedRedis::_reset() { + _cmd_num = 0; + + _set_cmd_indexes.clear(); + + _georadius_cmd_indexes.clear(); +} + +template +void QueuedRedis::_invalidate() { + _valid = false; + + _reset(); +} + +template +void QueuedRedis::_rewrite_replies(std::vector &replies) const { + _rewrite_replies(_set_cmd_indexes, reply::rewrite_set_reply, replies); + + _rewrite_replies(_georadius_cmd_indexes, reply::rewrite_georadius_reply, replies); +} + +template +template +void QueuedRedis::_rewrite_replies(const std::vector &indexes, + Func rewriter, + std::vector &replies) const { + for (auto idx : indexes) { + assert(idx < replies.size()); + + auto &reply = replies[idx]; + + assert(reply); + + rewriter(*reply); + } +} + +inline std::size_t QueuedReplies::size() const { + return _replies.size(); +} + +inline redisReply& QueuedReplies::get(std::size_t idx) { + _index_check(idx); + + auto &reply = _replies[idx]; + + assert(reply); + + return *reply; +} + +template +inline Result QueuedReplies::get(std::size_t idx) { + auto &reply = get(idx); + + return reply::parse(reply); +} + +template +inline void QueuedReplies::get(std::size_t idx, Output output) { + auto &reply = get(idx); + + reply::to_array(reply, output); +} + +inline void QueuedReplies::_index_check(std::size_t idx) const { + if (idx >= size()) { + throw Error("Out of range"); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis++.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis++.h new file mode 100644 index 00000000..0da0ebb1 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis++.h @@ -0,0 +1,25 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H +#define SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H + +#include "redis.h" +#include "redis_cluster.h" +#include "queued_redis.h" +#include "sentinel.h" + +#endif // end SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.h new file mode 100644 index 00000000..b54afb96 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.h @@ -0,0 +1,1523 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_H +#define SEWENEW_REDISPLUSPLUS_REDIS_H + +#include +#include +#include +#include +#include +#include "connection_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "sentinel.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class Redis { +public: + Redis(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : _pool(pool_opts, connection_opts) {} + + // Construct Redis instance with URI: + // "tcp://127.0.0.1", "tcp://127.0.0.1:6379", or "unix://path/to/socket" + explicit Redis(const std::string &uri); + + Redis(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role, + const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(SimpleSentinel(sentinel, master_name, role), pool_opts, connection_opts) {} + + Redis(const Redis &) = delete; + Redis& operator=(const Redis &) = delete; + + Redis(Redis &&) = default; + Redis& operator=(Redis &&) = default; + + Pipeline pipeline(); + + Transaction transaction(bool piped = false); + + Subscriber subscriber(); + + template + auto command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type; + + template + Result command(const StringView &cmd_name, Args &&...args); + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // CONNECTION commands. + + void auth(const StringView &password); + + std::string echo(const StringView &msg); + + std::string ping(); + + std::string ping(const StringView &msg); + + // After sending QUIT, only the current connection will be close, while + // other connections in the pool is still open. This is a strange behavior. + // So we DO NOT support the QUIT command. If you want to quit connection to + // server, just destroy the Redis object. + // + // void quit(); + + // We get a connection from the pool, and send the SELECT command to switch + // to a specified DB. However, when we try to send other commands to the + // given DB, we might get a different connection from the pool, and these + // commands, in fact, work on other DB. e.g. + // + // redis.select(1); // get a connection from the pool and switch to the 1th DB + // redis.get("key"); // might get another connection from the pool, + // // and try to get 'key' on the default DB + // + // Obviously, this is NOT what we expect. So we DO NOT support SELECT command. + // In order to select a DB, we can specify the DB index with the ConnectionOptions. + // + // However, since Pipeline and Transaction always send multiple commands on a + // single connection, these two classes have a *select* method. + // + // void select(long long idx); + + void swapdb(long long idx1, long long idx2); + + // SERVER commands. + + void bgrewriteaof(); + + void bgsave(); + + long long dbsize(); + + void flushall(bool async = false); + + void flushdb(bool async = false); + + std::string info(); + + std::string info(const StringView §ion); + + long long lastsave(); + + void save(); + + // KEY commands. + + long long del(const StringView &key); + + template + long long del(Input first, Input last); + + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + OptionalString dump(const StringView &key); + + long long exists(const StringView &key); + + template + long long exists(Input first, Input last); + + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + bool expire(const StringView &key, long long timeout); + + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + bool expireat(const StringView &key, long long timestamp); + + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + template + void keys(const StringView &pattern, Output output); + + bool move(const StringView &key, long long db); + + bool persist(const StringView &key); + + bool pexpire(const StringView &key, long long timeout); + + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + bool pexpireat(const StringView &key, long long timestamp); + + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + long long pttl(const StringView &key); + + OptionalString randomkey(); + + void rename(const StringView &key, const StringView &newkey); + + bool renamenx(const StringView &key, const StringView &newkey); + + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + template + long long scan(long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long scan(long long cursor, + Output output); + + template + long long scan(long long cursor, + const StringView &pattern, + Output output); + + template + long long scan(long long cursor, + long long count, + Output output); + + long long touch(const StringView &key); + + template + long long touch(Input first, Input last); + + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + long long ttl(const StringView &key); + + std::string type(const StringView &key); + + long long unlink(const StringView &key); + + template + long long unlink(Input first, Input last); + + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + long long wait(long long numslaves, long long timeout); + + long long wait(long long numslaves, const std::chrono::milliseconds &timeout); + + // STRING commands. + + long long append(const StringView &key, const StringView &str); + + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + long long decr(const StringView &key); + + long long decrby(const StringView &key, long long decrement); + + OptionalString get(const StringView &key); + + long long getbit(const StringView &key, long long offset); + + std::string getrange(const StringView &key, long long start, long long end); + + OptionalString getset(const StringView &key, const StringView &val); + + long long incr(const StringView &key); + + long long incrby(const StringView &key, long long increment); + + double incrbyfloat(const StringView &key, double increment); + + template + void mget(Input first, Input last, Output output); + + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + template + void mset(Input first, Input last); + + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + template + bool msetnx(Input first, Input last); + + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + void setex(const StringView &key, + long long ttl, + const StringView &val); + + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + bool setnx(const StringView &key, const StringView &val); + + long long setrange(const StringView &key, long long offset, const StringView &val); + + long long strlen(const StringView &key); + + // LIST commands. + + OptionalStringPair blpop(const StringView &key, long long timeout); + + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + OptionalStringPair brpop(const StringView &key, long long timeout); + + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + OptionalString lindex(const StringView &key, long long index); + + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + long long llen(const StringView &key); + + OptionalString lpop(const StringView &key); + + long long lpush(const StringView &key, const StringView &val); + + template + long long lpush(const StringView &key, Input first, Input last); + + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + long long lpushx(const StringView &key, const StringView &val); + + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + long long lrem(const StringView &key, long long count, const StringView &val); + + void lset(const StringView &key, long long index, const StringView &val); + + void ltrim(const StringView &key, long long start, long long stop); + + OptionalString rpop(const StringView &key); + + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + long long rpush(const StringView &key, const StringView &val); + + template + long long rpush(const StringView &key, Input first, Input last); + + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + long long hdel(const StringView &key, const StringView &field); + + template + long long hdel(const StringView &key, Input first, Input last); + + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + bool hexists(const StringView &key, const StringView &field); + + OptionalString hget(const StringView &key, const StringView &field); + + template + void hgetall(const StringView &key, Output output); + + long long hincrby(const StringView &key, const StringView &field, long long increment); + + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + template + void hkeys(const StringView &key, Output output); + + long long hlen(const StringView &key); + + template + void hmget(const StringView &key, Input first, Input last, Output output); + + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + template + void hmset(const StringView &key, Input first, Input last); + + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + bool hset(const StringView &key, const StringView &field, const StringView &val); + + bool hset(const StringView &key, const std::pair &item); + + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + bool hsetnx(const StringView &key, const std::pair &item); + + long long hstrlen(const StringView &key, const StringView &field); + + template + void hvals(const StringView &key, Output output); + + // SET commands. + + long long sadd(const StringView &key, const StringView &member); + + template + long long sadd(const StringView &key, Input first, Input last); + + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + long long scard(const StringView &key); + + template + void sdiff(Input first, Input last, Output output); + + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + long long sdiffstore(const StringView &destination, const StringView &key); + + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + void sinter(Input first, Input last, Output output); + + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + long long sinterstore(const StringView &destination, const StringView &key); + + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + bool sismember(const StringView &key, const StringView &member); + + template + void smembers(const StringView &key, Output output); + + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + OptionalString spop(const StringView &key); + + template + void spop(const StringView &key, long long count, Output output); + + OptionalString srandmember(const StringView &key); + + template + void srandmember(const StringView &key, long long count, Output output); + + long long srem(const StringView &key, const StringView &member); + + template + long long srem(const StringView &key, Input first, Input last); + + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + template + void sunion(Input first, Input last, Output output); + + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + long long sunionstore(const StringView &destination, const StringView &key); + + template + long long sunionstore(const StringView &destination, Input first, Input last); + + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + long long zcard(const StringView &key); + + template + long long zcount(const StringView &key, const Interval &interval); + + double zincrby(const StringView &key, double increment, const StringView &member); + + // There's no aggregation type parameter for single key overload, since these 3 types + // have the same effect. + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + // If *Input* is an iterator of a container of string, + // we use the default weight, i.e. 1, and send + // *ZINTERSTORE destination numkeys key [key ...] [AGGREGATE SUM|MIN|MAX]* command. + // If *Input* is an iterator of a container of pair, i.e. key-weight pair, + // we send the command with the given weights: + // *ZINTERSTORE destination numkeys key [key ...] + // [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]* + // + // The following code use the default weight: + // + // vector keys = {"k1", "k2", "k3"}; + // redis.zinterstore(destination, keys.begin(), keys.end()); + // + // On the other hand, the following code use the given weights: + // + // vector> keys_with_weights = {{"k1", 1}, {"k2", 2}, {"k3", 3}}; + // redis.zinterstore(destination, keys_with_weights.begin(), keys_with_weights.end()); + // + // NOTE: `keys_with_weights` can also be of type `unordered_map`. + // However, it will be slower than vector>, since we use + // `distance(first, last)` to calculate the *numkeys* parameter. + // + // This also applies to *ZUNIONSTORE* command. + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + long long zlexcount(const StringView &key, const Interval &interval); + + Optional> zpopmax(const StringView &key); + + template + void zpopmax(const StringView &key, long long count, Output output); + + Optional> zpopmin(const StringView &key); + + template + void zpopmin(const StringView &key, long long count, Output output); + + // If *output* is an iterator of a container of string, + // we send *ZRANGE key start stop* command. + // If it's an iterator of a container of pair, + // we send *ZRANGE key start stop WITHSCORES* command. + // + // The following code sends *ZRANGE* without the *WITHSCORES* option: + // + // vector result; + // redis.zrange("key", 0, -1, back_inserter(result)); + // + // On the other hand, the following code sends command with *WITHSCORES* option: + // + // unordered_map with_score; + // redis.zrange("key", 0, -1, inserter(with_score, with_score.end())); + // + // This also applies to other commands with the *WITHSCORES* option, + // e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, *ZREVRANGEBYSCORE*. + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrank(const StringView &key, const StringView &member); + + long long zrem(const StringView &key, const StringView &member); + + template + long long zrem(const StringView &key, Input first, Input last); + + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + OptionalDouble zscore(const StringView &key, const StringView &member); + + // There's no aggregation type parameter for single key overload, since these 3 types + // have the same effect. + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + // See *zinterstore* comment for how to use this method. + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + bool pfadd(const StringView &key, const StringView &element); + + template + bool pfadd(const StringView &key, Input first, Input last); + + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + void script_exists(Input first, Input last, Output output); + + template + void script_exists(std::initializer_list il, Output output) { + script_exists(il.begin(), il.end(), output); + } + + void script_flush(); + + void script_kill(); + + std::string script_load(const StringView &script); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Transaction commands. + void watch(const StringView &key); + + template + void watch(Input first, Input last); + + template + void watch(std::initializer_list il) { + watch(il.begin(), il.end()); + } + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first ,last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + class ConnectionPoolGuard { + public: + ConnectionPoolGuard(ConnectionPool &pool, + Connection &connection) : _pool(pool), _connection(connection) {} + + ~ConnectionPoolGuard() { + _pool.release(std::move(_connection)); + } + + private: + ConnectionPool &_pool; + Connection &_connection; + }; + + template + friend class QueuedRedis; + + friend class RedisCluster; + + // For internal use. + explicit Redis(const ConnectionSPtr &connection); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _command(Connection &connection, Cmd cmd, Args &&...args); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + // Pool Mode. + // Public constructors create a *Redis* instance with a pool. + // In this case, *_connection* is a null pointer, and is never used. + ConnectionPool _pool; + + // Single Connection Mode. + // Private constructor creats a *Redis* instance with a single connection. + // This is used when we create Transaction, Pipeline and Subscriber. + // In this case, *_pool* is empty, and is never used. + ConnectionSPtr _connection; +}; + +} + +} + +#include "redis.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.hpp b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.hpp new file mode 100644 index 00000000..3a227a6f --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis.hpp @@ -0,0 +1,1365 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_HPP + +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +template +auto Redis::command(Cmd cmd, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (_connection) { + // Single Connection Mode. + // TODO: In this case, should we reconnect? + if (_connection->broken()) { + throw Error("Connection is broken"); + } + + return _command(*_connection, cmd, std::forward(args)...); + } else { + // Pool Mode, i.e. get connection from pool. + auto connection = _pool.fetch(); + + assert(!connection.broken()); + + ConnectionPoolGuard guard(_pool, connection); + + return _command(connection, cmd, std::forward(args)...); + } +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, ReplyUPtr>::type { + auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) { + CmdArgs cmd_args; + cmd_args.append(cmd_name, std::forward(args)...); + connection.send(cmd_args); + }; + + return command(cmd, cmd_name, std::forward(args)...); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (first == last) { + throw Error("command: empty range"); + } + + auto cmd = [](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +Result Redis::command(const StringView &cmd_name, Args &&...args) { + auto r = command(cmd_name, std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(const StringView &cmd_name, Args &&...args) + -> typename std::enable_if::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto Redis::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto Redis::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long Redis::del(Input first, Input last) { + if (first == last) { + throw Error("DEL: no key specified"); + } + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::exists(Input first, Input last) { + if (first == last) { + throw Error("EXISTS: no key specified"); + } + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool Redis::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool Redis::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +template +void Redis::keys(const StringView &pattern, Output output) { + auto reply = command(cmd::keys, pattern); + + reply::to_array(*reply, output); +} + +inline bool Redis::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool Redis::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void Redis::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long Redis::scan(long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::scan, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::scan(long long cursor, + const StringView &pattern, + Output output) { + return scan(cursor, pattern, 10, output); +} + +template +inline long long Redis::scan(long long cursor, + long long count, + Output output) { + return scan(cursor, "*", count, output); +} + +template +inline long long Redis::scan(long long cursor, + Output output) { + return scan(cursor, "*", 10, output); +} + +template +long long Redis::touch(Input first, Input last) { + if (first == last) { + throw Error("TOUCH: no key specified"); + } + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::unlink(Input first, Input last) { + if (first == last) { + throw Error("UNLINK: no key specified"); + } + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +inline long long Redis::wait(long long numslaves, const std::chrono::milliseconds &timeout) { + return wait(numslaves, timeout.count()); +} + +// STRING commands. + +template +long long Redis::bitop(BitOp op, const StringView &destination, Input first, Input last) { + if (first == last) { + throw Error("BITOP: no key specified"); + } + + auto reply = command(cmd::bitop_range, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::mget(Input first, Input last, Output output) { + if (first == last) { + throw Error("MGET: no key specified"); + } + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::mset(Input first, Input last) { + if (first == last) { + throw Error("MSET: no key specified"); + } + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool Redis::msetnx(Input first, Input last) { + if (first == last) { + throw Error("MSETNX: no key specified"); + } + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void Redis::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void Redis::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair Redis::blpop(Input first, Input last, long long timeout) { + if (first == last) { + throw Error("BLPOP: no key specified"); + } + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair Redis::brpop(Input first, Input last, long long timeout) { + if (first == last) { + throw Error("BRPOP: no key specified"); + } + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair Redis::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString Redis::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long Redis::lpush(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("LPUSH: no key specified"); + } + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long Redis::rpush(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("RPUSH: no key specified"); + } + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long Redis::hdel(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("HDEL: no key specified"); + } + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void Redis::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmget(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("HMGET: no key specified"); + } + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void Redis::hmset(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("HMSET: no key specified"); + } + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +inline void Redis::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long Redis::sadd(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("SADD: no key specified"); + } + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sdiff(Input first, Input last, Output output) { + if (first == last) { + throw Error("SDIFF: no key specified"); + } + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sdiffstore(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("SDIFFSTORE: no key specified"); + } + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::sinter(Input first, Input last, Output output) { + if (first == last) { + throw Error("SINTER: no key specified"); + } + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sinterstore(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("SINTERSTORE: no key specified"); + } + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void Redis::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void Redis::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long Redis::srem(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("SREM: no key specified"); + } + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void Redis::sunion(Input first, Input last, Output output) { + if (first == last) { + throw Error("SUNION: no key specified"); + } + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long Redis::sunionstore(const StringView &destination, Input first, Input last) { + if (first == last) { + throw Error("SUNIONSTORE: no key specified"); + } + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto Redis::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto Redis::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto Redis::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto Redis::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto Redis::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long Redis::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + if (first == last) { + throw Error("ZADD: no key specified"); + } + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long Redis::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + if (first == last) { + throw Error("ZINTERSTORE: no key specified"); + } + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long Redis::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void Redis::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void Redis::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zrem(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("ZREM: no key specified"); + } + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long Redis::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void Redis::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void Redis::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void Redis::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long Redis::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long Redis::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + if (first == last) { + throw Error("ZUNIONSTORE: no key specified"); + } + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool Redis::pfadd(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("PFADD: no key specified"); + } + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long Redis::pfcount(Input first, Input last) { + if (first == last) { + throw Error("PFCOUNT: no key specified"); + } + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void Redis::pfmerge(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("PFMERGE: no key specified"); + } + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long Redis::geoadd(const StringView &key, + Input first, + Input last) { + if (first == last) { + throw Error("GEOADD: no key specified"); + } + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void Redis::geohash(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("GEOHASH: no key specified"); + } + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::geopos(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("GEOPOS: no key specified"); + } + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void Redis::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void Redis::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + auto reply = command(cmd::eval, script, keys, args); + + return reply::parse(*reply); +} + +template +void Redis::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + auto reply = command(cmd::eval, script, keys, args); + + reply::to_array(*reply, output); +} + +template +Result Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + auto reply = command(cmd::evalsha, script, keys, args); + + return reply::parse(*reply); +} + +template +void Redis::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + auto reply = command(cmd::evalsha, script, keys, args); + + reply::to_array(*reply, output); +} + +template +void Redis::script_exists(Input first, Input last, Output output) { + if (first == last) { + throw Error("SCRIPT EXISTS: no key specified"); + } + + auto reply = command(cmd::script_exists_range, first, last); + + reply::to_array(*reply, output); +} + +// Transaction commands. + +template +void Redis::watch(Input first, Input last) { + auto reply = command(cmd::watch_range, first, last); + + reply::parse(*reply); +} + +// Stream commands. + +template +long long Redis::xack(const StringView &key, const StringView &group, Input first, Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, const StringView &id, Input first, Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string Redis::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void Redis::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long Redis::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto Redis::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void Redis::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREAD: no key specified"); + } + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREAD: no key specified"); + } + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREADGROUP: no key specified"); + } + + auto reply = command(cmd::xreadgroup_range, group, consumer, first, last, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = command(cmd::xreadgroup_block, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto Redis::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREADGROUP: no key specified"); + } + + auto reply = command(cmd::xreadgroup_block_range, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void Redis::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +ReplyUPtr Redis::_command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + auto reply = connection.recv(); + + return reply; +} + +template +inline ReplyUPtr Redis::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr Redis::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr Redis::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_HPP diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.h new file mode 100644 index 00000000..50a22136 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.h @@ -0,0 +1,1395 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H + +#include +#include +#include +#include +#include "shards_pool.h" +#include "reply.h" +#include "command_options.h" +#include "utils.h" +#include "subscriber.h" +#include "pipeline.h" +#include "transaction.h" +#include "redis.h" + +namespace sw { + +namespace redis { + +template +class QueuedRedis; + +using Transaction = QueuedRedis; + +using Pipeline = QueuedRedis; + +class RedisCluster { +public: + RedisCluster(const ConnectionOptions &connection_opts, + const ConnectionPoolOptions &pool_opts = {}) : + _pool(pool_opts, connection_opts) {} + + // Construct RedisCluster with URI: + // "tcp://127.0.0.1" or "tcp://127.0.0.1:6379" + // Only need to specify one URI. + explicit RedisCluster(const std::string &uri); + + RedisCluster(const RedisCluster &) = delete; + RedisCluster& operator=(const RedisCluster &) = delete; + + RedisCluster(RedisCluster &&) = default; + RedisCluster& operator=(RedisCluster &&) = default; + + Redis redis(const StringView &hash_tag); + + Pipeline pipeline(const StringView &hash_tag); + + Transaction transaction(const StringView &hash_tag, bool piped = false); + + Subscriber subscriber(); + + template + auto command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type; + + template + auto command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type; + + template + auto command(Input first, Input last) + -> typename std::enable_if::value, Result>::type; + + template + auto command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type; + + // KEY commands. + + long long del(const StringView &key); + + template + long long del(Input first, Input last); + + template + long long del(std::initializer_list il) { + return del(il.begin(), il.end()); + } + + OptionalString dump(const StringView &key); + + long long exists(const StringView &key); + + template + long long exists(Input first, Input last); + + template + long long exists(std::initializer_list il) { + return exists(il.begin(), il.end()); + } + + bool expire(const StringView &key, long long timeout); + + bool expire(const StringView &key, const std::chrono::seconds &timeout); + + bool expireat(const StringView &key, long long timestamp); + + bool expireat(const StringView &key, + const std::chrono::time_point &tp); + + bool persist(const StringView &key); + + bool pexpire(const StringView &key, long long timeout); + + bool pexpire(const StringView &key, const std::chrono::milliseconds &timeout); + + bool pexpireat(const StringView &key, long long timestamp); + + bool pexpireat(const StringView &key, + const std::chrono::time_point &tp); + + long long pttl(const StringView &key); + + void rename(const StringView &key, const StringView &newkey); + + bool renamenx(const StringView &key, const StringView &newkey); + + void restore(const StringView &key, + const StringView &val, + long long ttl, + bool replace = false); + + void restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds{0}, + bool replace = false); + + // TODO: sort + + long long touch(const StringView &key); + + template + long long touch(Input first, Input last); + + template + long long touch(std::initializer_list il) { + return touch(il.begin(), il.end()); + } + + long long ttl(const StringView &key); + + std::string type(const StringView &key); + + long long unlink(const StringView &key); + + template + long long unlink(Input first, Input last); + + template + long long unlink(std::initializer_list il) { + return unlink(il.begin(), il.end()); + } + + // STRING commands. + + long long append(const StringView &key, const StringView &str); + + long long bitcount(const StringView &key, long long start = 0, long long end = -1); + + long long bitop(BitOp op, const StringView &destination, const StringView &key); + + template + long long bitop(BitOp op, const StringView &destination, Input first, Input last); + + template + long long bitop(BitOp op, const StringView &destination, std::initializer_list il) { + return bitop(op, destination, il.begin(), il.end()); + } + + long long bitpos(const StringView &key, + long long bit, + long long start = 0, + long long end = -1); + + long long decr(const StringView &key); + + long long decrby(const StringView &key, long long decrement); + + OptionalString get(const StringView &key); + + long long getbit(const StringView &key, long long offset); + + std::string getrange(const StringView &key, long long start, long long end); + + OptionalString getset(const StringView &key, const StringView &val); + + long long incr(const StringView &key); + + long long incrby(const StringView &key, long long increment); + + double incrbyfloat(const StringView &key, double increment); + + template + void mget(Input first, Input last, Output output); + + template + void mget(std::initializer_list il, Output output) { + mget(il.begin(), il.end(), output); + } + + template + void mset(Input first, Input last); + + template + void mset(std::initializer_list il) { + mset(il.begin(), il.end()); + } + + template + bool msetnx(Input first, Input last); + + template + bool msetnx(std::initializer_list il) { + return msetnx(il.begin(), il.end()); + } + + void psetex(const StringView &key, + long long ttl, + const StringView &val); + + void psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val); + + bool set(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), + UpdateType type = UpdateType::ALWAYS); + + void setex(const StringView &key, + long long ttl, + const StringView &val); + + void setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val); + + bool setnx(const StringView &key, const StringView &val); + + long long setrange(const StringView &key, long long offset, const StringView &val); + + long long strlen(const StringView &key); + + // LIST commands. + + OptionalStringPair blpop(const StringView &key, long long timeout); + + OptionalStringPair blpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(Input first, Input last, long long timeout); + + template + OptionalStringPair blpop(std::initializer_list il, long long timeout) { + return blpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair blpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair blpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return blpop(il.begin(), il.end(), timeout); + } + + OptionalStringPair brpop(const StringView &key, long long timeout); + + OptionalStringPair brpop(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(Input first, Input last, long long timeout); + + template + OptionalStringPair brpop(std::initializer_list il, long long timeout) { + return brpop(il.begin(), il.end(), timeout); + } + + template + OptionalStringPair brpop(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + template + OptionalStringPair brpop(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) { + return brpop(il.begin(), il.end(), timeout); + } + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + long long timeout); + + OptionalString brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout = std::chrono::seconds{0}); + + OptionalString lindex(const StringView &key, long long index); + + long long linsert(const StringView &key, + InsertPosition position, + const StringView &pivot, + const StringView &val); + + long long llen(const StringView &key); + + OptionalString lpop(const StringView &key); + + long long lpush(const StringView &key, const StringView &val); + + template + long long lpush(const StringView &key, Input first, Input last); + + template + long long lpush(const StringView &key, std::initializer_list il) { + return lpush(key, il.begin(), il.end()); + } + + long long lpushx(const StringView &key, const StringView &val); + + template + void lrange(const StringView &key, long long start, long long stop, Output output); + + long long lrem(const StringView &key, long long count, const StringView &val); + + void lset(const StringView &key, long long index, const StringView &val); + + void ltrim(const StringView &key, long long start, long long stop); + + OptionalString rpop(const StringView &key); + + OptionalString rpoplpush(const StringView &source, const StringView &destination); + + long long rpush(const StringView &key, const StringView &val); + + template + long long rpush(const StringView &key, Input first, Input last); + + template + long long rpush(const StringView &key, std::initializer_list il) { + return rpush(key, il.begin(), il.end()); + } + + long long rpushx(const StringView &key, const StringView &val); + + // HASH commands. + + long long hdel(const StringView &key, const StringView &field); + + template + long long hdel(const StringView &key, Input first, Input last); + + template + long long hdel(const StringView &key, std::initializer_list il) { + return hdel(key, il.begin(), il.end()); + } + + bool hexists(const StringView &key, const StringView &field); + + OptionalString hget(const StringView &key, const StringView &field); + + template + void hgetall(const StringView &key, Output output); + + long long hincrby(const StringView &key, const StringView &field, long long increment); + + double hincrbyfloat(const StringView &key, const StringView &field, double increment); + + template + void hkeys(const StringView &key, Output output); + + long long hlen(const StringView &key); + + template + void hmget(const StringView &key, Input first, Input last, Output output); + + template + void hmget(const StringView &key, std::initializer_list il, Output output) { + hmget(key, il.begin(), il.end(), output); + } + + template + void hmset(const StringView &key, Input first, Input last); + + template + void hmset(const StringView &key, std::initializer_list il) { + hmset(key, il.begin(), il.end()); + } + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long hscan(const StringView &key, + long long cursor, + Output output); + + bool hset(const StringView &key, const StringView &field, const StringView &val); + + bool hset(const StringView &key, const std::pair &item); + + bool hsetnx(const StringView &key, const StringView &field, const StringView &val); + + bool hsetnx(const StringView &key, const std::pair &item); + + long long hstrlen(const StringView &key, const StringView &field); + + template + void hvals(const StringView &key, Output output); + + // SET commands. + + long long sadd(const StringView &key, const StringView &member); + + template + long long sadd(const StringView &key, Input first, Input last); + + template + long long sadd(const StringView &key, std::initializer_list il) { + return sadd(key, il.begin(), il.end()); + } + + long long scard(const StringView &key); + + template + void sdiff(Input first, Input last, Output output); + + template + void sdiff(std::initializer_list il, Output output) { + sdiff(il.begin(), il.end(), output); + } + + long long sdiffstore(const StringView &destination, const StringView &key); + + template + long long sdiffstore(const StringView &destination, + Input first, + Input last); + + template + long long sdiffstore(const StringView &destination, + std::initializer_list il) { + return sdiffstore(destination, il.begin(), il.end()); + } + + template + void sinter(Input first, Input last, Output output); + + template + void sinter(std::initializer_list il, Output output) { + sinter(il.begin(), il.end(), output); + } + + long long sinterstore(const StringView &destination, const StringView &key); + + template + long long sinterstore(const StringView &destination, + Input first, + Input last); + + template + long long sinterstore(const StringView &destination, + std::initializer_list il) { + return sinterstore(destination, il.begin(), il.end()); + } + + bool sismember(const StringView &key, const StringView &member); + + template + void smembers(const StringView &key, Output output); + + bool smove(const StringView &source, + const StringView &destination, + const StringView &member); + + OptionalString spop(const StringView &key); + + template + void spop(const StringView &key, long long count, Output output); + + OptionalString srandmember(const StringView &key); + + template + void srandmember(const StringView &key, long long count, Output output); + + long long srem(const StringView &key, const StringView &member); + + template + long long srem(const StringView &key, Input first, Input last); + + template + long long srem(const StringView &key, std::initializer_list il) { + return srem(key, il.begin(), il.end()); + } + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long sscan(const StringView &key, + long long cursor, + Output output); + + template + void sunion(Input first, Input last, Output output); + + template + void sunion(std::initializer_list il, Output output) { + sunion(il.begin(), il.end(), output); + } + + long long sunionstore(const StringView &destination, const StringView &key); + + template + long long sunionstore(const StringView &destination, Input first, Input last); + + template + long long sunionstore(const StringView &destination, std::initializer_list il) { + return sunionstore(destination, il.begin(), il.end()); + } + + // SORTED SET commands. + + auto bzpopmax(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmax(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmax(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + template + auto bzpopmax(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmax(il.begin(), il.end(), timeout); + } + + auto bzpopmin(const StringView &key, long long timeout) + -> Optional>; + + auto bzpopmin(const StringView &key, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(Input first, Input last, long long timeout) + -> Optional>; + + template + auto bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional>; + + template + auto bzpopmin(std::initializer_list il, long long timeout) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + template + auto bzpopmin(std::initializer_list il, + const std::chrono::seconds &timeout = std::chrono::seconds{0}) + -> Optional> { + return bzpopmin(il.begin(), il.end(), timeout); + } + + // We don't support the INCR option, since you can always use ZINCRBY instead. + long long zadd(const StringView &key, + const StringView &member, + double score, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + Input first, + Input last, + UpdateType type = UpdateType::ALWAYS, + bool changed = false); + + template + long long zadd(const StringView &key, + std::initializer_list il, + UpdateType type = UpdateType::ALWAYS, + bool changed = false) { + return zadd(key, il.begin(), il.end(), type, changed); + } + + long long zcard(const StringView &key); + + template + long long zcount(const StringView &key, const Interval &interval); + + double zincrby(const StringView &key, double increment, const StringView &member); + + long long zinterstore(const StringView &destination, const StringView &key, double weight); + + template + long long zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zinterstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zinterstore(destination, il.begin(), il.end(), type); + } + + template + long long zlexcount(const StringView &key, const Interval &interval); + + Optional> zpopmax(const StringView &key); + + template + void zpopmax(const StringView &key, long long count, Output output); + + Optional> zpopmin(const StringView &key); + + template + void zpopmin(const StringView &key, long long count, Output output); + + // If *output* is an iterator of a container of string, + // we send *ZRANGE key start stop* command. + // If it's an iterator of a container of pair, + // we send *ZRANGE key start stop WITHSCORES* command. + // + // The following code sends *ZRANGE* without the *WITHSCORES* option: + // + // vector result; + // redis.zrange("key", 0, -1, back_inserter(result)); + // + // On the other hand, the following code sends command with *WITHSCORES* option: + // + // unordered_map with_score; + // redis.zrange("key", 0, -1, inserter(with_score, with_score.end())); + // + // This also applies to other commands with the *WITHSCORES* option, + // e.g. *ZRANGEBYSCORE*, *ZREVRANGE*, *ZREVRANGEBYSCORE*. + template + void zrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrank(const StringView &key, const StringView &member); + + long long zrem(const StringView &key, const StringView &member); + + template + long long zrem(const StringView &key, Input first, Input last); + + template + long long zrem(const StringView &key, std::initializer_list il) { + return zrem(key, il.begin(), il.end()); + } + + template + long long zremrangebylex(const StringView &key, const Interval &interval); + + long long zremrangebyrank(const StringView &key, long long start, long long stop); + + template + long long zremrangebyscore(const StringView &key, const Interval &interval); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrange(const StringView &key, long long start, long long stop, Output output); + + template + void zrevrangebylex(const StringView &key, const Interval &interval, Output output); + + template + void zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, const Interval &interval, Output output); + + // See *zrange* comment on how to send command with *WITHSCORES* option. + template + void zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output); + + OptionalLongLong zrevrank(const StringView &key, const StringView &member); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + long long count, + Output output); + + template + long long zscan(const StringView &key, + long long cursor, + Output output); + + OptionalDouble zscore(const StringView &key, const StringView &member); + + long long zunionstore(const StringView &destination, const StringView &key, double weight); + + template + long long zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type = Aggregation::SUM); + + template + long long zunionstore(const StringView &destination, + std::initializer_list il, + Aggregation type = Aggregation::SUM) { + return zunionstore(destination, il.begin(), il.end(), type); + } + + // HYPERLOGLOG commands. + + bool pfadd(const StringView &key, const StringView &element); + + template + bool pfadd(const StringView &key, Input first, Input last); + + template + bool pfadd(const StringView &key, std::initializer_list il) { + return pfadd(key, il.begin(), il.end()); + } + + long long pfcount(const StringView &key); + + template + long long pfcount(Input first, Input last); + + template + long long pfcount(std::initializer_list il) { + return pfcount(il.begin(), il.end()); + } + + void pfmerge(const StringView &destination, const StringView &key); + + template + void pfmerge(const StringView &destination, Input first, Input last); + + template + void pfmerge(const StringView &destination, std::initializer_list il) { + pfmerge(destination, il.begin(), il.end()); + } + + // GEO commands. + + long long geoadd(const StringView &key, + const std::tuple &member); + + template + long long geoadd(const StringView &key, + Input first, + Input last); + + template + long long geoadd(const StringView &key, + std::initializer_list il) { + return geoadd(key, il.begin(), il.end()); + } + + OptionalDouble geodist(const StringView &key, + const StringView &member1, + const StringView &member2, + GeoUnit unit = GeoUnit::M); + + template + void geohash(const StringView &key, Input first, Input last, Output output); + + template + void geohash(const StringView &key, std::initializer_list il, Output output) { + geohash(key, il.begin(), il.end(), output); + } + + template + void geopos(const StringView &key, Input first, Input last, Output output); + + template + void geopos(const StringView &key, std::initializer_list il, Output output) { + geopos(key, il.begin(), il.end(), output); + } + + // TODO: + // 1. since we have different overloads for georadius and georadius-store, + // we might use the GEORADIUS_RO command in the future. + // 2. there're too many parameters for this method, we might refactor it. + OptionalLongLong georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // If *output* is an iterator of a container of string, we send *GEORADIUS* command + // without any options and only get the members in the specified geo range. + // If *output* is an iterator of a container of a tuple, the type of the tuple decides + // options we send with the *GEORADIUS* command. If the tuple has an element of type + // double, we send the *WITHDIST* option. If it has an element of type string, we send + // the *WITHHASH* option. If it has an element of type pair, we send + // the *WITHCOORD* option. For example: + // + // The following code only gets the members in range, i.e. without any option. + // + // vector members; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(members)) + // + // The following code sends the command with *WITHDIST* option. + // + // vector> with_dist; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist)) + // + // The following code sends the command with *WITHDIST* and *WITHHASH* options. + // + // vector> with_dist_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_hash)) + // + // The following code sends the command with *WITHDIST*, *WITHCOORD* and *WITHHASH* options. + // + // vector, string>> with_dist_coord_hash; + // redis.georadius("key", make_pair(10.1, 10.2), 10, GeoUnit::KM, 10, true, + // back_inserter(with_dist_coord_hash)) + // + // This also applies to *GEORADIUSBYMEMBER*. + template + void georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + OptionalLongLong georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + const StringView &destination, + bool store_dist, + long long count); + + // See comments on *GEORADIUS*. + template + void georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output); + + // SCRIPTING commands. + + template + Result eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + template + Result evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args); + + template + void evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output); + + // PUBSUB commands. + + long long publish(const StringView &channel, const StringView &message); + + // Stream commands. + + long long xack(const StringView &key, const StringView &group, const StringView &id); + + template + long long xack(const StringView &key, const StringView &group, Input first, Input last); + + template + long long xack(const StringView &key, const StringView &group, std::initializer_list il) { + return xack(key, group, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, const StringView &id, Input first, Input last); + + template + std::string xadd(const StringView &key, const StringView &id, std::initializer_list il) { + return xadd(key, id, il.begin(), il.end()); + } + + template + std::string xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx = true); + + template + std::string xadd(const StringView &key, + const StringView &id, + std::initializer_list il, + long long count, + bool approx = true) { + return xadd(key, id, il.begin(), il.end(), count, approx); + } + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output); + + template + void xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + std::initializer_list il, + Output output) { + xclaim(key, group, consumer, min_idle_time, il.begin(), il.end(), output); + } + + long long xdel(const StringView &key, const StringView &id); + + template + long long xdel(const StringView &key, Input first, Input last); + + template + long long xdel(const StringView &key, std::initializer_list il) { + return xdel(key, il.begin(), il.end()); + } + + void xgroup_create(const StringView &key, + const StringView &group, + const StringView &id, + bool mkstream = false); + + void xgroup_setid(const StringView &key, const StringView &group, const StringView &id); + + long long xgroup_destroy(const StringView &key, const StringView &group); + + long long xgroup_delconsumer(const StringView &key, + const StringView &group, + const StringView &consumer); + + long long xlen(const StringView &key); + + template + auto xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple; + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output); + + template + void xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + Output output) { + xread(key, id, 0, output); + } + + template + auto xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, Input last, Output output) + -> typename std::enable_if::value>::type { + xread(first, last, 0, output); + } + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output); + + template + void xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + xread(key, id, timeout, 0, output); + } + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xread(first, last, timeout, 0, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + Output output) { + xreadgroup(group, consumer, key, id, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + Output output) { + xreadgroup(group, consumer, key, id, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first ,last, 0, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output); + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, count, false, output); + } + + template + void xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + Output output) { + return xreadgroup(group, consumer, key, id, timeout, 0, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type; + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, count, false, output); + } + + template + auto xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + Output output) + -> typename std::enable_if::value>::type { + xreadgroup(group, consumer, first, last, timeout, 0, false, output); + } + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output); + + template + void xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output); + + long long xtrim(const StringView &key, long long count, bool approx = true); + +private: + class Command { + public: + explicit Command(const StringView &cmd_name) : _cmd_name(cmd_name) {} + + template + void operator()(Connection &connection, Args &&...args) const { + CmdArgs cmd_args; + cmd_args.append(_cmd_name, std::forward(args)...); + connection.send(cmd_args); + } + + private: + StringView _cmd_name; + }; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type; + + template + auto _generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type; + + template + ReplyUPtr _command(Cmd cmd, Connection &connection, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::true_type, const StringView &key, Args &&...args); + + template + ReplyUPtr _command(Cmd cmd, std::false_type, Input &&first, Args &&...args); + + template + ReplyUPtr _command(const StringView &cmd_name, const IndexSequence &, Args &&...args) { + return command(cmd_name, NthValue(std::forward(args)...)...); + } + + template + ReplyUPtr _range_command(Cmd cmd, std::true_type, Input input, Args &&...args); + + template + ReplyUPtr _range_command(Cmd cmd, std::false_type, Input input, Args &&...args); + + void _asking(Connection &connection); + + template + ReplyUPtr _score_command(std::true_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(std::false_type, Cmd cmd, Args &&... args); + + template + ReplyUPtr _score_command(Cmd cmd, Args &&... args); + + ShardsPool _pool; +}; + +} + +} + +#include "redis_cluster.hpp" + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.hpp b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.hpp new file mode 100644 index 00000000..61da3f06 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/redis_cluster.hpp @@ -0,0 +1,1415 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP +#define SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP + +#include +#include "command.h" +#include "reply.h" +#include "utils.h" +#include "errors.h" +#include "shards_pool.h" + +namespace sw { + +namespace redis { + +template +auto RedisCluster::command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, ReplyUPtr>::type { + return _command(cmd, + std::is_convertible::type, StringView>(), + std::forward(key), + std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && !IsIter::type>::value, ReplyUPtr>::type { + auto cmd = Command(cmd_name); + + return _generic_command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if::value + || std::is_arithmetic::type>::value, Result>::type { + auto r = command(cmd_name, std::forward(key), std::forward(args)...); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(const StringView &cmd_name, Key &&key, Args &&...args) + -> typename std::enable_if<(std::is_convertible::value + || std::is_arithmetic::type>::value) + && IsIter::type>::value, void>::type { + auto r = _command(cmd_name, + MakeIndexSequence(), + std::forward(key), + std::forward(args)...); + + assert(r); + + reply::to_array(*r, LastValue(std::forward(args)...)); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, ReplyUPtr>::type { + if (first == last || std::next(first) == last) { + throw Error("command: invalid range"); + } + + const auto &key = *first; + ++first; + + auto cmd = [&key](Connection &connection, Input first, Input last) { + CmdArgs cmd_args; + cmd_args.append(key); + while (first != last) { + cmd_args.append(*first); + ++first; + } + connection.send(cmd_args); + }; + + return command(cmd, first, last); +} + +template +auto RedisCluster::command(Input first, Input last) + -> typename std::enable_if::value, Result>::type { + auto r = command(first, last); + + assert(r); + + return reply::parse(*r); +} + +template +auto RedisCluster::command(Input first, Input last, Output output) + -> typename std::enable_if::value, void>::type { + auto r = command(first, last); + + assert(r); + + reply::to_array(*r, output); +} + +// KEY commands. + +template +long long RedisCluster::del(Input first, Input last) { + if (first == last) { + throw Error("DEL: no key specified"); + } + + auto reply = command(cmd::del_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::exists(Input first, Input last) { + if (first == last) { + throw Error("EXISTS: no key specified"); + } + + auto reply = command(cmd::exists_range, first, last); + + return reply::parse(*reply); +} + +inline bool RedisCluster::expire(const StringView &key, const std::chrono::seconds &timeout) { + return expire(key, timeout.count()); +} + +inline bool RedisCluster::expireat(const StringView &key, + const std::chrono::time_point &tp) { + return expireat(key, tp.time_since_epoch().count()); +} + +inline bool RedisCluster::pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { + return pexpire(key, timeout.count()); +} + +inline bool RedisCluster::pexpireat(const StringView &key, + const std::chrono::time_point &tp) { + return pexpireat(key, tp.time_since_epoch().count()); +} + +inline void RedisCluster::restore(const StringView &key, + const StringView &val, + const std::chrono::milliseconds &ttl, + bool replace) { + return restore(key, val, ttl.count(), replace); +} + +template +long long RedisCluster::touch(Input first, Input last) { + if (first == last) { + throw Error("TOUCH: no key specified"); + } + + auto reply = command(cmd::touch_range, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::unlink(Input first, Input last) { + if (first == last) { + throw Error("UNLINK: no key specified"); + } + + auto reply = command(cmd::unlink_range, first, last); + + return reply::parse(*reply); +} + +// STRING commands. + +template +long long RedisCluster::bitop(BitOp op, const StringView &destination, Input first, Input last) { + if (first == last) { + throw Error("BITOP: no key specified"); + } + + auto reply = _command(cmd::bitop_range, destination, op, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::mget(Input first, Input last, Output output) { + if (first == last) { + throw Error("MGET: no key specified"); + } + + auto reply = command(cmd::mget, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::mset(Input first, Input last) { + if (first == last) { + throw Error("MSET: no key specified"); + } + + auto reply = command(cmd::mset, first, last); + + reply::parse(*reply); +} + +template +bool RedisCluster::msetnx(Input first, Input last) { + if (first == last) { + throw Error("MSETNX: no key specified"); + } + + auto reply = command(cmd::msetnx, first, last); + + return reply::parse(*reply); +} + +inline void RedisCluster::psetex(const StringView &key, + const std::chrono::milliseconds &ttl, + const StringView &val) { + return psetex(key, ttl.count(), val); +} + +inline void RedisCluster::setex(const StringView &key, + const std::chrono::seconds &ttl, + const StringView &val) { + setex(key, ttl.count(), val); +} + +// LIST commands. + +template +OptionalStringPair RedisCluster::blpop(Input first, Input last, long long timeout) { + if (first == last) { + throw Error("BLPOP: no key specified"); + } + + auto reply = command(cmd::blpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::blpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return blpop(first, last, timeout.count()); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, Input last, long long timeout) { + if (first == last) { + throw Error("BRPOP: no key specified"); + } + + auto reply = command(cmd::brpop_range, first, last, timeout); + + return reply::parse(*reply); +} + +template +OptionalStringPair RedisCluster::brpop(Input first, + Input last, + const std::chrono::seconds &timeout) { + return brpop(first, last, timeout.count()); +} + +inline OptionalString RedisCluster::brpoplpush(const StringView &source, + const StringView &destination, + const std::chrono::seconds &timeout) { + return brpoplpush(source, destination, timeout.count()); +} + +template +inline long long RedisCluster::lpush(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("LPUSH: no key specified"); + } + + auto reply = command(cmd::lpush_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::lrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = command(cmd::lrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline long long RedisCluster::rpush(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("RPUSH: no key specified"); + } + + auto reply = command(cmd::rpush_range, key, first, last); + + return reply::parse(*reply); +} + +// HASH commands. + +template +inline long long RedisCluster::hdel(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("HDEL: no key specified"); + } + + auto reply = command(cmd::hdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +inline void RedisCluster::hgetall(const StringView &key, Output output) { + auto reply = command(cmd::hgetall, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hkeys(const StringView &key, Output output) { + auto reply = command(cmd::hkeys, key); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmget(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("HMGET: no key specified"); + } + + auto reply = command(cmd::hmget, key, first, last); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::hmset(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("HMSET: no key specified"); + } + + auto reply = command(cmd::hmset, key, first, last); + + reply::parse(*reply); +} + +template +long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::hscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return hscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return hscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::hscan(const StringView &key, + long long cursor, + Output output) { + return hscan(key, cursor, "*", 10, output); +} + +template +inline void RedisCluster::hvals(const StringView &key, Output output) { + auto reply = command(cmd::hvals, key); + + reply::to_array(*reply, output); +} + +// SET commands. + +template +long long RedisCluster::sadd(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("SADD: no key specified"); + } + + auto reply = command(cmd::sadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sdiff(Input first, Input last, Output output) { + if (first == last) { + throw Error("SDIFF: no key specified"); + } + + auto reply = command(cmd::sdiff, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sdiffstore(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("SDIFFSTORE: no key specified"); + } + + auto reply = command(cmd::sdiffstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::sinter(Input first, Input last, Output output) { + if (first == last) { + throw Error("SINTER: no key specified"); + } + + auto reply = command(cmd::sinter, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sinterstore(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("SINTERSTORE: no key specified"); + } + + auto reply = command(cmd::sinterstore_range, destination, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::smembers(const StringView &key, Output output) { + auto reply = command(cmd::smembers, key); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::spop(const StringView &key, long long count, Output output) { + auto reply = command(cmd::spop_range, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::srandmember(const StringView &key, long long count, Output output) { + auto reply = command(cmd::srandmember_range, key, count); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::srem(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("SREM: no key specified"); + } + + auto reply = command(cmd::srem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::sscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return sscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return sscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::sscan(const StringView &key, + long long cursor, + Output output) { + return sscan(key, cursor, "*", 10, output); +} + +template +void RedisCluster::sunion(Input first, Input last, Output output) { + if (first == last) { + throw Error("SUNION: no key specified"); + } + + auto reply = command(cmd::sunion, first, last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::sunionstore(const StringView &destination, Input first, Input last) { + if (first == last) { + throw Error("SUNIONSTORE: no key specified"); + } + + auto reply = command(cmd::sunionstore_range, destination, first, last); + + return reply::parse(*reply); +} + +// SORTED SET commands. + +inline auto RedisCluster::bzpopmax(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmax(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmax_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmax(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmax(first, last, timeout.count()); +} + +inline auto RedisCluster::bzpopmin(const StringView &key, const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(key, timeout.count()); +} + +template +auto RedisCluster::bzpopmin(Input first, Input last, long long timeout) + -> Optional> { + auto reply = command(cmd::bzpopmin_range, first, last, timeout); + + return reply::parse>>(*reply); +} + +template +inline auto RedisCluster::bzpopmin(Input first, + Input last, + const std::chrono::seconds &timeout) + -> Optional> { + return bzpopmin(first, last, timeout.count()); +} + +template +long long RedisCluster::zadd(const StringView &key, + Input first, + Input last, + UpdateType type, + bool changed) { + if (first == last) { + throw Error("ZADD: no key specified"); + } + + auto reply = command(cmd::zadd_range, key, first, last, type, changed); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zcount, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zinterstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + if (first == last) { + throw Error("ZINTERSTORE: no key specified"); + } + + auto reply = command(cmd::zinterstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zlexcount(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zlexcount, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zpopmax(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmax, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zpopmin(const StringView &key, long long count, Output output) { + auto reply = command(cmd::zpopmin, key, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, const Interval &interval, Output output) { + zrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + Output output) { + zrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrangebyscore, + key, + interval, + opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zrem(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("ZREM: no key specified"); + } + + auto reply = command(cmd::zrem_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebylex(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebylex, key, interval); + + return reply::parse(*reply); +} + +template +long long RedisCluster::zremrangebyscore(const StringView &key, const Interval &interval) { + auto reply = command(cmd::zremrangebyscore, key, interval); + + return reply::parse(*reply); +} + +template +void RedisCluster::zrevrange(const StringView &key, long long start, long long stop, Output output) { + auto reply = _score_command(cmd::zrevrange, key, start, stop); + + reply::to_array(*reply, output); +} + +template +inline void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + Output output) { + zrevrangebylex(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebylex(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = command(cmd::zrevrangebylex, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, const Interval &interval, Output output) { + zrevrangebyscore(key, interval, {}, output); +} + +template +void RedisCluster::zrevrangebyscore(const StringView &key, + const Interval &interval, + const LimitOptions &opts, + Output output) { + auto reply = _score_command(cmd::zrevrangebyscore, key, interval, opts); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + long long count, + Output output) { + auto reply = command(cmd::zscan, key, cursor, pattern, count); + + return reply::parse_scan_reply(*reply, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + const StringView &pattern, + Output output) { + return zscan(key, cursor, pattern, 10, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + long long count, + Output output) { + return zscan(key, cursor, "*", count, output); +} + +template +inline long long RedisCluster::zscan(const StringView &key, + long long cursor, + Output output) { + return zscan(key, cursor, "*", 10, output); +} + +template +long long RedisCluster::zunionstore(const StringView &destination, + Input first, + Input last, + Aggregation type) { + if (first == last) { + throw Error("ZUNIONSTORE: no key specified"); + } + + auto reply = command(cmd::zunionstore_range, + destination, + first, + last, + type); + + return reply::parse(*reply); +} + +// HYPERLOGLOG commands. + +template +bool RedisCluster::pfadd(const StringView &key, Input first, Input last) { + if (first == last) { + throw Error("PFADD: no key specified"); + } + + auto reply = command(cmd::pfadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +long long RedisCluster::pfcount(Input first, Input last) { + if (first == last) { + throw Error("PFCOUNT: no key specified"); + } + + auto reply = command(cmd::pfcount_range, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::pfmerge(const StringView &destination, + Input first, + Input last) { + if (first == last) { + throw Error("PFMERGE: no key specified"); + } + + auto reply = command(cmd::pfmerge_range, destination, first, last); + + reply::parse(*reply); +} + +// GEO commands. + +template +inline long long RedisCluster::geoadd(const StringView &key, + Input first, + Input last) { + if (first == last) { + throw Error("GEOADD: no key specified"); + } + + auto reply = command(cmd::geoadd_range, key, first, last); + + return reply::parse(*reply); +} + +template +void RedisCluster::geohash(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("GEOHASH: no key specified"); + } + + auto reply = command(cmd::geohash_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::geopos(const StringView &key, Input first, Input last, Output output) { + if (first == last) { + throw Error("GEOPOS: no key specified"); + } + + auto reply = command(cmd::geopos_range, key, first, last); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadius(const StringView &key, + const std::pair &loc, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadius, + key, + loc, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::georadiusbymember(const StringView &key, + const StringView &member, + double radius, + GeoUnit unit, + long long count, + bool asc, + Output output) { + auto reply = command(cmd::georadiusbymember, + key, + member, + radius, + unit, + count, + asc, + WithCoord::type>::value, + WithDist::type>::value, + WithHash::type>::value); + + reply::to_array(*reply, output); +} + +// SCRIPTING commands. + +template +Result RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + if (keys.size() == 0) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, *keys.begin(), script, keys, args); + + return reply::parse(*reply); +} + +template +void RedisCluster::eval(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + if (keys.size() == 0) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::eval, *keys.begin(), script, keys, args); + + reply::to_array(*reply, output); +} + +template +Result RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args) { + if (keys.size() == 0) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = _command(cmd::evalsha, *keys.begin(), script, keys, args); + + return reply::parse(*reply); +} + +template +void RedisCluster::evalsha(const StringView &script, + std::initializer_list keys, + std::initializer_list args, + Output output) { + if (keys.size() == 0) { + throw Error("DO NOT support Lua script without key"); + } + + auto reply = command(cmd::evalsha, *keys.begin(), script, keys, args); + + reply::to_array(*reply, output); +} + +// Stream commands. + +template +long long RedisCluster::xack(const StringView &key, + const StringView &group, + Input first, + Input last) { + auto reply = command(cmd::xack_range, key, group, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last) { + auto reply = command(cmd::xadd_range, key, id, first, last); + + return reply::parse(*reply); +} + +template +std::string RedisCluster::xadd(const StringView &key, + const StringView &id, + Input first, + Input last, + long long count, + bool approx) { + auto reply = command(cmd::xadd_maxlen_range, key, id, first, last, count, approx); + + return reply::parse(*reply); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + const StringView &id, + Output output) { + auto reply = command(cmd::xclaim, key, group, consumer, min_idle_time.count(), id); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xclaim(const StringView &key, + const StringView &group, + const StringView &consumer, + const std::chrono::milliseconds &min_idle_time, + Input first, + Input last, + Output output) { + auto reply = command(cmd::xclaim_range, + key, + group, + consumer, + min_idle_time.count(), + first, + last); + + reply::to_array(*reply, output); +} + +template +long long RedisCluster::xdel(const StringView &key, Input first, Input last) { + auto reply = command(cmd::xdel_range, key, first, last); + + return reply::parse(*reply); +} + +template +auto RedisCluster::xpending(const StringView &key, const StringView &group, Output output) + -> std::tuple { + auto reply = command(cmd::xpending, key, group); + + return reply::parse_xpending_reply(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xpending_detail, key, group, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xpending(const StringView &key, + const StringView &group, + const StringView &start, + const StringView &end, + long long count, + const StringView &consumer, + Output output) { + auto reply = command(cmd::xpending_per_consumer, key, group, start, end, count, consumer); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + Output output) { + auto reply = command(cmd::xrange, key, start, end); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrange(const StringView &key, + const StringView &start, + const StringView &end, + long long count, + Output output) { + auto reply = command(cmd::xrange_count, key, start, end, count); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + long long count, + Output output) { + auto reply = command(cmd::xread, key, id, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, Input last, long long count, Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREAD: no key specified"); + } + + auto reply = command(cmd::xread_range, first, last, count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xread(const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + Output output) { + auto reply = command(cmd::xread_block, key, id, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xread(Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREAD: no key specified"); + } + + auto reply = command(cmd::xread_block_range, first, last, timeout.count(), count); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup, key, group, consumer, key, id, count, noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREADGROUP: no key specified"); + } + + auto reply = _command(cmd::xreadgroup_range, + first->first, + group, + consumer, + first, + last, + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + const StringView &key, + const StringView &id, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) { + auto reply = _command(cmd::xreadgroup_block, + key, + group, + consumer, + key, + id, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +auto RedisCluster::xreadgroup(const StringView &group, + const StringView &consumer, + Input first, + Input last, + const std::chrono::milliseconds &timeout, + long long count, + bool noack, + Output output) + -> typename std::enable_if::value>::type { + if (first == last) { + throw Error("XREADGROUP: no key specified"); + } + + auto reply = _command(cmd::xreadgroup_block_range, + first->first, + group, + consumer, + first, + last, + timeout.count(), + count, + noack); + + if (!reply::is_nil(*reply)) { + reply::to_array(*reply, output); + } +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + Output output) { + auto reply = command(cmd::xrevrange, key, end, start); + + reply::to_array(*reply, output); +} + +template +void RedisCluster::xrevrange(const StringView &key, + const StringView &end, + const StringView &start, + long long count, + Output output) { + auto reply = command(cmd::xrevrange_count, key, end, start, count); + + reply::to_array(*reply, output); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::value, + ReplyUPtr>::type { + return command(cmd, std::forward(key), std::forward(args)...); +} + +template +auto RedisCluster::_generic_command(Cmd cmd, Key &&key, Args &&...args) + -> typename std::enable_if::type>::value, + ReplyUPtr>::type { + auto k = std::to_string(std::forward(key)); + return command(cmd, k, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::true_type, const StringView &key, Args &&...args) { + return _command(cmd, key, key, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, std::false_type, Input &&first, Args &&...args) { + return _range_command(cmd, + std::is_convertible< + typename std::decay< + decltype(*std::declval())>::type, StringView>(), + std::forward(first), + std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::true_type, Input input, Args &&...args) { + return _command(cmd, *input, input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_range_command(Cmd cmd, std::false_type, Input input, Args &&...args) { + return _command(cmd, std::get<0>(*input), input, std::forward(args)...); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, Connection &connection, Args &&...args) { + assert(!connection.broken()); + + cmd(connection, std::forward(args)...); + + return connection.recv(); +} + +template +ReplyUPtr RedisCluster::_command(Cmd cmd, const StringView &key, Args &&...args) { + for (auto idx = 0; idx < 2; ++idx) { + try { + auto guarded_connection = _pool.fetch(key); + + return _command(cmd, guarded_connection.connection(), std::forward(args)...); + } catch (const IoError &err) { + // When master is down, one of its replicas will be promoted to be the new master. + // If we try to send command to the old master, we'll get an *IoError*. + // In this case, we need to update the slots mapping. + _pool.update(); + } catch (const ClosedError &err) { + // Node might be removed. + // 1. Get up-to-date slot mapping to check if the node still exists. + _pool.update(); + + // TODO: + // 2. If it's NOT exist, update slot mapping, and retry. + // 3. If it's still exist, that means the node is down, NOT removed, throw exception. + } catch (const MovedError &err) { + // Slot mapping has been changed, update it and try again. + _pool.update(); + } catch (const AskError &err) { + auto guarded_connection = _pool.fetch(err.node()); + auto &connection = guarded_connection.connection(); + + // 1. send ASKING command. + _asking(connection); + + // 2. resend last command. + try { + return _command(cmd, connection, std::forward(args)...); + } catch (const MovedError &err) { + throw Error("Slot migrating... ASKING node hasn't been set to IMPORTING state"); + } + } // For other exceptions, just throw it. + } + + // Possible failures: + // 1. Source node has already run 'CLUSTER SETSLOT xxx NODE xxx', + // while the destination node has NOT run it. + // In this case, client will be redirected by both nodes with MovedError. + // 2. Other failures... + throw Error("Failed to send command with key: " + std::string(key.data(), key.size())); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::true_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., true); +} + +template +inline ReplyUPtr RedisCluster::_score_command(std::false_type, Cmd cmd, Args &&... args) { + return command(cmd, std::forward(args)..., false); +} + +template +inline ReplyUPtr RedisCluster::_score_command(Cmd cmd, Args &&... args) { + return _score_command(typename IsKvPairIter::type(), + cmd, + std::forward(args)...); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REDIS_CLUSTER_HPP diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/reply.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/reply.h new file mode 100644 index 00000000..b309de5b --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/reply.h @@ -0,0 +1,363 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_REPLY_H +#define SEWENEW_REDISPLUSPLUS_REPLY_H + +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +struct ReplyDeleter { + void operator()(redisReply *reply) const { + if (reply != nullptr) { + freeReplyObject(reply); + } + } +}; + +using ReplyUPtr = std::unique_ptr; + +namespace reply { + +template +struct ParseTag {}; + +template +inline T parse(redisReply &reply) { + return parse(ParseTag(), reply); +} + +void parse(ParseTag, redisReply &reply); + +std::string parse(ParseTag, redisReply &reply); + +long long parse(ParseTag, redisReply &reply); + +double parse(ParseTag, redisReply &reply); + +bool parse(ParseTag, redisReply &reply); + +template +Optional parse(ParseTag>, redisReply &reply); + +template +std::pair parse(ParseTag>, redisReply &reply); + +template +std::tuple parse(ParseTag>, redisReply &reply); + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template ::value, int>::type = 0> +T parse(ParseTag, redisReply &reply); + +template +long long parse_scan_reply(redisReply &reply, Output output); + +inline bool is_error(redisReply &reply) { + return reply.type == REDIS_REPLY_ERROR; +} + +inline bool is_nil(redisReply &reply) { + return reply.type == REDIS_REPLY_NIL; +} + +inline bool is_string(redisReply &reply) { + return reply.type == REDIS_REPLY_STRING; +} + +inline bool is_status(redisReply &reply) { + return reply.type == REDIS_REPLY_STATUS; +} + +inline bool is_integer(redisReply &reply) { + return reply.type == REDIS_REPLY_INTEGER; +} + +inline bool is_array(redisReply &reply) { + return reply.type == REDIS_REPLY_ARRAY; +} + +std::string to_status(redisReply &reply); + +template +void to_array(redisReply &reply, Output output); + +// Rewrite set reply to bool type +void rewrite_set_reply(redisReply &reply); + +// Rewrite georadius reply to OptionalLongLong type +void rewrite_georadius_reply(redisReply &reply); + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple; + +} + +// Inline implementations. + +namespace reply { + +namespace detail { + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.element == nullptr) { + // Empty array. + return; + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + auto *sub_reply = reply.element[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null array element reply"); + } + + *output = parse::type>(*sub_reply); + + ++output; + } +} + +bool is_flat_array(redisReply &reply); + +template +void to_flat_array(redisReply &reply, Output output) { + if (reply.element == nullptr) { + // Empty array. + return; + } + + if (reply.elements % 2 != 0) { + throw ProtoError("Not string pair array reply"); + } + + for (std::size_t idx = 0; idx != reply.elements; idx += 2) { + auto *key_reply = reply.element[idx]; + auto *val_reply = reply.element[idx + 1]; + if (key_reply == nullptr || val_reply == nullptr) { + throw ProtoError("Null string array reply"); + } + + using Pair = typename IterType::type; + using FirstType = typename std::decay::type; + using SecondType = typename std::decay::type; + *output = std::make_pair(parse(*key_reply), + parse(*val_reply)); + + ++output; + } +} + +template +void to_array(std::true_type, redisReply &reply, Output output) { + if (is_flat_array(reply)) { + to_flat_array(reply, output); + } else { + to_array(reply, output); + } +} + +template +void to_array(std::false_type, redisReply &reply, Output output) { + to_array(reply, output); +} + +template +std::tuple parse_tuple(redisReply **reply, std::size_t idx) { + assert(reply != nullptr); + + auto *sub_reply = reply[idx]; + if (sub_reply == nullptr) { + throw ProtoError("Null reply"); + } + + return std::make_tuple(parse(*sub_reply)); +} + +template +auto parse_tuple(redisReply **reply, std::size_t idx) -> + typename std::enable_if>::type { + assert(reply != nullptr); + + return std::tuple_cat(parse_tuple(reply, idx), + parse_tuple(reply, idx + 1)); +} + +} + +template +Optional parse(ParseTag>, redisReply &reply) { + if (reply::is_nil(reply)) { + return {}; + } + + return Optional(parse(reply)); +} + +template +std::pair parse(ParseTag>, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != 2) { + throw ProtoError("NOT key-value PAIR reply"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null PAIR reply"); + } + + auto *first = reply.element[0]; + auto *second = reply.element[1]; + if (first == nullptr || second == nullptr) { + throw ProtoError("Null pair reply"); + } + + return std::make_pair(parse::type>(*first), + parse::type>(*second)); +} + +template +std::tuple parse(ParseTag>, redisReply &reply) { + constexpr auto size = sizeof...(Args); + + static_assert(size > 0, "DO NOT support parsing tuple with 0 element"); + + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + if (reply.elements != size) { + throw ProtoError("Expect tuple reply with " + std::to_string(size) + "elements"); + } + + if (reply.element == nullptr) { + throw ProtoError("Null TUPLE reply"); + } + + return detail::parse_tuple(reply.element, 0); +} + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::back_inserter(container)); + + return container; +} + +template ::value, int>::type> +T parse(ParseTag, redisReply &reply) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + T container; + + to_array(reply, std::inserter(container, container.end())); + + return container; +} + +template +long long parse_scan_reply(redisReply &reply, Output output) { + if (reply.elements != 2 || reply.element == nullptr) { + throw ProtoError("Invalid scan reply"); + } + + auto *cursor_reply = reply.element[0]; + auto *data_reply = reply.element[1]; + if (cursor_reply == nullptr || data_reply == nullptr) { + throw ProtoError("Invalid cursor reply or data reply"); + } + + auto cursor_str = reply::parse(*cursor_reply); + auto new_cursor = 0; + try { + new_cursor = std::stoll(cursor_str); + } catch (const std::exception &e) { + throw ProtoError("Invalid cursor reply: " + cursor_str); + } + + reply::to_array(*data_reply, output); + + return new_cursor; +} + +template +void to_array(redisReply &reply, Output output) { + if (!is_array(reply)) { + throw ProtoError("Expect ARRAY reply"); + } + + detail::to_array(typename IsKvPairIter::type(), reply, output); +} + +template +auto parse_xpending_reply(redisReply &reply, Output output) + -> std::tuple { + if (!is_array(reply) || reply.elements != 4) { + throw ProtoError("expect array reply with 4 elements"); + } + + for (std::size_t idx = 0; idx != reply.elements; ++idx) { + if (reply.element[idx] == nullptr) { + throw ProtoError("null array reply"); + } + } + + auto num = parse(*(reply.element[0])); + auto start = parse(*(reply.element[1])); + auto end = parse(*(reply.element[2])); + + auto &entry_reply = *(reply.element[3]); + if (!is_nil(entry_reply)) { + to_array(entry_reply, output); + } + + return std::make_tuple(num, std::move(start), std::move(end)); +} + +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_REPLY_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/sentinel.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/sentinel.h new file mode 100644 index 00000000..e80d1e56 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/sentinel.h @@ -0,0 +1,138 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SENTINEL_H +#define SEWENEW_REDISPLUSPLUS_SENTINEL_H + +#include +#include +#include +#include +#include +#include "connection.h" +#include "shards.h" +#include "reply.h" + +namespace sw { + +namespace redis { + +struct SentinelOptions { + std::vector> nodes; + + std::string password; + + bool keep_alive = true; + + std::chrono::milliseconds connect_timeout{100}; + + std::chrono::milliseconds socket_timeout{100}; + + std::chrono::milliseconds retry_interval{100}; + + std::size_t max_retry = 2; +}; + +class Sentinel { +public: + explicit Sentinel(const SentinelOptions &sentinel_opts); + + Sentinel(const Sentinel &) = delete; + Sentinel& operator=(const Sentinel &) = delete; + + Sentinel(Sentinel &&) = delete; + Sentinel& operator=(Sentinel &&) = delete; + + ~Sentinel() = default; + +private: + Connection master(const std::string &master_name, const ConnectionOptions &opts); + + Connection slave(const std::string &master_name, const ConnectionOptions &opts); + + class Iterator; + + friend class SimpleSentinel; + + std::list _parse_options(const SentinelOptions &opts) const; + + Optional _get_master_addr_by_name(Connection &connection, const StringView &name); + + std::vector _get_slave_addr_by_name(Connection &connection, const StringView &name); + + Connection _connect_redis(const Node &node, ConnectionOptions opts); + + Role _get_role(Connection &connection); + + std::vector _parse_slave_info(redisReply &reply) const; + + std::list _healthy_sentinels; + + std::list _broken_sentinels; + + SentinelOptions _sentinel_opts; + + std::mutex _mutex; +}; + +class SimpleSentinel { +public: + SimpleSentinel(const std::shared_ptr &sentinel, + const std::string &master_name, + Role role); + + SimpleSentinel() = default; + + SimpleSentinel(const SimpleSentinel &) = default; + SimpleSentinel& operator=(const SimpleSentinel &) = default; + + SimpleSentinel(SimpleSentinel &&) = default; + SimpleSentinel& operator=(SimpleSentinel &&) = default; + + ~SimpleSentinel() = default; + + explicit operator bool() const { + return bool(_sentinel); + } + + Connection create(const ConnectionOptions &opts); + +private: + std::shared_ptr _sentinel; + + std::string _master_name; + + Role _role = Role::MASTER; +}; + +class StopIterError : public Error { +public: + StopIterError() : Error("StopIterError") {} + + StopIterError(const StopIterError &) = default; + StopIterError& operator=(const StopIterError &) = default; + + StopIterError(StopIterError &&) = default; + StopIterError& operator=(StopIterError &&) = default; + + virtual ~StopIterError() = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SENTINEL_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards.h new file mode 100644 index 00000000..a0593acb --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards.h @@ -0,0 +1,115 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_H + +#include +#include +#include "errors.h" + +namespace sw { + +namespace redis { + +using Slot = std::size_t; + +struct SlotRange { + Slot min; + Slot max; +}; + +inline bool operator<(const SlotRange &lhs, const SlotRange &rhs) { + return lhs.max < rhs.max; +} + +struct Node { + std::string host; + int port; +}; + +inline bool operator==(const Node &lhs, const Node &rhs) { + return lhs.host == rhs.host && lhs.port == rhs.port; +} + +struct NodeHash { + std::size_t operator()(const Node &node) const noexcept { + auto host_hash = std::hash{}(node.host); + auto port_hash = std::hash{}(node.port); + return host_hash ^ (port_hash << 1); + } +}; + +using Shards = std::map; + +class RedirectionError : public ReplyError { +public: + RedirectionError(const std::string &msg); + + RedirectionError(const RedirectionError &) = default; + RedirectionError& operator=(const RedirectionError &) = default; + + RedirectionError(RedirectionError &&) = default; + RedirectionError& operator=(RedirectionError &&) = default; + + virtual ~RedirectionError() = default; + + Slot slot() const { + return _slot; + } + + const Node& node() const { + return _node; + } + +private: + std::pair _parse_error(const std::string &msg) const; + + Slot _slot = 0; + Node _node; +}; + +class MovedError : public RedirectionError { +public: + explicit MovedError(const std::string &msg) : RedirectionError(msg) {} + + MovedError(const MovedError &) = default; + MovedError& operator=(const MovedError &) = default; + + MovedError(MovedError &&) = default; + MovedError& operator=(MovedError &&) = default; + + virtual ~MovedError() = default; +}; + +class AskError : public RedirectionError { +public: + explicit AskError(const std::string &msg) : RedirectionError(msg) {} + + AskError(const AskError &) = default; + AskError& operator=(const AskError &) = default; + + AskError(AskError &&) = default; + AskError& operator=(AskError &&) = default; + + virtual ~AskError() = default; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards_pool.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards_pool.h new file mode 100644 index 00000000..1184806e --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/shards_pool.h @@ -0,0 +1,137 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H +#define SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H + +#include +#include +#include +#include +#include +#include "reply.h" +#include "connection_pool.h" +#include "shards.h" + +namespace sw { + +namespace redis { + +using ConnectionPoolSPtr = std::shared_ptr; + +class GuardedConnection { +public: + GuardedConnection(const ConnectionPoolSPtr &pool) : _pool(pool), + _connection(_pool->fetch()) { + assert(!_connection.broken()); + } + + GuardedConnection(const GuardedConnection &) = delete; + GuardedConnection& operator=(const GuardedConnection &) = delete; + + GuardedConnection(GuardedConnection &&) = default; + GuardedConnection& operator=(GuardedConnection &&) = default; + + ~GuardedConnection() { + _pool->release(std::move(_connection)); + } + + Connection& connection() { + return _connection; + } + +private: + ConnectionPoolSPtr _pool; + Connection _connection; +}; + +class ShardsPool { +public: + ShardsPool() = default; + + ShardsPool(const ShardsPool &that) = delete; + ShardsPool& operator=(const ShardsPool &that) = delete; + + ShardsPool(ShardsPool &&that); + ShardsPool& operator=(ShardsPool &&that); + + ~ShardsPool() = default; + + ShardsPool(const ConnectionPoolOptions &pool_opts, + const ConnectionOptions &connection_opts); + + // Fetch a connection by key. + GuardedConnection fetch(const StringView &key); + + // Randomly pick a connection. + GuardedConnection fetch(); + + // Fetch a connection by node. + GuardedConnection fetch(const Node &node); + + void update(); + + ConnectionOptions connection_options(const StringView &key); + + ConnectionOptions connection_options(); + +private: + void _move(ShardsPool &&that); + + void _init_pool(const Shards &shards); + + Shards _cluster_slots(Connection &connection) const; + + ReplyUPtr _cluster_slots_command(Connection &connection) const; + + Shards _parse_reply(redisReply &reply) const; + + std::pair _parse_slot_info(redisReply &reply) const; + + // Get slot by key. + std::size_t _slot(const StringView &key) const; + + // Randomly pick a slot. + std::size_t _slot() const; + + ConnectionPoolSPtr& _get_pool(Slot slot); + + GuardedConnection _fetch(Slot slot); + + ConnectionOptions _connection_options(Slot slot); + + using NodeMap = std::unordered_map; + + NodeMap::iterator _add_node(const Node &node); + + ConnectionPoolOptions _pool_opts; + + ConnectionOptions _connection_opts; + + Shards _shards; + + NodeMap _pools; + + std::mutex _mutex; + + static const std::size_t SHARDS = 16383; +}; + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/subscriber.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/subscriber.h new file mode 100644 index 00000000..8b7c5cfb --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/subscriber.h @@ -0,0 +1,231 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H +#define SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H + +#include +#include +#include +#include "connection.h" +#include "reply.h" +#include "command.h" +#include "utils.h" + +namespace sw { + +namespace redis { + +// @NOTE: Subscriber is NOT thread-safe. +// Subscriber uses callbacks to handle messages. There are 6 kinds of messages: +// 1) MESSAGE: message sent to a channel. +// 2) PMESSAGE: message sent to channels of a given pattern. +// 3) SUBSCRIBE: meta message sent when we successfully subscribe to a channel. +// 4) UNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel. +// 5) PSUBSCRIBE: meta message sent when we successfully subscribe to a channel pattern. +// 6) PUNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel pattern. +// +// Use Subscriber::on_message(MsgCallback) to set the callback function for message of +// *MESSAGE* type, and the callback interface is: +// void (std::string channel, std::string msg) +// +// Use Subscriber::on_pmessage(PatternMsgCallback) to set the callback function for message of +// *PMESSAGE* type, and the callback interface is: +// void (std::string pattern, std::string channel, std::string msg) +// +// Messages of other types are called *META MESSAGE*, they have the same callback interface. +// Use Subscriber::on_meta(MetaCallback) to set the callback function: +// void (Subscriber::MsgType type, OptionalString channel, long long num) +// +// NOTE: If we haven't subscribe/psubscribe to any channel/pattern, and try to +// unsubscribe/punsubscribe without any parameter, i.e. unsubscribe/punsubscribe all +// channels/patterns, *channel* will be null. So the second parameter of meta callback +// is of type *OptionalString*. +// +// All these callback interfaces pass std::string by value, and you can take their ownership +// (i.e. std::move) safely. +// +// If you don't set callback for a specific kind of message, Subscriber::consume() will +// receive the message, and ignore it, i.e. no callback will be called. +class Subscriber { +public: + Subscriber(const Subscriber &) = delete; + Subscriber& operator=(const Subscriber &) = delete; + + Subscriber(Subscriber &&) = default; + Subscriber& operator=(Subscriber &&) = default; + + ~Subscriber() = default; + + enum class MsgType { + SUBSCRIBE, + UNSUBSCRIBE, + PSUBSCRIBE, + PUNSUBSCRIBE, + MESSAGE, + PMESSAGE + }; + + template + void on_message(MsgCb msg_callback); + + template + void on_pmessage(PMsgCb pmsg_callback); + + template + void on_meta(MetaCb meta_callback); + + void subscribe(const StringView &channel); + + template + void subscribe(Input first, Input last); + + template + void subscribe(std::initializer_list channels) { + subscribe(channels.begin(), channels.end()); + } + + void unsubscribe(); + + void unsubscribe(const StringView &channel); + + template + void unsubscribe(Input first, Input last); + + template + void unsubscribe(std::initializer_list channels) { + unsubscribe(channels.begin(), channels.end()); + } + + void psubscribe(const StringView &pattern); + + template + void psubscribe(Input first, Input last); + + template + void psubscribe(std::initializer_list channels) { + psubscribe(channels.begin(), channels.end()); + } + + void punsubscribe(); + + void punsubscribe(const StringView &channel); + + template + void punsubscribe(Input first, Input last); + + template + void punsubscribe(std::initializer_list channels) { + punsubscribe(channels.begin(), channels.end()); + } + + void consume(); + +private: + friend class Redis; + + friend class RedisCluster; + + explicit Subscriber(Connection connection); + + MsgType _msg_type(redisReply *reply) const; + + void _check_connection(); + + void _handle_message(redisReply &reply); + + void _handle_pmessage(redisReply &reply); + + void _handle_meta(MsgType type, redisReply &reply); + + using MsgCallback = std::function; + + using PatternMsgCallback = std::function; + + using MetaCallback = std::function; + + using TypeIndex = std::unordered_map; + static const TypeIndex _msg_type_index; + + Connection _connection; + + MsgCallback _msg_callback = nullptr; + + PatternMsgCallback _pmsg_callback = nullptr; + + MetaCallback _meta_callback = nullptr; +}; + +template +void Subscriber::on_message(MsgCb msg_callback) { + _msg_callback = msg_callback; +} + +template +void Subscriber::on_pmessage(PMsgCb pmsg_callback) { + _pmsg_callback = pmsg_callback; +} + +template +void Subscriber::on_meta(MetaCb meta_callback) { + _meta_callback = meta_callback; +} + +template +void Subscriber::subscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::subscribe_range(_connection, first, last); +} + +template +void Subscriber::unsubscribe(Input first, Input last) { + _check_connection(); + + cmd::unsubscribe_range(_connection, first, last); +} + +template +void Subscriber::psubscribe(Input first, Input last) { + if (first == last) { + return; + } + + _check_connection(); + + cmd::psubscribe_range(_connection, first, last); +} + +template +void Subscriber::punsubscribe(Input first, Input last) { + _check_connection(); + + cmd::punsubscribe_range(_connection, first, last); +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/transaction.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/transaction.h new file mode 100644 index 00000000..f19f2488 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/transaction.h @@ -0,0 +1,77 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_TRANSACTION_H +#define SEWENEW_REDISPLUSPLUS_TRANSACTION_H + +#include +#include +#include "connection.h" +#include "errors.h" + +namespace sw { + +namespace redis { + +class TransactionImpl { +public: + explicit TransactionImpl(bool piped) : _piped(piped) {} + + template + void command(Connection &connection, Cmd cmd, Args &&...args); + + std::vector exec(Connection &connection, std::size_t cmd_num); + + void discard(Connection &connection, std::size_t cmd_num); + +private: + void _open_transaction(Connection &connection); + + void _close_transaction(); + + void _get_queued_reply(Connection &connection); + + void _get_queued_replies(Connection &connection, std::size_t cmd_num); + + std::vector _exec(Connection &connection); + + void _discard(Connection &connection); + + bool _in_transaction = false; + + bool _piped; +}; + +template +void TransactionImpl::command(Connection &connection, Cmd cmd, Args &&...args) { + assert(!connection.broken()); + + if (!_in_transaction) { + _open_transaction(connection); + } + + cmd(connection, std::forward(args)...); + + if (!_piped) { + _get_queued_reply(connection); + } +} + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_TRANSACTION_H diff --git a/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/utils.h b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/utils.h new file mode 100644 index 00000000..e29e64e1 --- /dev/null +++ b/ext/redis-plus-plus-1.1.1/install/centos8/include/sw/redis++/utils.h @@ -0,0 +1,269 @@ +/************************************************************************** + Copyright (c) 2017 sewenew + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *************************************************************************/ + +#ifndef SEWENEW_REDISPLUSPLUS_UTILS_H +#define SEWENEW_REDISPLUSPLUS_UTILS_H + +#include +#include +#include + +namespace sw { + +namespace redis { + +// By now, not all compilers support std::string_view, +// so we make our own implementation. +class StringView { +public: + constexpr StringView() noexcept = default; + + constexpr StringView(const char *data, std::size_t size) : _data(data), _size(size) {} + + StringView(const char *data) : _data(data), _size(std::strlen(data)) {} + + StringView(const std::string &str) : _data(str.data()), _size(str.size()) {} + + constexpr StringView(const StringView &) noexcept = default; + + StringView& operator=(const StringView &) noexcept = default; + + constexpr const char* data() const noexcept { + return _data; + } + + constexpr std::size_t size() const noexcept { + return _size; + } + +private: + const char *_data = nullptr; + std::size_t _size = 0; +}; + +template +class Optional { +public: + Optional() = default; + + Optional(const Optional &) = default; + Optional& operator=(const Optional &) = default; + + Optional(Optional &&) = default; + Optional& operator=(Optional &&) = default; + + ~Optional() = default; + + template + explicit Optional(Args &&...args) : _value(true, T(std::forward(args)...)) {} + + explicit operator bool() const { + return _value.first; + } + + T& value() { + return _value.second; + } + + const T& value() const { + return _value.second; + } + + T* operator->() { + return &(_value.second); + } + + const T* operator->() const { + return &(_value.second); + } + + T& operator*() { + return _value.second; + } + + const T& operator*() const { + return _value.second; + } + +private: + std::pair _value; +}; + +using OptionalString = Optional; + +using OptionalLongLong = Optional; + +using OptionalDouble = Optional; + +using OptionalStringPair = Optional>; + +template +struct IsKvPair : std::false_type {}; + +template +struct IsKvPair> : std::true_type {}; + +template +using Void = void; + +template > +struct IsInserter : std::false_type {}; + +template +//struct IsInserter> : std::true_type {}; +struct IsInserter::value>::type> + : std::true_type {}; + +template > +struct IterType { + using type = typename std::iterator_traits::value_type; +}; + +template +//struct IterType> { +struct IterType::value>::type> { + typename std::enable_if::value>::type> { + using type = typename std::decay::type; +}; + +template > +struct IsIter : std::false_type {}; + +template +struct IsIter::value>::type> : std::true_type {}; + +template +//struct IsIter::iterator_category>> +struct IsIter::value_type>::value>::type> + : std::integral_constant::value> {}; + +template +struct IsKvPairIter : IsKvPair::type> {}; + +template +struct TupleWithType : std::false_type {}; + +template +struct TupleWithType> : std::false_type {}; + +template +struct TupleWithType> : TupleWithType> {}; + +template +struct TupleWithType> : std::true_type {}; + +template +struct IndexSequence {}; + +template +struct MakeIndexSequence : MakeIndexSequence {}; + +template +struct MakeIndexSequence<0, Is...> : IndexSequence {}; + +// NthType and NthValue are taken from +// https://stackoverflow.com/questions/14261183 +template +struct NthType {}; + +template +struct NthType<0, Arg, Args...> { + using type = Arg; +}; + +template +struct NthType { + using type = typename NthType::type; +}; + +template +struct LastType { + using type = typename NthType::type; +}; + +struct InvalidLastType {}; + +template <> +struct LastType<> { + using type = InvalidLastType; +}; + +template +auto NthValue(Arg &&arg, Args &&...) + -> typename std::enable_if<(I == 0), decltype(std::forward(arg))>::type { + return std::forward(arg); +} + +template +auto NthValue(Arg &&, Args &&...args) + -> typename std::enable_if<(I > 0), + decltype(std::forward::type>( + std::declval::type>()))>::type { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template +auto LastValue(Args &&...args) + -> decltype(std::forward::type>( + std::declval::type>())) { + return std::forward::type>( + NthValue(std::forward(args)...)); +} + +template > +struct HasPushBack : std::false_type {}; + +template +struct HasPushBack().push_back(std::declval()) + )>::value>::type> : std::true_type {}; + +template > +struct HasInsert : std::false_type {}; + +template +struct HasInsert().insert(std::declval(), + std::declval())), + typename T::iterator>::value>::type> : std::true_type {}; + +template +struct IsSequenceContainer + : std::integral_constant::value + && !std::is_same::type, std::string>::value> {}; + +template +struct IsAssociativeContainer + : std::integral_constant::value && !HasPushBack::value> {}; + +uint16_t crc16(const char *buf, int len); + +} + +} + +#endif // end SEWENEW_REDISPLUSPLUS_UTILS_H