From c396da1f2a6aae1c34db3e1fde0f7a4b391a4563 Mon Sep 17 00:00:00 2001 From: Dvir Volk Date: Mon, 2 May 2016 19:52:22 +0300 Subject: [PATCH] added docs --- API.md | 769 ++++++++++++++++++++++++++++ FUNCTIONS.md | 1245 ++++++++++++++++++++++++++++++++++++++++++++++ example/Makefile | 2 +- example/module.c | 1 - rmutil/Makefile | 2 +- rmutil/strings.c | 1 + rmutil/strings.h | 2 - rmutil/util.h | 14 +- rmutil/vector.c | 1 - rmutil/vector.h | 70 +-- 10 files changed, 2062 insertions(+), 45 deletions(-) create mode 100644 API.md create mode 100644 FUNCTIONS.md diff --git a/API.md b/API.md new file mode 100644 index 0000000..2918edf --- /dev/null +++ b/API.md @@ -0,0 +1,769 @@ +Redis Modules API reference manual +=== + +Redis modules make possible to extend Redis functionality using external +modules, implementing new Redis commands at a speed and with features +similar to what can be done inside the core itself. + +Redis modules are dynamic libraries, that can be loaded into Redis at +startup or using the `MODULE LOAD` command. Redis exports a C API, in the +form of a single C header file called `redismodule.h`. Modules are meant +to be written in C, however it will be possible to use C++ or other languages +that have C binding functionalities. + +Modules are designed in order to be loaded into different versions of Redis, +so a given module does not need to be designed, or recompiled, in order to +run with a specific version of Redis. For this reason, the module will +register to the Redis core using a specific API version. The current API +version is "1". + +This document is about an alpha version of Redis modules. API, functionalities +and other details may change in the future. + +# Loading modules + +In order to test the module you are developing, you can load the module +using the following `redis.conf` configuration directive: + + loadmodule /path/to/mymodule.so + +It is also possible to load a module at runtime using the following command: + + MODULE LOAD /path/to/mymodule.so + +In order to list all loaded modules, use: + + MODULE LIST + +Finally, you can unload (and later reload if you wish) a module using the +following command: + + MODULE UNLOAD mymodule + +Note that `mymodule` above is not the filename without the `.so` suffix, but +instead, the name the module used to register itself into the Redis core. +The name can be obtained using `MODULE LIST`. However it is good practice +that the filename of the dynamic library is the same as the name the module +uses to register itself into the Redis core. + +# The simplest module you can write + +In order to show the different parts of a module, here we'll show a very +simple module that implements a command that outputs a random number. + + #include "redismodule.h" + #include + + int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + RedisModule_ReplyWithLongLong(ctx,rand()); + return REDISMODULE_OK; + } + + int RedisModule_OnLoad(RedisModuleCtx *ctx) { + if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1) + == REDISMODULE_ERR) return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx,"helloworld.rand", + HelloworldRand_RedisCommand) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + return REDISMODULE_OK; + } + +The example module has two functions. One implements a command called +HELLOWORLD.RAND. This function is specific of that module. However the +other function called `RedisModule_OnLoad()` must be present in each +Redis module. It is the entry point for the module to be initialized, +register its commands, and potentially other private data structures +it uses. + +Note that it is a good idea for modules to call commands with the +name of the module followed by a dot, and finally the command name, +like in the case of `HELLOWORLD.RAND`. This way it is less likely to +have collisions. + +Note that if different modules have colliding commands, they'll not be +able to work in Redis at the same time, since the function +`RedisModule_CreateCommand` will fail in one of the modules, so the module +loading will abort returning an error condition. + +# Module initialization + +The above example shows the usage of the function `RedisModule_Init()`. +It should be the first function called by the module `OnLoad` function. +The following is the function prototype: + + int RedisModule_Init(RedisModuleCtx *ctx, const char *modulename, + int module_version, int api_version); + +The `Init` function announces the Redis core that the module has a given +name, its version (that is reported by `MODULE LIST`), and that is willing +to use a specific version of the API. + +If the API version is wrong, the name is already taken, or there are other +similar errors, the function will return `REDISMODULE_ERR`, and the module +`OnLoad` function should return ASAP with an error. + +Before the `Init` function is called, no other API function can be called, +otherwise the module will segfault and the Redis instance will crash. + +The second function called, `RedisModule_CreateCommand`, is used in order +to register commands into the Redis core. The following is the prototype: + + int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *cmdname, + RedisModuleCmdFunc cmdfunc); + +As you can see, most Redis modules API calls all take as first argument +the `context` of the module, so that they have a reference to the module +calling it, to the command and client executing a given command, and so forth. + +To create a new command, the above function needs the context, the command +name, and the function pointer of the function implementing the command, +which must have the following prototype: + + + int mycommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); + +The command function arguments are just the context, that will be passed +to all the other API calls, the command argument vector, and total number +of arguments, as passed by the user. + +As you can see, the arguments are provided as pointers to a specific data +type, the `RedisModuleString`. This is an opaque data type you have API +functions to access and use, direct access to its fields is never needed. + +Zooming into the example command implementation, we can find another call: + + int RedisModule_ReplyWithLongLong(RedisModuleCtx *ctx, long long integer); + +This function returns an integer to the client that invoked the command, +exactly like other Redis commands do, like for example `INCR` or `SCARD`. + +# Setup and dependencies of a Redis module + +Redis modules don't depend on Redis or some other library, nor they +need to be compiled with a specific `redismodule.h` file. In order +to create a new module, just copy a recent version of `redismodule.h` +in your source tree, link all the libraries you want, and create +a dynamic library having the `RedisModule_OnLoad()` function symbol +exported. + +The module will be able to load into different versions of Redis. + +# Working with RedisModuleString objects + +The command argument vector `argv` passed to module commands, and the +return value of other module APIs functions, are of type `RedisModuleString`. + +Usually you directly pass module strings to other API calls, however sometimes +you may need to directly access the string object. + +There are a few functions in order to work with string objects: + + const char *RedisModule_StringPtrLen(RedisModuleString *string, size_t *len); + +The above function accesses a string by returning its pointer and setting its +length in `len`. +You should never write to a string object pointer, as you can see from the +`const` pointer qualifier. + +However, if you want, you can create new string objects using the following +API: + + RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len); + +The string returned by the above command must be freed using a corresponding +call to `RedisModule_FreeString()`: + + void RedisModule_FreeString(RedisModuleString *str); + +However if you want to avoid having to free strings, the automatic memory +management, covered later in this document, can be a good alternative, by +doing it for you. + +Note that the strings provided via the argument vector `argv` never need +to be freed. You only need to free new strings you create, or new strings +returned by other APIs, where it is specified that the returned string must +be freed. + +## Creating strings from numbers or parsing strings as numbers + +Creating a new string from an integer is a very common operation, so there +is a function to do this: + + RedisModuleString *mystr = RedisModule_CreateStringFromLongLong(ctx,10); + +Similarly in order to parse a string as a number: + + long long myval; + if (RedisModule_StringToLongLong(ctx,argv[1],&myval) == REDISMODULE_OK) { + /* Do something with 'myval' */ + } + +## Accessing Redis keys from modules + +Most Redis modules, in order to be useful, have to interact with the Redis +data space (this is not always true, for example an ID generator may +never touch Redis keys). Redis modules have two different APIs in order to +access the Redis data space, one is a low level API that provides very +fast access and a set of functions to manipulate Redis data structures. +The other API is more high level, and allows to call Redis commands and +fetch the result, similarly to how Lua scripts access Redis. + +The high level API is also useful in order to access Redis functionalities +that are not available as APIs. + +In general modules developers should prefer the low level API, because commands +implemented using the low level API run at a speed comparable to the speed +of native Redis commands. However there are definitely use cases for the +higher level API. For example often the bottleneck could be processing the +data and not accessing it. + +Also note that sometimes using the low level API is not harder compared to +the higher level one. + +# Calling Redis commands + +The high level API to access Redis is the sum of the `RedisModule_Call()` +function, together with the functions needed in order to access the +reply object returned by `Call()`. + +`RedisModule_Call` uses a special calling convention, with a format specifier +that is used to specify what kind of objects you are passing as arguments +to the function. + +Redis commands are invoked just using a command name and a list of arguments. +However when calling commands, the arguments may originate from different +kind of strings: null-terminated C strings, RedisModuleString objects as +received from the `argv` parameter in the command implementation, binary +safe C buffers with a pointer and a length, and so forth. + +For example if I want to call `INCRBY` using a first argument (the key) +a string received in the argument vector `argv`, which is an array +of RedisModuleString object pointers, and a C string representing the +number "10" as second argument (the increment), I'll use the following +function call: + + RedisModuleCallReply *reply; + reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10"); + +The first argument is the context, and the second is always a null terminated +C string with the command name. The third argument is the format specifier +where each character corresponds to the type of the arguments that will follow. +In the above case `"sc"` means a RedisModuleString object, and a null +terminated C string. The other arguments are just the two arguments as +specified. In fact `argv[1]` is a RedisModuleString and `"10"` is a null +terminated C string. + +This is the full list of format specifiers: + +* **c** -- Null terminated C string pointer. +* **b** -- C buffer, two arguments needed: C string pointer and `size_t` length. +* **s** -- RedisModuleString as received in `argv` or by other Redis module APIs returning a RedisModuleString object. +* **l** -- Long long integer. +* **v** -- Array of RedisModuleString objects. +* **!** -- This modifier just tells the function to replicate the command to slaves and AOF. It is ignored from the point of view of arguments parsing. + +The function returns a `RedisModuleCallReply` object on success, on +error NULL is returned. + +NULL is returned when the command name is invalid, the format specifier uses +characters that are not recognized, or when the command is called with the +wrong number of arguments. In the above cases the `errno` var is set to `EINVAL`. NULL is also returned when, in an instance with Cluster enabled, the target +keys are about non local hash slots. In this case `errno` is set to `EPERM`. + +## Working with RedisModuleCallReply objects. + +`RedisModuleCall` returns reply objects that can be accessed using the +`RedisModule_CallReply*` family of functions. + +In order to obtain the type or reply (corresponding to one of the data types +supported by the Redis protocol), the function `RedisModule_CallReplyType()` +is used: + + reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10"); + if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) { + long long myval = RedisModule_CallReplyInteger(reply); + /* Do something with myval. */ + } + +Valid reply types are: + +* `REDISMODULE_REPLY_STRING` Bulk string or status replies. +* `REDISMODULE_REPLY_ERROR` Errors. +* `REDISMODULE_REPLY_INTEGER` Signed 64 bit integers. +* `REDISMODULE_REPLY_ARRAY` Array of replies. +* `REDISMODULE_REPLY_NULL` NULL reply. + +Strings, errors and arrays have an associated length. For strings and errors +the length corresponds to the length of the string. For arrays the length +is the number of elements. To obtain the reply length the following function +is used: + + size_t reply_len = RedisModule_CallReplyLength(reply); + +In order to obtain the value of an integer reply, the following function is used, as already shown in the example above: + + long long reply_integer_val = RedisModule_CallReplyInteger(reply); + +Called with a reply object of the wrong type, the above function always +returns `LLONG_MIN`. + +Sub elements of array replies are accessed this way: + + RedisModuleCallReply *subreply; + subreply = RedisModule_CallReplyArrayElement(reply,idx); + +The above function returns NULL if you try to access out of range elements. + +Strings and errors (which are like strings but with a different type) can +be accessed using in the following way, making sure to never write to +the resulting pointer (that is returned as as `const` pointer so that +misusing must be pretty explicit): + + size_t len; + char *ptr = RedisModule_CallReplyStringPtr(reply,&len); + +If the reply type is not a string or an error, NULL is returned. + +RedisCallReply objects are not the same as module string objects +(RedisModuleString types). However sometimes you may need to pass replies +of type string or integer, to API functions expecting a module string. + +When this is the case, you may want to evaluate if using the low level +API could be a simpler way to implement your command, or you can use +the following function in order to create a new string object from a +call reply of type string, error or integer: + + RedisModuleString *mystr = RedisModule_CreateStringFromCallReply(myreply); + +If the reply is not of the right type, NULL is returned. +The returned string object should be released with `RedisModule_FreeString()` +as usually, or by enabling automatic memory management (see corresponding +section). + +# Releasing call reply objects + +Reply objects must be freed using `RedisModule_FreeCallRelpy`. For arrays, +you need to free only the top level reply, not the nested replies. +Currently the module implementation provides a protection in order to avoid +crashing if you free a nested reply object for error, however this feature +is not guaranteed to be here forever, so should not be considered part +of the API. + +If you use automatic memory management (explained later in this document) +you don't need to free replies (but you still could if you wish to release +memory ASAP). + +## Returning values from Redis commands + +Like normal Redis commands, new commands implemented via modules must be +able to return values to the caller. The API exports a set of functions for +this goal, in order to return the usual types of the Redis protocol, and +arrays of such types as elemented. Also errors can be returned with any +error string and code (the error code is the initial uppercase letters in +the error message, like the "BUSY" string in the "BUSY the sever is busy" error +message). + +All the functions to send a reply to the client are called +`RedisModule_ReplyWith`. + +To return an error, use: + + RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err); + +There is a predefined error string for key of wrong type errors: + + REDISMODULE_ERRORMSG_WRONGTYPE + +Example usage: + + RedisModule_ReplyWithError(ctx,"ERR invalid arguments"); + +We already saw how to reply with a long long in the examples above: + + RedisModule_ReplyWithLongLong(ctx,12345); + +To reply with a simple string, that can't contain binary values or newlines, +(so it's suitable to send small words, like "OK") we use: + + RedisModule_ReplyWithSimpleString(ctx,"OK"); + +It's possible to reply with "bulk strings" that are binary safe, using +two different functions: + + int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len); + + int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str); + +The first function gets a C pointer and length. The second a RedisMoudleString +object. Use one or the other depending on the source type you have at hand. + +In order to reply with an array, you just need to use a function to emit the +array length, followed by as many calls to the above functions as the number +of elements of the array are: + + RedisModule_ReplyWithArray(ctx,2); + RedisModule_ReplyWithStringBuffer(ctx,"age",3); + RedisModule_ReplyWithLongLong(ctx,22); + +To return nested arrays is easy, your nested array element just uses another +call to `RedisModule_ReplyWithArray()` followed by the calls to emit the +sub array elements. + +## Returning arrays with dynamic length + +Sometimes it is not possible to know beforehand the number of items of +an array. As an example, think of a Redis module implementing a FACTOR +command that given a number outputs the prime factors. Instead of +factorializing the number, storing the prime factors into an array, and +later produce the command reply, a better solution is to start an array +reply where the length is not known, and set it later. This is accomplished +with a special argument to `RedisModule_ReplyWithArray()`: + + RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN); + +The above call starts an array reply so we can use other `ReplyWith` calls +in order to produce the array items. Finally in order to set the length +se use the following call: + + RedisModule_ReplySetArrayLength(ctx, number_of_items); + +In the case of the FACTOR command, this translates to some code similar +to this: + + RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN); + number_of_factors = 0; + while(still_factors) { + RedisModule_ReplyWithLongLong(ctx, some_factor); + number_of_factors++; + } + RedisModule_ReplySetArrayLength(ctx, number_of_factors); + +Another common use case for this feature is iterating over the arrays of +some collection and only returning the ones passing some kind of filtering. + +It is possible to have multiple nested arrays with postponed reply. +Each call to `SetArray()` will set the length of the latest corresponding +call to `ReplyWithArray()`: + + RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN); + ... generate 100 elements ... + RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN); + ... generate 10 elements ... + RedisModule_ReplySetArrayLength(ctx, 10); + RedisModule_ReplySetArrayLength(ctx, 100); + +This creates a 100 items array having as last element a 10 items array. + +# Arity and type checks + +Often commands need to check that the number of arguments and type of the key +is correct. In order to report a wrong arity, there is a specific function +called `RedisModule_WrongArity()`. The usage is trivial: + + if (argc != 2) return RedisModule_WrongArity(ctx); + +Checking for the wrong type involves opening the key and checking the type: + + RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], + REDISMODULE_READ|REDISMODULE_WRITE); + + int keytype = RedisModule_KeyType(key); + if (keytype != REDISMODULE_KEYTYPE_STRING && + keytype != REDISMODULE_KEYTYPE_EMPTY) + { + RedisModule_CloseKey(key); + return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); + } + +Note that you often want to proceed with a command both if the key +is of the expected type, or if it's empty. + +## Low level access to keys + +Low level access to keys allow to perform operations on value objects associated +to keys directly, with a speed similar to what Redis uses internally to +implement the built-in commands. + +Once a key is opened, a key pointer is returned that will be used with all the +other low level API calls in order to perform operations on the key or its +associated value. + +Because the API is meant to be very fast, it cannot do too many run-time +checks, so the user must be aware of certain rules to follow: + +* Opening the same key multiple times where at least one instance is opened for writing, is undefined and may lead to crashes. +* While a key is open, it should only be accessed via the low level key API. For example opening a key, then calling DEL on the same key using the `RedisModule_Call()` API will result into a crash. However it is safe to open a key, perform some operation with the low level API, closing it, then using other APIs to manage the same key, and later opening it again to do some more work. + +In order to open a key the `RedisModule_OpenKey` function is used. It returns +a key pointer, that we'll use with all the next calls to access and modify +the value: + + RedisModuleKey *key; + key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ); + +The second argument is the key name, that must be a `RedisModuleString` object. +The third argument is the mode: `REDISMODULE_READ` or `REDISMODULE_WRITE`. +It is possible to use `|` to bitwise OR the two modes to open the key in +both modes. Currently a key opened for writing can also be accessed for reading +but this is to be considered an implementation detail. The right mode should +be used in sane modules. + +You can open non exisitng keys for writing, since the keys will be created +when an attempt to write to the key is performed. However when opening keys +just for reading, `RedisModule_OpenKey` will return NULL if the key does not +exist. + +Once you are done using a key, you can close it with: + + RedisModule_CloseKey(key); + +Note that if automatic memory management is enabled, you are not forced to +close keys. When the module function returns, Redis will take care to close +all the keys which are still open. + +## Getting the key type + +In order to obtain the value of a key, use the `RedisModule_KeyType()` function: + + int keytype = RedisModule_KeyType(key); + +It returns one of the following values: + + REDISMODULE_KEYTYPE_EMPTY + REDISMODULE_KEYTYPE_STRING + REDISMODULE_KEYTYPE_LIST + REDISMODULE_KEYTYPE_HASH + REDISMODULE_KEYTYPE_SET + REDISMODULE_KEYTYPE_ZSET + +The above are just the usual Redis key types, with the addition of an empty +type, that signals the key pointer is associated with an empty key that +does not yet exists. + +## Creating new keys + +To create a new key, open it for writing and then write to it using one +of the key writing functions. Example: + + RedisModuleKey *key; + key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ); + if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { + RedisModule_StringSet(key,argv[2]); + } + +## Deleting keys + +Just use: + + RedisModule_DeleteKey(key); + +The function returns `REDISMODULE_ERR` if the key is not open for writing. +Note that after a key gets deleted, it is setup in order to be targeted +by new key commands. For example `RedisModule_KeyType()` will return it is +an empty key, and writing to it will create a new key, possibly of another +type (depending on the API used). + +## Managing key expires (TTLs) + +To control key expires two functions are provided, that are able to set, +modify, get, and unset the time to live associated with a key. + +One function is used in order to query the current expire of an open key: + + mstime_t RedisModule_GetExpire(RedisModuleKey *key); + +The function returns the time to live of the key in milliseconds, or +`REDISMODULE_NO_EXPIRE` as a special value to signal the key has no associated +expire or does not exist at all (you can differentiate the two cases checking +if the key type is `REDISMODULE_KEYTYPE_EMPTY`). + +In order to change the expire of a key the following function is used instead: + + int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire); + +When called on a non existing key, `REDISMODULE_ERR` is returned, because +the function can only associate expires to existing open keys (non existing +open keys are only useful in order to create new values with data type +specific write operations). + +Again the `expire` time is specified in milliseconds. If the key has currently +no expire, a new expire is set. If the key already have an expire, it is +replaced with the new value. + +If the key has an expire, and the special value `REDISMODULE_NO_EXPIRE` is +used as a new expire, the expire is removed, similarly to the Redis +`PERSIST` command. In case the key was already persistent, no operation is +performed. + +## Obtaining the length of values + +There is a single function in order to retrieve the length of the value +associated to an open key. The returned length is value-specific, and is +the string length for strings, and the number of elements for the aggregated +data types (how many elements there is in a list, set, sorted set, hash). + + size_t len = RedisModule_ValueLength(key); + +If the key does not exist, 0 is returned by the function: + +## String type API + +Setting a new string value, like the Redis `SET` command does, is performed +using: + + int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str); + +The function works exactly like the Redis `SET` command itself, that is, if +there is a prior value (of any type) it will be deleted. + +Accessing existing string values is performed using DMA (direct memory +access) for speed. The API will return a pointer and a length, so that's +possible to access and, if needed, modify the string directly. + + size_t len, j; + char *myptr = RedisModule_StringDMA(key,REDISMODULE_WRITE,&len); + for (j = 0; j < len; j++) myptr[j] = 'A'; + +In the above example we write directly on the string. Note that if you want +to write, you must be sure to ask for `WRITE` mode. + +DMA pointers are only valid if no other operations are performed with the key +before using the pointer, after the DMA call. + +Sometimes when we want to manipulate strings directly, we need to change +their size as well. For this scope, the `RedisModule_StringTruncate` function +is used. Example: + + RedisModule_StringTruncate(mykey,1024); + +The function truncates, or enlarges the string as needed, padding it with +zero bytes if the previos length is smaller than the new length we request. +If the string does not exist since `key` is associated to an open empty key, +a string value is created and associated to the key. + +Note that every time `StringTruncate()` is called, we need to re-obtain +the DMA pointer again, since the old may be invalid. + +## List type API + +It's possible to push and pop values from list values: + + int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele); + RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where); + +In both the APIs the `where` argument specifies if to push or pop from tail +or head, using the following macros: + + REDISMODULE_LIST_HEAD + REDISMODULE_LIST_TAIL + +Elements returned by `RedisModule_ListPop()` are like strings craeted with +`RedisModule_CreateString()`, they must be released with +`RedisModule_FreeString()` or by enabling automatic memory management. + +## Set type API + +Work in progress. + +## Sorted set type API + +Work in progress. + +## Hash type API + +Work in progress. + +## Iterating aggregated values + +Work in progress. + +# Replicating commands + +If you want to use module commands exactly like normal Redis commands, in the +context of replicated Redis instances, or using the AOF file for persistence, +it is important for module commands to handle their replication in a consistent +way. + +When using the higher level APIs to invoke commands, replication happens +automatically if you use the "!" modifier in the format string of +`RedisModule_Call()` as in the following example: + + reply = RedisModule_Call(ctx,"INCR","!sc",argv[1],"10"); + +As you can see the format specifier is `"!sc"`. The bang is not parsed as a +format specifier, but it internally flags the command as "must replicate". + +If you use the above programming style, there are no problems. +However sometimes things are more complex than that, and you use the low level +API. In this case, if there are no side effects in the command execution, and +it consistently always performs the same work, what is possible to do is to +replicate the command verbatim as the user executed it. To do that, you just +need to call the following function: + + RedisModule_ReplicateVerbatim(ctx); + +When you use the above API, you should not use any other replication function +since they are not guaranteed to mix well. + +However this is not the only option. It's also possible to exactly tell +Redis what commands to replicate as the effect of the command execution, using +an API similar to `RedisModule_Call()` but that instead of calling the command +sends it to the AOF / slaves stream. Example: + + RedisModule_Replicate(ctx,"INCRBY","cl","foo",my_increment); + +It's possible to call `RedisModule_Replicate` multiple times, and each +will emit a command. All the sequence emitted is wrapped between a +`MULTI/EXEC` transaction, so that the AOF and replication effects are the +same as executing a single command. + +Note that `Call()` replication and `Replicate()` replication have a rule, +in case you want to mix both forms of replication (not necessarily a good +idea if there are simpler approaches). Commands replicated with `Call()` +are always the first emitted in the final `MULTI/EXEC` block, while all +the commands emitted with `Replicate()` will follow. + +# Automatic memory management + +Normally when writing programs in the C language, programmers need to manage +memory manually. This is why the Redis modules API has functions to release +strings, close open keys, free replies, and so forth. + +However given that commands are executed in a contained environment and +with a set of strict APIs, Redis is able to provide automatic memory management +to modules, at the cost of some performance (most of the time, a very low +cost). + +When automatic memory management is enabled: + +1. You don't need to close open keys. +2. You don't need to free replies. +3. You don't need to free RedisModuleString objects. + +However you can still do it, if you want. For example, automatic memory +management may be active, but inside a loop allocating a lot of strings, +you may still want to free strings no longer used. + +In order to enable automatic memory management, just call the following +function at the start of the command implementation: + + RedisModule_AutoMemory(ctx); + +Automatic memory management is usually the way to go, however experienced +C programmers may not use it in order to gain some speed and memory usage +benefit. + +# Writing commands compatible with Redis Cluster + +Work in progress. Implement and document the following API: + + RedisModule_IsKeysPositionRequest(ctx); + RedisModule_KeyAtPos(ctx,pos); + +Command implementations, on keys position request, must reply with +`REDISMODULE_KEYPOS_OK` to signal the request was processed, otherwise +Cluster returns an error for those module commands that are not able to +describe the position of keys. + diff --git a/FUNCTIONS.md b/FUNCTIONS.md new file mode 100644 index 0000000..69d57b7 --- /dev/null +++ b/FUNCTIONS.md @@ -0,0 +1,1245 @@ +# Generated API for Redis Modules and Redis Module Utils + +## module.c +* [RedisModule_FreeCallReply](#redismodule_freecallreply) + +* [RedisModule_CloseKey](#redismodule_closekey) + +* [RedisModule_ZsetRangeStop](#redismodule_zsetrangestop) + +* [RedisModule_GetApi](#redismodule_getapi) + +* [RedisModule_IsKeysPositionRequest](#redismodule_iskeyspositionrequest) + +* [RedisModule_KeyAtPos](#redismodule_keyatpos) + +* [RedisModule_CreateCommand](#redismodule_createcommand) + +* [RedisModule_SetModuleAttribs](#redismodule_setmoduleattribs) + +* [RedisModule_AutoMemory](#redismodule_automemory) + +* [RedisModule_CreateString](#redismodule_createstring) + +* [RedisModule_CreateStringFromLongLong](#redismodule_createstringfromlonglong) + +* [RedisModule_FreeString](#redismodule_freestring) + +* [RedisModule_StringPtrLen](#redismodule_stringptrlen) + +* [RedisModule_StringToLongLong](#redismodule_stringtolonglong) + +* [RedisModule_StringToDouble](#redismodule_stringtodouble) + +* [RedisModule_WrongArity](#redismodule_wrongarity) + +* [RedisModule_ReplyWithLongLong](#redismodule_replywithlonglong) + +* [RedisModule_ReplyWithError](#redismodule_replywitherror) + +* [RedisModule_ReplyWithSimpleString](#redismodule_replywithsimplestring) + +* [RedisModule_ReplyWithArray](#redismodule_replywitharray) + +* [RedisModule_ReplySetArrayLength](#redismodule_replysetarraylength) + +* [RedisModule_ReplyWithStringBuffer](#redismodule_replywithstringbuffer) + +* [RedisModule_ReplyWithString](#redismodule_replywithstring) + +* [RedisModule_ReplyWithNull](#redismodule_replywithnull) + +* [RedisModule_ReplyWithCallReply](#redismodule_replywithcallreply) + +* [RedisModule_ReplyWithDouble](#redismodule_replywithdouble) + +* [RedisModule_Replicate](#redismodule_replicate) + +* [RedisModule_ReplicateVerbatim](#redismodule_replicateverbatim) + +* [RedisModule_GetSelectedDb](#redismodule_getselecteddb) + +* [RedisModule_SelectDb](#redismodule_selectdb) + +* [RedisModule_OpenKey](#redismodule_openkey) + +* [RedisModule_CloseKey](#redismodule_closekey) + +* [RedisModule_KeyType](#redismodule_keytype) + +* [RedisModule_ValueLength](#redismodule_valuelength) + +* [RedisModule_DeleteKey](#redismodule_deletekey) + +* [RedisModule_GetExpire](#redismodule_getexpire) + +* [RedisModule_SetExpire](#redismodule_setexpire) + +* [RedisModule_StringSet](#redismodule_stringset) + +* [RedisModule_StringDMA](#redismodule_stringdma) + +* [RedisModule_StringTruncate](#redismodule_stringtruncate) + +* [RedisModule_ListPush](#redismodule_listpush) + +* [RedisModule_ListPop](#redismodule_listpop) + +* [RedisModule_ZsetAddFlagsToCoreFlags](#redismodule_zsetaddflagstocoreflags) + +* [RedisModule_ZsetAddFlagsFromCoreFlags](#redismodule_zsetaddflagsfromcoreflags) + +* [RedisModule_ZsetAdd](#redismodule_zsetadd) + +* [RedisModule_ZsetIncrby](#redismodule_zsetincrby) + +* [RedisModule_ZsetRem](#redismodule_zsetrem) + +* [RedisModule_ZsetScore](#redismodule_zsetscore) + +* [RedisModule_ZsetRangeStop](#redismodule_zsetrangestop) + +* [RedisModule_ZsetRangeEndReached](#redismodule_zsetrangeendreached) + +* [RedisModule_ZsetFirstInScoreRange](#redismodule_zsetfirstinscorerange) + +* [RedisModule_ZsetLastInScoreRange](#redismodule_zsetlastinscorerange) + +* [RedisModule_ZsetFirstInLexRange](#redismodule_zsetfirstinlexrange) + +* [RedisModule_ZsetLastInLexRange](#redismodule_zsetlastinlexrange) + +* [RedisModule_ZsetRangeCurrentElement](#redismodule_zsetrangecurrentelement) + +* [RedisModule_ZsetRangeNext](#redismodule_zsetrangenext) + +* [RedisModule_ZsetRangePrev](#redismodule_zsetrangeprev) + +* [RedisModule_HashSet](#redismodule_hashset) + +* [RedisModule_HashGet](#redismodule_hashget) + +* [RedisModule_FreeCallReply_Rec](#redismodule_freecallreply_rec) + +* [RedisModule_FreeCallReply](#redismodule_freecallreply) + +* [RedisModule_CallReplyType](#redismodule_callreplytype) + +* [RedisModule_CallReplyLength](#redismodule_callreplylength) + +* [RedisModule_CallReplyArrayElement](#redismodule_callreplyarrayelement) + +* [RedisModule_CallReplyInteger](#redismodule_callreplyinteger) + +* [RedisModule_CallReplyStringPtr](#redismodule_callreplystringptr) + +* [RedisModule_CreateStringFromCallReply](#redismodule_createstringfromcallreply) + +* [RedisModule_Call](#redismodule_call) + +* [RedisModule_CallReplyProto](#redismodule_callreplyproto) + +## util.h +* [RMUtil_ArgExists](#rmutil_argexists) + +* [RMUtil_ParseArgs](#rmutil_parseargs) + +* [RMUtil_ParseArgsAfter](#rmutil_parseargsafter) + +* [RMUtil_GetRedisInfo](#rmutil_getredisinfo) + +## strings.h +* [RMUtil_CreateFormattedString](#rmutil_createformattedstring) + +* [RMUtil_StringEquals](#rmutil_stringequals) + +* [RMUtil_StringToLower](#rmutil_stringtolower) + +* [RMUtil_StringToUpper](#rmutil_stringtoupper) + +## vector.h +* [Vector_Get](#vector_get) + +* [Vector_Resize](#vector_resize) + +* [Vector_Size](#vector_size) + +* [Vector_Cap](#vector_cap) + +* [Vector_Free](#vector_free) +### RedisModule_FreeCallReply +``` +void RedisModule_FreeCallReply(RedisModuleCallReply *reply); +``` + -------------------------------------------------------------------------- + Prototypes + -------------------------------------------------------------------------- + + +### RedisModule_CloseKey +``` +void RedisModule_CloseKey(RedisModuleKey *key); +``` + -------------------------------------------------------------------------- + Prototypes + -------------------------------------------------------------------------- + + +### RedisModule_ZsetRangeStop +``` +void RedisModule_ZsetRangeStop(RedisModuleKey *key); +``` + -------------------------------------------------------------------------- + Prototypes + -------------------------------------------------------------------------- + + +### RedisModule_GetApi +``` +int RedisModule_GetApi(const char *funcname, void **targetPtrPtr) { +``` + Lookup the requested module API and store the function pointer into the + target pointer. The function returns REDISMODULE_ERR if there is no such + named API, otherwise REDISMODULE_OK. + + This function is not meant to be used by modules developer, it is only + used implicitly by including redismodule.h. + + +### RedisModule_IsKeysPositionRequest +``` +int RedisModule_IsKeysPositionRequest(RedisModuleCtx *ctx) { +``` + Return non-zero if a module command, that was declared with the + flag "getkeys-api", is called in a special way to get the keys positions + and not to get executed. Otherwise zero is returned. + + +### RedisModule_KeyAtPos +``` +void RedisModule_KeyAtPos(RedisModuleCtx *ctx, int pos) { +``` + When a module command is called in order to obtain the position of + keys, since it was flagged as "getkeys-api" during the registration, + the command implementation checks for this special call using the + RedisModule_IsKeysPositionRequest() API and uses this function in + order to report keys, like in the following example: + + if (RedisModule_IsKeysPositionRequest(ctx)) { + RedisModule_KeyAtPos(ctx,1); + RedisModule_KeyAtPos(ctx,2); + } + + Note: in the example below the get keys API would not be needed since + keys are at fixed positions. This interface is only used for commands + with a more complex structure. + + +### RedisModule_CreateCommand +``` +int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) { +``` + Register a new command in the Redis server, that will be handled by + calling the function pointer 'func' using the RedisModule calling + convention. The function returns REDISMODULE_ERR if the specified command + name is already busy or a set of invalid flags were passed, otherwise + REDISMODULE_OK is returned and the new command is registered. + + This function must be called during the initialization of the module + inside the RedisModule_OnLoad() function. Calling this function outside + of the initialization function is not defined. + + The command function type is the following: + + int MyCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); + + And is supposed to always return REDISMODULE_OK. + + The set of flags 'strflags' specify the behavior of the command, and should + be passed as a C string compoesd of space separated words, like for + example "write deny-oom". The set of flags are: + + * **"write"**: The command may modify the data set (it may also read from it). + * **"readonly"**: The command returns data from keys but never writes. + * **"admin"**: The command is an administrative command (may change replication + or perform similar tasks). + * **"deny-oom"**: The command may use additional memory and should be denied during + out of memory conditions. + * **"deny-script"**: Don't allow this command in Lua scripts. + * **"allow-loading"**: Allow this command while the server is loading data. Only + commands not interacting with the data set should be allowed + to run in this mode. If not sure don't use this flag. + * **"pubsub"**: The command publishes things on Pub/Sub channels. + * **"random"**: The command may have different outputs even starting from the + same input arguments and key values. + * **"allow-stale"**: The command is allowed to run on slaves that don't serve stale + data. Don't use if you don't know what this means. + * **"no-monitor"**: Don't propoagate the command on monitor. Use this if the command + has sensible data among the arguments. + * **"fast"**: The command time complexity is not greater than O(log(N)) where + N is the size of the collection or anything else representing + the normal scalability issue with the command. + * **"getkeys-api"**: The command implements the interface to return the arguments + that are keys. Used when start/stop/step is not enough because + of the command syntax. + * **"no-cluster"**: The command should not register in Redis Cluster since is not + designed to work with it because, for example, is unable to + report the position of the keys, programmatically creates key + names, or any other reason. + + +### RedisModule_SetModuleAttribs +``` +void RedisModule_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int apiver){ +``` + Called by RM_Init() to setup the ctx->module structure. + + This is an internal function, Redis modules developers don't need + to use it. + + +### RedisModule_AutoMemory +``` +void RedisModule_AutoMemory(RedisModuleCtx *ctx) { +``` + Enable automatic memory management. See API.md for more information. + + The function must be called as the first function of a command implementation + that wants to use automatic memory. + + +### RedisModule_CreateString +``` +RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len) +``` + Create a new module string object. The returned string must be freed + with RedisModule_FreeString(), unless automatic memory is enabled. + + The string is created by copying the `len` bytes starting + at `ptr`. No reference is retained to the passed buffer. + + +### RedisModule_CreateStringFromLongLong +``` +RedisModuleString *RedisModule_CreateStringFromLongLong(RedisModuleCtx *ctx, long long ll) { +``` + Like RedisModule_CreatString(), but creates a string starting from a long long + integer instead of taking a buffer and its length. + + The returned string must be released with RedisModule_FreeString() or by + enabling automatic memory management. + + +### RedisModule_FreeString +``` +void RedisModule_FreeString(RedisModuleCtx *ctx, RedisModuleString *str) { +``` + Free a module string object obtained with one of the Redis modules API calls + that return new string objects. + + It is possible to call this function even when automatic memory management + is enabled. In that case the string will be released ASAP and removed + from the pool of string to release at the end. + + +### RedisModule_StringPtrLen +``` +const char *RedisModule_StringPtrLen(RedisModuleString *str, size_t *len) { +``` + Given a string module object, this function returns the string pointer + and length of the string. The returned pointer and length should only + be used for read only accesses and never modified. + + +### RedisModule_StringToLongLong +``` +int RedisModule_StringToLongLong(RedisModuleString *str, long long *ll) { +``` + Convert the string into a long long integer, storing it at *ll. + Returns REDISMODULE_OK on success. If the string can't be parsed + as a valid, strict long long (no spaces before/after), REDISMODULE_ERR + is returned. + + +### RedisModule_StringToDouble +``` +int RedisModule_StringToDouble(RedisModuleString *str, double *d) { +``` + Convert the string into a double, storing it at *d. + Returns REDISMODULE_OK on success or REDISMODULE_ERR if the string is + not a valid string representation of a double value. + + +### RedisModule_WrongArity +``` +int RedisModule_WrongArity(RedisModuleCtx *ctx) { +``` + Send an error about the number of arguments given to the command, + citing the command name in the error message. + + Example: + + if (argc != 3) return RedisModule_WrongArity(ctx); + + +### RedisModule_ReplyWithLongLong +``` +int RedisModule_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll) { +``` + Send an integer reply to the client, with the specified long long value. + The function always returns REDISMODULE_OK. + + +### RedisModule_ReplyWithError +``` +int RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err) { +``` + Reply with the error 'err'. + + Note that 'err' must contain all the error, including + the initial error code. The function only provides the initial "-", so + the usage is, for example: + + RM_ReplyWithError(ctx,"ERR Wrong Type"); + + and not just: + + RM_ReplyWithError(ctx,"Wrong Type"); + + The function always returns REDISMODULE_OK. + + +### RedisModule_ReplyWithSimpleString +``` +int RedisModule_ReplyWithSimpleString(RedisModuleCtx *ctx, const char *msg) { +``` + Reply with a simple string (+... \r\n in RESP protocol). This replies + are suitable only when sending a small non-binary string with small + overhead, like "OK" or similar replies. + + The function always returns REDISMODULE_OK. + + +### RedisModule_ReplyWithArray +``` +int RedisModule_ReplyWithArray(RedisModuleCtx *ctx, long len) { +``` + Reply with an array type of 'len' elements. However 'len' other calls + to ReplyWith* style functions must follow in order to emit the elements + of the array. + + When producing arrays with a number of element that is not known beforehand + the function can be called with the special count + REDISMODULE_POSTPONED_ARRAY_LEN, and the actual number of elements can be + later set with RedisModule_ReplySetArrayLength() (which will set the + latest "open" count if there are multiple ones). + + The function always returns REDISMODULE_OK. + + +### RedisModule_ReplySetArrayLength +``` +void RedisModule_ReplySetArrayLength(RedisModuleCtx *ctx, long len) { +``` + When RedisModule_ReplyWithArray() is used with the argument + REDISMODULE_POSTPONED_ARRAY_LEN, because we don't know beforehand the number + of items we are going to output as elements of the array, this function + will take care to set the array length. + + Since it is possible to have multiple array replies pending with unknown + length, this function guarantees to always set the latest array length + that was created in a postponed way. + + For example in order to output an array like [1,[10,20,30]] we + could write: + + RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN); + RedisModule_ReplyWithLongLong(ctx,1); + RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN); + RedisModule_ReplyWithLongLong(ctx,10); + RedisModule_ReplyWithLongLong(ctx,20); + RedisModule_ReplyWithLongLong(ctx,30); + RedisModule_ReplySetArrayLength(ctx,3); // Set len of 10,20,30 array. + RedisModule_ReplySetArrayLength(ctx,2); // Set len of top array + + Note that in the above example there is no reason to postpone the array + length, since we produce a fixed number of elements, but in the practice + the code may use an interator or other ways of creating the output so + that is not easy to calculate in advance the number of elements. + + +### RedisModule_ReplyWithStringBuffer +``` +int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len) { +``` + Reply with a bulk string, taking in input a C buffer pointer and length. + + The function always returns REDISMODULE_OK. + + +### RedisModule_ReplyWithString +``` +int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str) { +``` + Reply with a bulk string, taking in input a RedisModuleString object. + + The function always returns REDISMODULE_OK. + + +### RedisModule_ReplyWithNull +``` +int RedisModule_ReplyWithNull(RedisModuleCtx *ctx) { +``` + Reply to the client with a NULL. In the RESP protocol a NULL is encoded + as the string "$-1\r\n". + + The function always returns REDISMODULE_OK. + + +### RedisModule_ReplyWithCallReply +``` +int RedisModule_ReplyWithCallReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply) { +``` + Reply exactly what a Redis command returned us with RedisModule_Call(). + This function is useful when we use RedisModule_Call() in order to + execute some command, as we want to reply to the client exactly the + same reply we obtained by the command. + + The function always returns REDISMODULE_OK. + + +### RedisModule_ReplyWithDouble +``` +int RedisModule_ReplyWithDouble(RedisModuleCtx *ctx, double d) { +``` + Send a string reply obtained converting the double 'd' into a bulk string. + This function is basically equivalent to converting a double into + a string into a C buffer, and then calling the function + RedisModule_ReplyWithStringBuffer() with the buffer and length. + + The function always returns REDISMODULE_OK. + + +### RedisModule_Replicate +``` +int RedisModule_Replicate(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) { +``` + Replicate the specified command and arguments to slaves and AOF, as effect + of execution of the calling command implementation. + + The replicated commands are always wrapped into the MULTI/EXEC that + contains all the commands replicated in a given module command + execution. However the commands replicated with RedisModule_Call() + are the first items, the ones replicated with RedisModule_Replicate() + will all follow before the EXEC. + + Modules should try to use one interface or the other. + + This command follows exactly the same interface of RedisModule_Call(), + so a set of format specifiers must be passed, followed by arguments + matching the provided format specifiers. + + Please refer to RedisModule_Call() for more information. + + The command returns REDISMODULE_ERR if the format specifiers are invalid + or the command name does not belong to a known command. + + +### RedisModule_ReplicateVerbatim +``` +int RedisModule_ReplicateVerbatim(RedisModuleCtx *ctx) { +``` + This function will replicate the command exactly as it was invoked + by the client. Note that this function will not wrap the command into + a MULTI/EXEC stanza, so it should not be mixed with other replication + commands. + + Basically this form of replication is useful when you want to propagate + the command to the slaves and AOF file exactly as it was called, since + the command can just be re-executed to deterministically re-create the + new state starting from the old one. + + The function always returns REDISMODULE_OK. + + +### RedisModule_GetSelectedDb +``` +int RedisModule_GetSelectedDb(RedisModuleCtx *ctx) { +``` + Return the currently selected DB. + + +### RedisModule_SelectDb +``` +int RedisModule_SelectDb(RedisModuleCtx *ctx, int newid) { +``` + Change the currently selected DB. Returns an error if the id + is out of range. + + Note that the client will retain the currently selected DB even after + the Redis command implemented by the module calling this function + returns. + + If the module command wishes to change something in a different DB and + returns back to the original one, it should call RedisModule_GetSelectedDb() + before in order to restore the old DB number before returning. + + +### RedisModule_OpenKey +``` +void *RedisModule_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) { +``` + Return an handle representing a Redis key, so that it is possible + to call other APIs with the key handle as argument to perform + operations on the key. + + The return value is the handle repesenting the key, that must be + closed with RM_CloseKey(). + + If the key does not exist and WRITE mode is requested, the handle + is still returned, since it is possible to perform operations on + a yet not existing key (that will be created, for example, after + a list push operation). If the mode is just READ instead, and the + key does not exist, NULL is returned. However it is still safe to + call RedisModule_CloseKey() and RedisModule_KeyType() on a NULL + value. + + +### RedisModule_CloseKey +``` +void RedisModule_CloseKey(RedisModuleKey *key) { +``` + Close a key handle. + + +### RedisModule_KeyType +``` +int RedisModule_KeyType(RedisModuleKey *key) { +``` + Return the type of the key. If the key pointer is NULL then + REDISMODULE_KEYTYPE_EMPTY is returned. + + +### RedisModule_ValueLength +``` +size_t RedisModule_ValueLength(RedisModuleKey *key) { +``` + Return the length of the value associated with the key. + For strings this is the length of the string. For all the other types + is the number of elements (just counting keys for hashes). + + If the key pointer is NULL or the key is empty, zero is returned. + + +### RedisModule_DeleteKey +``` +int RedisModule_DeleteKey(RedisModuleKey *key) { +``` + If the key is open for writing, remove it, and setup the key to + accept new writes as an empty key (that will be created on demand). + On success REDISMODULE_OK is returned. If the key is not open for + writing REDISMODULE_ERR is returned. + + +### RedisModule_GetExpire +``` +mstime_t RedisModule_GetExpire(RedisModuleKey *key) { +``` + Return the key expire value, as milliseconds of remaining TTL. + If no TTL is associated with the key or if the key is empty, + REDISMODULE_NO_EXPIRE is returned. + + +### RedisModule_SetExpire +``` +int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire) { +``` + Set a new expire for the key. If the special expire + REDISMODULE_NO_EXPIRE is set, the expire is cancelled if there was + one (the same as the PERSIST command). + + Note that the expire must be provided as a positive integer representing + the number of milliseconds of TTL the key should have. + + The function returns REDISMODULE_OK on success or REDISMODULE_ERR if + the key was not open for writing or is an empty key. + + +### RedisModule_StringSet +``` +int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str) { +``` + If the key is open for writing, set the specified string 'str' as the + value of the key, deleting the old value if any. + On success REDISMODULE_OK is returned. If the key is not open for + writing or there is an active iterator, REDISMODULE_ERR is returned. + + +### RedisModule_StringDMA +``` +char *RedisModule_StringDMA(RedisModuleKey *key, size_t *len, int mode) { +``` + Prepare the key associated string value for DMA access, and returns + a pointer and size (by reference), that the user can use to read or + modify the string in-place accessing it directly via pointer. + + The 'mode' is composed by bitwise OR-ing the following flags: + + REDISMODULE_READ -- Read access + REDISMODULE_WRITE -- Write access + + If the DMA is not requested for writing, the pointer returned should + only be accessed in a read-only fashion. + + On error (wrong type) NULL is returned. + + DMA access rules: + + 1. No other key writing function should be called since the moment + the pointer is obtained, for all the time we want to use DMA access + to read or modify the string. + + 2. Each time RM_StringTruncate() is called, to continue with the DMA + access, RM_StringDMA() should be called again to re-obtain + a new pointer and length. + + 3. If the returned pointer is not NULL, but the length is zero, no + byte can be touched (the string is empty, or the key itself is empty) + so a RM_StringTruncate() call should be used if there is to enlarge + the string, and later call StringDMA() again to get the pointer. + + +### RedisModule_StringTruncate +``` +int RedisModule_StringTruncate(RedisModuleKey *key, size_t newlen) { +``` + If the string is open for writing and is of string type, resize it, padding + with zero bytes if the new length is greater than the old one. + + After this call, RM_StringDMA() must be called again to continue + DMA access with the new pointer. + + The function returns REDISMODULE_OK on success, and REDISMODULE_ERR on + error, that is, the key is not open for writing, is not a string + or resizing for more than 512 MB is requested. + + If the key is empty, a string key is created with the new string value + unless the new length value requested is zero. + + +### RedisModule_ListPush +``` +int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele) { +``` + Push an element into a list, on head or tail depending on 'where' argumnet. + If the key pointer is about an empty key opened for writing, the key + is created. On error (key opened for read-only operations or of the wrong + type) REDISMODULE_ERR is returned, otherwise REDISMODULE_OK is returned. + + +### RedisModule_ListPop +``` +RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where) { +``` + Pop an element from the list, and returns it as a module string object + that the user should be free with RM_FreeString() or by enabling + automatic memory. 'where' specifies if the element should be popped from + head or tail. The command returns NULL if: + 1) The list is empty. + 2) The key was not open for writing. + 3) The key is not a list. + + +### RedisModule_ZsetAddFlagsToCoreFlags +``` +int RedisModule_ZsetAddFlagsToCoreFlags(int flags) { +``` + Conversion from/to public flags of the Modules API and our private flags, + so that we have everything decoupled. + + +### RedisModule_ZsetAddFlagsFromCoreFlags +``` +int RedisModule_ZsetAddFlagsFromCoreFlags(int flags) { +``` + See previous function comment. + + +### RedisModule_ZsetAdd +``` +int RedisModule_ZsetAdd(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr) { +``` + Add a new element into a sorted set, with the specified 'score'. + If the element already exists, the score is updated. + + A new sorted set is created at value if the key is an empty open key + setup for writing. + + Additional flags can be passed to the function via a pointer, the flags + are both used to receive input and to communicate state when the function + returns. 'flagsptr' can be NULL if no special flags are used. + + The input flags are: + + REDISMODULE_ZADD_XX: Element must already exist. Do nothing otherwise. + REDISMODULE_ZADD_NX: Element must not exist. Do nothing otherwise. + + The output flags are: + + REDISMODULE_ZADD_ADDED: The new element was added to the sorted set. + REDISMODULE_ZADD_UPDATED: The score of the element was updated. + REDISMODULE_ZADD_NOP: No operation was performed because XX or NX flags. + + On success the function returns REDISMODULE_OK. On the following errors + REDISMODULE_ERR is returned: + + - The key was not opened for writing. + - The key is of the wrong type. + - 'score' double value is not a number (NaN). + + +### RedisModule_ZsetIncrby +``` +int RedisModule_ZsetIncrby(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore) { +``` + This function works exactly like RM_ZsetAdd(), but instead of setting + a new score, the score of the existing element is incremented, or if the + element does not already exist, it is added assuming the old score was + zero. + + The input and output flags, and the return value, have the same exact + meaning, with the only difference that this function will return + REDISMODULE_ERR even when 'score' is a valid double number, but adding it + to the existing score resuts into a NaN (not a number) condition. + + This function has an additional field 'newscore', if not NULL is filled + with the new score of the element after the increment, if no error + is returned. + + +### RedisModule_ZsetRem +``` +int RedisModule_ZsetRem(RedisModuleKey *key, RedisModuleString *ele, int *deleted) { +``` + Remove the specified element from the sorted set. + The function returns REDISMODULE_OK on success, and REDISMODULE_ERR + on one of the following conditions: + + - The key was not opened for writing. + - The key is of the wrong type. + + The return value does NOT indicate the fact the element was really + removed (since it existed) or not, just if the function was executed + with success. + + In order to know if the element was removed, the additional argument + 'deleted' must be passed, that populates the integer by reference + setting it to 1 or 0 depending on the outcome of the operation. + The 'deleted' argument can be NULL if the caller is not interested + to know if the element was really removed. + + Empty keys will be handled correctly by doing nothing. + + +### RedisModule_ZsetScore +``` +int RedisModule_ZsetScore(RedisModuleKey *key, RedisModuleString *ele, double *score) { +``` + On success retrieve the double score associated at the sorted set element + 'ele' and returns REDISMODULE_OK. Otherwise REDISMODULE_ERR is returned + to signal one of the following conditions: + + - There is no such element 'ele' in the sorted set. + - The key is not a sorted set. + - The key is an open empty key. + + +### RedisModule_ZsetRangeStop +``` +void RedisModule_ZsetRangeStop(RedisModuleKey *key) { +``` + Stop a sorted set iteration. + + +### RedisModule_ZsetRangeEndReached +``` +int RedisModule_ZsetRangeEndReached(RedisModuleKey *key) { +``` + Return the "End of range" flag value to signal the end of the iteration. + + +### RedisModule_ZsetFirstInScoreRange +``` +int RedisModule_ZsetFirstInScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex) { +``` + Setup a sorted set iterator seeking the first element in the specified + range. Returns REDISMODULE_OK if the iterator was correctly initialized + otherwise REDISMODULE_ERR is returned in the following conditions: + + 1. The value stored at key is not a sorted set or the key is empty. + + The range is specified according to the two double values 'min' and 'max'. + Both can be infinite using the following two macros: + + REDISMODULE_POSITIVE_INFINITE for positive infinite value + REDISMODULE_NEGATIVE_INFINITE for negative infinite value + + 'minex' and 'maxex' parameters, if true, respectively setup a range + where the min and max value are exclusive (not included) instead of + inclusive. + + +### RedisModule_ZsetLastInScoreRange +``` +int RedisModule_ZsetLastInScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex) { +``` + Exactly like RedisModule_ZsetFirstInScoreRange() but the last element of + the range is selected for the start of the iteration instead. + + +### RedisModule_ZsetFirstInLexRange +``` +int RedisModule_ZsetFirstInLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) { +``` + Setup a sorted set iterator seeking the first element in the specified + lexicographical range. Returns REDISMODULE_OK if the iterator was correctly + initialized otherwise REDISMODULE_ERR is returned in the + following conditions: + + 1. The value stored at key is not a sorted set or the key is empty. + 2. The lexicographical range 'min' and 'max' format is invalid. + + 'min' and 'max' should be provided as two RedisModuleString objects + in the same format as the parameters passed to the ZRANGEBYLEX command. + The function does not take ownership of the objects, so they can be released + ASAP after the iterator is setup. + + +### RedisModule_ZsetLastInLexRange +``` +int RedisModule_ZsetLastInLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) { +``` + Exactly like RedisModule_ZsetFirstInLexRange() but the last element of + the range is selected for the start of the iteration instead. + + +### RedisModule_ZsetRangeCurrentElement +``` +RedisModuleString *RedisModule_ZsetRangeCurrentElement(RedisModuleKey *key, double *score) { +``` + Return the current sorted set element of an active sorted set iterator + or NULL if the range specified in the iterator does not include any + element. + + +### RedisModule_ZsetRangeNext +``` +int RedisModule_ZsetRangeNext(RedisModuleKey *key) { +``` + Go to the next element of the sorted set iterator. Returns 1 if there was + a next element, 0 if we are already at the latest element or the range + does not include any item at all. + + +### RedisModule_ZsetRangePrev +``` +int RedisModule_ZsetRangePrev(RedisModuleKey *key) { +``` + Go to the previous element of the sorted set iterator. Returns 1 if there was + a previous element, 0 if we are already at the first element or the range + does not include any item at all. + + +### RedisModule_HashSet +``` +int RedisModule_HashSet(RedisModuleKey *key, int flags, ...) { +``` + Set the field of the specified hash field to the specified value. + If the key is an empty key open for writing, it is created with an empty + hash value, in order to set the specified field. + + The function is variadic and the user must specify pairs of field + names and values, both as RedisModuleString pointers (unless the + CFIELD option is set, see later). + + Example to set the hash argv[1] to the value argv[2]: + + RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],argv[2],NULL); + + The function can also be used in order to delete fields (if they exist) + by setting them to the specified value of REDISMODULE_HASH_DELETE: + + RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1], + REDISMODULE_HASH_DELETE,NULL); + + The behavior of the command changes with the specified flags, that can be + set to REDISMODULE_HASH_NONE if no special behavior is needed. + + REDISMODULE_HASH_NX: The operation is performed only if the field was not + already existing in the hash. + REDISMODULE_HASH_XX: The operation is performed only if the field was + already existing, so that a new value could be + associated to an existing filed, but no new fields + are created. + REDISMODULE_HASH_CFIELDS: The field names passed are null terminated C + strings instead of RedisModuleString objects. + + Unless NX is specified, the command overwrites the old field value with + the new one. + + When using REDISMODULE_HASH_CFIELDS, field names are reported using + normal C strings, so for example to delete the field "foo" the following + code can be used: + + RedisModule_HashSet(key,REDISMODULE_HASH_CFIELDS,"foo", + REDISMODULE_HASH_DELETE,NULL); + + Return value: + + The number of fields updated (that may be less than the number of fields + specified because of the XX or NX options). + + In the following case the return value is always zero: + + - The key was not open for writing. + - The key was associated with a non Hash value. + + +### RedisModule_HashGet +``` +int RedisModule_HashGet(RedisModuleKey *key, int flags, ...) { +``` + Get fields from an hash value. This function is called using a variable + number of arguments, alternating a field name (as a StringRedisModule + pointer) with a pointer to a StringRedisModule pointer, that is set to the + value of the field if the field exist, or NULL if the field did not exist. + At the end of the field/value-ptr pairs, NULL must be specified as last + argument to signal the end of the arguments in the variadic function. + + This is an example usage: + + RedisModuleString *first, *second; + RedisModule_HashGet(mykey,REDISMODULE_HASH_NONE,argv[1],&first, + argv[2],&second,NULL); + + As with RedisModule_HashSet() the behavior of the command can be specified + passing flags different than REDISMODULE_HASH_NONE: + + REDISMODULE_HASH_CFIELD: field names as null terminated C strings. + + REDISMODULE_HASH_EXISTS: instead of setting the value of the field + expecting a RedisModuleString pointer to pointer, the function just + reports if the field esists or not and expects an integer pointer + as the second element of each pair. + + Example of REDISMODULE_HASH_CFIELD: + + RedisModuleString *username, *hashedpass; + RedisModule_HashGet(mykey,"username",&username,"hp",&hashedpass, NULL); + + Example of REDISMODULE_HASH_EXISTS: + + int exists; + RedisModule_HashGet(mykey,argv[1],&exists,NULL); + + The function returns REDISMODULE_OK on success and REDISMODULE_ERR if + the key is not an hash value. + + Memory management: + + The returned RedisModuleString objects should be released with + RedisModule_FreeString(), or by enabling automatic memory management. + + +### RedisModule_FreeCallReply_Rec +``` +void RedisModule_FreeCallReply_Rec(RedisModuleCallReply *reply, int freenested){ +``` + Free a Call reply and all the nested replies it contains if it's an + array. + + +### RedisModule_FreeCallReply +``` +void RedisModule_FreeCallReply(RedisModuleCallReply *reply) { +``` + Wrapper for the recursive free reply function. This is needed in order + to have the first level function to return on nested replies, but only + if called by the module API. + + +### RedisModule_CallReplyType +``` +int RedisModule_CallReplyType(RedisModuleCallReply *reply) { +``` + Return the reply type. + + +### RedisModule_CallReplyLength +``` +size_t RedisModule_CallReplyLength(RedisModuleCallReply *reply) { +``` + Return the reply type length, where applicable. + + +### RedisModule_CallReplyArrayElement +``` +RedisModuleCallReply *RedisModule_CallReplyArrayElement(RedisModuleCallReply *reply, size_t idx) { +``` + Return the 'idx'-th nested call reply element of an array reply, or NULL + if the reply type is wrong or the index is out of range. + + +### RedisModule_CallReplyInteger +``` +long long RedisModule_CallReplyInteger(RedisModuleCallReply *reply) { +``` + Return the long long of an integer reply. + + +### RedisModule_CallReplyStringPtr +``` +const char *RedisModule_CallReplyStringPtr(RedisModuleCallReply *reply, size_t *len) { +``` + Return the pointer and length of a string or error reply. + + +### RedisModule_CreateStringFromCallReply +``` +RedisModuleString *RedisModule_CreateStringFromCallReply(RedisModuleCallReply *reply) { +``` + Return a new string object from a call reply of type string, error or + integer. Otherwise (wrong reply type) return NULL. + + +### RedisModule_Call +``` +RedisModuleCallReply *RedisModule_Call(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) { +``` + Exported API to call any Redis command from modules. + On success a RedisModuleCallReply object is returned, otherwise + NULL is returned and errno is set to the following values: + + EINVAL: command non existing, wrong arity, wrong format specifier. + EPERM: operation in Cluster instance with key in non local slot. + + +### RedisModule_CallReplyProto +``` +const char *RedisModule_CallReplyProto(RedisModuleCallReply *reply, size_t *len) { +``` + Return a pointer, and a length, to the protocol returned by the command + that returned the reply object. + + +### RMUtil_ArgExists +``` +int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset); +``` + Return the offset of an arg if it exists in the arg list, or 0 if it's not there + + +### RMUtil_ParseArgs +``` +int RMUtil_ParseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, ...); +``` +Automatically conver the arg list to corresponding variable pointers according to a given format. +You pass it the command arg list and count, the starting offset, a parsing format, and pointers to the variables. +The format is a string consisting of the following identifiers: + + c -- pointer to a Null terminated C string pointer. + s -- pointer to a RedisModuleString + l -- pointer to Long long integer. + d -- pointer to a Double + * -- do not parse this argument at all + +Example: If I want to parse args[1], args[2] as a long long and double, I do: + double d; + long long l; + RMUtil_ParseArgs(argv, argc, 1, "ld", &l, &d); + + +### RMUtil_ParseArgsAfter +``` +int RMUtil_ParseArgsAfter(const char *token, RedisModuleString **argv, int argc, const char *fmt, ...); +``` +Same as RMUtil_ParseArgs, but only parses the arguments after `token`, if it was found. +This is useful for optional stuff like [LIMIT [offset] [limit]] + + +### RMUtil_GetRedisInfo +``` +RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx); +``` + Get redis INFO result and parse it as RMUtilInfo. + Returns NULL if something goes wrong. + The resulting object needs to be freed with RMUtilRedisInfo_Free + + +### RMUtil_CreateFormattedString +``` +RedisModuleString *RMUtil_CreateFormattedString(RedisModuleCtx *ctx, const char *fmt, ...); +``` + Create a new RedisModuleString object from a printf-style format and arguments. + Note that RedisModuleString objects CANNOT be used as formatting arguments. + + +### RMUtil_StringEquals +``` +int RMUtil_StringEquals(RedisModuleString *s1, RedisModuleString *s2); +``` + Return 1 if the two strings are equal. Case *sensitive* + + +### RMUtil_StringToLower +``` +void RMUtil_StringToLower(RedisModuleString *s); +``` + Converts a redis string to lowercase in place without reallocating anything + + +### RMUtil_StringToUpper +``` +void RMUtil_StringToUpper(RedisModuleString *s); +``` + Converts a redis string to uppercase in place without reallocating anything + + +### Vector_Get +``` +int Vector_Get(Vector *v, int pos, void *ptr); +``` + get the element at index pos. The value is copied in to ptr. If pos is outside + the vector capacity, we return 0 + otherwise 1 + + +### Vector_Resize +``` +int Vector_Resize(Vector *v, int newcap); +``` + resize capacity of v + + +### Vector_Size +``` +inline int Vector_Size(Vector *v) { return v->top; } +``` + return the used size of the vector, regardless of capacity + + +### Vector_Cap +``` +inline int Vector_Cap(Vector *v) { return v->cap; } +``` + return the actual capacity + + +### Vector_Free +``` +void Vector_Free(Vector *v); +``` + free the vector and the underlying data. Does not release its elements if + they are pointers + + diff --git a/example/Makefile b/example/Makefile index 89267b7..128bb6e 100644 --- a/example/Makefile +++ b/example/Makefile @@ -18,7 +18,7 @@ else SHOBJ_CFLAGS ?= -dynamic -fno-common -g -ggdb SHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup endif -CFLAGS = -I$(RM_INCLUDE_DIR) -g -fPIC -lc -lm -Og -std=gnu99 +CFLAGS = -I$(RM_INCLUDE_DIR) -Wall -g -fPIC -lc -lm -Og -std=gnu99 CC=gcc .SUFFIXES: .c .so .xo .o diff --git a/example/module.c b/example/module.c index a8a0101..a3f2525 100644 --- a/example/module.c +++ b/example/module.c @@ -137,7 +137,6 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx) { // register example.hgetset - using the shortened utility registration macro RMUtil_RegisterWriteCmd(ctx, "example.hgetset", HGetSetCommand); - // register the unit test RMUtil_RegisterWriteCmd(ctx, "example.test", TestModule); diff --git a/rmutil/Makefile b/rmutil/Makefile index f29834d..f8ddb63 100644 --- a/rmutil/Makefile +++ b/rmutil/Makefile @@ -3,7 +3,7 @@ ifndef RM_INCLUDE_DIR RM_INCLUDE_DIR=../ endif -CFLAGS = -g -fPIC -lc -lm -O3 -std=gnu99 -I$(RM_INCLUDE_DIR) -Wall +CFLAGS = -g -fPIC -lc -lm -O3 -std=gnu99 -I$(RM_INCLUDE_DIR) -Wall -Wno-unused-function CC=gcc OBJS=util.o strings.o sds.o vector.o diff --git a/rmutil/strings.c b/rmutil/strings.c index 08502b9..eb3ae0a 100644 --- a/rmutil/strings.c +++ b/rmutil/strings.c @@ -21,6 +21,7 @@ RedisModuleString *RMUtil_CreateFormattedString(RedisModuleCtx *ctx, const char int RMUtil_StringEquals(RedisModuleString *s1, RedisModuleString *s2) { + const char *c1, *c2; size_t l1, l2; c1 = RedisModule_StringPtrLen(s1, &l1); diff --git a/rmutil/strings.h b/rmutil/strings.h index 2203d85..71df2b4 100644 --- a/rmutil/strings.h +++ b/rmutil/strings.h @@ -17,6 +17,4 @@ void RMUtil_StringToLower(RedisModuleString *s); /* Converts a redis string to uppercase in place without reallocating anything */ void RMUtil_StringToUpper(RedisModuleString *s); - - #endif diff --git a/rmutil/util.h b/rmutil/util.h index 322da89..3927b10 100644 --- a/rmutil/util.h +++ b/rmutil/util.h @@ -4,7 +4,7 @@ #include #include -// make sure the response is not NULL or an error, and if it is sends the error to the client and exit the current function +/// make sure the response is not NULL or an error, and if it is sends the error to the client and exit the current function #define RMUTIL_ASSERT_NOERROR(r) \ if (r == NULL) { \ return RedisModule_ReplyWithError(ctx,"ERR reply is NULL"); \ @@ -23,10 +23,10 @@ /* RedisModule utilities. */ -/* Return the offset of an arg if it exists in the arg list, or 0 if it's not there */ +/** Return the offset of an arg if it exists in the arg list, or 0 if it's not there */ int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset); -/* +/** Automatically conver the arg list to corresponding variable pointers according to a given format. You pass it the command arg list and count, the starting offset, a parsing format, and pointers to the variables. The format is a string consisting of the following identifiers: @@ -64,14 +64,14 @@ typedef struct { int numEntries; } RMUtilInfo; -/* +/** * Get redis INFO result and parse it as RMUtilInfo. * Returns NULL if something goes wrong. * The resulting object needs to be freed with RMUtilRedisInfo_Free */ RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx); -/* +/** * Free an RMUtilInfo object and its entries */ void RMUtilRedisInfo_Free(RMUtilInfo *info); @@ -82,13 +82,13 @@ void RMUtilRedisInfo_Free(RMUtilInfo *info); */ int RMUtilInfo_GetInt(RMUtilInfo *info, const char *key, long long *val); -/* +/** * Get a string value from an info object. The value is placed in str. * Returns 1 if the key was found, 0 if not */ int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str); -/* +/** * Get a double value from an info object. Returns 1 if the value was found and is * a correctly formatted double, 0 otherwise. the value is placed in 'd' */ diff --git a/rmutil/vector.c b/rmutil/vector.c index ebd4668..1b81d90 100644 --- a/rmutil/vector.c +++ b/rmutil/vector.c @@ -67,4 +67,3 @@ void Vector_Free(Vector *v) { free(v->data); free(v); } - \ No newline at end of file diff --git a/rmutil/vector.h b/rmutil/vector.h index 5682d75..a599c45 100644 --- a/rmutil/vector.h +++ b/rmutil/vector.h @@ -4,61 +4,67 @@ #include #include -/** -* Generic resizable vector that can be used if you just want to store stuff temporarily. +/* +* Generic resizable vector that can be used if you just want to store stuff +* temporarily. * Works like C++ std::vector with an underlying resizable buffer */ typedef struct { - char *data; - int elemSize; - int cap; - int top; + char *data; + int elemSize; + int cap; + int top; } Vector; -// Create a new vector with element size. This should generally be used internall by the NewVector macro +/* Create a new vector with element size. This should generally be used + * internall by the NewVector macro */ Vector *__newVectorSize(size_t elemSize, size_t cap); // Put a pointer in the vector. To be used internall by the library int __vector_PutPtr(Vector *v, int pos, void *elem); -// Create a new vector for a given type and a given capacity. -// e.g. NewVector(int, 0) - empty vector of ints +/* +* Create a new vector for a given type and a given capacity. +* e.g. NewVector(int, 0) - empty vector of ints +*/ #define NewVector(type, cap) __newVectorSize(sizeof(type), cap) -// get the element at index pos. The value is copied in to ptr. If pos is outside the vector capacity, we return 0 -// otherwise 1 +/* +* get the element at index pos. The value is copied in to ptr. If pos is outside +* the vector capacity, we return 0 +* otherwise 1 +*/ int Vector_Get(Vector *v, int pos, void *ptr); -//#define Vector_Getx(v, pos, ptr) pos < v->cap ? 1 : 0; *ptr = *(typeof(ptr))(v->data + v->elemSize*pos) +//#define Vector_Getx(v, pos, ptr) pos < v->cap ? 1 : 0; *ptr = +//*(typeof(ptr))(v->data + v->elemSize*pos) -// Put an element at pos. -// Note: If pos is outside the vector capacity, we resize it accordingly -#define Vector_Put(v, pos, elem) __vector_PutPtr(v, pos, &(typeof(elem)){elem}) +/* +* Put an element at pos. +* Note: If pos is outside the vector capacity, we resize it accordingly +*/ +#define Vector_Put(v, pos, elem) __vector_PutPtr(v, pos, &(typeof(elem)){elem}) -// Push an element at the end of v, resizing it if needed. This macro wraps __vector_PushPtr +/* Push an element at the end of v, resizing it if needed. This macro wraps + * __vector_PushPtr */ #define Vector_Push(v, elem) __vector_PushPtr(v, &(typeof(elem)){elem}) int __vector_PushPtr(Vector *v, void *elem); -// resize capacity of v +/* resize capacity of v */ int Vector_Resize(Vector *v, int newcap); -// return the used size of the vector, regardless of capacity -inline int Vector_Size(Vector *v) { - return v->top; -} +/* return the used size of the vector, regardless of capacity */ +inline int Vector_Size(Vector *v) { return v->top; } + +/* return the actual capacity */ +inline int Vector_Cap(Vector *v) { return v->cap; } -// return the actual capacity - inline int Vector_Cap(Vector *v) { - return v->cap; - } - - // free the vector and the underlying data. Does not release its elements if they are pointers - void Vector_Free(Vector *v); - - int __vecotr_PutPtr(Vector *v, int pos, void *elem); - +/* free the vector and the underlying data. Does not release its elements if + * they are pointers*/ +void Vector_Free(Vector *v); +int __vecotr_PutPtr(Vector *v, int pos, void *elem); -#endif \ No newline at end of file +#endif \ No newline at end of file