-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(stdlib): add vim.base64 module (#25843)
Add base64 encode() and decode() functions to a vim.base64 module.
- Loading branch information
Showing
10 changed files
with
448 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- @meta | ||
|
||
--- Encode {str} using Base64. | ||
--- | ||
--- @param str string String to encode | ||
--- @return string Encoded string | ||
function vim.base64.encode(str) end | ||
|
||
--- Decode a Base64 encoded string. | ||
--- | ||
--- @param str string Base64 encoded string | ||
--- @return string Decoded string | ||
function vim.base64.decode(str) end |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
#include <assert.h> | ||
#include <stddef.h> | ||
#include <string.h> | ||
|
||
#include "nvim/base64.h" | ||
#include "nvim/memory.h" | ||
|
||
#ifdef HAVE_BE64TOH | ||
# include ENDIAN_INCLUDE_FILE | ||
#endif | ||
|
||
static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
|
||
// Indices are 1-based because we use 0 to indicate a letter that is not part of the alphabet | ||
static const uint8_t char_to_index[256] = { | ||
['A'] = 1, ['B'] = 2, ['C'] = 3, ['D'] = 4, ['E'] = 5, ['F'] = 6, ['G'] = 7, ['H'] = 8, | ||
['I'] = 9, ['J'] = 10, ['K'] = 11, ['L'] = 12, ['M'] = 13, ['N'] = 14, ['O'] = 15, ['P'] = 16, | ||
['Q'] = 17, ['R'] = 18, ['S'] = 19, ['T'] = 20, ['U'] = 21, ['V'] = 22, ['W'] = 23, ['X'] = 24, | ||
['Y'] = 25, ['Z'] = 26, ['a'] = 27, ['b'] = 28, ['c'] = 29, ['d'] = 30, ['e'] = 31, ['f'] = 32, | ||
['g'] = 33, ['h'] = 34, ['i'] = 35, ['j'] = 36, ['k'] = 37, ['l'] = 38, ['m'] = 39, ['n'] = 40, | ||
['o'] = 41, ['p'] = 42, ['q'] = 43, ['r'] = 44, ['s'] = 45, ['t'] = 46, ['u'] = 47, ['v'] = 48, | ||
['w'] = 49, ['x'] = 50, ['y'] = 51, ['z'] = 52, ['0'] = 53, ['1'] = 54, ['2'] = 55, ['3'] = 56, | ||
['4'] = 57, ['5'] = 58, ['6'] = 59, ['7'] = 60, ['8'] = 61, ['9'] = 62, ['+'] = 63, ['/'] = 64, | ||
}; | ||
|
||
#ifndef HAVE_BE64TOH | ||
static inline uint64_t htobe64(uint64_t host_64bits) | ||
{ | ||
# ifdef ORDER_BIG_ENDIAN | ||
return host_64bits; | ||
# else | ||
uint8_t *buf = (uint8_t *)&host_64bits; | ||
uint64_t ret = 0; | ||
for (size_t i = 8; i; i--) { | ||
ret |= ((uint64_t)buf[i - 1]) << ((8 - i) * 8); | ||
} | ||
return ret; | ||
# endif | ||
} | ||
|
||
static inline uint32_t htobe32(uint32_t host_32bits) | ||
{ | ||
# ifdef ORDER_BIG_ENDIAN | ||
return host_32bits; | ||
# else | ||
uint8_t *buf = (uint8_t *)&host_32bits; | ||
uint32_t ret = 0; | ||
for (size_t i = 4; i; i--) { | ||
ret |= ((uint32_t)buf[i - 1]) << ((4 - i) * 8); | ||
} | ||
return ret; | ||
# endif | ||
} | ||
#endif | ||
|
||
/// Encode a string using Base64. | ||
/// | ||
/// @param src String to encode | ||
/// @param src_len Length of the string | ||
/// @return Base64 encoded string | ||
char *base64_encode(const char *src, size_t src_len) | ||
{ | ||
assert(src != NULL); | ||
|
||
const size_t out_len = ((src_len + 2) / 3) * 4; | ||
char *dest = xmalloc(out_len + 1); | ||
|
||
size_t src_i = 0; | ||
size_t out_i = 0; | ||
|
||
const uint8_t *s = (const uint8_t *)src; | ||
|
||
// Read 8 bytes at a time as much as we can | ||
for (; src_i + 7 < src_len; src_i += 6) { | ||
uint64_t bits_h; | ||
memcpy(&bits_h, &s[src_i], sizeof(uint64_t)); | ||
const uint64_t bits_be = htobe64(bits_h); | ||
dest[out_i + 0] = alphabet[(bits_be >> 58) & 0x3F]; | ||
dest[out_i + 1] = alphabet[(bits_be >> 52) & 0x3F]; | ||
dest[out_i + 2] = alphabet[(bits_be >> 46) & 0x3F]; | ||
dest[out_i + 3] = alphabet[(bits_be >> 40) & 0x3F]; | ||
dest[out_i + 4] = alphabet[(bits_be >> 34) & 0x3F]; | ||
dest[out_i + 5] = alphabet[(bits_be >> 28) & 0x3F]; | ||
dest[out_i + 6] = alphabet[(bits_be >> 22) & 0x3F]; | ||
dest[out_i + 7] = alphabet[(bits_be >> 16) & 0x3F]; | ||
out_i += sizeof(uint64_t); | ||
} | ||
|
||
for (; src_i + 3 < src_len; src_i += 3) { | ||
uint32_t bits_h; | ||
memcpy(&bits_h, &s[src_i], sizeof(uint32_t)); | ||
const uint32_t bits_be = htobe32(bits_h); | ||
dest[out_i + 0] = alphabet[(bits_be >> 26) & 0x3F]; | ||
dest[out_i + 1] = alphabet[(bits_be >> 20) & 0x3F]; | ||
dest[out_i + 2] = alphabet[(bits_be >> 14) & 0x3F]; | ||
dest[out_i + 3] = alphabet[(bits_be >> 8) & 0x3F]; | ||
out_i += sizeof(uint32_t); | ||
} | ||
|
||
if (src_i + 2 < src_len) { | ||
dest[out_i + 0] = alphabet[s[src_i] >> 2]; | ||
dest[out_i + 1] = alphabet[((s[src_i] & 0x3) << 4) | (s[src_i + 1] >> 4)]; | ||
dest[out_i + 2] = alphabet[(s[src_i + 1] & 0xF) << 2 | (s[src_i + 2] >> 6)]; | ||
dest[out_i + 3] = alphabet[(s[src_i + 2] & 0x3F)]; | ||
out_i += 4; | ||
} else if (src_i + 1 < src_len) { | ||
dest[out_i + 0] = alphabet[s[src_i] >> 2]; | ||
dest[out_i + 1] = alphabet[((s[src_i] & 0x3) << 4) | (s[src_i + 1] >> 4)]; | ||
dest[out_i + 2] = alphabet[(s[src_i + 1] & 0xF) << 2]; | ||
out_i += 3; | ||
} else if (src_i < src_len) { | ||
dest[out_i + 0] = alphabet[s[src_i] >> 2]; | ||
dest[out_i + 1] = alphabet[(s[src_i] & 0x3) << 4]; | ||
out_i += 2; | ||
} | ||
|
||
for (; out_i < out_len; out_i++) { | ||
dest[out_i] = '='; | ||
} | ||
|
||
dest[out_len] = '\0'; | ||
|
||
return dest; | ||
} | ||
|
||
/// Decode a Base64 encoded string. | ||
/// | ||
/// @param src Base64 encoded string | ||
/// @param src_len Length of {src} | ||
/// @return Decoded string | ||
char *base64_decode(const char *src, size_t src_len) | ||
{ | ||
assert(src != NULL); | ||
|
||
char *dest = NULL; | ||
|
||
if (src_len % 4 != 0) { | ||
goto invalid; | ||
} | ||
|
||
size_t out_len = (src_len / 4) * 3; | ||
if (src_len >= 1 && src[src_len - 1] == '=') { | ||
out_len--; | ||
} | ||
if (src_len >= 2 && src[src_len - 2] == '=') { | ||
out_len--; | ||
} | ||
|
||
const uint8_t *s = (const uint8_t *)src; | ||
|
||
dest = xmalloc(out_len + 1); | ||
|
||
int acc = 0; | ||
int acc_len = 0; | ||
size_t out_i = 0; | ||
size_t src_i = 0; | ||
int leftover_i = -1; | ||
|
||
for (; src_i < src_len; src_i++) { | ||
const uint8_t c = s[src_i]; | ||
const uint8_t d = char_to_index[c]; | ||
if (d == 0) { | ||
if (c == '=') { | ||
leftover_i = (int)src_i; | ||
break; | ||
} | ||
goto invalid; | ||
} | ||
|
||
acc = ((acc << 6) & 0xFFF) + (d - 1); | ||
acc_len += 6; | ||
if (acc_len >= 8) { | ||
acc_len -= 8; | ||
dest[out_i] = (char)(acc >> acc_len); | ||
out_i += 1; | ||
} | ||
} | ||
|
||
if (acc_len > 4 || ((acc & ((1 << acc_len) - 1)) != 0)) { | ||
goto invalid; | ||
} | ||
|
||
if (leftover_i >= -1) { | ||
int padding_len = acc_len / 2; | ||
int padding_chars = 0; | ||
for (; (size_t)leftover_i < src_len; leftover_i++) { | ||
const uint8_t c = s[leftover_i]; | ||
if (c != '=') { | ||
goto invalid; | ||
} | ||
padding_chars += 1; | ||
} | ||
|
||
if (padding_chars != padding_len) { | ||
goto invalid; | ||
} | ||
} | ||
|
||
dest[out_len] = '\0'; | ||
|
||
return dest; | ||
|
||
invalid: | ||
if (dest) { | ||
xfree((void *)dest); | ||
} | ||
|
||
return NULL; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#ifndef NVIM_BASE64_H | ||
#define NVIM_BASE64_H | ||
|
||
#include <stddef.h> | ||
|
||
#ifdef INCLUDE_GENERATED_DECLARATIONS | ||
# include "base64.h.generated.h" | ||
#endif | ||
|
||
#endif // NVIM_BASE64_H |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
#include <assert.h> | ||
#include <lauxlib.h> | ||
#include <lua.h> | ||
|
||
#include "nvim/base64.h" | ||
#include "nvim/lua/base64.h" | ||
#include "nvim/memory.h" | ||
|
||
static int nlua_base64_encode(lua_State *L) | ||
{ | ||
if (lua_gettop(L) < 1) { | ||
return luaL_error(L, "Expected 1 argument"); | ||
} | ||
|
||
if (lua_type(L, 1) != LUA_TSTRING) { | ||
luaL_argerror(L, 1, "expected string"); | ||
} | ||
|
||
size_t src_len = 0; | ||
const char *src = lua_tolstring(L, 1, &src_len); | ||
|
||
const char *ret = base64_encode(src, src_len); | ||
assert(ret != NULL); | ||
lua_pushstring(L, ret); | ||
xfree((void *)ret); | ||
|
||
return 1; | ||
} | ||
|
||
static int nlua_base64_decode(lua_State *L) | ||
{ | ||
if (lua_gettop(L) < 1) { | ||
return luaL_error(L, "Expected 1 argument"); | ||
} | ||
|
||
if (lua_type(L, 1) != LUA_TSTRING) { | ||
luaL_argerror(L, 1, "expected string"); | ||
} | ||
|
||
size_t src_len = 0; | ||
const char *src = lua_tolstring(L, 1, &src_len); | ||
|
||
const char *ret = base64_decode(src, src_len); | ||
if (ret == NULL) { | ||
return luaL_error(L, "Invalid input"); | ||
} | ||
|
||
lua_pushstring(L, ret); | ||
xfree((void *)ret); | ||
|
||
return 1; | ||
} | ||
|
||
static const luaL_Reg base64_functions[] = { | ||
{ "encode", nlua_base64_encode }, | ||
{ "decode", nlua_base64_decode }, | ||
{ NULL, NULL }, | ||
}; | ||
|
||
int luaopen_base64(lua_State *L) | ||
{ | ||
lua_newtable(L); | ||
luaL_register(L, NULL, base64_functions); | ||
return 1; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#ifndef NVIM_LUA_BASE64_H | ||
#define NVIM_LUA_BASE64_H | ||
|
||
#include <lauxlib.h> | ||
#include <lua.h> | ||
#include <lualib.h> | ||
|
||
#ifdef INCLUDE_GENERATED_DECLARATIONS | ||
# include "lua/base64.h.generated.h" | ||
#endif | ||
|
||
#endif // NVIM_LUA_BASE64_H |
Oops, something went wrong.