mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 08:08:53 -05:00
cdd60793d5
Non-API impacting renames. Signed-off-by: Drew DeVault <sir@cmpwn.com>
746 lines
28 KiB
C
746 lines
28 KiB
C
// SPDX-FileCopyrightText: 2024 Redict Contributors
|
|
// SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
|
|
//
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
/* This module is used to test a use case of a module that stores information
|
|
* about keys in global memory, and relies on the enhanced data type callbacks to
|
|
* get key name and dbid on various operations.
|
|
*
|
|
* it simulates a simple memory allocator. The smallest allocation unit of
|
|
* the allocator is a mem block with a size of 4KB. Multiple mem blocks are combined
|
|
* using a linked list. These linked lists are placed in a global dict named 'mem_pool'.
|
|
* Each db has a 'mem_pool'. You can use the 'mem.alloc' command to allocate a specified
|
|
* number of mem blocks, and use 'mem.free' to release the memory. Use 'mem.write', 'mem.read'
|
|
* to write and read the specified mem block (note that each mem block can only be written once).
|
|
* Use 'mem.usage' to get the memory usage under different dbs, and it will return the size
|
|
* mem blocks and used mem blocks under the db.
|
|
* The specific structure diagram is as follows:
|
|
*
|
|
*
|
|
* Global variables of the module:
|
|
*
|
|
* mem blocks link
|
|
* ┌─────┬─────┐
|
|
* │ │ │ ┌───┐ ┌───┐ ┌───┐
|
|
* │ k1 │ ───┼───►│4KB├───►│4KB├───►│4KB│
|
|
* │ │ │ └───┘ └───┘ └───┘
|
|
* ├─────┼─────┤
|
|
* ┌───────┐ ┌────► │ │ │ ┌───┐ ┌───┐
|
|
* │ │ │ │ k2 │ ───┼───►│4KB├───►│4KB│
|
|
* │ db0 ├──────┘ │ │ │ └───┘ └───┘
|
|
* │ │ ├─────┼─────┤
|
|
* ├───────┤ │ │ │ ┌───┐ ┌───┐ ┌───┐
|
|
* │ │ │ k3 │ ───┼───►│4KB├───►│4KB├───►│4KB│
|
|
* │ db1 ├──►null │ │ │ └───┘ └───┘ └───┘
|
|
* │ │ └─────┴─────┘
|
|
* ├───────┤ dict
|
|
* │ │
|
|
* │ db2 ├─────────┐
|
|
* │ │ │
|
|
* ├───────┤ │ ┌─────┬─────┐
|
|
* │ │ │ │ │ │ ┌───┐ ┌───┐ ┌───┐
|
|
* │ db3 ├──►null │ │ k1 │ ───┼───►│4KB├───►│4KB├───►│4KB│
|
|
* │ │ │ │ │ │ └───┘ └───┘ └───┘
|
|
* └───────┘ │ ├─────┼─────┤
|
|
* mem_pool[MAX_DB] │ │ │ │ ┌───┐ ┌───┐
|
|
* └──►│ k2 │ ───┼───►│4KB├───►│4KB│
|
|
* │ │ │ └───┘ └───┘
|
|
* └─────┴─────┘
|
|
* dict
|
|
*
|
|
*
|
|
* Keys in redict database:
|
|
*
|
|
* ┌───────┐
|
|
* │ size │
|
|
* ┌───────────►│ used │
|
|
* │ │ mask │
|
|
* ┌─────┬─────┐ │ └───────┘ ┌───────┐
|
|
* │ │ │ │ MemAllocObject │ size │
|
|
* │ k1 │ ───┼─┘ ┌───────────►│ used │
|
|
* │ │ │ │ │ mask │
|
|
* ├─────┼─────┤ ┌───────┐ ┌─────┬─────┐ │ └───────┘
|
|
* │ │ │ │ size │ │ │ │ │ MemAllocObject
|
|
* │ k2 │ ───┼─────────────►│ used │ │ k1 │ ───┼─┘
|
|
* │ │ │ │ mask │ │ │ │
|
|
* ├─────┼─────┤ └───────┘ ├─────┼─────┤
|
|
* │ │ │ MemAllocObject │ │ │
|
|
* │ k3 │ ───┼─┐ │ k2 │ ───┼─┐
|
|
* │ │ │ │ │ │ │ │
|
|
* └─────┴─────┘ │ ┌───────┐ └─────┴─────┘ │ ┌───────┐
|
|
* redict db[0] │ │ size │ redict db[1]│ │ size │
|
|
* └───────────►│ used │ └───────────►│ used │
|
|
* │ mask │ │ mask │
|
|
* └───────┘ └───────┘
|
|
* MemAllocObject MemAllocObject
|
|
*
|
|
**/
|
|
|
|
#include "redictmodule.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
static RedictModuleType *MemAllocType;
|
|
|
|
#define MAX_DB 16
|
|
RedictModuleDict *mem_pool[MAX_DB];
|
|
typedef struct MemAllocObject {
|
|
long long size;
|
|
long long used;
|
|
uint64_t mask;
|
|
} MemAllocObject;
|
|
|
|
MemAllocObject *createMemAllocObject(void) {
|
|
MemAllocObject *o = RedictModule_Calloc(1, sizeof(*o));
|
|
return o;
|
|
}
|
|
|
|
/*---------------------------- mem block apis ------------------------------------*/
|
|
#define BLOCK_SIZE 4096
|
|
struct MemBlock {
|
|
char block[BLOCK_SIZE];
|
|
struct MemBlock *next;
|
|
};
|
|
|
|
void MemBlockFree(struct MemBlock *head) {
|
|
if (head) {
|
|
struct MemBlock *block = head->next, *next;
|
|
RedictModule_Free(head);
|
|
while (block) {
|
|
next = block->next;
|
|
RedictModule_Free(block);
|
|
block = next;
|
|
}
|
|
}
|
|
}
|
|
struct MemBlock *MemBlockCreate(long long num) {
|
|
if (num <= 0) {
|
|
return NULL;
|
|
}
|
|
|
|
struct MemBlock *head = RedictModule_Calloc(1, sizeof(struct MemBlock));
|
|
struct MemBlock *block = head;
|
|
while (--num) {
|
|
block->next = RedictModule_Calloc(1, sizeof(struct MemBlock));
|
|
block = block->next;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
long long MemBlockNum(const struct MemBlock *head) {
|
|
long long num = 0;
|
|
const struct MemBlock *block = head;
|
|
while (block) {
|
|
num++;
|
|
block = block->next;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
size_t MemBlockWrite(struct MemBlock *head, long long block_index, const char *data, size_t size) {
|
|
size_t w_size = 0;
|
|
struct MemBlock *block = head;
|
|
while (block_index-- && block) {
|
|
block = block->next;
|
|
}
|
|
|
|
if (block) {
|
|
size = size > BLOCK_SIZE ? BLOCK_SIZE:size;
|
|
memcpy(block->block, data, size);
|
|
w_size += size;
|
|
}
|
|
|
|
return w_size;
|
|
}
|
|
|
|
int MemBlockRead(struct MemBlock *head, long long block_index, char *data, size_t size) {
|
|
size_t r_size = 0;
|
|
struct MemBlock *block = head;
|
|
while (block_index-- && block) {
|
|
block = block->next;
|
|
}
|
|
|
|
if (block) {
|
|
size = size > BLOCK_SIZE ? BLOCK_SIZE:size;
|
|
memcpy(data, block->block, size);
|
|
r_size += size;
|
|
}
|
|
|
|
return r_size;
|
|
}
|
|
|
|
void MemPoolFreeDb(RedictModuleCtx *ctx, int dbid) {
|
|
RedictModuleString *key;
|
|
void *tdata;
|
|
RedictModuleDictIter *iter = RedictModule_DictIteratorStartC(mem_pool[dbid], "^", NULL, 0);
|
|
while((key = RedictModule_DictNext(ctx, iter, &tdata)) != NULL) {
|
|
MemBlockFree((struct MemBlock *)tdata);
|
|
}
|
|
RedictModule_DictIteratorStop(iter);
|
|
RedictModule_FreeDict(NULL, mem_pool[dbid]);
|
|
mem_pool[dbid] = RedictModule_CreateDict(NULL);
|
|
}
|
|
|
|
struct MemBlock *MemBlockClone(const struct MemBlock *head) {
|
|
struct MemBlock *newhead = NULL;
|
|
if (head) {
|
|
newhead = RedictModule_Calloc(1, sizeof(struct MemBlock));
|
|
memcpy(newhead->block, head->block, BLOCK_SIZE);
|
|
struct MemBlock *newblock = newhead;
|
|
const struct MemBlock *oldblock = head->next;
|
|
while (oldblock) {
|
|
newblock->next = RedictModule_Calloc(1, sizeof(struct MemBlock));
|
|
newblock = newblock->next;
|
|
memcpy(newblock->block, oldblock->block, BLOCK_SIZE);
|
|
oldblock = oldblock->next;
|
|
}
|
|
}
|
|
|
|
return newhead;
|
|
}
|
|
|
|
/*---------------------------- event handler ------------------------------------*/
|
|
void swapDbCallback(RedictModuleCtx *ctx, RedictModuleEvent e, uint64_t sub, void *data) {
|
|
REDICTMODULE_NOT_USED(ctx);
|
|
REDICTMODULE_NOT_USED(e);
|
|
REDICTMODULE_NOT_USED(sub);
|
|
|
|
RedictModuleSwapDbInfo *ei = data;
|
|
|
|
// swap
|
|
RedictModuleDict *tmp = mem_pool[ei->dbnum_first];
|
|
mem_pool[ei->dbnum_first] = mem_pool[ei->dbnum_second];
|
|
mem_pool[ei->dbnum_second] = tmp;
|
|
}
|
|
|
|
void flushdbCallback(RedictModuleCtx *ctx, RedictModuleEvent e, uint64_t sub, void *data) {
|
|
REDICTMODULE_NOT_USED(ctx);
|
|
REDICTMODULE_NOT_USED(e);
|
|
int i;
|
|
RedictModuleFlushInfo *fi = data;
|
|
|
|
RedictModule_AutoMemory(ctx);
|
|
|
|
if (sub == REDICTMODULE_SUBEVENT_FLUSHDB_START) {
|
|
if (fi->dbnum != -1) {
|
|
MemPoolFreeDb(ctx, fi->dbnum);
|
|
} else {
|
|
for (i = 0; i < MAX_DB; i++) {
|
|
MemPoolFreeDb(ctx, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---------------------------- command implementation ------------------------------------*/
|
|
|
|
/* MEM.ALLOC key block_num */
|
|
int MemAlloc_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
|
|
RedictModule_AutoMemory(ctx);
|
|
|
|
if (argc != 3) {
|
|
return RedictModule_WrongArity(ctx);
|
|
}
|
|
|
|
long long block_num;
|
|
if ((RedictModule_StringToLongLong(argv[2], &block_num) != REDICTMODULE_OK) || block_num <= 0) {
|
|
return RedictModule_ReplyWithError(ctx, "ERR invalid block_num: must be a value greater than 0");
|
|
}
|
|
|
|
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_READ | REDICTMODULE_WRITE);
|
|
int type = RedictModule_KeyType(key);
|
|
if (type != REDICTMODULE_KEYTYPE_EMPTY && RedictModule_ModuleTypeGetType(key) != MemAllocType) {
|
|
return RedictModule_ReplyWithError(ctx, REDICTMODULE_ERRORMSG_WRONGTYPE);
|
|
}
|
|
|
|
MemAllocObject *o;
|
|
if (type == REDICTMODULE_KEYTYPE_EMPTY) {
|
|
o = createMemAllocObject();
|
|
RedictModule_ModuleTypeSetValue(key, MemAllocType, o);
|
|
} else {
|
|
o = RedictModule_ModuleTypeGetValue(key);
|
|
}
|
|
|
|
struct MemBlock *mem = MemBlockCreate(block_num);
|
|
RedictModule_Assert(mem != NULL);
|
|
RedictModule_DictSet(mem_pool[RedictModule_GetSelectedDb(ctx)], argv[1], mem);
|
|
o->size = block_num;
|
|
o->used = 0;
|
|
o->mask = 0;
|
|
|
|
RedictModule_ReplyWithLongLong(ctx, block_num);
|
|
RedictModule_ReplicateVerbatim(ctx);
|
|
return REDICTMODULE_OK;
|
|
}
|
|
|
|
/* MEM.FREE key */
|
|
int MemFree_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
|
|
RedictModule_AutoMemory(ctx);
|
|
|
|
if (argc != 2) {
|
|
return RedictModule_WrongArity(ctx);
|
|
}
|
|
|
|
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_READ);
|
|
int type = RedictModule_KeyType(key);
|
|
if (type != REDICTMODULE_KEYTYPE_EMPTY && RedictModule_ModuleTypeGetType(key) != MemAllocType) {
|
|
return RedictModule_ReplyWithError(ctx, REDICTMODULE_ERRORMSG_WRONGTYPE);
|
|
}
|
|
|
|
int ret = 0;
|
|
MemAllocObject *o;
|
|
if (type == REDICTMODULE_KEYTYPE_EMPTY) {
|
|
RedictModule_ReplyWithLongLong(ctx, ret);
|
|
return REDICTMODULE_OK;
|
|
} else {
|
|
o = RedictModule_ModuleTypeGetValue(key);
|
|
}
|
|
|
|
int nokey;
|
|
struct MemBlock *mem = (struct MemBlock *)RedictModule_DictGet(mem_pool[RedictModule_GetSelectedDb(ctx)], argv[1], &nokey);
|
|
if (!nokey && mem) {
|
|
RedictModule_DictDel(mem_pool[RedictModule_GetSelectedDb(ctx)], argv[1], NULL);
|
|
MemBlockFree(mem);
|
|
o->used = 0;
|
|
o->size = 0;
|
|
o->mask = 0;
|
|
ret = 1;
|
|
}
|
|
|
|
RedictModule_ReplyWithLongLong(ctx, ret);
|
|
RedictModule_ReplicateVerbatim(ctx);
|
|
return REDICTMODULE_OK;
|
|
}
|
|
|
|
/* MEM.WRITE key block_index data */
|
|
int MemWrite_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
|
|
RedictModule_AutoMemory(ctx);
|
|
|
|
if (argc != 4) {
|
|
return RedictModule_WrongArity(ctx);
|
|
}
|
|
|
|
long long block_index;
|
|
if ((RedictModule_StringToLongLong(argv[2], &block_index) != REDICTMODULE_OK) || block_index < 0) {
|
|
return RedictModule_ReplyWithError(ctx, "ERR invalid block_index: must be a value greater than 0");
|
|
}
|
|
|
|
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_READ | REDICTMODULE_WRITE);
|
|
int type = RedictModule_KeyType(key);
|
|
if (type != REDICTMODULE_KEYTYPE_EMPTY && RedictModule_ModuleTypeGetType(key) != MemAllocType) {
|
|
return RedictModule_ReplyWithError(ctx, REDICTMODULE_ERRORMSG_WRONGTYPE);
|
|
}
|
|
|
|
MemAllocObject *o;
|
|
if (type == REDICTMODULE_KEYTYPE_EMPTY) {
|
|
return RedictModule_ReplyWithError(ctx, "ERR Memory has not been allocated");
|
|
} else {
|
|
o = RedictModule_ModuleTypeGetValue(key);
|
|
}
|
|
|
|
if (o->mask & (1UL << block_index)) {
|
|
return RedictModule_ReplyWithError(ctx, "ERR block is busy");
|
|
}
|
|
|
|
int ret = 0;
|
|
int nokey;
|
|
struct MemBlock *mem = (struct MemBlock *)RedictModule_DictGet(mem_pool[RedictModule_GetSelectedDb(ctx)], argv[1], &nokey);
|
|
if (!nokey && mem) {
|
|
size_t len;
|
|
const char *buf = RedictModule_StringPtrLen(argv[3], &len);
|
|
ret = MemBlockWrite(mem, block_index, buf, len);
|
|
o->mask |= (1UL << block_index);
|
|
o->used++;
|
|
}
|
|
|
|
RedictModule_ReplyWithLongLong(ctx, ret);
|
|
RedictModule_ReplicateVerbatim(ctx);
|
|
return REDICTMODULE_OK;
|
|
}
|
|
|
|
/* MEM.READ key block_index */
|
|
int MemRead_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
|
|
RedictModule_AutoMemory(ctx);
|
|
|
|
if (argc != 3) {
|
|
return RedictModule_WrongArity(ctx);
|
|
}
|
|
|
|
long long block_index;
|
|
if ((RedictModule_StringToLongLong(argv[2], &block_index) != REDICTMODULE_OK) || block_index < 0) {
|
|
return RedictModule_ReplyWithError(ctx, "ERR invalid block_index: must be a value greater than 0");
|
|
}
|
|
|
|
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_READ);
|
|
int type = RedictModule_KeyType(key);
|
|
if (type != REDICTMODULE_KEYTYPE_EMPTY && RedictModule_ModuleTypeGetType(key) != MemAllocType) {
|
|
return RedictModule_ReplyWithError(ctx, REDICTMODULE_ERRORMSG_WRONGTYPE);
|
|
}
|
|
|
|
MemAllocObject *o;
|
|
if (type == REDICTMODULE_KEYTYPE_EMPTY) {
|
|
return RedictModule_ReplyWithError(ctx, "ERR Memory has not been allocated");
|
|
} else {
|
|
o = RedictModule_ModuleTypeGetValue(key);
|
|
}
|
|
|
|
if (!(o->mask & (1UL << block_index))) {
|
|
return RedictModule_ReplyWithNull(ctx);
|
|
}
|
|
|
|
int nokey;
|
|
struct MemBlock *mem = (struct MemBlock *)RedictModule_DictGet(mem_pool[RedictModule_GetSelectedDb(ctx)], argv[1], &nokey);
|
|
RedictModule_Assert(nokey == 0 && mem != NULL);
|
|
|
|
char buf[BLOCK_SIZE];
|
|
MemBlockRead(mem, block_index, buf, sizeof(buf));
|
|
|
|
/* Assuming that the contents are all c-style strings */
|
|
RedictModule_ReplyWithStringBuffer(ctx, buf, strlen(buf));
|
|
return REDICTMODULE_OK;
|
|
}
|
|
|
|
/* MEM.USAGE dbid */
|
|
int MemUsage_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
|
|
RedictModule_AutoMemory(ctx);
|
|
|
|
if (argc != 2) {
|
|
return RedictModule_WrongArity(ctx);
|
|
}
|
|
|
|
long long dbid;
|
|
if ((RedictModule_StringToLongLong(argv[1], (long long *)&dbid) != REDICTMODULE_OK)) {
|
|
return RedictModule_ReplyWithError(ctx, "ERR invalid value: must be a integer");
|
|
}
|
|
|
|
if (dbid < 0 || dbid >= MAX_DB) {
|
|
return RedictModule_ReplyWithError(ctx, "ERR dbid out of range");
|
|
}
|
|
|
|
|
|
long long size = 0, used = 0;
|
|
|
|
void *data;
|
|
RedictModuleString *key;
|
|
RedictModuleDictIter *iter = RedictModule_DictIteratorStartC(mem_pool[dbid], "^", NULL, 0);
|
|
while((key = RedictModule_DictNext(ctx, iter, &data)) != NULL) {
|
|
int dbbackup = RedictModule_GetSelectedDb(ctx);
|
|
RedictModule_SelectDb(ctx, dbid);
|
|
RedictModuleKey *openkey = RedictModule_OpenKey(ctx, key, REDICTMODULE_READ);
|
|
int type = RedictModule_KeyType(openkey);
|
|
RedictModule_Assert(type != REDICTMODULE_KEYTYPE_EMPTY && RedictModule_ModuleTypeGetType(openkey) == MemAllocType);
|
|
MemAllocObject *o = RedictModule_ModuleTypeGetValue(openkey);
|
|
used += o->used;
|
|
size += o->size;
|
|
RedictModule_CloseKey(openkey);
|
|
RedictModule_SelectDb(ctx, dbbackup);
|
|
}
|
|
RedictModule_DictIteratorStop(iter);
|
|
|
|
RedictModule_ReplyWithArray(ctx, 4);
|
|
RedictModule_ReplyWithSimpleString(ctx, "total");
|
|
RedictModule_ReplyWithLongLong(ctx, size);
|
|
RedictModule_ReplyWithSimpleString(ctx, "used");
|
|
RedictModule_ReplyWithLongLong(ctx, used);
|
|
return REDICTMODULE_OK;
|
|
}
|
|
|
|
/* MEM.ALLOCANDWRITE key block_num block_index data block_index data ... */
|
|
int MemAllocAndWrite_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
|
|
RedictModule_AutoMemory(ctx);
|
|
|
|
if (argc < 3) {
|
|
return RedictModule_WrongArity(ctx);
|
|
}
|
|
|
|
long long block_num;
|
|
if ((RedictModule_StringToLongLong(argv[2], &block_num) != REDICTMODULE_OK) || block_num <= 0) {
|
|
return RedictModule_ReplyWithError(ctx, "ERR invalid block_num: must be a value greater than 0");
|
|
}
|
|
|
|
RedictModuleKey *key = RedictModule_OpenKey(ctx, argv[1], REDICTMODULE_READ | REDICTMODULE_WRITE);
|
|
int type = RedictModule_KeyType(key);
|
|
if (type != REDICTMODULE_KEYTYPE_EMPTY && RedictModule_ModuleTypeGetType(key) != MemAllocType) {
|
|
return RedictModule_ReplyWithError(ctx, REDICTMODULE_ERRORMSG_WRONGTYPE);
|
|
}
|
|
|
|
MemAllocObject *o;
|
|
if (type == REDICTMODULE_KEYTYPE_EMPTY) {
|
|
o = createMemAllocObject();
|
|
RedictModule_ModuleTypeSetValue(key, MemAllocType, o);
|
|
} else {
|
|
o = RedictModule_ModuleTypeGetValue(key);
|
|
}
|
|
|
|
struct MemBlock *mem = MemBlockCreate(block_num);
|
|
RedictModule_Assert(mem != NULL);
|
|
RedictModule_DictSet(mem_pool[RedictModule_GetSelectedDb(ctx)], argv[1], mem);
|
|
o->used = 0;
|
|
o->mask = 0;
|
|
o->size = block_num;
|
|
|
|
int i = 3;
|
|
long long block_index;
|
|
for (; i < argc; i++) {
|
|
/* Security is guaranteed internally, so no security check. */
|
|
RedictModule_StringToLongLong(argv[i], &block_index);
|
|
size_t len;
|
|
const char * buf = RedictModule_StringPtrLen(argv[i + 1], &len);
|
|
MemBlockWrite(mem, block_index, buf, len);
|
|
o->used++;
|
|
o->mask |= (1UL << block_index);
|
|
}
|
|
|
|
RedictModule_ReplyWithSimpleString(ctx, "OK");
|
|
RedictModule_ReplicateVerbatim(ctx);
|
|
return REDICTMODULE_OK;
|
|
}
|
|
|
|
/*---------------------------- type callbacks ------------------------------------*/
|
|
|
|
void *MemAllocRdbLoad(RedictModuleIO *rdb, int encver) {
|
|
if (encver != 0) {
|
|
return NULL;
|
|
}
|
|
|
|
MemAllocObject *o = createMemAllocObject();
|
|
o->size = RedictModule_LoadSigned(rdb);
|
|
o->used = RedictModule_LoadSigned(rdb);
|
|
o->mask = RedictModule_LoadUnsigned(rdb);
|
|
|
|
const RedictModuleString *key = RedictModule_GetKeyNameFromIO(rdb);
|
|
int dbid = RedictModule_GetDbIdFromIO(rdb);
|
|
|
|
if (o->size) {
|
|
size_t size;
|
|
char *tmpbuf;
|
|
long long num = o->size;
|
|
struct MemBlock *head = RedictModule_Calloc(1, sizeof(struct MemBlock));
|
|
tmpbuf = RedictModule_LoadStringBuffer(rdb, &size);
|
|
memcpy(head->block, tmpbuf, size > BLOCK_SIZE ? BLOCK_SIZE:size);
|
|
RedictModule_Free(tmpbuf);
|
|
struct MemBlock *block = head;
|
|
while (--num) {
|
|
block->next = RedictModule_Calloc(1, sizeof(struct MemBlock));
|
|
block = block->next;
|
|
|
|
tmpbuf = RedictModule_LoadStringBuffer(rdb, &size);
|
|
memcpy(block->block, tmpbuf, size > BLOCK_SIZE ? BLOCK_SIZE:size);
|
|
RedictModule_Free(tmpbuf);
|
|
}
|
|
|
|
RedictModule_DictSet(mem_pool[dbid], (RedictModuleString *)key, head);
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
void MemAllocRdbSave(RedictModuleIO *rdb, void *value) {
|
|
MemAllocObject *o = value;
|
|
RedictModule_SaveSigned(rdb, o->size);
|
|
RedictModule_SaveSigned(rdb, o->used);
|
|
RedictModule_SaveUnsigned(rdb, o->mask);
|
|
|
|
const RedictModuleString *key = RedictModule_GetKeyNameFromIO(rdb);
|
|
int dbid = RedictModule_GetDbIdFromIO(rdb);
|
|
|
|
if (o->size) {
|
|
int nokey;
|
|
struct MemBlock *mem = (struct MemBlock *)RedictModule_DictGet(mem_pool[dbid], (RedictModuleString *)key, &nokey);
|
|
RedictModule_Assert(nokey == 0 && mem != NULL);
|
|
|
|
struct MemBlock *block = mem;
|
|
while (block) {
|
|
RedictModule_SaveStringBuffer(rdb, block->block, BLOCK_SIZE);
|
|
block = block->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MemAllocAofRewrite(RedictModuleIO *aof, RedictModuleString *key, void *value) {
|
|
MemAllocObject *o = (MemAllocObject *)value;
|
|
if (o->size) {
|
|
int dbid = RedictModule_GetDbIdFromIO(aof);
|
|
int nokey;
|
|
size_t i = 0, j = 0;
|
|
struct MemBlock *mem = (struct MemBlock *)RedictModule_DictGet(mem_pool[dbid], (RedictModuleString *)key, &nokey);
|
|
RedictModule_Assert(nokey == 0 && mem != NULL);
|
|
size_t array_size = o->size * 2;
|
|
RedictModuleString ** string_array = RedictModule_Calloc(array_size, sizeof(RedictModuleString *));
|
|
while (mem) {
|
|
string_array[i] = RedictModule_CreateStringFromLongLong(NULL, j);
|
|
string_array[i + 1] = RedictModule_CreateString(NULL, mem->block, BLOCK_SIZE);
|
|
mem = mem->next;
|
|
i += 2;
|
|
j++;
|
|
}
|
|
RedictModule_EmitAOF(aof, "mem.allocandwrite", "slv", key, o->size, string_array, array_size);
|
|
for (i = 0; i < array_size; i++) {
|
|
RedictModule_FreeString(NULL, string_array[i]);
|
|
}
|
|
RedictModule_Free(string_array);
|
|
} else {
|
|
RedictModule_EmitAOF(aof, "mem.allocandwrite", "sl", key, o->size);
|
|
}
|
|
}
|
|
|
|
void MemAllocFree(void *value) {
|
|
RedictModule_Free(value);
|
|
}
|
|
|
|
void MemAllocUnlink(RedictModuleString *key, const void *value) {
|
|
REDICTMODULE_NOT_USED(key);
|
|
REDICTMODULE_NOT_USED(value);
|
|
|
|
/* When unlink and unlink2 exist at the same time, we will only call unlink2. */
|
|
RedictModule_Assert(0);
|
|
}
|
|
|
|
void MemAllocUnlink2(RedictModuleKeyOptCtx *ctx, const void *value) {
|
|
MemAllocObject *o = (MemAllocObject *)value;
|
|
|
|
const RedictModuleString *key = RedictModule_GetKeyNameFromOptCtx(ctx);
|
|
int dbid = RedictModule_GetDbIdFromOptCtx(ctx);
|
|
|
|
if (o->size) {
|
|
void *oldval;
|
|
RedictModule_DictDel(mem_pool[dbid], (RedictModuleString *)key, &oldval);
|
|
RedictModule_Assert(oldval != NULL);
|
|
MemBlockFree((struct MemBlock *)oldval);
|
|
}
|
|
}
|
|
|
|
void MemAllocDigest(RedictModuleDigest *md, void *value) {
|
|
MemAllocObject *o = (MemAllocObject *)value;
|
|
RedictModule_DigestAddLongLong(md, o->size);
|
|
RedictModule_DigestAddLongLong(md, o->used);
|
|
RedictModule_DigestAddLongLong(md, o->mask);
|
|
|
|
int dbid = RedictModule_GetDbIdFromDigest(md);
|
|
const RedictModuleString *key = RedictModule_GetKeyNameFromDigest(md);
|
|
|
|
if (o->size) {
|
|
int nokey;
|
|
struct MemBlock *mem = (struct MemBlock *)RedictModule_DictGet(mem_pool[dbid], (RedictModuleString *)key, &nokey);
|
|
RedictModule_Assert(nokey == 0 && mem != NULL);
|
|
|
|
struct MemBlock *block = mem;
|
|
while (block) {
|
|
RedictModule_DigestAddStringBuffer(md, (const char *)block->block, BLOCK_SIZE);
|
|
block = block->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void *MemAllocCopy2(RedictModuleKeyOptCtx *ctx, const void *value) {
|
|
const MemAllocObject *old = value;
|
|
MemAllocObject *new = createMemAllocObject();
|
|
new->size = old->size;
|
|
new->used = old->used;
|
|
new->mask = old->mask;
|
|
|
|
int from_dbid = RedictModule_GetDbIdFromOptCtx(ctx);
|
|
int to_dbid = RedictModule_GetToDbIdFromOptCtx(ctx);
|
|
const RedictModuleString *fromkey = RedictModule_GetKeyNameFromOptCtx(ctx);
|
|
const RedictModuleString *tokey = RedictModule_GetToKeyNameFromOptCtx(ctx);
|
|
|
|
if (old->size) {
|
|
int nokey;
|
|
struct MemBlock *oldmem = (struct MemBlock *)RedictModule_DictGet(mem_pool[from_dbid], (RedictModuleString *)fromkey, &nokey);
|
|
RedictModule_Assert(nokey == 0 && oldmem != NULL);
|
|
struct MemBlock *newmem = MemBlockClone(oldmem);
|
|
RedictModule_Assert(newmem != NULL);
|
|
RedictModule_DictSet(mem_pool[to_dbid], (RedictModuleString *)tokey, newmem);
|
|
}
|
|
|
|
return new;
|
|
}
|
|
|
|
size_t MemAllocMemUsage2(RedictModuleKeyOptCtx *ctx, const void *value, size_t sample_size) {
|
|
REDICTMODULE_NOT_USED(ctx);
|
|
REDICTMODULE_NOT_USED(sample_size);
|
|
uint64_t size = 0;
|
|
MemAllocObject *o = (MemAllocObject *)value;
|
|
|
|
size += sizeof(*o);
|
|
size += o->size * sizeof(struct MemBlock);
|
|
|
|
return size;
|
|
}
|
|
|
|
size_t MemAllocMemFreeEffort2(RedictModuleKeyOptCtx *ctx, const void *value) {
|
|
REDICTMODULE_NOT_USED(ctx);
|
|
MemAllocObject *o = (MemAllocObject *)value;
|
|
return o->size;
|
|
}
|
|
|
|
int RedictModule_OnLoad(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
|
|
REDICTMODULE_NOT_USED(argv);
|
|
REDICTMODULE_NOT_USED(argc);
|
|
|
|
if (RedictModule_Init(ctx, "datatype2", 1,REDICTMODULE_APIVER_1) == REDICTMODULE_ERR) {
|
|
return REDICTMODULE_ERR;
|
|
}
|
|
|
|
RedictModuleTypeMethods tm = {
|
|
.version = REDICTMODULE_TYPE_METHOD_VERSION,
|
|
.rdb_load = MemAllocRdbLoad,
|
|
.rdb_save = MemAllocRdbSave,
|
|
.aof_rewrite = MemAllocAofRewrite,
|
|
.free = MemAllocFree,
|
|
.digest = MemAllocDigest,
|
|
.unlink = MemAllocUnlink,
|
|
// .defrag = MemAllocDefrag, // Tested in defragtest.c
|
|
.unlink2 = MemAllocUnlink2,
|
|
.copy2 = MemAllocCopy2,
|
|
.mem_usage2 = MemAllocMemUsage2,
|
|
.free_effort2 = MemAllocMemFreeEffort2,
|
|
};
|
|
|
|
MemAllocType = RedictModule_CreateDataType(ctx, "mem_alloc", 0, &tm);
|
|
if (MemAllocType == NULL) {
|
|
return REDICTMODULE_ERR;
|
|
}
|
|
|
|
if (RedictModule_CreateCommand(ctx, "mem.alloc", MemAlloc_RedictCommand, "write deny-oom", 1, 1, 1) == REDICTMODULE_ERR) {
|
|
return REDICTMODULE_ERR;
|
|
}
|
|
|
|
if (RedictModule_CreateCommand(ctx, "mem.free", MemFree_RedictCommand, "write deny-oom", 1, 1, 1) == REDICTMODULE_ERR) {
|
|
return REDICTMODULE_ERR;
|
|
}
|
|
|
|
if (RedictModule_CreateCommand(ctx, "mem.write", MemWrite_RedictCommand, "write deny-oom", 1, 1, 1) == REDICTMODULE_ERR) {
|
|
return REDICTMODULE_ERR;
|
|
}
|
|
|
|
if (RedictModule_CreateCommand(ctx, "mem.read", MemRead_RedictCommand, "readonly", 1, 1, 1) == REDICTMODULE_ERR) {
|
|
return REDICTMODULE_ERR;
|
|
}
|
|
|
|
if (RedictModule_CreateCommand(ctx, "mem.usage", MemUsage_RedictCommand, "readonly", 1, 1, 1) == REDICTMODULE_ERR) {
|
|
return REDICTMODULE_ERR;
|
|
}
|
|
|
|
/* used for internal aof rewrite */
|
|
if (RedictModule_CreateCommand(ctx, "mem.allocandwrite", MemAllocAndWrite_RedictCommand, "write deny-oom", 1, 1, 1) == REDICTMODULE_ERR) {
|
|
return REDICTMODULE_ERR;
|
|
}
|
|
|
|
for(int i = 0; i < MAX_DB; i++){
|
|
mem_pool[i] = RedictModule_CreateDict(NULL);
|
|
}
|
|
|
|
RedictModule_SubscribeToServerEvent(ctx, RedictModuleEvent_FlushDB, flushdbCallback);
|
|
RedictModule_SubscribeToServerEvent(ctx, RedictModuleEvent_SwapDB, swapDbCallback);
|
|
|
|
return REDICTMODULE_OK;
|
|
}
|