Skip to content

Instantly share code, notes, and snippets.

@starwing
Created November 20, 2020 13:28
Show Gist options
  • Save starwing/44a03312d2d89b24deb8a0ac216dc4d1 to your computer and use it in GitHub Desktop.
Save starwing/44a03312d2d89b24deb8a0ac216dc4d1 to your computer and use it in GitHub Desktop.
Lua Base58
#include <lua.h>
#include <lauxlib.h>
#include <string.h>
#define BASE 58
#define BYTE (1<<CHAR_BIT)
#define BASE58_ALPHABET "base58.Alphabet"
#define BASE58_ALPHABETS(X) \
X(Bitcoin, "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") \
X(IPFS, "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") \
X(Flickr, "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ") \
X(Ripple, "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz") \
typedef struct base58_Alphabet {
char entab[BASE];
char detab[BYTE];
} base58_Alphabet;
static int Lbase58_alphabet(lua_State *L) {
size_t i, len;
const char *abt = luaL_checklstring(L, 1, &len);
base58_Alphabet *ab = (base58_Alphabet*)
lua_newuserdata(L, sizeof(base58_Alphabet));
luaL_argcheck(L, len == BASE, 1, "invalid alphabet length");
memcpy(ab->entab, abt, len);
memset(ab->detab, ~0, BYTE);
for (i = 0; i < BASE; ++i)
ab->detab[(int)abt[i]] = i;
luaL_setmetatable(L, BASE58_ALPHABET);
return 1;
}
static int base58_iencode(lua_State *L, lua_Integer v) {
luaL_Buffer B;
char *out, *i, *j;
const base58_Alphabet *ab = (const base58_Alphabet*)
luaL_checkudata(L, 2, BASE58_ALPHABET);
luaL_buffinit(L, &B);
i = out = luaL_prepbuffsize(&B, sizeof(v)*CHAR_BIT * 138/100 + 1);
luaL_argcheck(L, v >= 0, 1, "integer negative");
while (v > 0)
*out++ = v % BASE, v /= BASE;
luaL_addsize(&B, out - i);
for (j = out-1; i < j; ++i, --j) {
char t = *i;
*i = ab->entab[(int)*j];
*j = ab->entab[(int)t];
}
if (i == j) *i = ab->entab[(int)*i];
luaL_pushresult(&B);
return 1;
}
static int base58_sencode(lua_State *L, const char *s, size_t len) {
const base58_Alphabet *ab = (const base58_Alphabet*)
luaL_checkudata(L, 2, BASE58_ALPHABET);
int i, j, prefixs = 0, outrevs, carry, capacity;
luaL_Buffer B;
unsigned char *out;
luaL_buffinit(L, &B);
for (; len > 0 && *s == 0; ++s, --len)
++prefixs;
capacity = prefixs + len * 138/100 + 1; /* log256 / log58 */
out = (unsigned char*)luaL_prepbuffsize(&B, capacity);
memset(out, 0, capacity);
outrevs = capacity - 1;
for (; len > 0; ++s, --len) {
int outidx = capacity - 1;
carry = *s & (BYTE-1);
for (; outidx > outrevs || carry != 0; --outidx) {
carry += out[outidx] << CHAR_BIT;
out[outidx] = carry % BASE;
carry /= BASE;
}
outrevs = outidx;
}
for (i = 0, j = outrevs + 1 - prefixs; j < capacity; ++i, ++j)
out[i] = ab->entab[(int)out[j]];
luaL_addsize(&B, capacity - (outrevs + 1 - prefixs));
luaL_pushresult(&B);
return 1;
}
static int Lbase58_encode(lua_State *L) {
size_t len;
const char *s;
int isint;
lua_Integer v = lua_tointegerx(L, 1, &isint);
if (isint) return base58_iencode(L, v);
s = lua_tolstring(L, 1, &len);
if (s != NULL) return base58_sencode(L, s, len);
lua_pushfstring(L, "integer/string expected, got %s",
luaL_typename(L, 1));
return luaL_argerror(L, 1, lua_tostring(L, -1));
}
static int Lbase58_decode(lua_State *L) {
size_t len;
const char *s = luaL_checklstring(L, 1, &len);
const base58_Alphabet *ab = (const base58_Alphabet*)
luaL_checkudata(L, 2, BASE58_ALPHABET);
int i, j, prefixs = 0, capacity, outrevs, carry;
luaL_Buffer B;
unsigned char *out;
luaL_buffinit(L, &B);
for (; len > 0 && *s == ab->entab[0]; ++s, --len)
++prefixs;
capacity = prefixs + len * 733/1000 + 1; /* log58 / log256 */
out = (unsigned char*)luaL_prepbuffsize(&B, capacity);
memset(out, 0, capacity);
outrevs = capacity - 1;
for (; len > 0; ++s, --len) {
int outidx = capacity - 1;
carry = ab->detab[*s & (BYTE-1)];
luaL_argcheck(L, carry != ~0, 1, "invalid input");
for (; outidx > outrevs || carry != 0; --outidx) {
carry += out[outidx] * BASE;
out[outidx] = carry & (BYTE-1);
carry >>= CHAR_BIT;
}
outrevs = outidx;
}
for (i = 0, j = outrevs + 1 - prefixs; j < capacity; ++i, ++j)
out[i] = out[j];
luaL_addsize(&B, capacity - (outrevs + 1 - prefixs));
luaL_pushresult(&B);
return 1;
}
static int Lbase58_idecode(lua_State *L) {
size_t len;
const char *s = luaL_checklstring(L, 1, &len);
const base58_Alphabet *ab = (const base58_Alphabet*)
luaL_checkudata(L, 2, BASE58_ALPHABET);
lua_Integer v = 0;
for (; len > 0; ++s, --len)
{
int d = ab->detab[*s & (BYTE-1)];
luaL_argcheck(L, d != ~0, 1, "invalid input");
v = v*BASE + d;
}
lua_pushinteger(L, v);
return 1;
}
LUALIB_API int luaopen_base58(lua_State *L) {
luaL_Reg libs[] = {
{ "encode", Lbase58_encode },
{ "decode", Lbase58_decode },
{ "idecode", Lbase58_idecode },
{ "alphabet", Lbase58_alphabet },
{ NULL, NULL }
};
luaL_newmetatable(L, BASE58_ALPHABET);
luaL_newlib(L, libs);
#define X(name,v) \
lua_pushcfunction(L, Lbase58_alphabet); \
lua_pushliteral(L, v); \
lua_call(L, 1, 1); \
lua_setfield(L, -2, #name);
BASE58_ALPHABETS(X)
#undef X
return 1;
}
// cc: flags+='-ggdb -shared -undefined dynamic_lookup' output='base58.so'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment