]>
git.saurik.com Git - redis.git/blob - src/t_string.c
4 /*-----------------------------------------------------------------------------
6 *----------------------------------------------------------------------------*/
8 void setGenericCommand(redisClient
*c
, int nx
, robj
*key
, robj
*val
, robj
*expire
) {
10 long seconds
= 0; /* initialized to avoid an harmness warning */
13 if (getLongFromObjectOrReply(c
, expire
, &seconds
, NULL
) != REDIS_OK
)
16 addReplyError(c
,"invalid expire time in SETEX");
21 retval
= dbAdd(c
->db
,key
,val
);
22 if (retval
== REDIS_ERR
) {
24 dbReplace(c
->db
,key
,val
);
27 addReply(c
,shared
.czero
);
33 touchWatchedKey(c
->db
,key
);
35 removeExpire(c
->db
,key
);
36 if (expire
) setExpire(c
->db
,key
,time(NULL
)+seconds
);
37 addReply(c
, nx
? shared
.cone
: shared
.ok
);
40 void setCommand(redisClient
*c
) {
41 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
42 setGenericCommand(c
,0,c
->argv
[1],c
->argv
[2],NULL
);
45 void setnxCommand(redisClient
*c
) {
46 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
47 setGenericCommand(c
,1,c
->argv
[1],c
->argv
[2],NULL
);
50 void setexCommand(redisClient
*c
) {
51 c
->argv
[3] = tryObjectEncoding(c
->argv
[3]);
52 setGenericCommand(c
,0,c
->argv
[1],c
->argv
[3],c
->argv
[2]);
55 int getGenericCommand(redisClient
*c
) {
58 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
)
61 if (o
->type
!= REDIS_STRING
) {
62 addReply(c
,shared
.wrongtypeerr
);
70 void getCommand(redisClient
*c
) {
74 void getsetCommand(redisClient
*c
) {
75 if (getGenericCommand(c
) == REDIS_ERR
) return;
76 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
77 dbReplace(c
->db
,c
->argv
[1],c
->argv
[2]);
78 incrRefCount(c
->argv
[2]);
79 touchWatchedKey(c
->db
,c
->argv
[1]);
81 removeExpire(c
->db
,c
->argv
[1]);
84 static int getBitOffsetFromArgument(redisClient
*c
, robj
*o
, size_t *offset
) {
86 char *err
= "bit offset is not an integer or out of range";
88 if (getLongLongFromObjectOrReply(c
,o
,&loffset
,err
) != REDIS_OK
)
91 /* Limit offset to SIZE_T_MAX or 1GB in bytes */
93 ((unsigned long long)loffset
>= (unsigned)SIZE_T_MAX
) ||
94 ((unsigned long long)loffset
>> 3) >= (1024*1024*1024))
100 *offset
= (size_t)loffset
;
104 void setbitCommand(redisClient
*c
) {
109 if (getBitOffsetFromArgument(c
,c
->argv
[2],&bitoffset
) != REDIS_OK
)
112 on
= ((sds
)c
->argv
[3]->ptr
)[0] - '0';
113 if (sdslen(c
->argv
[3]->ptr
) != 1 || (on
& ~1)) {
114 addReplyError(c
,"bit should be 0 or 1");
118 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
120 sds value
= sdssetbit(sdsempty(),bitoffset
,on
);
121 o
= createObject(REDIS_STRING
,value
);
122 dbAdd(c
->db
,c
->argv
[1],o
);
124 if (checkType(c
,o
,REDIS_STRING
)) return;
126 /* Create a copy when the object is shared or encoded. */
127 if (o
->refcount
!= 1 || o
->encoding
!= REDIS_ENCODING_RAW
) {
128 robj
*decoded
= getDecodedObject(o
);
129 o
= createStringObject(decoded
->ptr
, sdslen(decoded
->ptr
));
130 decrRefCount(decoded
);
131 dbReplace(c
->db
,c
->argv
[1],o
);
134 o
->ptr
= sdssetbit(o
->ptr
,bitoffset
,on
);
136 touchWatchedKey(c
->db
,c
->argv
[1]);
138 addReply(c
,shared
.cone
);
141 void getbitCommand(redisClient
*c
) {
143 size_t bitoffset
, byte
, bitmask
;
147 if (getBitOffsetFromArgument(c
,c
->argv
[2],&bitoffset
) != REDIS_OK
)
150 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
151 checkType(c
,o
,REDIS_STRING
)) return;
153 byte
= bitoffset
>> 3;
154 bitmask
= 1 << (7 - (bitoffset
& 0x7));
155 if (o
->encoding
!= REDIS_ENCODING_RAW
) {
156 if (byte
< (size_t)ll2string(llbuf
,sizeof(llbuf
),(long)o
->ptr
))
157 on
= llbuf
[byte
] & bitmask
;
159 if (byte
< sdslen(o
->ptr
))
160 on
= ((sds
)o
->ptr
)[byte
] & bitmask
;
162 addReply(c
, on
? shared
.cone
: shared
.czero
);
165 void mgetCommand(redisClient
*c
) {
168 addReplyMultiBulkLen(c
,c
->argc
-1);
169 for (j
= 1; j
< c
->argc
; j
++) {
170 robj
*o
= lookupKeyRead(c
->db
,c
->argv
[j
]);
172 addReply(c
,shared
.nullbulk
);
174 if (o
->type
!= REDIS_STRING
) {
175 addReply(c
,shared
.nullbulk
);
183 void msetGenericCommand(redisClient
*c
, int nx
) {
186 if ((c
->argc
% 2) == 0) {
187 addReplyError(c
,"wrong number of arguments for MSET");
190 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
191 * set nothing at all if at least one already key exists. */
193 for (j
= 1; j
< c
->argc
; j
+= 2) {
194 if (lookupKeyWrite(c
->db
,c
->argv
[j
]) != NULL
) {
200 addReply(c
, shared
.czero
);
204 for (j
= 1; j
< c
->argc
; j
+= 2) {
205 c
->argv
[j
+1] = tryObjectEncoding(c
->argv
[j
+1]);
206 dbReplace(c
->db
,c
->argv
[j
],c
->argv
[j
+1]);
207 incrRefCount(c
->argv
[j
+1]);
208 removeExpire(c
->db
,c
->argv
[j
]);
209 touchWatchedKey(c
->db
,c
->argv
[j
]);
211 server
.dirty
+= (c
->argc
-1)/2;
212 addReply(c
, nx
? shared
.cone
: shared
.ok
);
215 void msetCommand(redisClient
*c
) {
216 msetGenericCommand(c
,0);
219 void msetnxCommand(redisClient
*c
) {
220 msetGenericCommand(c
,1);
223 void incrDecrCommand(redisClient
*c
, long long incr
) {
227 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
228 if (o
!= NULL
&& checkType(c
,o
,REDIS_STRING
)) return;
229 if (getLongLongFromObjectOrReply(c
,o
,&value
,NULL
) != REDIS_OK
) return;
232 o
= createStringObjectFromLongLong(value
);
233 dbReplace(c
->db
,c
->argv
[1],o
);
234 touchWatchedKey(c
->db
,c
->argv
[1]);
236 addReply(c
,shared
.colon
);
238 addReply(c
,shared
.crlf
);
241 void incrCommand(redisClient
*c
) {
242 incrDecrCommand(c
,1);
245 void decrCommand(redisClient
*c
) {
246 incrDecrCommand(c
,-1);
249 void incrbyCommand(redisClient
*c
) {
252 if (getLongLongFromObjectOrReply(c
, c
->argv
[2], &incr
, NULL
) != REDIS_OK
) return;
253 incrDecrCommand(c
,incr
);
256 void decrbyCommand(redisClient
*c
) {
259 if (getLongLongFromObjectOrReply(c
, c
->argv
[2], &incr
, NULL
) != REDIS_OK
) return;
260 incrDecrCommand(c
,-incr
);
263 void appendCommand(redisClient
*c
) {
268 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
269 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
272 retval
= dbAdd(c
->db
,c
->argv
[1],c
->argv
[2]);
273 incrRefCount(c
->argv
[2]);
274 totlen
= stringObjectLen(c
->argv
[2]);
276 if (o
->type
!= REDIS_STRING
) {
277 addReply(c
,shared
.wrongtypeerr
);
280 /* If the object is specially encoded or shared we have to make
282 if (o
->refcount
!= 1 || o
->encoding
!= REDIS_ENCODING_RAW
) {
283 robj
*decoded
= getDecodedObject(o
);
285 o
= createStringObject(decoded
->ptr
, sdslen(decoded
->ptr
));
286 decrRefCount(decoded
);
287 dbReplace(c
->db
,c
->argv
[1],o
);
290 if (c
->argv
[2]->encoding
== REDIS_ENCODING_RAW
) {
291 o
->ptr
= sdscatlen(o
->ptr
,
292 c
->argv
[2]->ptr
, sdslen(c
->argv
[2]->ptr
));
294 o
->ptr
= sdscatprintf(o
->ptr
, "%ld",
295 (unsigned long) c
->argv
[2]->ptr
);
297 totlen
= sdslen(o
->ptr
);
299 touchWatchedKey(c
->db
,c
->argv
[1]);
301 addReplyLongLong(c
,totlen
);
304 void substrCommand(redisClient
*c
) {
306 long start
= atoi(c
->argv
[2]->ptr
);
307 long end
= atoi(c
->argv
[3]->ptr
);
308 size_t rangelen
, strlen
;
311 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
312 checkType(c
,o
,REDIS_STRING
)) return;
314 o
= getDecodedObject(o
);
315 strlen
= sdslen(o
->ptr
);
317 /* convert negative indexes */
318 if (start
< 0) start
= strlen
+start
;
319 if (end
< 0) end
= strlen
+end
;
320 if (start
< 0) start
= 0;
321 if (end
< 0) end
= 0;
323 /* indexes sanity checks */
324 if (start
> end
|| (size_t)start
>= strlen
) {
325 /* Out of range start or start > end result in null reply */
326 addReply(c
,shared
.nullbulk
);
330 if ((size_t)end
>= strlen
) end
= strlen
-1;
331 rangelen
= (end
-start
)+1;
333 /* Return the result */
334 addReplySds(c
,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen
));
335 range
= sdsnewlen((char*)o
->ptr
+start
,rangelen
);
336 addReplySds(c
,range
);
337 addReply(c
,shared
.crlf
);
341 void strlenCommand(redisClient
*c
) {
344 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
345 checkType(c
,o
,REDIS_STRING
)) return;
347 o
= getDecodedObject(o
);
348 addReplyLongLong(c
,sdslen(o
->ptr
));