@@ -122,6 +122,7 @@ struct AutoMemEntry {
122
122
#define VALKEYMODULE_AM_FREED 3 /* Explicitly freed by user already. */
123
123
#define VALKEYMODULE_AM_DICT 4
124
124
#define VALKEYMODULE_AM_INFO 5
125
+ #define VALKEYMODULE_AM_SHARED_SDS 6
125
126
126
127
/* The pool allocator block. Modules can allocate memory via this special
127
128
* allocator that will automatically release it all once the callback returns.
@@ -472,6 +473,7 @@ typedef int (*ValkeyModuleConfigSetBoolFunc)(const char *name, int val, void *pr
472
473
typedef int (*ValkeyModuleConfigSetEnumFunc)(const char *name, int val, void *privdata, ValkeyModuleString **err);
473
474
/* Apply signature, identical to valkeymodule.h */
474
475
typedef int (*ValkeyModuleConfigApplyFunc)(ValkeyModuleCtx *ctx, void *privdata, ValkeyModuleString **err);
476
+ typedef void *(*ValkeyModuleSharedSDSAllocFunc)(size_t len, size_t *alloc);
475
477
476
478
/* Struct representing a module config. These are stored in a list in the module struct */
477
479
struct ModuleConfig {
@@ -515,6 +517,7 @@ static void zsetKeyReset(ValkeyModuleKey *key);
515
517
static void moduleInitKeyTypeSpecific(ValkeyModuleKey *key);
516
518
void VM_FreeDict(ValkeyModuleCtx *ctx, ValkeyModuleDict *d);
517
519
void VM_FreeServerInfo(ValkeyModuleCtx *ctx, ValkeyModuleServerInfoData *data);
520
+ void VM_ReleaseSharedSDS(ValkeyModuleSharedSDS *shared_sds);
518
521
519
522
/* Helpers for VM_SetCommandInfo. */
520
523
static int moduleValidateCommandInfo(const ValkeyModuleCommandInfo *info);
@@ -2682,6 +2685,7 @@ void autoMemoryCollect(ValkeyModuleCtx *ctx) {
2682
2685
case VALKEYMODULE_AM_KEY: VM_CloseKey(ptr); break;
2683
2686
case VALKEYMODULE_AM_DICT: VM_FreeDict(NULL, ptr); break;
2684
2687
case VALKEYMODULE_AM_INFO: VM_FreeServerInfo(NULL, ptr); break;
2688
+ case VALKEYMODULE_AM_SHARED_SDS: VM_ReleaseSharedSDS(ptr); break;
2685
2689
}
2686
2690
}
2687
2691
ctx->flags |= VALKEYMODULE_CTX_AUTO_MEMORY;
@@ -5259,6 +5263,7 @@ int VM_ZsetRangePrev(ValkeyModuleKey *key) {
5259
5263
* are created.
5260
5264
* VALKEYMODULE_HASH_CFIELDS: The field names passed are null terminated C
5261
5265
* strings instead of ValkeyModuleString objects.
5266
+ * VALKEYMODULE_HASH_SHAREBLE_VALUES: The passed values are ValkeyModuleSharedSDS objects.
5262
5267
* VALKEYMODULE_HASH_COUNT_ALL: Include the number of inserted fields in the
5263
5268
* returned number, in addition to the number of
5264
5269
* updated and deleted fields. (Added in Redis OSS
@@ -5298,7 +5303,7 @@ int VM_ZsetRangePrev(ValkeyModuleKey *key) {
5298
5303
int VM_HashSet(ValkeyModuleKey *key, int flags, ...) {
5299
5304
va_list ap;
5300
5305
if (!key || (flags & ~(VALKEYMODULE_HASH_NX | VALKEYMODULE_HASH_XX | VALKEYMODULE_HASH_CFIELDS |
5301
- VALKEYMODULE_HASH_COUNT_ALL))) {
5306
+ VALKEYMODULE_HASH_COUNT_ALL | VALKEYMODULE_HASH_SHAREBLE_VALUES ))) {
5302
5307
errno = EINVAL;
5303
5308
return 0;
5304
5309
} else if (key->value && key->value->type != OBJ_HASH) {
@@ -5313,7 +5318,7 @@ int VM_HashSet(ValkeyModuleKey *key, int flags, ...) {
5313
5318
int count = 0;
5314
5319
va_start(ap, flags);
5315
5320
while (1) {
5316
- ValkeyModuleString *field, *value;
5321
+ ValkeyModuleString *field, *value = NULL ;
5317
5322
/* Get the field and value objects. */
5318
5323
if (flags & VALKEYMODULE_HASH_CFIELDS) {
5319
5324
char *cfield = va_arg(ap, char *);
@@ -5347,9 +5352,21 @@ int VM_HashSet(ValkeyModuleKey *key, int flags, ...) {
5347
5352
* to avoid a useless copy. */
5348
5353
if (flags & VALKEYMODULE_HASH_CFIELDS) low_flags |= HASH_SET_TAKE_FIELD;
5349
5354
5350
- robj *argv[2] = {field, value};
5351
- hashTypeTryConversion(key->value, argv, 0, 1);
5352
- int updated = hashTypeSet(key->value, field->ptr, value->ptr, low_flags);
5355
+ char *value_sds;
5356
+ if (flags & VALKEYMODULE_HASH_SHAREBLE_VALUES) {
5357
+ if (key->value->encoding == OBJ_ENCODING_LISTPACK) {
5358
+ /* Convert to hashtable encoding, as list pack encoding performs a deep copy
5359
+ * of the buffer, breaking ref-counting semantics. */
5360
+ hashTypeConvert(key->value, OBJ_ENCODING_HASHTABLE);
5361
+ }
5362
+ value_sds = ((ValkeyModuleSharedSDS *)value)->buf;
5363
+ } else {
5364
+ value_sds = value->ptr;
5365
+ robj *argv[2] = {field, value};
5366
+ hashTypeTryConversion(key->value, argv, 0, 1);
5367
+ }
5368
+
5369
+ int updated = hashTypeSet(key->value, field->ptr, value_sds, low_flags);
5353
5370
count += (flags & VALKEYMODULE_HASH_COUNT_ALL) ? 1 : updated;
5354
5371
5355
5372
/* If CFIELDS is active, SDS string ownership is now of hashTypeSet(),
@@ -5383,6 +5400,8 @@ int VM_HashSet(ValkeyModuleKey *key, int flags, ...) {
5383
5400
*
5384
5401
* VALKEYMODULE_HASH_CFIELDS: field names as null terminated C strings.
5385
5402
*
5403
+ * VALKEYMODULE_HASH_SHAREBLE_VALUES: The passed values are ValkeyModuleSharedSDS objects.
5404
+ *
5386
5405
* VALKEYMODULE_HASH_EXISTS: instead of setting the value of the field
5387
5406
* expecting a ValkeyModuleString pointer to pointer, the function just
5388
5407
* reports if the field exists or not and expects an integer pointer
@@ -5412,7 +5431,7 @@ int VM_HashGet(ValkeyModuleKey *key, int flags, ...) {
5412
5431
5413
5432
va_start(ap, flags);
5414
5433
while (1) {
5415
- ValkeyModuleString *field, **valueptr ;
5434
+ ValkeyModuleString *field;
5416
5435
int *existsptr;
5417
5436
/* Get the field object and the value pointer to pointer. */
5418
5437
if (flags & VALKEYMODULE_HASH_CFIELDS) {
@@ -5432,17 +5451,32 @@ int VM_HashGet(ValkeyModuleKey *key, int flags, ...) {
5432
5451
else
5433
5452
*existsptr = 0;
5434
5453
} else {
5435
- valueptr = va_arg(ap, ValkeyModuleString **);
5436
- if (key->value) {
5437
- *valueptr = hashTypeGetValueObject(key->value, field->ptr);
5438
- if (*valueptr) {
5439
- robj *decoded = getDecodedObject(*valueptr);
5440
- decrRefCount(*valueptr);
5441
- *valueptr = decoded;
5442
- }
5443
- if (*valueptr) autoMemoryAdd(key->ctx, VALKEYMODULE_AM_STRING, *valueptr);
5444
- } else {
5454
+ if (!key->value) {
5455
+ ValkeyModuleString **valueptr = va_arg(ap, ValkeyModuleString **);
5445
5456
*valueptr = NULL;
5457
+ } else {
5458
+ if (flags & VALKEYMODULE_HASH_SHAREBLE_VALUES) {
5459
+ ValkeyModuleSharedSDS **valueptr = va_arg(ap, ValkeyModuleSharedSDS **);
5460
+ *valueptr = NULL;
5461
+ /* shared SDS is supported only with hashtable encoding */
5462
+ if (key->value->encoding == OBJ_ENCODING_HASHTABLE) {
5463
+ sds value_sds = hashTypeGetFromHashTable(key->value, field->ptr);
5464
+ if (value_sds && sdsType(value_sds) == SDS_TYPE_32_SHARED) {
5465
+ *valueptr = (ValkeyModuleSharedSDS *)(value_sds - sdsHdrSize(sdsType(value_sds)));
5466
+ sdsRetain(*valueptr);
5467
+ autoMemoryAdd(key->ctx, VALKEYMODULE_AM_SHARED_SDS, *valueptr);
5468
+ }
5469
+ }
5470
+ } else {
5471
+ ValkeyModuleString **valueptr = va_arg(ap, ValkeyModuleString **);
5472
+ *valueptr = hashTypeGetValueObject(key->value, field->ptr);
5473
+ if (*valueptr) {
5474
+ robj *decoded = getDecodedObject(*valueptr);
5475
+ decrRefCount(*valueptr);
5476
+ *valueptr = decoded;
5477
+ }
5478
+ if (*valueptr) autoMemoryAdd(key->ctx, VALKEYMODULE_AM_STRING, *valueptr);
5479
+ }
5446
5480
}
5447
5481
}
5448
5482
@@ -13256,6 +13290,57 @@ ValkeyModuleScriptingEngineExecutionState VM_GetFunctionExecutionState(
13256
13290
return ret == SCRIPT_CONTINUE ? VMSE_STATE_EXECUTING : VMSE_STATE_KILLED;
13257
13291
}
13258
13292
13293
+ /* --------------------------------------------------------------------------
13294
+ * ## Shared SDS APIs
13295
+ * -------------------------------------------------------------------------- */
13296
+
13297
+ /* Create a new module shared SDS object. The newly created SDS object's intrusive
13298
+ * reference count is initialized to 1.
13299
+ * The caller is responsible for invoking `ValkeyModule_ReleaseSharedSDS` when the
13300
+ * object is no longer needed to ensure proper cleanup.
13301
+ *
13302
+ * Parameters:
13303
+ * - `len`: Specifies the size of the allocated SDS buffer.
13304
+ * - `allocfn`: A custom memory allocation function, allowing fine-grained control
13305
+ * over the allocation strategy.
13306
+ * - `freecbfn`: A callback function triggered on deallocation. Note that this does
13307
+ * not free the object itself but is primarily used for statistical tracking.
13308
+ *
13309
+ * Returns:
13310
+ * - A pointer to the created shared SDS object.
13311
+ */
13312
+ ValkeyModuleSharedSDS *VM_CreateSharedSDS(size_t len, ValkeyModuleSharedSDSAllocFunc allocfn, ValkeyModuleSharedSDSFreeCBFunc freecbfn) {
13313
+ size_t alloc;
13314
+ void *buf = allocfn(len + sizeof(ValkeyModuleSharedSDS) + 1, &alloc);
13315
+ return sdsInitShared((char *)buf, len, alloc, freecbfn);
13316
+ }
13317
+
13318
+ /* Retrieves the pointer to the shared SDS buffer along with its length.
13319
+ *
13320
+ * Parameters:
13321
+ * - `shared_sds`: A pointer to the `ValkeyModuleSharedSDS` object.
13322
+ * - `len`: Output parameter that stores the length of the SDS buffer.
13323
+ *
13324
+ * Returns:
13325
+ * - A pointer to the SDS buffer string.
13326
+ */
13327
+ char *VM_SharedSDSPtrLen(ValkeyModuleSharedSDS *shared_sds, size_t *len) {
13328
+ *len = shared_sds->len;
13329
+ return (char *)shared_sds + sizeof(ValkeyModuleSharedSDS);
13330
+ }
13331
+
13332
+ /* Releases a shared SDS object by decrementing its intrusive reference count.
13333
+ *
13334
+ * Every shared SDS object created by `VM_CreateSharedSDS` must be released
13335
+ * using `VM_ReleaseSharedSDS` to ensure proper memory management.
13336
+ *
13337
+ * Parameters:
13338
+ * - `shared_sds`: A pointer to the `ValkeyModuleSharedSDS` object to be released.
13339
+ */
13340
+ void VM_ReleaseSharedSDS(ValkeyModuleSharedSDS *shared_sds) {
13341
+ sdsfree(shared_sds->buf);
13342
+ }
13343
+
13259
13344
/* MODULE command.
13260
13345
*
13261
13346
* MODULE LIST
@@ -14130,4 +14215,7 @@ void moduleRegisterCoreAPI(void) {
14130
14215
REGISTER_API(RegisterScriptingEngine);
14131
14216
REGISTER_API(UnregisterScriptingEngine);
14132
14217
REGISTER_API(GetFunctionExecutionState);
14218
+ REGISTER_API(CreateSharedSDS);
14219
+ REGISTER_API(SharedSDSPtrLen);
14220
+ REGISTER_API(ReleaseSharedSDS);
14133
14221
}
0 commit comments