mirror of https://github.com/arendst/Tasmota.git
Merge pull request #12912 from s-hadinger/berry_bytes_b64
Berry add base64 to bytes
This commit is contained in:
commit
ca36cd2238
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,34 +1,36 @@
|
||||||
#include "be_constobj.h"
|
#include "be_constobj.h"
|
||||||
|
|
||||||
static be_define_const_map_slots(be_class_bytes_map) {
|
static be_define_const_map_slots(be_class_bytes_map) {
|
||||||
{ be_const_key(resize, 10), be_const_func(m_resize) },
|
{ be_const_key(dot_p, -1), be_const_index(0) },
|
||||||
{ be_const_key(asstring, 11), be_const_func(m_asstring) },
|
{ be_const_key(seti, 6), be_const_func(m_set) },
|
||||||
{ be_const_key(init, -1), be_const_func(m_init) },
|
{ be_const_key(get, 7), be_const_func(m_getu) },
|
||||||
{ be_const_key(seti, -1), be_const_func(m_set) },
|
{ be_const_key(size, 10), be_const_func(m_size) },
|
||||||
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
|
{ be_const_key(resize, 1), be_const_func(m_resize) },
|
||||||
{ be_const_key(item, 18), be_const_func(m_item) },
|
{ be_const_key(opt_add, 22), be_const_func(m_merge) },
|
||||||
{ be_const_key(dot_p, 3), be_const_index(0) },
|
|
||||||
{ be_const_key(geti, -1), be_const_func(m_geti) },
|
|
||||||
{ be_const_key(opt_connect, -1), be_const_func(m_connect) },
|
|
||||||
{ be_const_key(tostring, -1), be_const_func(m_tostring) },
|
|
||||||
{ be_const_key(size, -1), be_const_func(m_size) },
|
|
||||||
{ be_const_key(getbits, -1), be_const_closure(getbits_closure) },
|
{ be_const_key(getbits, -1), be_const_closure(getbits_closure) },
|
||||||
{ be_const_key(fromstring, -1), be_const_func(m_fromstring) },
|
{ be_const_key(geti, 16), be_const_func(m_geti) },
|
||||||
{ be_const_key(opt_add, -1), be_const_func(m_merge) },
|
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
|
||||||
{ be_const_key(_buffer, 2), be_const_func(m_buffer) },
|
|
||||||
{ be_const_key(copy, 8), be_const_func(m_copy) },
|
|
||||||
{ be_const_key(get, -1), be_const_func(m_getu) },
|
|
||||||
{ be_const_key(set, -1), be_const_func(m_set) },
|
|
||||||
{ be_const_key(opt_eq, -1), be_const_func(m_equal) },
|
|
||||||
{ be_const_key(opt_neq, -1), be_const_func(m_nequal) },
|
|
||||||
{ be_const_key(clear, 16), be_const_func(m_clear) },
|
|
||||||
{ be_const_key(setbits, -1), be_const_closure(setbits_closure) },
|
|
||||||
{ be_const_key(add, -1), be_const_func(m_add) },
|
{ be_const_key(add, -1), be_const_func(m_add) },
|
||||||
|
{ be_const_key(fromb64, -1), be_const_func(m_fromb64) },
|
||||||
|
{ be_const_key(opt_neq, -1), be_const_func(m_nequal) },
|
||||||
|
{ be_const_key(set, -1), be_const_func(m_set) },
|
||||||
|
{ be_const_key(asstring, -1), be_const_func(m_asstring) },
|
||||||
|
{ be_const_key(copy, 3), be_const_func(m_copy) },
|
||||||
|
{ be_const_key(opt_eq, 2), be_const_func(m_equal) },
|
||||||
|
{ be_const_key(tob64, -1), be_const_func(m_tob64) },
|
||||||
|
{ be_const_key(setbits, 12), be_const_closure(setbits_closure) },
|
||||||
|
{ be_const_key(_buffer, -1), be_const_func(m_buffer) },
|
||||||
|
{ be_const_key(fromstring, 0), be_const_func(m_fromstring) },
|
||||||
|
{ be_const_key(tostring, 9), be_const_func(m_tostring) },
|
||||||
|
{ be_const_key(item, 8), be_const_func(m_item) },
|
||||||
|
{ be_const_key(init, 23), be_const_func(m_init) },
|
||||||
|
{ be_const_key(opt_connect, -1), be_const_func(m_connect) },
|
||||||
|
{ be_const_key(clear, -1), be_const_func(m_clear) },
|
||||||
};
|
};
|
||||||
|
|
||||||
static be_define_const_map(
|
static be_define_const_map(
|
||||||
be_class_bytes_map,
|
be_class_bytes_map,
|
||||||
23
|
25
|
||||||
);
|
);
|
||||||
|
|
||||||
BE_EXPORT_VARIABLE be_define_const_class(
|
BE_EXPORT_VARIABLE be_define_const_class(
|
||||||
|
|
|
@ -28,6 +28,198 @@ typedef struct buf_impl {
|
||||||
uint8_t buf[]; // the actual data
|
uint8_t buf[]; // the actual data
|
||||||
} buf_impl;
|
} buf_impl;
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
** Base64 lib from https://github.com/Densaugeo/base64_arduino
|
||||||
|
**
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
/* binary_to_base64:
|
||||||
|
* Description:
|
||||||
|
* Converts a single byte from a binary value to the corresponding base64 character
|
||||||
|
* Parameters:
|
||||||
|
* v - Byte to convert
|
||||||
|
* Returns:
|
||||||
|
* ascii code of base64 character. If byte is >= 64, then there is not corresponding base64 character
|
||||||
|
* and 255 is returned
|
||||||
|
*/
|
||||||
|
static unsigned char binary_to_base64(unsigned char v);
|
||||||
|
|
||||||
|
/* base64_to_binary:
|
||||||
|
* Description:
|
||||||
|
* Converts a single byte from a base64 character to the corresponding binary value
|
||||||
|
* Parameters:
|
||||||
|
* c - Base64 character (as ascii code)
|
||||||
|
* Returns:
|
||||||
|
* 6-bit binary value
|
||||||
|
*/
|
||||||
|
static unsigned char base64_to_binary(unsigned char c);
|
||||||
|
|
||||||
|
/* encode_base64_length:
|
||||||
|
* Description:
|
||||||
|
* Calculates length of base64 string needed for a given number of binary bytes
|
||||||
|
* Parameters:
|
||||||
|
* input_length - Amount of binary data in bytes
|
||||||
|
* Returns:
|
||||||
|
* Number of base64 characters needed to encode input_length bytes of binary data
|
||||||
|
*/
|
||||||
|
static unsigned int encode_base64_length(unsigned int input_length);
|
||||||
|
|
||||||
|
/* decode_base64_length:
|
||||||
|
* Description:
|
||||||
|
* Calculates number of bytes of binary data in a base64 string
|
||||||
|
* Parameters:
|
||||||
|
* input - Base64-encoded null-terminated string
|
||||||
|
* Returns:
|
||||||
|
* Number of bytes of binary data in input
|
||||||
|
*/
|
||||||
|
static unsigned int decode_base64_length(unsigned char input[]);
|
||||||
|
|
||||||
|
/* encode_base64:
|
||||||
|
* Description:
|
||||||
|
* Converts an array of bytes to a base64 null-terminated string
|
||||||
|
* Parameters:
|
||||||
|
* input - Pointer to input data
|
||||||
|
* input_length - Number of bytes to read from input pointer
|
||||||
|
* output - Pointer to output string. Null terminator will be added automatically
|
||||||
|
* Returns:
|
||||||
|
* Length of encoded string in bytes (not including null terminator)
|
||||||
|
*/
|
||||||
|
static unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]);
|
||||||
|
|
||||||
|
/* decode_base64:
|
||||||
|
* Description:
|
||||||
|
* Converts a base64 null-terminated string to an array of bytes
|
||||||
|
* Parameters:
|
||||||
|
* input - Pointer to input string
|
||||||
|
* output - Pointer to output array
|
||||||
|
* Returns:
|
||||||
|
* Number of bytes in the decoded binary
|
||||||
|
*/
|
||||||
|
static unsigned int decode_base64(unsigned char input[], unsigned char output[]);
|
||||||
|
|
||||||
|
static unsigned char binary_to_base64(unsigned char v) {
|
||||||
|
// Capital letters - 'A' is ascii 65 and base64 0
|
||||||
|
if(v < 26) return v + 'A';
|
||||||
|
|
||||||
|
// Lowercase letters - 'a' is ascii 97 and base64 26
|
||||||
|
if(v < 52) return v + 71;
|
||||||
|
|
||||||
|
// Digits - '0' is ascii 48 and base64 52
|
||||||
|
if(v < 62) return v - 4;
|
||||||
|
|
||||||
|
// '+' is ascii 43 and base64 62
|
||||||
|
if(v == 62) return '+';
|
||||||
|
|
||||||
|
// '/' is ascii 47 and base64 63
|
||||||
|
if(v == 63) return '/';
|
||||||
|
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char base64_to_binary(unsigned char c) {
|
||||||
|
// Capital letters - 'A' is ascii 65 and base64 0
|
||||||
|
if('A' <= c && c <= 'Z') return c - 'A';
|
||||||
|
|
||||||
|
// Lowercase letters - 'a' is ascii 97 and base64 26
|
||||||
|
if('a' <= c && c <= 'z') return c - 71;
|
||||||
|
|
||||||
|
// Digits - '0' is ascii 48 and base64 52
|
||||||
|
if('0' <= c && c <= '9') return c + 4;
|
||||||
|
|
||||||
|
// '+' is ascii 43 and base64 62
|
||||||
|
if(c == '+') return 62;
|
||||||
|
|
||||||
|
// '/' is ascii 47 and base64 63
|
||||||
|
if(c == '/') return 63;
|
||||||
|
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int encode_base64_length(unsigned int input_length) {
|
||||||
|
return (input_length + 2)/3*4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int decode_base64_length(unsigned char input[]) {
|
||||||
|
unsigned char *start = input;
|
||||||
|
|
||||||
|
while(base64_to_binary(input[0]) < 64) {
|
||||||
|
++input;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int input_length = input - start;
|
||||||
|
|
||||||
|
unsigned int output_length = input_length/4*3;
|
||||||
|
|
||||||
|
switch(input_length % 4) {
|
||||||
|
default: return output_length;
|
||||||
|
case 2: return output_length + 1;
|
||||||
|
case 3: return output_length + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) {
|
||||||
|
unsigned int full_sets = input_length/3;
|
||||||
|
|
||||||
|
// While there are still full sets of 24 bits...
|
||||||
|
for(unsigned int i = 0; i < full_sets; ++i) {
|
||||||
|
output[0] = binary_to_base64( input[0] >> 2);
|
||||||
|
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
|
||||||
|
output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6);
|
||||||
|
output[3] = binary_to_base64( input[2] & 0x3F);
|
||||||
|
|
||||||
|
input += 3;
|
||||||
|
output += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(input_length % 3) {
|
||||||
|
case 0:
|
||||||
|
output[0] = '\0';
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
output[0] = binary_to_base64( input[0] >> 2);
|
||||||
|
output[1] = binary_to_base64((input[0] & 0x03) << 4);
|
||||||
|
output[2] = '=';
|
||||||
|
output[3] = '=';
|
||||||
|
output[4] = '\0';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
output[0] = binary_to_base64( input[0] >> 2);
|
||||||
|
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
|
||||||
|
output[2] = binary_to_base64((input[1] & 0x0F) << 2);
|
||||||
|
output[3] = '=';
|
||||||
|
output[4] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encode_base64_length(input_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int decode_base64(unsigned char input[], unsigned char output[]) {
|
||||||
|
unsigned int output_length = decode_base64_length(input);
|
||||||
|
|
||||||
|
// While there are still full sets of 24 bits...
|
||||||
|
for(unsigned int i = 2; i < output_length; i += 3) {
|
||||||
|
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
|
||||||
|
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
|
||||||
|
output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]);
|
||||||
|
|
||||||
|
input += 4;
|
||||||
|
output += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(output_length % 3) {
|
||||||
|
case 1:
|
||||||
|
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
|
||||||
|
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_length;
|
||||||
|
}
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
** Buffer low-level implementation
|
** Buffer low-level implementation
|
||||||
**
|
**
|
||||||
|
@ -343,13 +535,26 @@ static size_t tohex(char * out, size_t outsz, const uint8_t * in, size_t insz) {
|
||||||
|
|
||||||
static int m_tostring(bvm *vm)
|
static int m_tostring(bvm *vm)
|
||||||
{
|
{
|
||||||
|
int argc = be_top(vm);
|
||||||
|
int max_len = 32; /* limit to 32 bytes by default */
|
||||||
|
int truncated = 0;
|
||||||
|
if (argc > 1 && be_isint(vm, 2)) {
|
||||||
|
max_len = be_toint(vm, 2); /* you can specify the len as second argument, or 0 for unlimited */
|
||||||
|
}
|
||||||
buf_impl * buf = bytes_check_data(vm, 0);
|
buf_impl * buf = bytes_check_data(vm, 0);
|
||||||
size_t len = buf->len;
|
size_t len = buf->len;
|
||||||
size_t hex_len = len * 2 + 5 + 2 + 2 + 1; /* reserve size for `bytes("")\0` - 9 chars */
|
if (max_len > 0 && len > max_len) {
|
||||||
|
len = max_len; /* limit output size */
|
||||||
|
truncated = 1;
|
||||||
|
}
|
||||||
|
size_t hex_len = len * 2 + 5 + 2 + 2 + 1 + truncated * 3; /* reserve size for `bytes("")\0` - 9 chars */
|
||||||
|
|
||||||
char * hex_out = be_pushbuffer(vm, hex_len);
|
char * hex_out = be_pushbuffer(vm, hex_len);
|
||||||
size_t l = be_strlcpy(hex_out, "bytes('", hex_len);
|
size_t l = be_strlcpy(hex_out, "bytes('", hex_len);
|
||||||
l += tohex(&hex_out[l], hex_len - l, buf_get_buf(buf), buf->len);
|
l += tohex(&hex_out[l], hex_len - l, buf_get_buf(buf), len);
|
||||||
|
if (truncated) {
|
||||||
|
l += be_strlcpy(&hex_out[l], "...", hex_len - l);
|
||||||
|
}
|
||||||
l += be_strlcpy(&hex_out[l], "')", hex_len - l);
|
l += be_strlcpy(&hex_out[l], "')", hex_len - l);
|
||||||
|
|
||||||
be_pushnstring(vm, hex_out, l); /* make escape string from buffer */
|
be_pushnstring(vm, hex_out, l); /* make escape string from buffer */
|
||||||
|
@ -699,6 +904,56 @@ static int m_nequal(bvm *vm)
|
||||||
return bytes_equal(vm, bfalse);
|
return bytes_equal(vm, bfalse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts bytes() to a base64 string
|
||||||
|
*
|
||||||
|
* Note: there are no line breaks inserted
|
||||||
|
*
|
||||||
|
* `b.tob64() -> string`
|
||||||
|
*/
|
||||||
|
static int m_tob64(bvm *vm)
|
||||||
|
{
|
||||||
|
buf_impl * buf = bytes_check_data(vm, 0);
|
||||||
|
size_t len = buf->len;
|
||||||
|
size_t b64_len = encode_base64_length(len) + 1; /* size of base64 encoded string for this binary length, add NULL terminator */
|
||||||
|
|
||||||
|
char * b64_out = be_pushbuffer(vm, b64_len);
|
||||||
|
size_t converted = encode_base64(buf_get_buf(buf), len, b64_out);
|
||||||
|
|
||||||
|
be_pushnstring(vm, b64_out, converted); /* make string from buffer */
|
||||||
|
be_remove(vm, -2); /* remove buffer */
|
||||||
|
be_return(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts base63 to bytes()
|
||||||
|
*
|
||||||
|
* `bytes().fromb64() -> bytes()`
|
||||||
|
*/
|
||||||
|
static int m_fromb64(bvm *vm)
|
||||||
|
{
|
||||||
|
int argc = be_top(vm);
|
||||||
|
if (argc >= 2 && be_isstring(vm, 2)) {
|
||||||
|
const char *s = be_tostring(vm, 2);
|
||||||
|
size_t len = be_strlen(vm, 2);
|
||||||
|
size_t bin_len = decode_base64_length(s); /* do a first pass to calculate the buffer size */
|
||||||
|
|
||||||
|
buf_impl * buf = bytes_check_data(vm, 0);
|
||||||
|
buf = bytes_resize(vm, buf, bin_len); /* resize if needed */
|
||||||
|
if (bin_len > buf->size) { /* avoid overflow */
|
||||||
|
be_raise(vm, "memory_error", "cannot allocate buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bin_len_final = decode_base64(s, buf_get_buf(buf)); /* decode */
|
||||||
|
buf->len = bin_len_final;
|
||||||
|
be_pop(vm, 1); /* remove arg to leave instance */
|
||||||
|
be_return(vm);
|
||||||
|
}
|
||||||
|
be_raise(vm, "type_error", "operand must be a string");
|
||||||
|
be_return_nil(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Advanced API
|
* Advanced API
|
||||||
*/
|
*/
|
||||||
|
@ -973,6 +1228,8 @@ void be_load_byteslib(bvm *vm)
|
||||||
{ "tostring", m_tostring },
|
{ "tostring", m_tostring },
|
||||||
{ "asstring", m_asstring },
|
{ "asstring", m_asstring },
|
||||||
{ "fromstring", m_fromstring },
|
{ "fromstring", m_fromstring },
|
||||||
|
{ "tob64", m_tob64 },
|
||||||
|
{ "fromb64", m_fromb64 },
|
||||||
{ "add", m_add },
|
{ "add", m_add },
|
||||||
{ "get", m_getu },
|
{ "get", m_getu },
|
||||||
{ "geti", m_geti },
|
{ "geti", m_geti },
|
||||||
|
@ -1006,6 +1263,8 @@ class be_class_bytes (scope: global, name: bytes) {
|
||||||
tostring, func(m_tostring)
|
tostring, func(m_tostring)
|
||||||
asstring, func(m_asstring)
|
asstring, func(m_asstring)
|
||||||
fromstring, func(m_fromstring)
|
fromstring, func(m_fromstring)
|
||||||
|
tob64, func(m_tob64)
|
||||||
|
fromb64, func(m_fromb64)
|
||||||
add, func(m_add)
|
add, func(m_add)
|
||||||
get, func(m_getu)
|
get, func(m_getu)
|
||||||
geti, func(m_geti)
|
geti, func(m_geti)
|
||||||
|
|
Loading…
Reference in New Issue