From 3b525948bc1f8d0b057c9d793f20f2fb9574cfd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Fri, 17 Jan 2025 19:58:19 +0100 Subject: [PATCH 1/7] Embed hash value in hash type entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Viktor Söderqvist --- src/sds.c | 2 + src/server.h | 2 +- src/t_hash.c | 233 ++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 205 insertions(+), 32 deletions(-) diff --git a/src/sds.c b/src/sds.c index d956f834e..257adec56 100644 --- a/src/sds.c +++ b/src/sds.c @@ -206,6 +206,8 @@ size_t sdscopytobuffer(unsigned char *buf, size_t buf_len, const_sds s, uint8_t } assert(buf_len >= required_keylen); memcpy(buf, sdsAllocPtr(s), required_keylen); + /* Store buf size in the sds alloc size field. */ + sdssetalloc((sds)(buf + sdsHdrSize(s[-1])), buf_len - sdsHdrSize(s[-1]) - 1); *hdr_size = sdsHdrSize(s[-1]); return required_keylen; } diff --git a/src/server.h b/src/server.h index 8f4fb6a6b..3afa6fdb2 100644 --- a/src/server.h +++ b/src/server.h @@ -3233,7 +3233,7 @@ robj *setTypeDup(robj *o); #define HASH_SET_TAKE_VALUE (1 << 1) #define HASH_SET_COPY 0 -typedef struct hashTypeEntry hashTypeEntry; +typedef void hashTypeEntry; hashTypeEntry *hashTypeCreateEntry(sds field, sds value); sds hashTypeEntryGetField(const hashTypeEntry *entry); sds hashTypeEntryGetValue(const hashTypeEntry *entry); diff --git a/src/t_hash.c b/src/t_hash.c index b6e6457bb..188e2d35d 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -34,45 +34,193 @@ * Hash Entry API *----------------------------------------------------------------------------*/ -struct hashTypeEntry { +/* The hashTypeEntry pointer is the field sds. We encode the entry layout in the + * field sds field for unused space, so sdsavail(entry) gives the entry encoding. + * + * ENTRY_ENC_EMB_VALUE, used when it fits in a cache line: + * + * +--------------+-------------------------+ + * | field | value | value | + * | hdr "foo" \0 | hdr_size | hdr "bar" \0 | + * +------^-------+-------------------------+ + * | + * | + * entry pointer = field sds + * + * ENTRY_ENC_PTR_VALUE, used for larger fields and values: + * + * +-------+--------------+ + * | value | field | + * | ptr | hdr "foo" \0 | + * +-------+------^-------+ + * | + * | + * entry pointer = field sds + */ + +typedef enum { + ENTRY_ENC_EMB_VALUE = 0, + ENTRY_ENC_PTR_VALUE +} hashTypeEntryEnc; + +/* Struct used for ENTRY_ENC_PTR_VALUE. */ +typedef struct { sds value; - unsigned char field_offset; unsigned char field_data[]; -}; +} hashTypeEntryPtrValue; + +static inline hashTypeEntryEnc entryGetEncoding(const hashTypeEntry *entry) { + return sdsavail(entry); +} + +/* Returns the containing struct for an entry without embedded value. */ +static hashTypeEntryPtrValue *getEntryStruct(const hashTypeEntry *entry) { + serverAssert(entryGetEncoding(entry) == ENTRY_ENC_PTR_VALUE); + unsigned char *buf = sdsAllocPtr(entry); + buf -= offsetof(hashTypeEntryPtrValue, field_data); + return (void *)buf; +} + +static inline bool canUseEmbeddedValueEntry(const_sds field, const_sds value) { + size_t field_size = sdscopytobuffer(NULL, 0, field, NULL); + size_t value_size = sdscopytobuffer(NULL, 0, value, NULL); + return field_size + 1 + value_size <= CACHE_LINE_SIZE; +} /* takes ownership of value, does not take ownership of field */ hashTypeEntry *hashTypeCreateEntry(sds field, sds value) { size_t field_size = sdscopytobuffer(NULL, 0, field, NULL); - - size_t total_size = sizeof(hashTypeEntry) + field_size; - hashTypeEntry *entry = zmalloc(total_size); - - entry->value = value; - sdscopytobuffer(entry->field_data, field_size, field, &entry->field_offset); - return entry; + sds embedded_field_sds; + if (canUseEmbeddedValueEntry(field, value)) { + /* Embed field and value, including one byte value-hdr-size. + * + * +--------------+-------------------------+ + * | field | value | value | + * | hdr "foo" \0 | hdr_size | hdr "bar" \0 | + * +--------------+-------------------------+ + */ + size_t value_size = sdscopytobuffer(NULL, 0, value, NULL); + size_t min_size = field_size + 1 + value_size; + size_t buf_size; + unsigned char *buf = zmalloc_usable(min_size, &buf_size); + uint8_t field_hdr_size; + sdscopytobuffer(buf, field_size, field, &field_hdr_size); + embedded_field_sds = (sds)(buf + field_hdr_size); + size_t value_buf_size = buf_size - field_size - 1; + sdscopytobuffer(buf + field_size + 1, value_buf_size, value, buf + field_size); + /* Unused space in the field sds is zero. We use this to encode that the + * entry is an embedded-value entry. For sds5, there is no usused space, + * which is why the value of ENTRY_ENC_EMB_VALUE was chosen to be 0. */ + serverAssert(entryGetEncoding(embedded_field_sds) == ENTRY_ENC_EMB_VALUE); + sdsfree(value); + } else { + /* Embed field, but not value. */ + unsigned char flags = field[-1]; + bool field_is_sds5 = (flags & SDS_TYPE_MASK) == SDS_TYPE_5; + if (field_is_sds5) field_size += 2; + size_t alloc_size = sizeof(void *) + field_size; + hashTypeEntryPtrValue *entry = zmalloc(alloc_size); + entry->value = sdsdup(value); + if (field_is_sds5) { + /* We can't use SDS_TYPE_5 to encode extra information in the unused + * allocation size, so convert to SDS_TYPE_8. */ + struct sdshdr8 *sh = (void *)entry->field_data; + sh->flags = SDS_TYPE_8; + sh->len = sdslen(field); + embedded_field_sds = (sds)(entry->field_data + sizeof(struct sdshdr8)); + memcpy(embedded_field_sds, field, sdslen(field) + 1); + } else { + uint8_t hdr_size; + sdscopytobuffer(entry->field_data, field_size, field, &hdr_size); + embedded_field_sds = (sds)(entry->field_data + hdr_size); + } + /* Flag that this is an entry with a value-pointer, not embedded value. */ + sdssetalloc(embedded_field_sds, sdslen(embedded_field_sds) + ENTRY_ENC_PTR_VALUE); + } + return (void *)embedded_field_sds; } +/* The entry pointer is the field sds, but that's an implementation detail. */ sds hashTypeEntryGetField(const hashTypeEntry *entry) { - const unsigned char *field = entry->field_data + entry->field_offset; - return (sds)field; + return (sds)entry; } sds hashTypeEntryGetValue(const hashTypeEntry *entry) { - return entry->value; + switch (entryGetEncoding(entry)) { + case ENTRY_ENC_EMB_VALUE: + { + /* To find the embedded value sds content, skip field, field null + * term, value hdr_size and hdr. */ + size_t offset = sdslen(entry) + 1; + char *buf = (char *)entry + offset; + char hdr_size = buf[0]; + return buf + 1 + hdr_size; + } + case ENTRY_ENC_PTR_VALUE: + { + const hashTypeEntryPtrValue *entry_struct = getEntryStruct(entry); + return entry_struct->value; + } + default: + serverPanic("Unknown type"); + } } -/* frees previous value, takes ownership of new value */ -static void hashTypeEntryReplaceValue(hashTypeEntry *entry, sds value) { - sdsfree(entry->value); - entry->value = value; +/* Returns the address of the entry allocation. */ +static void *hashTypeEntryAllocPtr(hashTypeEntry *entry) { + switch (entryGetEncoding(entry)) { + case ENTRY_ENC_EMB_VALUE: + return sdsAllocPtr(entry); + case ENTRY_ENC_PTR_VALUE: + return getEntryStruct(entry); + default: + serverPanic("Unknown type"); + } +} + +/* Frees previous value, takes ownership of new value, returns entry (may be + * reallocated). */ +static hashTypeEntry *hashTypeEntryReplaceValue(hashTypeEntry *entry, sds value) { + switch (entryGetEncoding(entry)) { + case ENTRY_ENC_EMB_VALUE: + { + /* TODO: Reuse existing allocation if possible. */ + hashTypeEntry *new_entry = hashTypeCreateEntry(hashTypeEntryGetField(entry), value); + freeHashTypeEntry(entry); + return new_entry; + } + case ENTRY_ENC_PTR_VALUE: + if (canUseEmbeddedValueEntry(hashTypeEntryGetField(entry), value)) { + /* Convert to entry with embedded value. */ + hashTypeEntry *new_entry = hashTypeCreateEntry(hashTypeEntryGetField(entry), value); + freeHashTypeEntry(entry); + return new_entry; + } else { + /* Not embedded value. */ + hashTypeEntryPtrValue *entry_struct = getEntryStruct(entry); + sdsfree(entry_struct->value); + entry_struct->value = value; + return entry; + } + default: + serverPanic("Unknown type"); + } } /* Returns allocation size of hashTypeEntry and data owned by hashTypeEntry, * even if not embedded in the same allocation. */ size_t hashTypeEntryAllocSize(hashTypeEntry *entry) { - size_t size = zmalloc_usable_size(entry); - size += sdsAllocSize(entry->value); - return size; + switch (entryGetEncoding(entry)) { + case ENTRY_ENC_EMB_VALUE: + return zmalloc_usable_size(sdsAllocPtr(entry)); + case ENTRY_ENC_PTR_VALUE: + { + hashTypeEntryPtrValue *entry_struct = getEntryStruct(entry); + return zmalloc_usable_size(entry_struct) + sdsAllocSize(entry_struct->value); + } + default: + serverPanic("Unknown type"); + } } /* Defragments a hashtable entry (field-value pair) if needed, using the @@ -83,25 +231,43 @@ size_t hashTypeEntryAllocSize(hashTypeEntry *entry) { * If the location of the hashTypeEntry changed we return the new location, * otherwise we return NULL. */ hashTypeEntry *hashTypeEntryDefrag(hashTypeEntry *entry, void *(*defragfn)(void *), sds (*sdsdefragfn)(sds)) { - hashTypeEntry *new_entry = defragfn(entry); - if (new_entry) entry = new_entry; - - sds new_value = sdsdefragfn(entry->value); - if (new_value) entry->value = new_value; - - return new_entry; + void *alloc_ptr = hashTypeEntryAllocPtr(entry); + switch (entryGetEncoding(entry)) { + case ENTRY_ENC_EMB_VALUE: + break; + case ENTRY_ENC_PTR_VALUE: + { + hashTypeEntryPtrValue *entry_struct = alloc_ptr; + sds new_value = sdsdefragfn(entry_struct->value); + if (new_value) entry_struct->value = new_value; + } + break; + } + return defragfn(alloc_ptr); } /* Used for releasing memory to OS to avoid unnecessary CoW. Called when we've * forked and memory won't be used again. See zmadvise_dontneed() */ void dismissHashTypeEntry(hashTypeEntry *entry) { /* Only dismiss values memory since the field size usually is small. */ - dismissSds(entry->value); + switch (entryGetEncoding(entry)) { + case ENTRY_ENC_EMB_VALUE: + break; + case ENTRY_ENC_PTR_VALUE: + dismissSds(getEntryStruct(entry)->value); + break; + } } void freeHashTypeEntry(hashTypeEntry *entry) { - sdsfree(entry->value); - zfree(entry); + void *alloc_ptr = hashTypeEntryAllocPtr(entry); + switch (entryGetEncoding(entry)) { + case ENTRY_ENC_EMB_VALUE: + break; + case ENTRY_ENC_PTR_VALUE: + sdsfree(((hashTypeEntryPtrValue *)alloc_ptr)->value); + } + zfree(alloc_ptr); } /*----------------------------------------------------------------------------- @@ -319,7 +485,12 @@ int hashTypeSet(robj *o, sds field, sds value, int flags) { hashtableInsertAtPosition(ht, entry, &position); } else { /* exists: replace value */ - hashTypeEntryReplaceValue(existing, v); + void *new_entry = hashTypeEntryReplaceValue(existing, v); + if (new_entry != existing) { + /* It has been reallocated. */ + int replaced = hashtableReplaceReallocatedEntry(ht, existing, new_entry); + serverAssert(replaced); + } update = 1; } } else { From cbb9052c57bba064e0b6d8852cbc75b5a8b2d2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Sun, 19 Jan 2025 10:53:25 +0100 Subject: [PATCH 2/7] Fix formatting and spelling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Viktor Söderqvist --- src/t_hash.c | 66 +++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/t_hash.c b/src/t_hash.c index 188e2d35d..fda6d925c 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -109,7 +109,7 @@ hashTypeEntry *hashTypeCreateEntry(sds field, sds value) { size_t value_buf_size = buf_size - field_size - 1; sdscopytobuffer(buf + field_size + 1, value_buf_size, value, buf + field_size); /* Unused space in the field sds is zero. We use this to encode that the - * entry is an embedded-value entry. For sds5, there is no usused space, + * entry is an embedded-value entry. For sds5, there is no unused space, * which is why the value of ENTRY_ENC_EMB_VALUE was chosen to be 0. */ serverAssert(entryGetEncoding(embedded_field_sds) == ENTRY_ENC_EMB_VALUE); sdsfree(value); @@ -147,20 +147,18 @@ sds hashTypeEntryGetField(const hashTypeEntry *entry) { sds hashTypeEntryGetValue(const hashTypeEntry *entry) { switch (entryGetEncoding(entry)) { - case ENTRY_ENC_EMB_VALUE: - { - /* To find the embedded value sds content, skip field, field null - * term, value hdr_size and hdr. */ - size_t offset = sdslen(entry) + 1; - char *buf = (char *)entry + offset; - char hdr_size = buf[0]; - return buf + 1 + hdr_size; - } - case ENTRY_ENC_PTR_VALUE: - { - const hashTypeEntryPtrValue *entry_struct = getEntryStruct(entry); - return entry_struct->value; - } + case ENTRY_ENC_EMB_VALUE: { + /* To find the embedded value sds content, skip field, field null term, + * value hdr_size and hdr. */ + size_t offset = sdslen(entry) + 1; + char *buf = (char *)entry + offset; + char hdr_size = buf[0]; + return buf + 1 + hdr_size; + } + case ENTRY_ENC_PTR_VALUE: { + const hashTypeEntryPtrValue *entry_struct = getEntryStruct(entry); + return entry_struct->value; + } default: serverPanic("Unknown type"); } @@ -182,14 +180,13 @@ static void *hashTypeEntryAllocPtr(hashTypeEntry *entry) { * reallocated). */ static hashTypeEntry *hashTypeEntryReplaceValue(hashTypeEntry *entry, sds value) { switch (entryGetEncoding(entry)) { - case ENTRY_ENC_EMB_VALUE: - { - /* TODO: Reuse existing allocation if possible. */ - hashTypeEntry *new_entry = hashTypeCreateEntry(hashTypeEntryGetField(entry), value); - freeHashTypeEntry(entry); - return new_entry; - } - case ENTRY_ENC_PTR_VALUE: + case ENTRY_ENC_EMB_VALUE: { + /* TODO: Reuse existing allocation if possible. */ + hashTypeEntry *new_entry = hashTypeCreateEntry(hashTypeEntryGetField(entry), value); + freeHashTypeEntry(entry); + return new_entry; + } + case ENTRY_ENC_PTR_VALUE: { if (canUseEmbeddedValueEntry(hashTypeEntryGetField(entry), value)) { /* Convert to entry with embedded value. */ hashTypeEntry *new_entry = hashTypeCreateEntry(hashTypeEntryGetField(entry), value); @@ -202,6 +199,7 @@ static hashTypeEntry *hashTypeEntryReplaceValue(hashTypeEntry *entry, sds value) entry_struct->value = value; return entry; } + } default: serverPanic("Unknown type"); } @@ -213,11 +211,10 @@ size_t hashTypeEntryAllocSize(hashTypeEntry *entry) { switch (entryGetEncoding(entry)) { case ENTRY_ENC_EMB_VALUE: return zmalloc_usable_size(sdsAllocPtr(entry)); - case ENTRY_ENC_PTR_VALUE: - { - hashTypeEntryPtrValue *entry_struct = getEntryStruct(entry); - return zmalloc_usable_size(entry_struct) + sdsAllocSize(entry_struct->value); - } + case ENTRY_ENC_PTR_VALUE: { + hashTypeEntryPtrValue *entry_struct = getEntryStruct(entry); + return zmalloc_usable_size(entry_struct) + sdsAllocSize(entry_struct->value); + } default: serverPanic("Unknown type"); } @@ -235,14 +232,15 @@ hashTypeEntry *hashTypeEntryDefrag(hashTypeEntry *entry, void *(*defragfn)(void switch (entryGetEncoding(entry)) { case ENTRY_ENC_EMB_VALUE: break; - case ENTRY_ENC_PTR_VALUE: - { - hashTypeEntryPtrValue *entry_struct = alloc_ptr; - sds new_value = sdsdefragfn(entry_struct->value); - if (new_value) entry_struct->value = new_value; - } + case ENTRY_ENC_PTR_VALUE: { + hashTypeEntryPtrValue *entry_struct = alloc_ptr; + sds new_value = sdsdefragfn(entry_struct->value); + if (new_value) entry_struct->value = new_value; break; } + default: + serverPanic("Unknown type"); + } return defragfn(alloc_ptr); } From 1ed116d1d3eb943fe9d55778e1418226d73136cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Sun, 19 Jan 2025 10:55:26 +0100 Subject: [PATCH 3/7] Fix missing include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Viktor Söderqvist --- src/t_hash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/t_hash.c b/src/t_hash.c index fda6d925c..55dee2e89 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -29,6 +29,7 @@ #include "server.h" #include +#include /*----------------------------------------------------------------------------- * Hash Entry API From df15b5baf84220b65e5896277d7849b671ad582e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Tue, 21 Jan 2025 08:55:12 +0100 Subject: [PATCH 4/7] Reuse allocation when replacing value in embedded entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Viktor Söderqvist --- src/t_hash.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/t_hash.c b/src/t_hash.c index 55dee2e89..b0ba8f319 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -182,7 +182,21 @@ static void *hashTypeEntryAllocPtr(hashTypeEntry *entry) { static hashTypeEntry *hashTypeEntryReplaceValue(hashTypeEntry *entry, sds value) { switch (entryGetEncoding(entry)) { case ENTRY_ENC_EMB_VALUE: { - /* TODO: Reuse existing allocation if possible. */ + /* Reuse the allocation if the new value fits and leaves no more than + * 25% unused space after replacing the value. */ + unsigned char *alloc_ptr = sdsAllocPtr(entry); + size_t field_size = ((unsigned char *)entry - alloc_ptr) + sdslen(entry) + 1; + size_t value_size = sdscopytobuffer(NULL, 0, value, NULL); + size_t required_size = field_size + 1 + value_size; + size_t alloc_size; + if (required_size <= CACHE_LINE_SIZE && + required_size <= (alloc_size = zmalloc_usable_size(alloc_ptr)) && + required_size >= alloc_size * 3 / 4) { + /* It fits in the allocation and leaves max 25% unused space. */ + sdscopytobuffer(alloc_ptr + field_size + 1, alloc_size - (field_size + 1), value, alloc_ptr + field_size); + sdsfree(value); + return entry; + } hashTypeEntry *new_entry = hashTypeCreateEntry(hashTypeEntryGetField(entry), value); freeHashTypeEntry(entry); return new_entry; From b79d924f7e53c7789adb361175e3030394b09815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Tue, 21 Jan 2025 08:56:10 +0100 Subject: [PATCH 5/7] Rename hashTypeEntryAllocSize to hashTypeEntryMemUsage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Viktor Söderqvist --- src/object.c | 2 +- src/server.h | 2 +- src/t_hash.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/object.c b/src/object.c index b8200dd81..79d4b62ca 100644 --- a/src/object.c +++ b/src/object.c @@ -1202,7 +1202,7 @@ size_t objectComputeSize(robj *key, robj *o, size_t sample_size, int dbid) { asize = sizeof(*o) + hashtableMemUsage(ht); while (hashtableNext(&iter, &next) && samples < sample_size) { - elesize += hashTypeEntryAllocSize(next); + elesize += hashTypeEntryMemUsage(next); samples++; } hashtableResetIterator(&iter); diff --git a/src/server.h b/src/server.h index 3afa6fdb2..745fb5e0b 100644 --- a/src/server.h +++ b/src/server.h @@ -3237,7 +3237,7 @@ typedef void hashTypeEntry; hashTypeEntry *hashTypeCreateEntry(sds field, sds value); sds hashTypeEntryGetField(const hashTypeEntry *entry); sds hashTypeEntryGetValue(const hashTypeEntry *entry); -size_t hashTypeEntryAllocSize(hashTypeEntry *entry); +size_t hashTypeEntryMemUsage(hashTypeEntry *entry); hashTypeEntry *hashTypeEntryDefrag(hashTypeEntry *entry, void *(*defragfn)(void *), sds (*sdsdefragfn)(sds)); void dismissHashTypeEntry(hashTypeEntry *entry); void freeHashTypeEntry(hashTypeEntry *entry); diff --git a/src/t_hash.c b/src/t_hash.c index b0ba8f319..4fcb11672 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -220,9 +220,9 @@ static hashTypeEntry *hashTypeEntryReplaceValue(hashTypeEntry *entry, sds value) } } -/* Returns allocation size of hashTypeEntry and data owned by hashTypeEntry, - * even if not embedded in the same allocation. */ -size_t hashTypeEntryAllocSize(hashTypeEntry *entry) { +/* Returns memory usage of a hashTypeEntry, including all allocations owned by + * the hashTypeEntry. */ +size_t hashTypeEntryMemUsage(hashTypeEntry *entry) { switch (entryGetEncoding(entry)) { case ENTRY_ENC_EMB_VALUE: return zmalloc_usable_size(sdsAllocPtr(entry)); From 62cb46f94b043fd79351fdc6715f376fe71a86f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Tue, 21 Jan 2025 10:13:56 +0100 Subject: [PATCH 6/7] Edit drawings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Viktor Söderqvist --- src/t_hash.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/t_hash.c b/src/t_hash.c index 4fcb11672..f6e2986f5 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -40,10 +40,10 @@ * * ENTRY_ENC_EMB_VALUE, used when it fits in a cache line: * - * +--------------+-------------------------+ - * | field | value | value | - * | hdr "foo" \0 | hdr_size | hdr "bar" \0 | - * +------^-------+-------------------------+ + * +--------------+----------------+--------------+ + * | field | 1 byte | value | + * | hdr "foo" \0 | value-hdr-size | hdr "bar" \0 | + * +------^-------+----------------+--------------+ * | * | * entry pointer = field sds @@ -95,10 +95,10 @@ hashTypeEntry *hashTypeCreateEntry(sds field, sds value) { if (canUseEmbeddedValueEntry(field, value)) { /* Embed field and value, including one byte value-hdr-size. * - * +--------------+-------------------------+ - * | field | value | value | - * | hdr "foo" \0 | hdr_size | hdr "bar" \0 | - * +--------------+-------------------------+ + * +--------------+----------------+--------------+ + * | field | 1 byte | value | + * | hdr "foo" \0 | value-hdr-size | hdr "bar" \0 | + * +--------------+----------------+--------------+ */ size_t value_size = sdscopytobuffer(NULL, 0, value, NULL); size_t min_size = field_size + 1 + value_size; From e40ccfd99e435134c1eb2a1ffd542435fd818c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Tue, 21 Jan 2025 10:41:51 +0100 Subject: [PATCH 7/7] Fix memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Viktor Söderqvist --- src/t_hash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t_hash.c b/src/t_hash.c index f6e2986f5..e3f7ca9b2 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -121,7 +121,7 @@ hashTypeEntry *hashTypeCreateEntry(sds field, sds value) { if (field_is_sds5) field_size += 2; size_t alloc_size = sizeof(void *) + field_size; hashTypeEntryPtrValue *entry = zmalloc(alloc_size); - entry->value = sdsdup(value); + entry->value = value; if (field_is_sds5) { /* We can't use SDS_TYPE_5 to encode extra information in the unused * allocation size, so convert to SDS_TYPE_8. */