Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/slact/hsss

Hash-Safe Script Splinterer: Conveniently embed lua scripts and their hashes in C. Best with Redis.
https://github.com/slact/hsss

Last synced: 22 days ago
JSON representation

Hash-Safe Script Splinterer: Conveniently embed lua scripts and their hashes in C. Best with Redis.

Awesome Lists containing this project

README

        

# Hsss

Hash-Safe Script Splinterer, a Lua Script and hash embedder into C source.
Good for putting Redis Lua scripts in your C headers.
```
Usage: hsss [options] files > output_file.h
--format [split|whole] Output as separate or a single struct
--struct [redis_lua_scripts_t]
C struct name
--row-struct [redis_lua_script_t]
Hash+name+script struct for 'whole' format.
--scripts [redis_lua_scripts]
Scripts variable (split or whole format)
--hashes [redis_lua_hashes] Hashes variable (split format)
--no-hashes Omit hashes variable (split format)
--names [redis_lua_script_names]
Script names variable (split format)
--no-names Omit script names variable (split format)
--count [redis_lua_scripts_count]
integer script count variable
--no-count Omit script count variable
--each-macro [REDIS_LUA_SCRIPTS_EACH]
Iterator macro
--no-each Omit the iterator macro
--no-parse Skip using luac to check script syntax
--no-static Don't make variables static (file-scoped)
--prefix PREFIX Prefix default names with this
```

## Example

Let's say you have two scripts in directory `example/`:

`echo.lua`:
```lua
--echoes the first argument
redis.call('echo', ARGS[1])

```

`delete.lua`
```lua
--deletes first key
redis.call('del', KEYS[1])

```

running `hsss --format whole example/*.lua` produces
```c
// don't edit this please, it was auto-generated by hsss
// https://github.com/slact/hsss

typedef struct {
char *name;
char *hash;
char *script;
} redis_lua_script_t;

typedef struct {
//deletes first key
redis_lua_script_t delete;

//echoes the first argument
redis_lua_script_t echo;

} redis_lua_scripts_t;

static redis_lua_scripts_t redis_lua_scripts = {
{"delete", "c6929c34f10b0fe8eaba42cde275652f32904e03",
"--deletes first key\n"
"redis.call('del', KEYS[1])\n"},

{"echo", "8f8f934c6049ab4d6337cfa53976893417b268bc",
"--echoes the first argument\n"
"redis.call('echo', ARGS[1])\n"}
};

const int redis_lua_scripts_count=2;
#define REDIS_LUA_SCRIPTS_EACH(script) \
for((script)=(redis_lua_script_t *)&redis_lua_scripts; (script) < (redis_lua_script_t *)(&redis_lua_scripts + 1); (script)++)
```

running `hsss --format split example/*.lua` produces
```c
// don't edit this please, it was auto-generated by hsss
// https://github.com/slact/hsss

typedef struct {
//deletes first key
char *delete;

//echoes the first argument
char *echo;

} redis_lua_script_t;

static redis_lua_script_t redis_lua_hashes = {
"c6929c34f10b0fe8eaba42cde275652f32904e03",
"8f8f934c6049ab4d6337cfa53976893417b268bc"
};

static redis_lua_script_t redis_lua_script_names = {
"delete",
"echo",
};

static redis_lua_script_t redis_lua_scripts = {
//delete
"--deletes first key\n"
"redis.call('del', KEYS[1])\n",

//echo
"--echoes the first argument\n"
"redis.call('echo', ARGS[1])\n"
};

const int redis_lua_scripts_count=2;
#define REDIS_LUA_SCRIPTS_EACH(script_src, script_name, script_hash) \
for((script_src)=(char **)&redis_lua_scripts, (script_hash)=(char **)&redis_lua_hashes, (script_name)=(char **)&redis_lua_script_names; (script_src) < (char **)(&redis_lua_scripts + 1); (script_src)++, (script_hash)++, (script_name)++)
```

## Using in your C code

- `EVALSHA`:

```c
//whole format
redisAsyncCommand(asyncContext, callback, data, "EVALSHA %s 0", redis_lua_scripts.script_name.hash);

//split format
redisAsyncCommand(asyncContext, callback, data, "EVALSHA %s 0", redis_lua_hashes.script_name);
```

- iterator macro, loading scripts
```c
//split format:

//privdata for error checking callback
typedef struct {
char *name;
char *hash;
} script_hash_and_name_t;

//error checking callback
static void redisLoadScriptCallback(redisAsyncContext *c, void *r, void *privdata) {
script_hash_and_name_t *hn = privdata;
redisReply *reply = r;
switch(reply->type) {
case REDIS_REPLY_ERROR:
printf("nchan: Failed loading redis lua script %s :%s", hn->name, reply->str);
break;
case REDIS_REPLY_STRING:
if(strcmp(reply->str, hn->hash)!=0) { //sha1 hash length is 40 chars
printf("nchan Redis lua script %s has unexpected hash %s (expected %s)", hn->name, reply->str, hn->hash);
}
break;
}
free(hn);
}

static void redisInitScripts(redisAsyncContext *c){
char **script, **script_name, **script_hash;

REDIS_LUA_SCRIPTS_EACH(script, script_name, script_hash) {
script_hash_and_name_t *hn = alloc(sizeof(*hn));
hn->name=*script_name;
hn->hash=*script_hash;
redisAsyncCommand(c, redisLoadScriptCallback, hn, "SCRIPT LOAD %s", *script);
}
}
```

```c
//whole format:

//error checking callback
static void redisLoadScriptCallback(redisAsyncContext *c, void *r, void *privdata) {
redis_lua_script_t *script = privdata;
redisReply *reply = r;
if (reply == NULL) return;
switch(reply->type) {
case REDIS_REPLY_ERROR:
printf("Failed loading redis lua script %s :%s", script->name, reply->str);
break;
case REDIS_REPLY_STRING:
if(nstrncmp(reply->str, script->hash, 40)!=0) {
printf("Redis lua script %s has unexpected hash %s (expected %s)", script->name, reply->str, script->hash);
}
break;
}
}

static void redisInitScripts(redisAsyncContext *c){
redis_lua_script_t *script;

REDIS_LUA_SCRIPTS_EACH(script) {
redisAsyncCommand(c, redisLoadScriptCallback, script, "SCRIPT LOAD %s", script->script);
}
}
```

## Using in your build tooling

`Makefile`:
```Makefile

lua_scripts.h: scripts/*.lua
hsss scripts/*.lua > lua_scripts.h

main_build_rule: lua_scripts.h

```

## License

The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).