"Fossies" - the Fresh Open Source Software Archive

Member "redis-6.0.8/src/t_hash.c" (10 Sep 2020, 27379 Bytes) of package /linux/misc/redis-6.0.8.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "t_hash.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.0.7_vs_6.0.8.

    1 /*
    2  * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions are met:
    7  *
    8  *   * Redistributions of source code must retain the above copyright notice,
    9  *     this list of conditions and the following disclaimer.
   10  *   * Redistributions in binary form must reproduce the above copyright
   11  *     notice, this list of conditions and the following disclaimer in the
   12  *     documentation and/or other materials provided with the distribution.
   13  *   * Neither the name of Redis nor the names of its contributors may be used
   14  *     to endorse or promote products derived from this software without
   15  *     specific prior written permission.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   27  * POSSIBILITY OF SUCH DAMAGE.
   28  */
   29 
   30 #include "server.h"
   31 #include <math.h>
   32 
   33 /*-----------------------------------------------------------------------------
   34  * Hash type API
   35  *----------------------------------------------------------------------------*/
   36 
   37 /* Check the length of a number of objects to see if we need to convert a
   38  * ziplist to a real hash. Note that we only check string encoded objects
   39  * as their string length can be queried in constant time. */
   40 void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
   41     int i;
   42 
   43     if (o->encoding != OBJ_ENCODING_ZIPLIST) return;
   44 
   45     for (i = start; i <= end; i++) {
   46         if (sdsEncodedObject(argv[i]) &&
   47             sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
   48         {
   49             hashTypeConvert(o, OBJ_ENCODING_HT);
   50             break;
   51         }
   52     }
   53 }
   54 
   55 /* Get the value from a ziplist encoded hash, identified by field.
   56  * Returns -1 when the field cannot be found. */
   57 int hashTypeGetFromZiplist(robj *o, sds field,
   58                            unsigned char **vstr,
   59                            unsigned int *vlen,
   60                            long long *vll)
   61 {
   62     unsigned char *zl, *fptr = NULL, *vptr = NULL;
   63     int ret;
   64 
   65     serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);
   66 
   67     zl = o->ptr;
   68     fptr = ziplistIndex(zl, ZIPLIST_HEAD);
   69     if (fptr != NULL) {
   70         fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);
   71         if (fptr != NULL) {
   72             /* Grab pointer to the value (fptr points to the field) */
   73             vptr = ziplistNext(zl, fptr);
   74             serverAssert(vptr != NULL);
   75         }
   76     }
   77 
   78     if (vptr != NULL) {
   79         ret = ziplistGet(vptr, vstr, vlen, vll);
   80         serverAssert(ret);
   81         return 0;
   82     }
   83 
   84     return -1;
   85 }
   86 
   87 /* Get the value from a hash table encoded hash, identified by field.
   88  * Returns NULL when the field cannot be found, otherwise the SDS value
   89  * is returned. */
   90 sds hashTypeGetFromHashTable(robj *o, sds field) {
   91     dictEntry *de;
   92 
   93     serverAssert(o->encoding == OBJ_ENCODING_HT);
   94 
   95     de = dictFind(o->ptr, field);
   96     if (de == NULL) return NULL;
   97     return dictGetVal(de);
   98 }
   99 
  100 /* Higher level function of hashTypeGet*() that returns the hash value
  101  * associated with the specified field. If the field is found C_OK
  102  * is returned, otherwise C_ERR. The returned object is returned by
  103  * reference in either *vstr and *vlen if it's returned in string form,
  104  * or stored in *vll if it's returned as a number.
  105  *
  106  * If *vll is populated *vstr is set to NULL, so the caller
  107  * can always check the function return by checking the return value
  108  * for C_OK and checking if vll (or vstr) is NULL. */
  109 int hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vlen, long long *vll) {
  110     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  111         *vstr = NULL;
  112         if (hashTypeGetFromZiplist(o, field, vstr, vlen, vll) == 0)
  113             return C_OK;
  114     } else if (o->encoding == OBJ_ENCODING_HT) {
  115         sds value;
  116         if ((value = hashTypeGetFromHashTable(o, field)) != NULL) {
  117             *vstr = (unsigned char*) value;
  118             *vlen = sdslen(value);
  119             return C_OK;
  120         }
  121     } else {
  122         serverPanic("Unknown hash encoding");
  123     }
  124     return C_ERR;
  125 }
  126 
  127 /* Like hashTypeGetValue() but returns a Redis object, which is useful for
  128  * interaction with the hash type outside t_hash.c.
  129  * The function returns NULL if the field is not found in the hash. Otherwise
  130  * a newly allocated string object with the value is returned. */
  131 robj *hashTypeGetValueObject(robj *o, sds field) {
  132     unsigned char *vstr;
  133     unsigned int vlen;
  134     long long vll;
  135 
  136     if (hashTypeGetValue(o,field,&vstr,&vlen,&vll) == C_ERR) return NULL;
  137     if (vstr) return createStringObject((char*)vstr,vlen);
  138     else return createStringObjectFromLongLong(vll);
  139 }
  140 
  141 /* Higher level function using hashTypeGet*() to return the length of the
  142  * object associated with the requested field, or 0 if the field does not
  143  * exist. */
  144 size_t hashTypeGetValueLength(robj *o, sds field) {
  145     size_t len = 0;
  146     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  147         unsigned char *vstr = NULL;
  148         unsigned int vlen = UINT_MAX;
  149         long long vll = LLONG_MAX;
  150 
  151         if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0)
  152             len = vstr ? vlen : sdigits10(vll);
  153     } else if (o->encoding == OBJ_ENCODING_HT) {
  154         sds aux;
  155 
  156         if ((aux = hashTypeGetFromHashTable(o, field)) != NULL)
  157             len = sdslen(aux);
  158     } else {
  159         serverPanic("Unknown hash encoding");
  160     }
  161     return len;
  162 }
  163 
  164 /* Test if the specified field exists in the given hash. Returns 1 if the field
  165  * exists, and 0 when it doesn't. */
  166 int hashTypeExists(robj *o, sds field) {
  167     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  168         unsigned char *vstr = NULL;
  169         unsigned int vlen = UINT_MAX;
  170         long long vll = LLONG_MAX;
  171 
  172         if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) return 1;
  173     } else if (o->encoding == OBJ_ENCODING_HT) {
  174         if (hashTypeGetFromHashTable(o, field) != NULL) return 1;
  175     } else {
  176         serverPanic("Unknown hash encoding");
  177     }
  178     return 0;
  179 }
  180 
  181 /* Add a new field, overwrite the old with the new value if it already exists.
  182  * Return 0 on insert and 1 on update.
  183  *
  184  * By default, the key and value SDS strings are copied if needed, so the
  185  * caller retains ownership of the strings passed. However this behavior
  186  * can be effected by passing appropriate flags (possibly bitwise OR-ed):
  187  *
  188  * HASH_SET_TAKE_FIELD -- The SDS field ownership passes to the function.
  189  * HASH_SET_TAKE_VALUE -- The SDS value ownership passes to the function.
  190  *
  191  * When the flags are used the caller does not need to release the passed
  192  * SDS string(s). It's up to the function to use the string to create a new
  193  * entry or to free the SDS string before returning to the caller.
  194  *
  195  * HASH_SET_COPY corresponds to no flags passed, and means the default
  196  * semantics of copying the values if needed.
  197  *
  198  */
  199 #define HASH_SET_TAKE_FIELD (1<<0)
  200 #define HASH_SET_TAKE_VALUE (1<<1)
  201 #define HASH_SET_COPY 0
  202 int hashTypeSet(robj *o, sds field, sds value, int flags) {
  203     int update = 0;
  204 
  205     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  206         unsigned char *zl, *fptr, *vptr;
  207 
  208         zl = o->ptr;
  209         fptr = ziplistIndex(zl, ZIPLIST_HEAD);
  210         if (fptr != NULL) {
  211             fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);
  212             if (fptr != NULL) {
  213                 /* Grab pointer to the value (fptr points to the field) */
  214                 vptr = ziplistNext(zl, fptr);
  215                 serverAssert(vptr != NULL);
  216                 update = 1;
  217 
  218                 /* Delete value */
  219                 zl = ziplistDelete(zl, &vptr);
  220 
  221                 /* Insert new value */
  222                 zl = ziplistInsert(zl, vptr, (unsigned char*)value,
  223                         sdslen(value));
  224             }
  225         }
  226 
  227         if (!update) {
  228             /* Push new field/value pair onto the tail of the ziplist */
  229             zl = ziplistPush(zl, (unsigned char*)field, sdslen(field),
  230                     ZIPLIST_TAIL);
  231             zl = ziplistPush(zl, (unsigned char*)value, sdslen(value),
  232                     ZIPLIST_TAIL);
  233         }
  234         o->ptr = zl;
  235 
  236         /* Check if the ziplist needs to be converted to a hash table */
  237         if (hashTypeLength(o) > server.hash_max_ziplist_entries)
  238             hashTypeConvert(o, OBJ_ENCODING_HT);
  239     } else if (o->encoding == OBJ_ENCODING_HT) {
  240         dictEntry *de = dictFind(o->ptr,field);
  241         if (de) {
  242             sdsfree(dictGetVal(de));
  243             if (flags & HASH_SET_TAKE_VALUE) {
  244                 dictGetVal(de) = value;
  245                 value = NULL;
  246             } else {
  247                 dictGetVal(de) = sdsdup(value);
  248             }
  249             update = 1;
  250         } else {
  251             sds f,v;
  252             if (flags & HASH_SET_TAKE_FIELD) {
  253                 f = field;
  254                 field = NULL;
  255             } else {
  256                 f = sdsdup(field);
  257             }
  258             if (flags & HASH_SET_TAKE_VALUE) {
  259                 v = value;
  260                 value = NULL;
  261             } else {
  262                 v = sdsdup(value);
  263             }
  264             dictAdd(o->ptr,f,v);
  265         }
  266     } else {
  267         serverPanic("Unknown hash encoding");
  268     }
  269 
  270     /* Free SDS strings we did not referenced elsewhere if the flags
  271      * want this function to be responsible. */
  272     if (flags & HASH_SET_TAKE_FIELD && field) sdsfree(field);
  273     if (flags & HASH_SET_TAKE_VALUE && value) sdsfree(value);
  274     return update;
  275 }
  276 
  277 /* Delete an element from a hash.
  278  * Return 1 on deleted and 0 on not found. */
  279 int hashTypeDelete(robj *o, sds field) {
  280     int deleted = 0;
  281 
  282     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  283         unsigned char *zl, *fptr;
  284 
  285         zl = o->ptr;
  286         fptr = ziplistIndex(zl, ZIPLIST_HEAD);
  287         if (fptr != NULL) {
  288             fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);
  289             if (fptr != NULL) {
  290                 zl = ziplistDelete(zl,&fptr); /* Delete the key. */
  291                 zl = ziplistDelete(zl,&fptr); /* Delete the value. */
  292                 o->ptr = zl;
  293                 deleted = 1;
  294             }
  295         }
  296     } else if (o->encoding == OBJ_ENCODING_HT) {
  297         if (dictDelete((dict*)o->ptr, field) == C_OK) {
  298             deleted = 1;
  299 
  300             /* Always check if the dictionary needs a resize after a delete. */
  301             if (htNeedsResize(o->ptr)) dictResize(o->ptr);
  302         }
  303 
  304     } else {
  305         serverPanic("Unknown hash encoding");
  306     }
  307     return deleted;
  308 }
  309 
  310 /* Return the number of elements in a hash. */
  311 unsigned long hashTypeLength(const robj *o) {
  312     unsigned long length = ULONG_MAX;
  313 
  314     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  315         length = ziplistLen(o->ptr) / 2;
  316     } else if (o->encoding == OBJ_ENCODING_HT) {
  317         length = dictSize((const dict*)o->ptr);
  318     } else {
  319         serverPanic("Unknown hash encoding");
  320     }
  321     return length;
  322 }
  323 
  324 hashTypeIterator *hashTypeInitIterator(robj *subject) {
  325     hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));
  326     hi->subject = subject;
  327     hi->encoding = subject->encoding;
  328 
  329     if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
  330         hi->fptr = NULL;
  331         hi->vptr = NULL;
  332     } else if (hi->encoding == OBJ_ENCODING_HT) {
  333         hi->di = dictGetIterator(subject->ptr);
  334     } else {
  335         serverPanic("Unknown hash encoding");
  336     }
  337     return hi;
  338 }
  339 
  340 void hashTypeReleaseIterator(hashTypeIterator *hi) {
  341     if (hi->encoding == OBJ_ENCODING_HT)
  342         dictReleaseIterator(hi->di);
  343     zfree(hi);
  344 }
  345 
  346 /* Move to the next entry in the hash. Return C_OK when the next entry
  347  * could be found and C_ERR when the iterator reaches the end. */
  348 int hashTypeNext(hashTypeIterator *hi) {
  349     if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
  350         unsigned char *zl;
  351         unsigned char *fptr, *vptr;
  352 
  353         zl = hi->subject->ptr;
  354         fptr = hi->fptr;
  355         vptr = hi->vptr;
  356 
  357         if (fptr == NULL) {
  358             /* Initialize cursor */
  359             serverAssert(vptr == NULL);
  360             fptr = ziplistIndex(zl, 0);
  361         } else {
  362             /* Advance cursor */
  363             serverAssert(vptr != NULL);
  364             fptr = ziplistNext(zl, vptr);
  365         }
  366         if (fptr == NULL) return C_ERR;
  367 
  368         /* Grab pointer to the value (fptr points to the field) */
  369         vptr = ziplistNext(zl, fptr);
  370         serverAssert(vptr != NULL);
  371 
  372         /* fptr, vptr now point to the first or next pair */
  373         hi->fptr = fptr;
  374         hi->vptr = vptr;
  375     } else if (hi->encoding == OBJ_ENCODING_HT) {
  376         if ((hi->de = dictNext(hi->di)) == NULL) return C_ERR;
  377     } else {
  378         serverPanic("Unknown hash encoding");
  379     }
  380     return C_OK;
  381 }
  382 
  383 /* Get the field or value at iterator cursor, for an iterator on a hash value
  384  * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */
  385 void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,
  386                                 unsigned char **vstr,
  387                                 unsigned int *vlen,
  388                                 long long *vll)
  389 {
  390     int ret;
  391 
  392     serverAssert(hi->encoding == OBJ_ENCODING_ZIPLIST);
  393 
  394     if (what & OBJ_HASH_KEY) {
  395         ret = ziplistGet(hi->fptr, vstr, vlen, vll);
  396         serverAssert(ret);
  397     } else {
  398         ret = ziplistGet(hi->vptr, vstr, vlen, vll);
  399         serverAssert(ret);
  400     }
  401 }
  402 
  403 /* Get the field or value at iterator cursor, for an iterator on a hash value
  404  * encoded as a hash table. Prototype is similar to
  405  * `hashTypeGetFromHashTable`. */
  406 sds hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what) {
  407     serverAssert(hi->encoding == OBJ_ENCODING_HT);
  408 
  409     if (what & OBJ_HASH_KEY) {
  410         return dictGetKey(hi->de);
  411     } else {
  412         return dictGetVal(hi->de);
  413     }
  414 }
  415 
  416 /* Higher level function of hashTypeCurrent*() that returns the hash value
  417  * at current iterator position.
  418  *
  419  * The returned element is returned by reference in either *vstr and *vlen if
  420  * it's returned in string form, or stored in *vll if it's returned as
  421  * a number.
  422  *
  423  * If *vll is populated *vstr is set to NULL, so the caller
  424  * can always check the function return by checking the return value
  425  * type checking if vstr == NULL. */
  426 void hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll) {
  427     if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
  428         *vstr = NULL;
  429         hashTypeCurrentFromZiplist(hi, what, vstr, vlen, vll);
  430     } else if (hi->encoding == OBJ_ENCODING_HT) {
  431         sds ele = hashTypeCurrentFromHashTable(hi, what);
  432         *vstr = (unsigned char*) ele;
  433         *vlen = sdslen(ele);
  434     } else {
  435         serverPanic("Unknown hash encoding");
  436     }
  437 }
  438 
  439 /* Return the key or value at the current iterator position as a new
  440  * SDS string. */
  441 sds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what) {
  442     unsigned char *vstr;
  443     unsigned int vlen;
  444     long long vll;
  445 
  446     hashTypeCurrentObject(hi,what,&vstr,&vlen,&vll);
  447     if (vstr) return sdsnewlen(vstr,vlen);
  448     return sdsfromlonglong(vll);
  449 }
  450 
  451 robj *hashTypeLookupWriteOrCreate(client *c, robj *key) {
  452     robj *o = lookupKeyWrite(c->db,key);
  453     if (o == NULL) {
  454         o = createHashObject();
  455         dbAdd(c->db,key,o);
  456     } else {
  457         if (o->type != OBJ_HASH) {
  458             addReply(c,shared.wrongtypeerr);
  459             return NULL;
  460         }
  461     }
  462     return o;
  463 }
  464 
  465 void hashTypeConvertZiplist(robj *o, int enc) {
  466     serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);
  467 
  468     if (enc == OBJ_ENCODING_ZIPLIST) {
  469         /* Nothing to do... */
  470 
  471     } else if (enc == OBJ_ENCODING_HT) {
  472         hashTypeIterator *hi;
  473         dict *dict;
  474         int ret;
  475 
  476         hi = hashTypeInitIterator(o);
  477         dict = dictCreate(&hashDictType, NULL);
  478 
  479         while (hashTypeNext(hi) != C_ERR) {
  480             sds key, value;
  481 
  482             key = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY);
  483             value = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE);
  484             ret = dictAdd(dict, key, value);
  485             if (ret != DICT_OK) {
  486                 serverLogHexDump(LL_WARNING,"ziplist with dup elements dump",
  487                     o->ptr,ziplistBlobLen(o->ptr));
  488                 serverPanic("Ziplist corruption detected");
  489             }
  490         }
  491         hashTypeReleaseIterator(hi);
  492         zfree(o->ptr);
  493         o->encoding = OBJ_ENCODING_HT;
  494         o->ptr = dict;
  495     } else {
  496         serverPanic("Unknown hash encoding");
  497     }
  498 }
  499 
  500 void hashTypeConvert(robj *o, int enc) {
  501     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  502         hashTypeConvertZiplist(o, enc);
  503     } else if (o->encoding == OBJ_ENCODING_HT) {
  504         serverPanic("Not implemented");
  505     } else {
  506         serverPanic("Unknown hash encoding");
  507     }
  508 }
  509 
  510 /*-----------------------------------------------------------------------------
  511  * Hash type commands
  512  *----------------------------------------------------------------------------*/
  513 
  514 void hsetnxCommand(client *c) {
  515     robj *o;
  516     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
  517     hashTypeTryConversion(o,c->argv,2,3);
  518 
  519     if (hashTypeExists(o, c->argv[2]->ptr)) {
  520         addReply(c, shared.czero);
  521     } else {
  522         hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);
  523         addReply(c, shared.cone);
  524         signalModifiedKey(c,c->db,c->argv[1]);
  525         notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
  526         server.dirty++;
  527     }
  528 }
  529 
  530 void hsetCommand(client *c) {
  531     int i, created = 0;
  532     robj *o;
  533 
  534     if ((c->argc % 2) == 1) {
  535         addReplyErrorFormat(c,"wrong number of arguments for '%s' command",c->cmd->name);
  536         return;
  537     }
  538 
  539     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
  540     hashTypeTryConversion(o,c->argv,2,c->argc-1);
  541 
  542     for (i = 2; i < c->argc; i += 2)
  543         created += !hashTypeSet(o,c->argv[i]->ptr,c->argv[i+1]->ptr,HASH_SET_COPY);
  544 
  545     /* HMSET (deprecated) and HSET return value is different. */
  546     char *cmdname = c->argv[0]->ptr;
  547     if (cmdname[1] == 's' || cmdname[1] == 'S') {
  548         /* HSET */
  549         addReplyLongLong(c, created);
  550     } else {
  551         /* HMSET */
  552         addReply(c, shared.ok);
  553     }
  554     signalModifiedKey(c,c->db,c->argv[1]);
  555     notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
  556     server.dirty++;
  557 }
  558 
  559 void hincrbyCommand(client *c) {
  560     long long value, incr, oldvalue;
  561     robj *o;
  562     sds new;
  563     unsigned char *vstr;
  564     unsigned int vlen;
  565 
  566     if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
  567     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
  568     if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&value) == C_OK) {
  569         if (vstr) {
  570             if (string2ll((char*)vstr,vlen,&value) == 0) {
  571                 addReplyError(c,"hash value is not an integer");
  572                 return;
  573             }
  574         } /* Else hashTypeGetValue() already stored it into &value */
  575     } else {
  576         value = 0;
  577     }
  578 
  579     oldvalue = value;
  580     if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
  581         (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
  582         addReplyError(c,"increment or decrement would overflow");
  583         return;
  584     }
  585     value += incr;
  586     new = sdsfromlonglong(value);
  587     hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
  588     addReplyLongLong(c,value);
  589     signalModifiedKey(c,c->db,c->argv[1]);
  590     notifyKeyspaceEvent(NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
  591     server.dirty++;
  592 }
  593 
  594 void hincrbyfloatCommand(client *c) {
  595     long double value, incr;
  596     long long ll;
  597     robj *o;
  598     sds new;
  599     unsigned char *vstr;
  600     unsigned int vlen;
  601 
  602     if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
  603     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
  604     if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&ll) == C_OK) {
  605         if (vstr) {
  606             if (string2ld((char*)vstr,vlen,&value) == 0) {
  607                 addReplyError(c,"hash value is not a float");
  608                 return;
  609             }
  610         } else {
  611             value = (long double)ll;
  612         }
  613     } else {
  614         value = 0;
  615     }
  616 
  617     value += incr;
  618     if (isnan(value) || isinf(value)) {
  619         addReplyError(c,"increment would produce NaN or Infinity");
  620         return;
  621     }
  622 
  623     char buf[MAX_LONG_DOUBLE_CHARS];
  624     int len = ld2string(buf,sizeof(buf),value,LD_STR_HUMAN);
  625     new = sdsnewlen(buf,len);
  626     hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
  627     addReplyBulkCBuffer(c,buf,len);
  628     signalModifiedKey(c,c->db,c->argv[1]);
  629     notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
  630     server.dirty++;
  631 
  632     /* Always replicate HINCRBYFLOAT as an HSET command with the final value
  633      * in order to make sure that differences in float pricision or formatting
  634      * will not create differences in replicas or after an AOF restart. */
  635     robj *aux, *newobj;
  636     aux = createStringObject("HSET",4);
  637     newobj = createRawStringObject(buf,len);
  638     rewriteClientCommandArgument(c,0,aux);
  639     decrRefCount(aux);
  640     rewriteClientCommandArgument(c,3,newobj);
  641     decrRefCount(newobj);
  642 }
  643 
  644 static void addHashFieldToReply(client *c, robj *o, sds field) {
  645     int ret;
  646 
  647     if (o == NULL) {
  648         addReplyNull(c);
  649         return;
  650     }
  651 
  652     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  653         unsigned char *vstr = NULL;
  654         unsigned int vlen = UINT_MAX;
  655         long long vll = LLONG_MAX;
  656 
  657         ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);
  658         if (ret < 0) {
  659             addReplyNull(c);
  660         } else {
  661             if (vstr) {
  662                 addReplyBulkCBuffer(c, vstr, vlen);
  663             } else {
  664                 addReplyBulkLongLong(c, vll);
  665             }
  666         }
  667 
  668     } else if (o->encoding == OBJ_ENCODING_HT) {
  669         sds value = hashTypeGetFromHashTable(o, field);
  670         if (value == NULL)
  671             addReplyNull(c);
  672         else
  673             addReplyBulkCBuffer(c, value, sdslen(value));
  674     } else {
  675         serverPanic("Unknown hash encoding");
  676     }
  677 }
  678 
  679 void hgetCommand(client *c) {
  680     robj *o;
  681 
  682     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL ||
  683         checkType(c,o,OBJ_HASH)) return;
  684 
  685     addHashFieldToReply(c, o, c->argv[2]->ptr);
  686 }
  687 
  688 void hmgetCommand(client *c) {
  689     robj *o;
  690     int i;
  691 
  692     /* Don't abort when the key cannot be found. Non-existing keys are empty
  693      * hashes, where HMGET should respond with a series of null bulks. */
  694     o = lookupKeyRead(c->db, c->argv[1]);
  695     if (o != NULL && o->type != OBJ_HASH) {
  696         addReply(c, shared.wrongtypeerr);
  697         return;
  698     }
  699 
  700     addReplyArrayLen(c, c->argc-2);
  701     for (i = 2; i < c->argc; i++) {
  702         addHashFieldToReply(c, o, c->argv[i]->ptr);
  703     }
  704 }
  705 
  706 void hdelCommand(client *c) {
  707     robj *o;
  708     int j, deleted = 0, keyremoved = 0;
  709 
  710     if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
  711         checkType(c,o,OBJ_HASH)) return;
  712 
  713     for (j = 2; j < c->argc; j++) {
  714         if (hashTypeDelete(o,c->argv[j]->ptr)) {
  715             deleted++;
  716             if (hashTypeLength(o) == 0) {
  717                 dbDelete(c->db,c->argv[1]);
  718                 keyremoved = 1;
  719                 break;
  720             }
  721         }
  722     }
  723     if (deleted) {
  724         signalModifiedKey(c,c->db,c->argv[1]);
  725         notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
  726         if (keyremoved)
  727             notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
  728                                 c->db->id);
  729         server.dirty += deleted;
  730     }
  731     addReplyLongLong(c,deleted);
  732 }
  733 
  734 void hlenCommand(client *c) {
  735     robj *o;
  736 
  737     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
  738         checkType(c,o,OBJ_HASH)) return;
  739 
  740     addReplyLongLong(c,hashTypeLength(o));
  741 }
  742 
  743 void hstrlenCommand(client *c) {
  744     robj *o;
  745 
  746     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
  747         checkType(c,o,OBJ_HASH)) return;
  748     addReplyLongLong(c,hashTypeGetValueLength(o,c->argv[2]->ptr));
  749 }
  750 
  751 static void addHashIteratorCursorToReply(client *c, hashTypeIterator *hi, int what) {
  752     if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
  753         unsigned char *vstr = NULL;
  754         unsigned int vlen = UINT_MAX;
  755         long long vll = LLONG_MAX;
  756 
  757         hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);
  758         if (vstr)
  759             addReplyBulkCBuffer(c, vstr, vlen);
  760         else
  761             addReplyBulkLongLong(c, vll);
  762     } else if (hi->encoding == OBJ_ENCODING_HT) {
  763         sds value = hashTypeCurrentFromHashTable(hi, what);
  764         addReplyBulkCBuffer(c, value, sdslen(value));
  765     } else {
  766         serverPanic("Unknown hash encoding");
  767     }
  768 }
  769 
  770 void genericHgetallCommand(client *c, int flags) {
  771     robj *o;
  772     hashTypeIterator *hi;
  773     int length, count = 0;
  774 
  775     robj *emptyResp = (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) ?
  776         shared.emptymap[c->resp] : shared.emptyarray;
  777     if ((o = lookupKeyReadOrReply(c,c->argv[1],emptyResp))
  778         == NULL || checkType(c,o,OBJ_HASH)) return;
  779 
  780     /* We return a map if the user requested keys and values, like in the
  781      * HGETALL case. Otherwise to use a flat array makes more sense. */
  782     length = hashTypeLength(o);
  783     if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) {
  784         addReplyMapLen(c, length);
  785     } else {
  786         addReplyArrayLen(c, length);
  787     }
  788 
  789     hi = hashTypeInitIterator(o);
  790     while (hashTypeNext(hi) != C_ERR) {
  791         if (flags & OBJ_HASH_KEY) {
  792             addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);
  793             count++;
  794         }
  795         if (flags & OBJ_HASH_VALUE) {
  796             addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);
  797             count++;
  798         }
  799     }
  800 
  801     hashTypeReleaseIterator(hi);
  802 
  803     /* Make sure we returned the right number of elements. */
  804     if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) count /= 2;
  805     serverAssert(count == length);
  806 }
  807 
  808 void hkeysCommand(client *c) {
  809     genericHgetallCommand(c,OBJ_HASH_KEY);
  810 }
  811 
  812 void hvalsCommand(client *c) {
  813     genericHgetallCommand(c,OBJ_HASH_VALUE);
  814 }
  815 
  816 void hgetallCommand(client *c) {
  817     genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE);
  818 }
  819 
  820 void hexistsCommand(client *c) {
  821     robj *o;
  822     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
  823         checkType(c,o,OBJ_HASH)) return;
  824 
  825     addReply(c, hashTypeExists(o,c->argv[2]->ptr) ? shared.cone : shared.czero);
  826 }
  827 
  828 void hscanCommand(client *c) {
  829     robj *o;
  830     unsigned long cursor;
  831 
  832     if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;
  833     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||
  834         checkType(c,o,OBJ_HASH)) return;
  835     scanGenericCommand(c,o,cursor);
  836 }