Merge pull request #12912 from s-hadinger/berry_bytes_b64

Berry add base64 to bytes
This commit is contained in:
s-hadinger 2021-08-17 19:50:44 +02:00 committed by GitHub
commit ca36cd2238
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 1873 additions and 1607 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +1,36 @@
#include "be_constobj.h"
static be_define_const_map_slots(be_class_bytes_map) {
{ be_const_key(resize, 10), be_const_func(m_resize) },
{ be_const_key(asstring, 11), be_const_func(m_asstring) },
{ be_const_key(init, -1), be_const_func(m_init) },
{ be_const_key(seti, -1), be_const_func(m_set) },
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
{ be_const_key(item, 18), be_const_func(m_item) },
{ 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(dot_p, -1), be_const_index(0) },
{ be_const_key(seti, 6), be_const_func(m_set) },
{ be_const_key(get, 7), be_const_func(m_getu) },
{ be_const_key(size, 10), be_const_func(m_size) },
{ be_const_key(resize, 1), be_const_func(m_resize) },
{ be_const_key(opt_add, 22), be_const_func(m_merge) },
{ be_const_key(getbits, -1), be_const_closure(getbits_closure) },
{ be_const_key(fromstring, -1), be_const_func(m_fromstring) },
{ be_const_key(opt_add, -1), be_const_func(m_merge) },
{ 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(geti, 16), be_const_func(m_geti) },
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
{ 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(
be_class_bytes_map,
23
25
);
BE_EXPORT_VARIABLE be_define_const_class(

View File

@ -28,6 +28,198 @@ typedef struct buf_impl {
uint8_t buf[]; // the actual data
} 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
**
@ -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)
{
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);
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);
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);
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);
}
/*
* 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
*/
@ -973,6 +1228,8 @@ void be_load_byteslib(bvm *vm)
{ "tostring", m_tostring },
{ "asstring", m_asstring },
{ "fromstring", m_fromstring },
{ "tob64", m_tob64 },
{ "fromb64", m_fromb64 },
{ "add", m_add },
{ "get", m_getu },
{ "geti", m_geti },
@ -1006,6 +1263,8 @@ class be_class_bytes (scope: global, name: bytes) {
tostring, func(m_tostring)
asstring, func(m_asstring)
fromstring, func(m_fromstring)
tob64, func(m_tob64)
fromb64, func(m_fromb64)
add, func(m_add)
get, func(m_getu)
geti, func(m_geti)