Browse Source

v0.1.0

- Refactor commands.c, introduce REDIS_CMD__* macros
- Bug fixes
- Add more command implementations
tags/v0.1.0
Ryan Joseph 11 months ago
parent
commit
25a1cda5e0
10 changed files with 147 additions and 117 deletions
  1. +11
    -0
      README.md
  2. +1
    -0
      azuresphere/yarl.vcxproj
  3. +3
    -0
      azuresphere/yarl.vcxproj.filters
  4. +79
    -87
      src/commands.c
  5. +11
    -1
      src/commands.h
  6. +4
    -3
      src/object.c
  7. +2
    -2
      src/resp.c
  8. +0
    -7
      src/types.h
  9. +2
    -0
      src/yarl.h
  10. +34
    -17
      test/test.c

+ 11
- 0
README.md View File

@@ -9,3 +9,14 @@ Some day, that library will consume this one & morph into just a nice C++ facade

Additionally, I felt a simple, "bring your own file descriptor" interface
would do nicely on the simple MCU platforms to be targetted.

To that end, `RedisConnection_t` is `typedef`ed simply to `int`, a.k.a. a
file descriptor. It could be a socket FD, a pipe FD, even an actual file descriptor
if you're so inclined. Anything that can be `read(2)` from and `write(2)`en to. BYOFD!

## Building

The test app:
```
clang -o sphere -Wall -Werror -I./src -O0 -g ./src/*.c ./test/test.c
```

+ 1
- 0
azuresphere/yarl.vcxproj View File

@@ -25,6 +25,7 @@
<ClCompile Include="..\src\connection.c" />
<ClCompile Include="..\src\object.c" />
<ClCompile Include="..\src\resp.c" />
<ClCompile Include="..\test\test.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6e98c738-2f73-41b2-a0fe-79b0588fa810}</ProjectGuid>


+ 3
- 0
azuresphere/yarl.vcxproj.filters View File

@@ -53,5 +53,8 @@
<ClCompile Include="..\src\resp.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\test\test.c">
<Filter>Resource Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

+ 79
- 87
src/commands.c View File

@@ -2,112 +2,104 @@
#include "connection.h"
#include "object.h"
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
RedisReturnValue Redis_AUTH(RedisConnection_t conn, const char *password)
// optional arguments must be of type void*
RedisObject_t _RedisCommand_issue(RedisConnection_t conn, const char* cmd, uint32_t argCount, uint32_t ownershipBitMask, ...)
{
if (conn > 0)
{
size_t passwordLength = strlen(password);
if (passwordLength > 0)
{
RedisArray_t *command = RedisArray_init(2);
RedisObject_t cmdObj = {
.type = RedisObjectType_Array,
.obj = (void *)command,
.objIsOwned = true};
assert(cmd);
RedisObject_t authObj = {
.type = RedisObjectType_BulkString,
.obj = (void *)"AUTH",
.objIsOwned = false};
argCount += 1;
RedisArray_t* cmdArr = RedisArray_init(argCount);
RedisObject_t cmdObj = {
.type = RedisObjectType_Array,
.obj = (void *)cmdArr,
.objIsOwned = true };
RedisObject_t passwordObj = {
.type = RedisObjectType_BulkString,
.obj = (void *)password,
.objIsOwned = false};
RedisObject_t cmdNameObj = {
.type = RedisObjectType_BulkString,
.obj = (void *)cmd,
.objIsOwned = false };
command->objects[0] = authObj;
command->objects[1] = passwordObj;
cmdArr->objects[0] = cmdNameObj;
RedisObject_t cmdRet = RedisConnection_sendCommand(conn, cmdObj);
RedisObject_dealloc(cmdObj);
if (argCount > 0)
{
va_list args;
va_start(args, ownershipBitMask);
for (int i = 1; i < (argCount); i++)
{
RedisObject_t argObj = {
.type = RedisObjectType_BulkString,
.obj = (void*)va_arg(args, void*),
.objIsOwned = (bool)(ownershipBitMask & (uint32_t)(1 << i))
};
return cmdRet.type == RedisObjectType_SimpleString && cmdRet.obj &&
strcmp((const char *)cmdRet.obj, "OK") == 0
? RedisSuccess
: RedisAuthFailure;
cmdArr->objects[i] = argObj;
}
return RedisSuccess;
va_end(args);
}
return RedisNotConnectedFailure;
RedisObject_t cmdResult = RedisConnection_sendCommand(conn, cmdObj);
RedisObject_dealloc(cmdObj);
return cmdResult;
}
char *Redis_GET(RedisConnection_t conn, const char *key)
#define REDIS_CMD__GENERIC__PREDEALLOC(conn, cmd, count, bm, endCond, failReturn, preDealloc, deallocBeforeReturn, ...) \
RedisObject_t cmdRet = _RedisCommand_issue(conn, cmd, count, bm, ##__VA_ARGS__); \
if (cmdRet.type == RedisObjectType_InternalError || !cmdRet.obj) return failReturn; \
preDealloc; \
if (deallocBeforeReturn) RedisObject_dealloc(cmdRet); \
return endCond;
#define REDIS_CMD__GENERIC(conn, cmd, count, bm, endCond, failReturn, deallocBeforeReturn, ...) \
RedisObject_t cmdRet = _RedisCommand_issue(conn, cmd, count, bm, ##__VA_ARGS__); \
if (cmdRet.type == RedisObjectType_InternalError || !cmdRet.obj) return failReturn; \
if (deallocBeforeReturn) RedisObject_dealloc(cmdRet); \
return endCond;
#define REDIS_CMD__EXPECT_OK(conn, cmd, count, bm, ...) \
REDIS_CMD__GENERIC__PREDEALLOC(conn, cmd, count, bm, boolVal, false, \
bool boolVal = cmdRet.type == RedisObjectType_SimpleString && cmdRet.obj && \
strncmp((const char *)cmdRet.obj, "OK", strlen("OK")) == 0, true, ##__VA_ARGS__)
#define REDIS_CMD__EXPECT_BOOL(conn, cmd, count, bm, ...) \
REDIS_CMD__GENERIC__PREDEALLOC(conn, cmd, count, bm, boolVal, false, \
bool boolVal = cmdRet.type == RedisObjectType_Integer && (bool)*(int*)cmdRet.obj, true, ##__VA_ARGS__)
#define REDIS_CMD__EXPECT_INT(conn, cmd, count, bm, ...) \
REDIS_CMD__GENERIC__PREDEALLOC(conn, cmd, count, bm, intVal, -1, \
int intVal = *(int*)cmdRet.obj, true, ##__VA_ARGS__)
bool Redis_AUTH(RedisConnection_t conn, const char *password)
{
RedisArray_t *command = RedisArray_init(2);
RedisObject_t cmdObj = {
.type = RedisObjectType_Array,
.obj = (void *)command,
.objIsOwned = true};
RedisObject_t getObj = {
.type = RedisObjectType_BulkString,
.obj = (void *)"GET",
.objIsOwned = false};
RedisObject_t keyObj = {
.type = RedisObjectType_BulkString,
.obj = (void *)key,
.objIsOwned = false};
command->objects[0] = getObj;
command->objects[1] = keyObj;
RedisObject_t cmdRet = RedisConnection_sendCommand(conn, cmdObj);
RedisObject_dealloc(cmdObj);
if (cmdRet.type == RedisObjectType_InternalError || !cmdRet.obj)
return NULL;
REDIS_CMD__EXPECT_OK(conn, "AUTH", 1, 0, password);
}
return (char *)cmdRet.obj;
char *Redis_GET(RedisConnection_t conn, const char *key)
{
REDIS_CMD__GENERIC(conn, "GET", 1, 0, (char*)cmdRet.obj, NULL, false, key);
}
bool Redis_SET(RedisConnection_t conn, const char *key, const char *value)
{
RedisArray_t *command = RedisArray_init(3);
RedisObject_t cmdObj = {
.type = RedisObjectType_Array,
.obj = (void *)command,
.objIsOwned = true};
RedisObject_t setObj = {
.type = RedisObjectType_BulkString,
.obj = (void *)"SET",
.objIsOwned = false};
RedisObject_t keyObj = {
.type = RedisObjectType_BulkString,
.obj = (void *)key,
.objIsOwned = false};
RedisObject_t valObj = {
.type = RedisObjectType_BulkString,
.obj = (void *)value,
.objIsOwned = false};
command->objects[0] = setObj;
command->objects[1] = keyObj;
command->objects[2] = valObj;
REDIS_CMD__EXPECT_OK(conn, "SET", 2, 0, key, value);
}
RedisObject_t cmdRet = RedisConnection_sendCommand(conn, cmdObj);
RedisObject_dealloc(cmdObj);
bool Redis_DEL(RedisConnection_t conn, const char *key)
{
REDIS_CMD__EXPECT_BOOL(conn, "DEL", 1, 0, key);
}
if (cmdRet.type == RedisObjectType_InternalError || !cmdRet.obj)
return NULL;
bool Redis_EXISTS(RedisConnection_t conn, const char *key)
{
REDIS_CMD__EXPECT_BOOL(conn, "EXISTS", 1, 0, key);
}
return strncmp((const char *)cmdRet.obj, "OK", strlen("OK")) == 0;
}
int Redis_APPEND(RedisConnection_t conn, const char *key, const char *value)
{
REDIS_CMD__EXPECT_INT(conn, "APPEND", 2, 0, key, value);
}

+ 11
- 1
src/commands.h View File

@@ -4,10 +4,20 @@
#include "types.h"
#include "constants.h"
RedisReturnValue Redis_AUTH(RedisConnection_t conn, const char *password);
// any non-scalar return values are OWNED BY THE CALLER and must be free()ed
bool Redis_AUTH(RedisConnection_t conn, const char *password);
char *Redis_GET(RedisConnection_t conn, const char *key);
bool Redis_SET(RedisConnection_t conn, const char *key, const char *value);
bool Redis_DEL(RedisConnection_t conn, const char *key);
bool Redis_EXISTS(RedisConnection_t conn, const char *key);
int Redis_APPEND(RedisConnection_t conn, const char *key, const char *value);
int Redis_PUBLISH(RedisConnection_t conn, const char* channel, const char* message);
#endif // __YARL_COMMANDS__H__

+ 4
- 3
src/object.c View File

@@ -14,7 +14,7 @@ RedisObject_t RedisObject_parseSimpleString(RedisConnection_t conn)
RedisObject_t rObj = {
.type = RedisObjectType_SimpleString,
.obj = NULL,
.objIsOwned = true};
.objIsOwned = true };
char *readBuf = NULL;
size_t readOffset = 0;
@@ -44,7 +44,7 @@ RedisObject_t RedisObject_parseBulkString(RedisConnection_t conn)
RedisObject_t rObj = {
.type = RedisObjectType_BulkString,
.obj = NULL,
.objIsOwned = true};
.objIsOwned = true };
RedisObject_t lenObj = RedisObject_parseInteger(conn);
assert(lenObj.obj);
@@ -71,6 +71,7 @@ RedisObject_t RedisObject_parseInteger(RedisConnection_t conn)
rObj.type = RedisObjectType_Integer;
rObj.obj = (void *)malloc(sizeof(int));
rObj.objIsOwned = true;
memcpy(rObj.obj, &convNum, sizeof(int));
return rObj;
@@ -81,7 +82,7 @@ RedisObject_t RedisObject_parseArray(RedisConnection_t conn)
RedisObject_t rObj = {
.type = RedisObjectType_Array,
.obj = NULL,
.objIsOwned = true};
.objIsOwned = true };
RedisObject_t lenObj = RedisObject_parseInteger(conn);
assert(lenObj.obj);


+ 2
- 2
src/resp.c View File

@@ -98,7 +98,7 @@ char *RedisObject_RESP_array(RedisObject_t obj)
char *tRESP = RedisRESP_generate(rArr->objects[i]);
_RedisObject_RESP_array__coll_t tCol = {
.RESP = tRESP,
.length = strlen(tRESP)};
.length = strlen(tRESP) };
constRESPs[i] = tCol;
constRESPsTotalLen += tCol.length;
}
@@ -137,7 +137,7 @@ static RedisObjectRESPTypeMap_t RedisObject_RESP_map[] = {
{RedisObjectType_Integer, RedisObject_RESP_simpleString},
{RedisObjectType_Array, RedisObject_RESP_array},
{RedisObjectType_Error, RedisObject_RESP_simpleString},
{RedisObjectType_InternalError, NULL}};
{RedisObjectType_InternalError, NULL} };
char *RedisRESP_generate(RedisObject_t obj)
{


+ 0
- 7
src/types.h View File

@@ -7,13 +7,6 @@
typedef int RedisConnection_t;
typedef char RedisObjectType_t;
typedef enum RedisReturnValue
{
RedisSuccess = 0,
RedisNotConnectedFailure = 1,
RedisAuthFailure = 2,
} RedisReturnValue;
typedef struct RedisObject_t
{
RedisObjectType_t type;


+ 2
- 0
src/yarl.h View File

@@ -5,6 +5,8 @@
extern "C" {
#endif
#define YARL_VERSION "0.1.0"
#include "commands.h"
#ifdef __cplusplus


test/main.c → test/test.c View File

@@ -75,6 +75,8 @@ RedisConnection_t RedisConnect(const char *host, const char *port)
return (RedisConnection_t)sockfd;
}
#define TEST_KEY_NAME "YarlSaysHi"
int main(int argc, char** argv)
{
if (argc < 3)
@@ -95,24 +97,39 @@ int main(int argc, char** argv)
exit(rConn);
}
if (pass)
if (pass && !Redis_AUTH(rConn, pass))
{
RedisReturnValue authRet = Redis_AUTH(rConn, pass);
fprintf(stderr, "AUTH failed\n");
exit(-1);
}
if (authRet != RedisSuccess)
{
fprintf(stderr, "AUTH failed: %d\n", authRet);
exit(authRet);
}
bool exists = Redis_EXISTS(rConn, TEST_KEY_NAME);
printf("Exists already? %d\n", exists);
if (exists && !Redis_DEL(rConn, TEST_KEY_NAME))
{
fprintf(stderr, "DEL failed\n");
exit(-2);
}
if (!Redis_SET(rConn, TEST_KEY_NAME, "Hello, World!"))
{
fprintf(stderr, "SET failed\n");
exit(-2);
}
char* getVal = Redis_GET(rConn, TEST_KEY_NAME);
printf("Hello, World? %s\n", getVal);
free(getVal);
int appended = Redis_APPEND(rConn, TEST_KEY_NAME, " It's a beautiful day!");
getVal = Redis_GET(rConn, TEST_KEY_NAME);
printf("Appended %d bytes, now have '%s'\n", appended, getVal);
free(getVal);
if (!Redis_DEL(rConn, TEST_KEY_NAME))
{
fprintf(stderr, "DEL failed\n");
exit(-2);
}
if (!Redis_SET(rConn, "FromTheSphere", "Hello, World!"))
{
fprintf(stderr, "SET failed\n");
exit(-2);
}
char* getVal = Redis_GET(rConn, "FromTheSphere");
printf("Hello, World? %s\n", getVal);
free(getVal);
}

Loading…
Cancel
Save