Thanks to visit codestin.com
Credit goes to github.com

Skip to content
/ hsss Public

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

License

Notifications You must be signed in to change notification settings

slact/hsss

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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:

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

delete.lua

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

running hsss --format whole example/*.lua produces

// 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

// 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:

    //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

    //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);
      }
    }
    //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:

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.

About

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

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages