Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
1c247556
Commit
1c247556
authored
Sep 10, 2015
by
antirez
Browse files
Lazyfree: Hash converted to use plain SDS WIP 1.
parent
afc4b924
Changes
5
Hide whitespace changes
Inline
Side-by-side
src/object.c
View file @
1c247556
...
...
@@ -635,20 +635,29 @@ int getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, cons
return
C_OK
;
}
/* Helper function for getLongLongFromObject(). The function parses the string
* as a long long value in a strict way (no spaces before/after). On success
* C_OK is returned, otherwise C_ERR is returned. */
int
strict_strtoll
(
char
*
str
,
long
long
*
vp
)
{
char
*
eptr
;
long
long
value
;
errno
=
0
;
value
=
strtoll
(
o
->
ptr
,
&
eptr
,
10
);
if
(
isspace
(
str
[
0
])
||
eptr
[
0
]
!=
'\0'
||
errno
==
ERANGE
)
return
C_ERR
;
if
(
vp
)
*
vp
=
value
;
return
C_OK
;
}
int
getLongLongFromObject
(
robj
*
o
,
long
long
*
target
)
{
long
long
value
;
char
*
eptr
;
if
(
o
==
NULL
)
{
value
=
0
;
}
else
{
serverAssertWithInfo
(
NULL
,
o
,
o
->
type
==
OBJ_STRING
);
if
(
sdsEncodedObject
(
o
))
{
errno
=
0
;
value
=
strtoll
(
o
->
ptr
,
&
eptr
,
10
);
if
(
isspace
(((
char
*
)
o
->
ptr
)[
0
])
||
eptr
[
0
]
!=
'\0'
||
errno
==
ERANGE
)
return
C_ERR
;
if
(
strict_strtoll
(
o
->
ptr
,
&
value
)
==
C_ERR
)
return
C_ERR
;
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_INT
)
{
value
=
(
long
)
o
->
ptr
;
}
else
{
...
...
src/server.c
View file @
1c247556
...
...
@@ -579,37 +579,37 @@ dictType shaScriptObjectDictType = {
NULL
,
/* val dup */
dictSdsKeyCaseCompare
,
/* key compare */
dictSdsDestructor
,
/* key destructor */
dictObjectDestructor
/* val destructor */
dictObjectDestructor
/* val destructor */
};
/* Db->expires */
dictType
keyptrDictType
=
{
dictSdsHash
,
/* hash function */
NULL
,
/* key dup */
NULL
,
/* val dup */
dictSdsKeyCompare
,
/* key compare */
NULL
,
/* key destructor */
NULL
/* val destructor */
dictSdsHash
,
/* hash function */
NULL
,
/* key dup */
NULL
,
/* val dup */
dictSdsKeyCompare
,
/* key compare */
NULL
,
/* key destructor */
NULL
/* val destructor */
};
/* Command table. sds string -> command struct pointer. */
dictType
commandTableDictType
=
{
dictSdsCaseHash
,
/* hash function */
NULL
,
/* key dup */
NULL
,
/* val dup */
dictSdsKeyCaseCompare
,
/* key compare */
dictSdsDestructor
,
/* key destructor */
NULL
/* val destructor */
dictSdsCaseHash
,
/* hash function */
NULL
,
/* key dup */
NULL
,
/* val dup */
dictSdsKeyCaseCompare
,
/* key compare */
dictSdsDestructor
,
/* key destructor */
NULL
/* val destructor */
};
/* Hash type hash table (note that small hashes are represented with ziplists) */
dictType
hashDictType
=
{
dict
EncObj
Hash
,
/* hash function */
dict
Sds
Hash
,
/* hash function */
NULL
,
/* key dup */
NULL
,
/* val dup */
dict
EncObj
KeyCompare
,
/* key compare */
dict
Object
Destructor
,
/* key destructor */
dict
Object
Destructor
/* val destructor */
dict
sds
KeyCompare
,
/* key compare */
dict
Sds
Destructor
,
/* key destructor */
dict
Sds
Destructor
/* val destructor */
};
/* Keylist hash table type has unencoded redis objects as keys and
...
...
@@ -620,7 +620,7 @@ dictType keylistDictType = {
NULL
,
/* key dup */
NULL
,
/* val dup */
dictObjKeyCompare
,
/* key compare */
dictObjectDestructor
,
/* key destructor */
dictObjectDestructor
,
/* key destructor */
dictListDestructor
/* val destructor */
};
...
...
src/t_hash.c
View file @
1c247556
...
...
@@ -52,17 +52,9 @@ void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
}
}
/* Encode given objects in-place when the hash uses a dict. */
void
hashTypeTryObjectEncoding
(
robj
*
subject
,
robj
**
o1
,
robj
**
o2
)
{
if
(
subject
->
encoding
==
OBJ_ENCODING_HT
)
{
if
(
o1
)
*
o1
=
tryObjectEncoding
(
*
o1
);
if
(
o2
)
*
o2
=
tryObjectEncoding
(
*
o2
);
}
}
/* Get the value from a ziplist encoded hash, identified by field.
* Returns -1 when the field cannot be found. */
int
hashTypeGetFromZiplist
(
robj
*
o
,
robj
*
field
,
int
hashTypeGetFromZiplist
(
robj
*
o
,
sds
field
,
unsigned
char
**
vstr
,
unsigned
int
*
vlen
,
long
long
*
vll
)
...
...
@@ -72,12 +64,10 @@ int hashTypeGetFromZiplist(robj *o, robj *field,
serverAssert
(
o
->
encoding
==
OBJ_ENCODING_ZIPLIST
);
field
=
getDecodedObject
(
field
);
zl
=
o
->
ptr
;
fptr
=
ziplistIndex
(
zl
,
ZIPLIST_HEAD
);
if
(
fptr
!=
NULL
)
{
fptr
=
ziplistFind
(
fptr
,
field
->
ptr
,
sdslen
(
field
->
ptr
),
1
);
fptr
=
ziplistFind
(
fptr
,
field
,
sdslen
(
field
),
1
);
if
(
fptr
!=
NULL
)
{
/* Grab pointer to the value (fptr points to the field) */
vptr
=
ziplistNext
(
zl
,
fptr
);
...
...
@@ -85,8 +75,6 @@ int hashTypeGetFromZiplist(robj *o, robj *field,
}
}
decrRefCount
(
field
);
if
(
vptr
!=
NULL
)
{
ret
=
ziplistGet
(
vptr
,
vstr
,
vlen
,
vll
);
serverAssert
(
ret
);
...
...
@@ -97,56 +85,49 @@ int hashTypeGetFromZiplist(robj *o, robj *field,
}
/* Get the value from a hash table encoded hash, identified by field.
* Returns -1 when the field cannot be found. */
int
hashTypeGetFromHashTable
(
robj
*
o
,
robj
*
field
,
robj
**
value
)
{
* Returns NULL when the field cannot be found, otherwise the SDS value
* is returned. */
sds
hashTypeGetFromHashTable
(
robj
*
o
,
sds
field
)
{
dictEntry
*
de
;
serverAssert
(
o
->
encoding
==
OBJ_ENCODING_HT
);
de
=
dictFind
(
o
->
ptr
,
field
);
if
(
de
==
NULL
)
return
-
1
;
*
value
=
dictGetVal
(
de
);
return
0
;
if
(
de
==
NULL
)
return
NULL
;
return
dictGetVal
(
de
);
}
/* Higher level function of hashTypeGet*() that always returns a Redis
* object (either new or with refcount incremented), so that the caller
* can retain a reference or call decrRefCount after the usage.
/* Higher level function of hashTypeGet*() that returns the hash value
* associated with the specified field. If the field is found C_OK
* is returned, otherwise C_ERR. The returned object is returned by
* reference in either *vstr and *vlen if it's returned in string form,
* or stored in *vll if it's returned as a number.
*
* The lower level function can prevent copy on write so it is
* the preferred way of doing read operations. */
robj
*
hashTypeGetObject
(
robj
*
o
,
robj
*
field
)
{
robj
*
value
=
NULL
;
* If *vll is populated *vstr is set to NULL, so the caller
* can always check the function return by checking the return value
* for C_OK and checking if vll (or vstr) is NULL. */
int
hashTypeGetObject
(
robj
*
o
,
sds
field
,
unsigned
char
**
vstr
,
unsigned
int
*
vlen
,
long
long
*
vll
)
{
if
(
o
->
encoding
==
OBJ_ENCODING_ZIPLIST
)
{
unsigned
char
*
vstr
=
NULL
;
unsigned
int
vlen
=
UINT_MAX
;
long
long
vll
=
LLONG_MAX
;
if
(
hashTypeGetFromZiplist
(
o
,
field
,
&
vstr
,
&
vlen
,
&
vll
)
==
0
)
{
if
(
vstr
)
{
value
=
createStringObject
((
char
*
)
vstr
,
vlen
);
}
else
{
value
=
createStringObjectFromLongLong
(
vll
);
}
}
*
vstr
=
NULL
;
if
(
hashTypeGetFromZiplist
(
o
,
field
,
vstr
,
vlen
,
vll
)
==
0
)
return
C_OK
;
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_HT
)
{
robj
*
aux
;
if
(
hashTypeGetFromHashTable
(
o
,
field
,
&
aux
)
==
0
)
{
incrRefCount
(
aux
);
value
=
aux
;
sds
value
;
if
(
value
=
hashTypeGetFromHashTable
(
o
,
field
)
!=
NULL
)
{
*
vstr
=
value
;
*
vlen
=
sdslen
(
value
);
return
C_OK
;
}
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
return
value
;
return
C_ERR
;
}
/* Higher level function using hashTypeGet*() to return the length of the
* object associated with the requested field, or 0 if the field does not
* exist. */
size_t
hashTypeGetValueLength
(
robj
*
o
,
robj
*
field
)
{
size_t
hashTypeGetValueLength
(
robj
*
o
,
sds
field
)
{
size_t
len
=
0
;
if
(
o
->
encoding
==
OBJ_ENCODING_ZIPLIST
)
{
unsigned
char
*
vstr
=
NULL
;
...
...
@@ -156,10 +137,10 @@ size_t hashTypeGetValueLength(robj *o, robj *field) {
if
(
hashTypeGetFromZiplist
(
o
,
field
,
&
vstr
,
&
vlen
,
&
vll
)
==
0
)
len
=
vstr
?
vlen
:
sdigits10
(
vll
);
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_HT
)
{
robj
*
aux
;
sds
aux
;
if
(
hashTypeGetFromHashTable
(
o
,
field
,
&
aux
)
=
=
0
)
len
=
s
tringObjectL
en
(
aux
);
if
(
(
aux
=
hashTypeGetFromHashTable
(
o
,
field
)
)
!
=
NULL
)
len
=
s
dsl
en
(
aux
);
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
...
...
@@ -168,7 +149,7 @@ size_t hashTypeGetValueLength(robj *o, robj *field) {
/* Test if the specified field exists in the given hash. Returns 1 if the field
* exists, and 0 when it doesn't. */
int
hashTypeExists
(
robj
*
o
,
robj
*
field
)
{
int
hashTypeExists
(
robj
*
o
,
sds
field
)
{
if
(
o
->
encoding
==
OBJ_ENCODING_ZIPLIST
)
{
unsigned
char
*
vstr
=
NULL
;
unsigned
int
vlen
=
UINT_MAX
;
...
...
@@ -178,30 +159,26 @@ int hashTypeExists(robj *o, robj *field) {
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_HT
)
{
robj
*
aux
;
if
(
hashTypeGetFromHashTable
(
o
,
field
,
&
aux
)
=
=
0
)
return
1
;
if
(
hashTypeGetFromHashTable
(
o
,
field
)
!
=
NULL
)
return
1
;
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
return
0
;
}
/* Add an element, discard the old if the key already exists.
* Return 0 on insert and 1 on update.
* This function will take care of incrementing the reference count of the
* retained fields and value objects. */
int
hashTypeSet
(
robj
*
o
,
robj
*
field
,
robj
*
value
)
{
/* Add a new field, overwrite the old with the new value if it already exists.
* Return 0 on insert and 1 on update. The key and value SDS strings are copied
* if needed, so the caller retains ownership of the strings passed. */
int
hashTypeSet
(
robj
*
o
,
sds
field
,
sds
value
)
{
int
update
=
0
;
if
(
o
->
encoding
==
OBJ_ENCODING_ZIPLIST
)
{
unsigned
char
*
zl
,
*
fptr
,
*
vptr
;
field
=
getDecodedObject
(
field
);
value
=
getDecodedObject
(
value
);
zl
=
o
->
ptr
;
fptr
=
ziplistIndex
(
zl
,
ZIPLIST_HEAD
);
if
(
fptr
!=
NULL
)
{
fptr
=
ziplistFind
(
fptr
,
field
->
ptr
,
sdslen
(
field
->
ptr
),
1
);
fptr
=
ziplistFind
(
fptr
,
field
,
sdslen
(
field
),
1
);
if
(
fptr
!=
NULL
)
{
/* Grab pointer to the value (fptr points to the field) */
vptr
=
ziplistNext
(
zl
,
fptr
);
...
...
@@ -218,23 +195,23 @@ int hashTypeSet(robj *o, robj *field, robj *value) {
if
(
!
update
)
{
/* Push new field/value pair onto the tail of the ziplist */
zl
=
ziplistPush
(
zl
,
field
->
ptr
,
sdslen
(
field
->
ptr
),
ZIPLIST_TAIL
);
zl
=
ziplistPush
(
zl
,
value
->
ptr
,
sdslen
(
value
->
ptr
),
ZIPLIST_TAIL
);
zl
=
ziplistPush
(
zl
,
field
,
sdslen
(
field
),
ZIPLIST_TAIL
);
zl
=
ziplistPush
(
zl
,
value
,
sdslen
(
value
),
ZIPLIST_TAIL
);
}
o
->
ptr
=
zl
;
decrRefCount
(
field
);
decrRefCount
(
value
);
/* Check if the ziplist needs to be converted to a hash table */
if
(
hashTypeLength
(
o
)
>
server
.
hash_max_ziplist_entries
)
hashTypeConvert
(
o
,
OBJ_ENCODING_HT
);
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_HT
)
{
if
(
dictReplace
(
o
->
ptr
,
field
,
value
))
{
/* Insert */
incrRefCount
(
field
);
}
else
{
/* Update */
dictEntry
*
de
=
dictFind
(
o
->
ptr
,
field
);
if
(
de
)
{
sdsfree
(
dictGetVal
(
de
));
dictGetVal
(
de
)
=
sdsdup
(
value
);
update
=
1
;
}
else
{
dictAdd
(
o
->
ptr
,
sdsdup
(
key
),
sdsdup
(
value
));
}
incrRefCount
(
value
);
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
...
...
@@ -243,18 +220,16 @@ int hashTypeSet(robj *o, robj *field, robj *value) {
/* Delete an element from a hash.
* Return 1 on deleted and 0 on not found. */
int
hashTypeDelete
(
robj
*
o
,
robj
*
field
)
{
int
hashTypeDelete
(
robj
*
o
,
sds
field
)
{
int
deleted
=
0
;
if
(
o
->
encoding
==
OBJ_ENCODING_ZIPLIST
)
{
unsigned
char
*
zl
,
*
fptr
;
field
=
getDecodedObject
(
field
);
zl
=
o
->
ptr
;
fptr
=
ziplistIndex
(
zl
,
ZIPLIST_HEAD
);
if
(
fptr
!=
NULL
)
{
fptr
=
ziplistFind
(
fptr
,
field
->
ptr
,
sdslen
(
field
->
ptr
),
1
);
fptr
=
ziplistFind
(
fptr
,
field
,
sdslen
(
field
),
1
);
if
(
fptr
!=
NULL
)
{
zl
=
ziplistDelete
(
zl
,
&
fptr
);
zl
=
ziplistDelete
(
zl
,
&
fptr
);
...
...
@@ -276,7 +251,6 @@ int hashTypeDelete(robj *o, robj *field) {
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
return
deleted
;
}
...
...
@@ -291,7 +265,6 @@ unsigned long hashTypeLength(robj *o) {
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
return
length
;
}
...
...
@@ -308,15 +281,12 @@ hashTypeIterator *hashTypeInitIterator(robj *subject) {
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
return
hi
;
}
void
hashTypeReleaseIterator
(
hashTypeIterator
*
hi
)
{
if
(
hi
->
encoding
==
OBJ_ENCODING_HT
)
{
if
(
hi
->
encoding
==
OBJ_ENCODING_HT
)
dictReleaseIterator
(
hi
->
di
);
}
zfree
(
hi
);
}
...
...
@@ -378,41 +348,51 @@ void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,
}
/* Get the field or value at iterator cursor, for an iterator on a hash value
* encoded as a ziplist. Prototype is similar to `hashTypeGetFromHashTable`. */
void
hashTypeCurrentFromHashTable
(
hashTypeIterator
*
hi
,
int
what
,
robj
**
dst
)
{
* encoded as a hash table. Prototype is similar to
* `hashTypeGetFromHashTable`. */
sds
hashTypeCurrentFromHashTable
(
hashTypeIterator
*
hi
,
int
what
)
{
serverAssert
(
hi
->
encoding
==
OBJ_ENCODING_HT
);
if
(
what
&
OBJ_HASH_KEY
)
{
*
dst
=
dictGetKey
(
hi
->
de
);
return
dictGetKey
(
hi
->
de
);
}
else
{
*
dst
=
dictGetVal
(
hi
->
de
);
return
dictGetVal
(
hi
->
de
);
}
}
/* A non copy-on-write friendly but higher level version of hashTypeCurrent*()
* that returns an object with incremented refcount (or a new object). It is up
* to the caller to decrRefCount() the object if no reference is retained. */
robj
*
hashTypeCurrentObject
(
hashTypeIterator
*
hi
,
int
what
)
{
robj
*
dst
;
/* Higher level function of hashTypeCurrent*() that returns the hash value
* at current iterator position.
*
* The returned element is returned by reference in either *vstr and *vlen if
* it's returned in string form, or stored in *vll if it's returned as
* a number.
*
* If *vll is populated *vstr is set to NULL, so the caller
* can always check the function return by checking the return value
* type checking if vstr == NULL. */
void
hashTypeCurrentObject
(
hashTypeIterator
*
hi
,
int
what
,
unsigned
char
**
vstr
,
unsigned
int
*
vlen
,
long
long
*
vll
)
{
if
(
hi
->
encoding
==
OBJ_ENCODING_ZIPLIST
)
{
unsigned
char
*
vstr
=
NULL
;
unsigned
int
vlen
=
UINT_MAX
;
long
long
vll
=
LLONG_MAX
;
hashTypeCurrentFromZiplist
(
hi
,
what
,
&
vstr
,
&
vlen
,
&
vll
);
if
(
vstr
)
{
dst
=
createStringObject
((
char
*
)
vstr
,
vlen
);
}
else
{
dst
=
createStringObjectFromLongLong
(
vll
);
}
*
vstr
=
NULL
;
hashTypeCurrentFromZiplist
(
hi
,
what
,
vstr
,
vlen
,
vll
);
}
else
if
(
hi
->
encoding
==
OBJ_ENCODING_HT
)
{
hashTypeCurrentFromHashTable
(
hi
,
what
,
&
dst
);
incrRefCount
(
dst
);
sds
ele
=
hashTypeCurrentFromHashTable
(
hi
,
what
);
*
vstr
=
ele
;
*
vlen
=
sdslen
(
ele
);
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
return
dst
;
}
/* Return the key or value at the current iterator position as a new
* SDS string. */
sds
hashTypeCurrentObjectNewSds
(
hashTypeIterator
*
hi
,
int
what
)
{
unsigned
char
*
vstr
;
unsigned
int
vlen
;
long
long
vll
;
hashTypeCurrentObject
(
hi
,
what
,
&
vstr
,
&
vlen
,
&
vll
);
if
(
vstr
)
return
sdsnewlen
(
vstr
,
vlen
);
return
sdsfromlonglong
(
vll
);
}
robj
*
hashTypeLookupWriteOrCreate
(
client
*
c
,
robj
*
key
)
{
...
...
@@ -444,26 +424,24 @@ void hashTypeConvertZiplist(robj *o, int enc) {
dict
=
dictCreate
(
&
hashDictType
,
NULL
);
while
(
hashTypeNext
(
hi
)
!=
C_ERR
)
{
robj
*
field
,
*
value
;
unsigned
char
*
vstr
;
unsigned
int
vlen
;
long
long
vll
;
sds
key
,
value
;
field
=
hashTypeCurrentObject
(
hi
,
OBJ_HASH_KEY
);
field
=
tryObjectEncoding
(
field
);
value
=
hashTypeCurrentObject
(
hi
,
OBJ_HASH_VALUE
);
value
=
tryObjectEncoding
(
value
);
key
=
hashTypeCurrentObjectNewSds
(
hi
,
OBJ_HASH_KEY
);
value
=
hashTypeCurrentObjectNewSds
(
hi
,
OBJ_HASH_VALUE
);
ret
=
dictAdd
(
dict
,
field
,
value
);
if
(
ret
!=
DICT_OK
)
{
serverLogHexDump
(
LL_WARNING
,
"ziplist with dup elements dump"
,
o
->
ptr
,
ziplistBlobLen
(
o
->
ptr
));
server
Assert
(
ret
==
DICT_OK
);
server
Panic
(
"Ziplist corruption detected"
);
}
}
hashTypeReleaseIterator
(
hi
);
zfree
(
o
->
ptr
);
o
->
encoding
=
OBJ_ENCODING_HT
;
o
->
ptr
=
dict
;
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
...
...
@@ -489,8 +467,7 @@ void hsetCommand(client *c) {
if
((
o
=
hashTypeLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
hashTypeTryConversion
(
o
,
c
->
argv
,
2
,
3
);
hashTypeTryObjectEncoding
(
o
,
&
c
->
argv
[
2
],
&
c
->
argv
[
3
]);
update
=
hashTypeSet
(
o
,
c
->
argv
[
2
],
c
->
argv
[
3
]);
update
=
hashTypeSet
(
o
,
c
->
argv
[
2
]
->
ptr
,
c
->
argv
[
3
]
->
ptr
);
addReply
(
c
,
update
?
shared
.
czero
:
shared
.
cone
);
signalModifiedKey
(
c
->
db
,
c
->
argv
[
1
]);
notifyKeyspaceEvent
(
NOTIFY_HASH
,
"hset"
,
c
->
argv
[
1
],
c
->
db
->
id
);
...
...
@@ -502,11 +479,10 @@ void hsetnxCommand(client *c) {
if
((
o
=
hashTypeLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
hashTypeTryConversion
(
o
,
c
->
argv
,
2
,
3
);
if
(
hashTypeExists
(
o
,
c
->
argv
[
2
]))
{
if
(
hashTypeExists
(
o
,
c
->
argv
[
2
]
->
ptr
))
{
addReply
(
c
,
shared
.
czero
);
}
else
{
hashTypeTryObjectEncoding
(
o
,
&
c
->
argv
[
2
],
&
c
->
argv
[
3
]);
hashTypeSet
(
o
,
c
->
argv
[
2
],
c
->
argv
[
3
]);
hashTypeSet
(
o
,
c
->
argv
[
2
]
->
ptr
,
c
->
argv
[
3
]
->
ptr
);
addReply
(
c
,
shared
.
cone
);
signalModifiedKey
(
c
->
db
,
c
->
argv
[
1
]);
notifyKeyspaceEvent
(
NOTIFY_HASH
,
"hset"
,
c
->
argv
[
1
],
c
->
db
->
id
);
...
...
@@ -526,8 +502,7 @@ void hmsetCommand(client *c) {
if
((
o
=
hashTypeLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
hashTypeTryConversion
(
o
,
c
->
argv
,
2
,
c
->
argc
-
1
);
for
(
i
=
2
;
i
<
c
->
argc
;
i
+=
2
)
{
hashTypeTryObjectEncoding
(
o
,
&
c
->
argv
[
i
],
&
c
->
argv
[
i
+
1
]);
hashTypeSet
(
o
,
c
->
argv
[
i
],
c
->
argv
[
i
+
1
]);
hashTypeSet
(
o
,
c
->
argv
[
i
]
->
ptr
,
c
->
argv
[
i
+
1
]
->
ptr
);
}
addReply
(
c
,
shared
.
ok
);
signalModifiedKey
(
c
->
db
,
c
->
argv
[
1
]);
...
...
@@ -537,17 +512,20 @@ void hmsetCommand(client *c) {
void
hincrbyCommand
(
client
*
c
)
{
long
long
value
,
incr
,
oldvalue
;
robj
*
o
,
*
current
,
*
new
;
robj
*
o
;
sds
new
;
unsigned
char
*
vstr
;
unsigned
int
*
lven
;
if
(
getLongLongFromObjectOrReply
(
c
,
c
->
argv
[
3
],
&
incr
,
NULL
)
!=
C_OK
)
return
;
if
((
o
=
hashTypeLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
if
(
(
current
=
hashTypeGetObject
(
o
,
c
->
argv
[
2
]
)
)
!
=
NULL
)
{
if
(
getLongLongFromObjectOrReply
(
c
,
current
,
&
value
,
"hash value is not an integer"
)
!
=
C_OK
)
{
decrRefCount
(
current
);
return
;
}
decrRefCount
(
current
);
if
(
hashTypeGetObject
(
o
,
c
->
argv
[
2
]
,
&
vstr
,
&
vlen
,
&
value
)
=
=
C_OK
)
{
if
(
vstr
)
{
if
(
string2ll
(
vstr
,
vlen
,
&
value
)
=
=
0
)
{
addReplyError
(
c
,
"hash value is not an integer"
);
return
;
}
}
/* Else hashTypeGetObject() already stored it into &value */
}
else
{
value
=
0
;
}
...
...
@@ -559,10 +537,8 @@ void hincrbyCommand(client *c) {
return
;
}
value
+=
incr
;
new
=
createStringObjectFromLongLong
(
value
);
hashTypeTryObjectEncoding
(
o
,
&
c
->
argv
[
2
],
NULL
);
hashTypeSet
(
o
,
c
->
argv
[
2
],
new
);
decrRefCount
(
new
);
new
=
sdsfromlonglong
(
value
);
hashTypeSet
(
o
,
c
->
argv
[
2
]
->
ptr
,
new
);
addReplyLongLong
(
c
,
value
);
signalModifiedKey
(
c
->
db
,
c
->
argv
[
1
]);
notifyKeyspaceEvent
(
NOTIFY_HASH
,
"hincrby"
,
c
->
argv
[
1
],
c
->
db
->
id
);
...
...
@@ -570,25 +546,31 @@ void hincrbyCommand(client *c) {
}
void
hincrbyfloatCommand
(
client
*
c
)
{
double
long
value
,
incr
;
robj
*
o
,
*
current
,
*
new
,
*
aux
;
double
value
,
incr
;
long
long
ll
;
robj
*
o
,
*
doubleobj
;
sds
new
;
unsigned
char
*
vstr
;
unsigned
int
*
lven
;
if
(
getLongDoubleFromObjectOrReply
(
c
,
c
->
argv
[
3
],
&
incr
,
NULL
)
!=
C_OK
)
return
;
if
((
o
=
hashTypeLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
if
((
current
=
hashTypeGetObject
(
o
,
c
->
argv
[
2
]))
!=
NULL
)
{
if
(
getLongDoubleFromObjectOrReply
(
c
,
current
,
&
value
,
"hash value is not a valid float"
)
!=
C_OK
)
{
decrRefCount
(
current
);
return
;
if
(
hashTypeGetObject
(
o
,
c
->
argv
[
2
],
vstr
,
vlen
,
&
ll
)
==
C_OK
)
{
if
(
vstr
)
{
if
(
string2d
(
vstr
,
vlen
,
&
value
)
==
0
)
{
addReplyError
(
c
,
"hash value is not an integer"
);
return
;
}
}
else
{
value
=
(
double
)
ll
;
}
decrRefCount
(
current
);
}
else
{
value
=
0
;
}
value
+=
incr
;
new
=
createStringObjectFromLongDouble
(
value
,
1
);
hashTypeTryObjectEncoding
(
o
,
&
c
->
argv
[
2
],
NULL
);
doubleobj
=
createStringObjectFromLongDouble
(
value
,
1
);
decrRefCount
(
doubleobj
);
hashTypeSet
(
o
,
c
->
argv
[
2
],
new
);
addReplyBulk
(
c
,
new
);
signalModifiedKey
(
c
->
db
,
c
->
argv
[
1
]);
...
...
src/util.c
View file @
1c247556
...
...
@@ -330,7 +330,16 @@ int ll2string(char* dst, size_t dstlen, long long svalue) {
/* Convert a string into a long long. Returns 1 if the string could be parsed
* into a (non-overflowing) long long, 0 otherwise. The value will be set to
* the parsed value when appropriate. */
* the parsed value when appropriate.
*
* Note that this function demands that the string strictly represents
* a long long: no spaces or other characters before or after the string
* representing the number are accepted, nor zeroes at the start if not
* for the string "0" representing the zero number.
*
* Because of its strictness, it is safe to use this function to check if
* you can convert a string into a long long, and obtain back the string
* from the number without any loss in the string representation. */
int
string2ll
(
const
char
*
s
,
size_t
slen
,
long
long
*
value
)
{
const
char
*
p
=
s
;
size_t
plen
=
0
;
...
...
@@ -410,6 +419,30 @@ int string2l(const char *s, size_t slen, long *lval) {
return
1
;
}
/* Convert a string into a double. Returns 1 if the string could be parsed
* into a (non-overflowing) double, 0 otherwise. The value will be set to
* the parsed value when appropriate.
*
* Note that this function demands that the string strictly represents
* a double: no spaces or other characters before or after the string
* representing the number are accepted. */
int
string2d
(
const
char
*
s
,
size_t
slen
,
double
*
dp
)
{
double
value
;
errno
=
0
;
value
=
strtod
(
o
->
ptr
,
&
eptr
);
if
(
isspace
(((
char
*
)
o
->
ptr
)[
0
])
||
eptr
[
0
]
!=
'\0'
||
(
errno
==
ERANGE
&&
(
value
==
HUGE_VAL
||
value
==
-
HUGE_VAL
||
value
==
0
))
||
errno
==
EINVAL
||
isnan
(
value
))
return
0
;
if
(
dp
)
*
dp
=
value
;
return
1
;
}
/* Convert a double to a string representation. Returns the number of bytes
* required. The representation should always be parsable by strtod(3). */
int
d2string
(
char
*
buf
,
size_t
len
,
double
value
)
{
...
...
src/util.h
View file @
1c247556
...
...
@@ -41,6 +41,7 @@ uint32_t sdigits10(int64_t v);
int
ll2string
(
char
*
s
,
size_t
len
,
long
long
value
);
int
string2ll
(
const
char
*
s
,
size_t
slen
,
long
long
*
value
);
int
string2l
(
const
char
*
s
,
size_t
slen
,
long
*
value
);
int
string2d
(
const
char
*
s
,
size_t
slen
,
double
*
dp
);
int
d2string
(
char
*
buf
,
size_t
len
,
double
value
);
sds
getAbsolutePath
(
char
*
filename
);
int
pathIsBaseName
(
char
*
path
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment