Skip to content

Commit

Permalink
Added resp2XField() conversion function.
Browse files Browse the repository at this point in the history
  • Loading branch information
attipaci committed Dec 8, 2024
1 parent 0bbf444 commit 89b2868
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 13 deletions.
15 changes: 8 additions & 7 deletions include/redisx.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@
// Configuration constants ------------------------------------------------------->
#ifndef REDISX_TCP_PORT
/// Default TCP/IP port on which Redis server listens to clients.
# define REDISX_TCP_PORT 6379
# define REDISX_TCP_PORT 6379
#endif

#ifndef REDISX_TCP_BUF_SIZE
/// (bytes) Default TCP buffer size (send/recv) for Redis clients. Values <= 0 will use system default.
# define REDISX_TCP_BUF_SIZE 0
# define REDISX_TCP_BUF_SIZE 0
#endif

#ifndef REDISX_CMDBUF_SIZE
/// (bytes) Size of many internal arrays, and the max. send chunk size. At least ~16 bytes...
# define REDISX_CMDBUF_SIZE 8192
# define REDISX_CMDBUF_SIZE 8192
#endif

#ifndef REDISX_RCVBUF_SIZE
/// (bytes) Redis receive buffer size (at most that much is read from the socket in a single call).
# define REDISX_RCVBUF_SIZE 8192
# define REDISX_RCVBUF_SIZE 8192
#endif

#ifndef REDISX_SET_LISTENER_PRIORITY
Expand All @@ -55,7 +55,7 @@
#define REDISX_MINOR_VERSION 9

/// Integer sub version of the release
#define REDISX_PATCHLEVEL 1
#define REDISX_PATCHLEVEL 2

/// Additional release information in version, e.g. "-1", or "-rc1".
#define REDISX_RELEASE_STRING "-devel"
Expand Down Expand Up @@ -107,9 +107,9 @@ enum resp_type {
RESP3_SET = '~', ///< \hideinitializer RESP3 unordered set of elements
RESP3_ATTRIBUTE = '|', ///< \hideinitializer RESP3 dictionary of attributes (metadata)
RESP3_PUSH = '>', ///< \hideinitializer RESP3 dictionary of attributes (metadata)
RESP3_CONTINUED = ';' ///< \hideinitializer RESP3 dictionary of attributes (metadata)
};

#define RESP3_CONTINUED ';' ///< \hideinitializer RESP3 dictionary of attributes (metadata)

#define REDIS_INVALID_CHANNEL (-101) ///< \hideinitializer There is no such channel in the Redis instance.
#define REDIS_NULL (-102) ///< \hideinitializer Redis returned NULL
Expand Down Expand Up @@ -158,7 +158,7 @@ enum redisx_protocol {
* \sa redisxIsMapType()
*/
typedef struct RESP {
enum resp_type type; ///< RESP type RESP_ARRAY, RESP_INT ...
enum resp_type type; ///< RESP type; RESP_ARRAY, RESP_INT ...
int n; ///< Either the integer value of a RESP_INT or a RESP3_BOOLEAN response, or the
///< dimension of the value field.
void *value; ///< Pointer to text (char *) content or to an array of components
Expand Down Expand Up @@ -395,6 +395,7 @@ boolean redisxIsMapType(const RESP *r);
boolean redisxHasComponents(const RESP *r);
boolean redisxIsEqualRESP(const RESP *a, const RESP *b);
int redisxSplitText(RESP *resp, char **text);
XField *resp2XField(const char *name, const RESP *resp);

RedisMapEntry *redisxGetMapEntry(const RESP *map, const RESP *key);
RedisMapEntry *redisxGetKeywordEntry(const RESP *map, const char *key);
Expand Down
12 changes: 6 additions & 6 deletions src/redisx-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,9 @@ RESP *redisxReadReplyAsync(RedisClient *cl) {
resp->n = 0;
break;

case RESP_INT: // Nothing left to do for INT type response.
break;

case RESP3_BOOLEAN: {
switch(tolower(buf[1])) {
case 't': resp->n = TRUE; break;
Expand All @@ -946,9 +949,9 @@ RESP *redisxReadReplyAsync(RedisClient *cl) {
break;
}

case RESP_ARRAY:
case RESP3_SET:
case RESP3_PUSH:
case RESP_ARRAY: {
case RESP3_PUSH: {
RESP **component;
int i;

Expand Down Expand Up @@ -999,9 +1002,9 @@ RESP *redisxReadReplyAsync(RedisClient *cl) {
break;
}

case RESP_BULK_STRING:
case RESP3_BLOB_ERROR:
case RESP3_VERBATIM_STRING:
case RESP_BULK_STRING:
if(resp->n < 0) break; // no string token following!

resp->value = malloc(resp->n + 2); // <string>\r\n
Expand Down Expand Up @@ -1040,9 +1043,6 @@ RESP *redisxReadReplyAsync(RedisClient *cl) {

break;

case RESP_INT: // Nothing left to do for INT type response.
break;

default:
// FIXME workaround for Redis 4.x improper OK reply to QUIT
if(!strcmp(buf, "OK")) {
Expand Down
190 changes: 190 additions & 0 deletions src/resp.c
Original file line number Diff line number Diff line change
Expand Up @@ -517,4 +517,194 @@ RedisMapEntry *redisxGetKeywordEntry(const RESP *map, const char *key) {
return NULL;
}

static XType resp2xType(enum resp_type type) {
switch(type) {
case RESP3_NULL:
return X_UNKNOWN;
case RESP3_BOOLEAN:
return X_BOOLEAN;
case RESP_INT:
return X_LONG;
case RESP3_DOUBLE:
return X_DOUBLE;
case RESP_SIMPLE_STRING:
case RESP_BULK_STRING:
case RESP_ERROR:
case RESP3_BLOB_ERROR:
case RESP3_VERBATIM_STRING:
case RESP3_BIG_NUMBER:
return X_STRING;
case RESP_ARRAY:
case RESP3_SET:
case RESP3_PUSH:
return X_FIELD;
case RESP3_MAP:
case RESP3_ATTRIBUTE:
return X_STRUCT;
}

return X_UNKNOWN;
}


static XField *respArrayToXField(const char *name, const RESP **component, int n) {
static const char *fn = "respArrayToXField";

XField *f;
enum resp_type type = RESP3_NULL;
int i;

if(n < 0) return NULL;

for(i = 0; i < n; i++) {
if(i == 0) type = component[i]->type;
else if(component[i]->type != type) break;
}

if(i < n) {
// --------------------------------------------------------
// Heterogeneous array...

XField *array;

f = xCreate1DFieldArray(name, n);

if(!f->value) return x_trace_null(fn, "field array");

array = (XField *) f->value;

for(i = 0; i < n; i++) {
XField *e = resp2XField(array[i].name, component[i]);
if(e) {
array[i] = *e;
free(e);
}
}
}

else {
// --------------------------------------------------------
// Homogeneous array...

XType eType = resp2xType(type);
char *array;
int eSize;

if(eType == X_UNKNOWN) return NULL;

eSize = xElementSizeOf(eType);
array = (char *) calloc(1, n * eSize);

Check failure

Code scanning / CodeQL

Multiplication result converted to larger type High

Multiplication result may overflow 'int' before it is converted to 'size_t'.

f = xCreate1DField(name, eType, n, array);
f->flags = type;
if(!array) return x_trace_null(fn, "field array");

for(i = 0; i < n; i++) {
XField *e = resp2XField("<element>", component[i]);
if(e) {
memcpy(&array[i * eSize], e, sizeof(XField));
free(e);
}
}
}

return f;
}


static XField *respMap2XField(const char *name, const RedisMapEntry *map, int n) {
XStructure *s = xCreateStruct();
XField *f;

while(--n >= 0) {
const RedisMapEntry *e = &map[n];
XField *fi = NULL;
if(redisxIsStringType(e->key)) {
fi = resp2XField((char *) e->key->value, e->value->value);
fi->next = s->firstField;
s->firstField = fi;
}
else {
xvprintf("WARNING! cannot convert RESP map entry with non-string key");
errno = ENOSYS;
}
}

f = xCreateScalarField(name, X_STRUCT, s);
return f;
}

/**
* Converts a RESP to the xchange representation as an appropriate XField.
*
* <ul>
* <li>RESP3_NULL values are converted to NULL.</li>
* <li>Scalar values are converted to an XField with the equivalent type.</li>
* <li>Homogenerous arrays are converted to a field with a 1D array of corresponding xchange type.</li>
* <li>Heterogeneous arrays are converted to a field with a 1D array of X_FIELD type (containing an array of fields).</li>
* <li>Maps with string keywords are converted to an X_STRUCT.</li>
* <li>Maps with non-string keywords cannot be converted and will be ignored. However, `errno` is set to ENOSYS
* to indicate the failure, and warnings are printed to the standard error, provided redisxSetVerbose() was
* used to enable verbose output.</li>
* </ul>
*
* @param name
* @param resp
* @return
*/
XField *resp2XField(const char *name, const RESP *resp) {
static const char *fn = "resp2XField";

errno = 0;

if(!resp) {
x_error(0, EINVAL, fn, "input RESP is NULL");
return NULL;
}

switch(resp->type) {
case RESP3_NULL:
return NULL;

case RESP3_BOOLEAN:
return xCreateBooleanField(name, resp->n);

case RESP_INT:
return xCreateIntField(name, resp->n);

case RESP3_DOUBLE:
return xCreateDoubleField(name, *(double *) resp->value);

case RESP_SIMPLE_STRING:
case RESP_BULK_STRING:
case RESP_ERROR:
case RESP3_BLOB_ERROR:
case RESP3_VERBATIM_STRING:
case RESP3_BIG_NUMBER: {
XField *f = xCreateStringField(name, (char *) resp->value);
f->flags = resp->type;
return f;
}

case RESP_ARRAY:
case RESP3_SET:
case RESP3_PUSH: {
XField *f = respArrayToXField(name, (const RESP **) resp->value, resp->n);
if(!f) return x_trace_null(fn, NULL);
f->flags = resp->type;
return f;
}

case RESP3_MAP:
case RESP3_ATTRIBUTE: {
XField *f = respMap2XField(name, (const RedisMapEntry *) resp->value, resp->n);
if(!f) return x_trace_null(fn, NULL);
f->flags = resp->type;
return f;
}

}

return NULL;
}

0 comments on commit 89b2868

Please sign in to comment.