Created
November 20, 2020 13:28
-
-
Save starwing/44a03312d2d89b24deb8a0ac216dc4d1 to your computer and use it in GitHub Desktop.
Lua Base58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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