]>
git.saurik.com Git - redis.git/blob - src/t_string.c
e442a49fd48e340133d674d8c9fd363eb2e5cdb2
4 /*-----------------------------------------------------------------------------
6 *----------------------------------------------------------------------------*/
8 static int checkStringLength(redisClient
*c
, long long size
) {
9 if (size
> 512*1024*1024) {
10 addReplyError(c
,"string exceeds maximum allowed size (512MB)");
16 void setGenericCommand(redisClient
*c
, int nx
, robj
*key
, robj
*val
, robj
*expire
) {
18 long seconds
= 0; /* initialized to avoid an harmness warning */
21 if (getLongFromObjectOrReply(c
, expire
, &seconds
, NULL
) != REDIS_OK
)
24 addReplyError(c
,"invalid expire time in SETEX");
29 retval
= dbAdd(c
->db
,key
,val
);
30 if (retval
== REDIS_ERR
) {
32 dbReplace(c
->db
,key
,val
);
35 addReply(c
,shared
.czero
);
41 touchWatchedKey(c
->db
,key
);
43 removeExpire(c
->db
,key
);
44 if (expire
) setExpire(c
->db
,key
,time(NULL
)+seconds
);
45 addReply(c
, nx
? shared
.cone
: shared
.ok
);
48 void setCommand(redisClient
*c
) {
49 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
50 setGenericCommand(c
,0,c
->argv
[1],c
->argv
[2],NULL
);
53 void setnxCommand(redisClient
*c
) {
54 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
55 setGenericCommand(c
,1,c
->argv
[1],c
->argv
[2],NULL
);
58 void setexCommand(redisClient
*c
) {
59 c
->argv
[3] = tryObjectEncoding(c
->argv
[3]);
60 setGenericCommand(c
,0,c
->argv
[1],c
->argv
[3],c
->argv
[2]);
63 int getGenericCommand(redisClient
*c
) {
66 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
)
69 if (o
->type
!= REDIS_STRING
) {
70 addReply(c
,shared
.wrongtypeerr
);
78 void getCommand(redisClient
*c
) {
82 void getsetCommand(redisClient
*c
) {
83 if (getGenericCommand(c
) == REDIS_ERR
) return;
84 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
85 dbReplace(c
->db
,c
->argv
[1],c
->argv
[2]);
86 incrRefCount(c
->argv
[2]);
87 touchWatchedKey(c
->db
,c
->argv
[1]);
89 removeExpire(c
->db
,c
->argv
[1]);
92 static int getBitOffsetFromArgument(redisClient
*c
, robj
*o
, size_t *offset
) {
94 char *err
= "bit offset is not an integer or out of range";
96 if (getLongLongFromObjectOrReply(c
,o
,&loffset
,err
) != REDIS_OK
)
99 /* Limit offset to SIZE_T_MAX or 512MB in bytes */
101 ((unsigned long long)loffset
>= (unsigned)SIZE_T_MAX
) ||
102 ((unsigned long long)loffset
>> 3) >= (512*1024*1024))
104 addReplyError(c
,err
);
108 *offset
= (size_t)loffset
;
112 void setbitCommand(redisClient
*c
) {
114 char *err
= "bit is not an integer or out of range";
119 if (getBitOffsetFromArgument(c
,c
->argv
[2],&bitoffset
) != REDIS_OK
)
122 if (getLongLongFromObjectOrReply(c
,c
->argv
[3],&bitvalue
,err
) != REDIS_OK
)
125 /* A bit can only be set to be on or off... */
127 addReplyError(c
,err
);
131 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
133 o
= createObject(REDIS_STRING
,sdsempty());
134 dbAdd(c
->db
,c
->argv
[1],o
);
136 if (checkType(c
,o
,REDIS_STRING
)) return;
138 /* Create a copy when the object is shared or encoded. */
139 if (o
->refcount
!= 1 || o
->encoding
!= REDIS_ENCODING_RAW
) {
140 robj
*decoded
= getDecodedObject(o
);
141 o
= createStringObject(decoded
->ptr
, sdslen(decoded
->ptr
));
142 decrRefCount(decoded
);
143 dbReplace(c
->db
,c
->argv
[1],o
);
147 byte
= bitoffset
>> 3;
148 bit
= 7 - (bitoffset
& 0x7);
150 o
->ptr
= sdsgrowzero(o
->ptr
,byte
+1);
151 ((char*)o
->ptr
)[byte
] |= on
<< bit
;
152 ((char*)o
->ptr
)[byte
] &= ~((!on
) << bit
);
154 touchWatchedKey(c
->db
,c
->argv
[1]);
156 addReply(c
,shared
.cone
);
159 void getbitCommand(redisClient
*c
) {
161 size_t bitoffset
, byte
, bitmask
;
165 if (getBitOffsetFromArgument(c
,c
->argv
[2],&bitoffset
) != REDIS_OK
)
168 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
169 checkType(c
,o
,REDIS_STRING
)) return;
171 byte
= bitoffset
>> 3;
172 bitmask
= 1 << (7 - (bitoffset
& 0x7));
173 if (o
->encoding
!= REDIS_ENCODING_RAW
) {
174 if (byte
< (size_t)ll2string(llbuf
,sizeof(llbuf
),(long)o
->ptr
))
175 on
= llbuf
[byte
] & bitmask
;
177 if (byte
< sdslen(o
->ptr
))
178 on
= ((sds
)o
->ptr
)[byte
] & bitmask
;
180 addReply(c
, on
? shared
.cone
: shared
.czero
);
183 void setrangeCommand(redisClient
*c
) {
186 sds value
= c
->argv
[3]->ptr
;
188 if (getLongFromObjectOrReply(c
,c
->argv
[2],&offset
,NULL
) != REDIS_OK
)
191 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
193 /* Negative offset is always 0 for non-existing keys */
194 if (offset
< 0) offset
= 0;
196 /* Return 0 when setting nothing on a non-existing string */
197 if (sdslen(value
) == 0) {
198 addReply(c
,shared
.czero
);
202 /* Return when the resulting string exceeds allowed size */
203 if (checkStringLength(c
,offset
+sdslen(value
)) != REDIS_OK
)
206 o
= createObject(REDIS_STRING
,sdsempty());
207 dbAdd(c
->db
,c
->argv
[1],o
);
211 /* Key exists, check type */
212 if (checkType(c
,o
,REDIS_STRING
))
215 /* Find out existing value length */
216 if (o
->encoding
== REDIS_ENCODING_INT
) {
218 olen
= ll2string(llbuf
,sizeof(llbuf
),(long)o
->ptr
);
220 olen
= sdslen(o
->ptr
);
223 /* Return existing string length when setting nothing */
224 if (sdslen(value
) == 0) {
225 addReplyLongLong(c
,olen
);
229 /* Convert negative indexes. Note that for SETRANGE, the meaning of a
230 * negative index is a little different than for other commands.
231 * Here, an offset of -1 points to the trailing NULL byte of the
232 * string instead of the last character. */
234 offset
= olen
+1+offset
;
235 if (offset
< 0) offset
= 0;
238 /* Return when the resulting string exceeds allowed size */
239 if (checkStringLength(c
,offset
+sdslen(value
)) != REDIS_OK
)
242 /* Create a copy when the object is shared or encoded. */
243 if (o
->refcount
!= 1 || o
->encoding
!= REDIS_ENCODING_RAW
) {
244 robj
*decoded
= getDecodedObject(o
);
245 o
= createStringObject(decoded
->ptr
, sdslen(decoded
->ptr
));
246 decrRefCount(decoded
);
247 dbReplace(c
->db
,c
->argv
[1],o
);
251 if (sdslen(value
) > 0) {
252 o
->ptr
= sdsgrowzero(o
->ptr
,offset
+sdslen(value
));
253 memcpy((char*)o
->ptr
+offset
,value
,sdslen(value
));
254 touchWatchedKey(c
->db
,c
->argv
[1]);
257 addReplyLongLong(c
,sdslen(o
->ptr
));
260 void mgetCommand(redisClient
*c
) {
263 addReplyMultiBulkLen(c
,c
->argc
-1);
264 for (j
= 1; j
< c
->argc
; j
++) {
265 robj
*o
= lookupKeyRead(c
->db
,c
->argv
[j
]);
267 addReply(c
,shared
.nullbulk
);
269 if (o
->type
!= REDIS_STRING
) {
270 addReply(c
,shared
.nullbulk
);
278 void msetGenericCommand(redisClient
*c
, int nx
) {
281 if ((c
->argc
% 2) == 0) {
282 addReplyError(c
,"wrong number of arguments for MSET");
285 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
286 * set nothing at all if at least one already key exists. */
288 for (j
= 1; j
< c
->argc
; j
+= 2) {
289 if (lookupKeyWrite(c
->db
,c
->argv
[j
]) != NULL
) {
295 addReply(c
, shared
.czero
);
299 for (j
= 1; j
< c
->argc
; j
+= 2) {
300 c
->argv
[j
+1] = tryObjectEncoding(c
->argv
[j
+1]);
301 dbReplace(c
->db
,c
->argv
[j
],c
->argv
[j
+1]);
302 incrRefCount(c
->argv
[j
+1]);
303 removeExpire(c
->db
,c
->argv
[j
]);
304 touchWatchedKey(c
->db
,c
->argv
[j
]);
306 server
.dirty
+= (c
->argc
-1)/2;
307 addReply(c
, nx
? shared
.cone
: shared
.ok
);
310 void msetCommand(redisClient
*c
) {
311 msetGenericCommand(c
,0);
314 void msetnxCommand(redisClient
*c
) {
315 msetGenericCommand(c
,1);
318 void incrDecrCommand(redisClient
*c
, long long incr
) {
322 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
323 if (o
!= NULL
&& checkType(c
,o
,REDIS_STRING
)) return;
324 if (getLongLongFromObjectOrReply(c
,o
,&value
,NULL
) != REDIS_OK
) return;
327 o
= createStringObjectFromLongLong(value
);
328 dbReplace(c
->db
,c
->argv
[1],o
);
329 touchWatchedKey(c
->db
,c
->argv
[1]);
331 addReply(c
,shared
.colon
);
333 addReply(c
,shared
.crlf
);
336 void incrCommand(redisClient
*c
) {
337 incrDecrCommand(c
,1);
340 void decrCommand(redisClient
*c
) {
341 incrDecrCommand(c
,-1);
344 void incrbyCommand(redisClient
*c
) {
347 if (getLongLongFromObjectOrReply(c
, c
->argv
[2], &incr
, NULL
) != REDIS_OK
) return;
348 incrDecrCommand(c
,incr
);
351 void decrbyCommand(redisClient
*c
) {
354 if (getLongLongFromObjectOrReply(c
, c
->argv
[2], &incr
, NULL
) != REDIS_OK
) return;
355 incrDecrCommand(c
,-incr
);
358 void appendCommand(redisClient
*c
) {
363 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
364 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
367 retval
= dbAdd(c
->db
,c
->argv
[1],c
->argv
[2]);
368 incrRefCount(c
->argv
[2]);
369 totlen
= stringObjectLen(c
->argv
[2]);
371 if (o
->type
!= REDIS_STRING
) {
372 addReply(c
,shared
.wrongtypeerr
);
376 append
= getDecodedObject(c
->argv
[2]);
377 if (o
->encoding
== REDIS_ENCODING_RAW
&&
378 (sdslen(o
->ptr
) + sdslen(append
->ptr
)) > 512*1024*1024)
380 addReplyError(c
,"string exceeds maximum allowed size (512MB)");
381 decrRefCount(append
);
385 /* If the object is shared or encoded, we have to make a copy */
386 if (o
->refcount
!= 1 || o
->encoding
!= REDIS_ENCODING_RAW
) {
387 robj
*decoded
= getDecodedObject(o
);
388 o
= createStringObject(decoded
->ptr
, sdslen(decoded
->ptr
));
389 decrRefCount(decoded
);
390 dbReplace(c
->db
,c
->argv
[1],o
);
393 /* Append the value */
394 o
->ptr
= sdscatlen(o
->ptr
,append
->ptr
,sdslen(append
->ptr
));
395 decrRefCount(append
);
396 totlen
= sdslen(o
->ptr
);
398 touchWatchedKey(c
->db
,c
->argv
[1]);
400 addReplyLongLong(c
,totlen
);
403 void substrCommand(redisClient
*c
) {
405 long start
= atoi(c
->argv
[2]->ptr
);
406 long end
= atoi(c
->argv
[3]->ptr
);
407 size_t rangelen
, strlen
;
410 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
411 checkType(c
,o
,REDIS_STRING
)) return;
413 o
= getDecodedObject(o
);
414 strlen
= sdslen(o
->ptr
);
416 /* convert negative indexes */
417 if (start
< 0) start
= strlen
+start
;
418 if (end
< 0) end
= strlen
+end
;
419 if (start
< 0) start
= 0;
420 if (end
< 0) end
= 0;
422 /* indexes sanity checks */
423 if (start
> end
|| (size_t)start
>= strlen
) {
424 /* Out of range start or start > end result in null reply */
425 addReply(c
,shared
.nullbulk
);
429 if ((size_t)end
>= strlen
) end
= strlen
-1;
430 rangelen
= (end
-start
)+1;
432 /* Return the result */
433 addReplySds(c
,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen
));
434 range
= sdsnewlen((char*)o
->ptr
+start
,rangelen
);
435 addReplySds(c
,range
);
436 addReply(c
,shared
.crlf
);
440 void strlenCommand(redisClient
*c
) {
443 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
444 checkType(c
,o
,REDIS_STRING
)) return;
446 if (o
->encoding
== REDIS_ENCODING_RAW
) {
447 addReplyLongLong(c
,sdslen(o
->ptr
));
448 } else if (o
->encoding
== REDIS_ENCODING_INT
) {
450 int len
= ll2string(llbuf
,sizeof(llbuf
),(long)o
->ptr
);
451 addReplyLongLong(c
,len
);
453 redisPanic("Unknown string encoding");