/******************************************************************** ** Copyright (c) 2018-2020 Guan Wenliang - Stephan Hadinger ** This file is part of the Berry default interpreter. ** skiars@qq.com, https://github.com/Skiars/berry ** See Copyright Notice in the LICENSE file or at ** https://github.com/Skiars/berry/blob/master/LICENSE ********************************************************************/ #include "be_object.h" #include "be_string.h" #include "be_strlib.h" #include "be_list.h" #include "be_func.h" #include "be_exec.h" #include "be_vm.h" #include "be_mem.h" #include "be_constobj.h" #include #include #include "be_byteslib.h" /******************************************************************** ** 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 ** ** Extracted from Tasmota SBuffer lib ********************************************************************/ // static inline uint8_t* buf_get_buf(buf_impl* attr) // { // return &attr->bufptr[0]; // } // shrink or increase. If increase, fill with zeores. Cannot go beyond `size` static void buf_set_len(buf_impl* attr, const size_t len) { int32_t old_len = attr->len; attr->len = ((int32_t)len <= attr->size) ? (int32_t)len : attr->size; if (old_len < attr->len) { memset((void*) &attr->bufptr[old_len], 0, attr->len - old_len); } } static size_t buf_add1(buf_impl* attr, const uint8_t data) // append 8 bits value { if (attr->len < attr->size) { // do we have room for 1 byte attr->bufptr[attr->len++] = data; } return attr->len; } static size_t buf_add2_le(buf_impl* attr, const uint16_t data) // append 16 bits value { if (attr->len < attr->size - 1) { // do we have room for 2 bytes attr->bufptr[attr->len++] = data; attr->bufptr[attr->len++] = data >> 8; } return attr->len; } static size_t buf_add2_be(buf_impl* attr, const uint16_t data) // append 16 bits value { if (attr->len < attr->size - 1) { // do we have room for 2 bytes attr->bufptr[attr->len++] = data >> 8; attr->bufptr[attr->len++] = data; } return attr->len; } static size_t buf_add4_le(buf_impl* attr, const uint32_t data) // append 32 bits value { if (attr->len < attr->size - 3) { // do we have room for 4 bytes attr->bufptr[attr->len++] = data; attr->bufptr[attr->len++] = data >> 8; attr->bufptr[attr->len++] = data >> 16; attr->bufptr[attr->len++] = data >> 24; } return attr->len; } size_t buf_add4_be(buf_impl* attr, const uint32_t data) // append 32 bits value { if (attr->len < attr->size - 3) { // do we have room for 4 bytes attr->bufptr[attr->len++] = data >> 24; attr->bufptr[attr->len++] = data >> 16; attr->bufptr[attr->len++] = data >> 8; attr->bufptr[attr->len++] = data; } return attr->len; } static size_t buf_add_buf(buf_impl* attr, buf_impl* attr2) { if (attr->len + attr2->len <= attr->size) { for (int32_t i = 0; i < attr2->len; i++) { attr->bufptr[attr->len++] = attr2->bufptr[i]; } } return attr->len; } static size_t buf_add_raw(buf_impl* attr, const void* buf_raw, int32_t len) { uint8_t *buf = (uint8_t*) buf_raw; if ((len > 0) && (attr->len + len <= attr->size)) { for (int32_t i = 0; i < len; i++) { attr->bufptr[attr->len++] = buf[i]; } } return attr->len; } static uint8_t buf_get1(buf_impl* attr, int offset) { if ((offset >= 0) && (offset < attr->len)) { return attr->bufptr[offset]; } return 0; } static void buf_set1(buf_impl* attr, size_t offset, uint8_t data) { if ((int32_t)offset < attr->len) { attr->bufptr[offset] = data; } } static void buf_set2_le(buf_impl* attr, size_t offset, uint16_t data) { if ((int32_t)offset + 1 < attr->len) { attr->bufptr[offset] = data & 0xFF; attr->bufptr[offset+1] = data >> 8; } } static void buf_set2_be(buf_impl* attr, size_t offset, uint16_t data) { if ((int32_t)offset + 1 < attr->len) { attr->bufptr[offset+1] = data & 0xFF; attr->bufptr[offset] = data >> 8; } } static uint16_t buf_get2_le(buf_impl* attr, size_t offset) { if ((int32_t)offset + 1 < attr->len) { return attr->bufptr[offset] | (attr->bufptr[offset+1] << 8); } return 0; } static uint16_t buf_get2_be(buf_impl* attr, size_t offset) { if ((int32_t)offset + 1 < attr->len) { return attr->bufptr[offset+1] | (attr->bufptr[offset] << 8); } return 0; } static uint32_t buf_get3_le(buf_impl* attr, size_t offset) { if ((int32_t)offset + 2 < attr->len) { return attr->bufptr[offset] | (attr->bufptr[offset+1] << 8) | (attr->bufptr[offset+2] << 16); } return 0; } static uint32_t buf_get3_be(buf_impl* attr, size_t offset) { if ((int32_t)offset + 2 < attr->len) { return attr->bufptr[offset+2] | (attr->bufptr[offset+1] << 8) | (attr->bufptr[offset] << 16); } return 0; } static void buf_set3_le(buf_impl* attr, size_t offset, uint32_t data) { if ((int32_t)offset + 2 < attr->len) { attr->bufptr[offset] = data & 0xFF; attr->bufptr[offset+1] = (data >> 8) & 0xFF; attr->bufptr[offset+2] = (data >> 16) & 0xFF; } } static void buf_set3_be(buf_impl* attr, size_t offset, uint32_t data) { if ((int32_t)offset + 2 < attr->len) { attr->bufptr[offset+2] = data & 0xFF; attr->bufptr[offset+1] = (data >> 8) & 0xFF; attr->bufptr[offset] = (data >> 16) & 0xFF; } } static void buf_set4_le(buf_impl* attr, size_t offset, uint32_t data) { if ((int32_t)offset + 3 < attr->len) { attr->bufptr[offset] = data & 0xFF; attr->bufptr[offset+1] = (data >> 8) & 0xFF; attr->bufptr[offset+2] = (data >> 16) & 0xFF; attr->bufptr[offset+3] = data >> 24; } } static void buf_set4_be(buf_impl* attr, size_t offset, uint32_t data) { if ((int32_t)offset + 3 < attr->len) { attr->bufptr[offset+3] = data & 0xFF; attr->bufptr[offset+2] = (data >> 8) & 0xFF; attr->bufptr[offset+1] = (data >> 16) & 0xFF; attr->bufptr[offset] = data >> 24; } } static uint32_t buf_get4_le(buf_impl* attr, size_t offset) { if ((int32_t)offset + 3 < attr->len) { return attr->bufptr[offset] | (attr->bufptr[offset+1] << 8) | (attr->bufptr[offset+2] << 16) | (attr->bufptr[offset+3] << 24); } return 0; } static uint32_t buf_get4_be(buf_impl* attr, size_t offset) { if ((int32_t)offset + 3 < attr->len) { return attr->bufptr[offset+3] | (attr->bufptr[offset+2] << 8) | (attr->bufptr[offset+1] << 16) | (attr->bufptr[offset] << 24); } return 0; } // nullptr accepted static bbool buf_equals(buf_impl* buf1, buf_impl* buf2) { if (buf1 == buf2) { return btrue; } if (!buf1 || !buf2) { return bfalse; } // at least one attr is not empty // we know that both buf1 and buf2 are non-null if (buf1->len != buf2->len) { return bfalse; } size_t len = buf1->len; if (!buf1->bufptr && !buf2->bufptr) { return btrue; } /* if both are null then considered equal */ if (!buf1->bufptr || !buf2->bufptr) { return bfalse; } /* if only one is null, then not equal */ /* here none of the pointer are null */ for (uint32_t i=0; i= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; } else if (chr >= 'a' && chr <= 'f') { rVal = chr + 10 - 'a'; } return rVal; } // does not check if there is enough room before hand, truncated if buffer too small static void buf_add_hex(buf_impl* attr, const char *hex, size_t len) { uint8_t val; for (; len > 1; len -= 2) { val = asc2byte(*hex++) << 4; val |= asc2byte(*hex++); buf_add1(attr, val); } } /******************************************************************** ** Wrapping into lib ********************************************************************/ /* if the bufptr is null, don't try to dereference and raise an exception instead */ static void check_ptr(bvm *vm, const buf_impl* attr) { if (!attr->bufptr) { be_raise(vm, "value_error", "operation not allowed on pointer"); } } static void check_ptr_modifiable(bvm *vm, const buf_impl* attr) { if (attr->solidified) { be_raise(vm, "value_error", BYTES_READ_ONLY_MESSAGE); } if (!attr->bufptr) { be_raise(vm, "value_error", "operation not allowed on pointer"); } } /* load instance attribute into a single structure, and store 'previous' values in order to later update only the changed ones */ /* stack item 1 must contain the instance */ buf_impl m_read_attributes(bvm *vm, int idx) { buf_impl attr; be_getmember(vm, idx, ".p"); attr.bufptr = attr.prev_bufptr = be_tocomptr(vm, -1); be_pop(vm, 1); be_getmember(vm, idx, ".len"); attr.len = attr.prev_len = be_toint(vm, -1); be_pop(vm, 1); be_getmember(vm, idx, ".size"); int32_t signed_size = be_toint(vm, -1); attr.fixed = bfalse; attr.mapped = bfalse; attr.solidified = bfalse; if (signed_size < 0) { if (signed_size == BYTES_SIZE_MAPPED) { attr.mapped = btrue; } if (signed_size == BYTES_SIZE_SOLIDIFIED) { attr.solidified = btrue; } signed_size = attr.len; attr.fixed = btrue; } attr.size = attr.prev_size = signed_size; be_pop(vm, 1); return attr; } static void m_assert_not_readlonly(bvm *vm, const buf_impl* attr) { if (attr->solidified) { be_raise(vm, "value_error", BYTES_READ_ONLY_MESSAGE); } } /* Write back attributes to the bytes instance, only if values changed after loading */ /* stack item 1 must contain the instance */ void m_write_attributes(bvm *vm, int rel_idx, const buf_impl * attr) { m_assert_not_readlonly(vm, attr); int idx = be_absindex(vm, rel_idx); if (attr->bufptr != attr->prev_bufptr) { be_pushcomptr(vm, attr->bufptr); be_setmember(vm, idx, ".p"); be_pop(vm, 1); } if (attr->len != attr->prev_len) { be_pushint(vm, attr->len); be_setmember(vm, idx, ".len"); be_pop(vm, 1); } int32_t new_size = attr->size; if (attr->mapped) { new_size = BYTES_SIZE_MAPPED; } else if (attr->fixed) { new_size = BYTES_SIZE_FIXED; } if (new_size != attr->prev_size) { be_pushint(vm, new_size); be_setmember(vm, idx, ".size"); be_pop(vm, 1); } } // buf_impl * bytes_realloc(bvm *vm, buf_impl *oldbuf, int32_t size) void bytes_realloc(bvm *vm, buf_impl * attr, size_t size) { m_assert_not_readlonly(vm, attr); if (!attr->fixed && size < 4) { size = 4; } if (size > vm->bytesmaxsize) { size = vm->bytesmaxsize; } size_t oldsize = attr->bufptr ? attr->size : 0; attr->bufptr = (uint8_t*) be_realloc(vm, attr->bufptr, oldsize, size); /* malloc */ attr->size = size; if (!attr->bufptr) { attr->len = 0; /* allocate a new buffer */ } } /* allocate a new `bytes` object with pre-allocated size */ static void bytes_new_object(bvm *vm, size_t size) { be_getbuiltin(vm, "bytes"); be_pushint(vm, size); be_call(vm, 1); be_pop(vm, 1); } /* * constructor for bytes() * Arg0 is always self * * Option 1: main use * Arg1: string - a string representing the bytes in HEX * * Option 2: * Arg1: string - a string representing the bytes in HEX * Arg2: int - pre-reserved buffer size. If negative, size is fixed and cannot be later changed * * Option 3: used by subclasses like ctypes * Arg1: int - pre-reserved buffer size. If negative, size is fixed and cannot be later changed * * Option 4: mapped buffer * Arg1: comptr - buffer address of the mapped buffer * Arg2: int - buffer size. Always fixed (negative or positive) * * */ static int m_init(bvm *vm) { int argc = be_top(vm); buf_impl attr = { 0, 0, NULL, 0, -1, NULL, bfalse, bfalse, bfalse }; /* initialize prev_values to invalid to force a write at the end */ /* size cannot be 0, len cannot be negative */ const char * hex_in = NULL; int32_t size_arg = 0; if (argc > 1 && be_isint(vm, 2)) { size_arg = be_toint(vm, 2); /* raw size arg, can be positive or negative */ } else if (argc > 2 && be_isint(vm, 3)) { size_arg = be_toint(vm, 3); /* raw size arg, can be positive or negative */ } if (argc > 1 && be_iscomptr(vm, 2)) { if (size_arg) { attr.len = (size_arg < 0) ? -size_arg : size_arg; attr.bufptr = be_tocomptr(vm, 2); attr.fixed = btrue; attr.mapped = btrue; m_write_attributes(vm, 1, &attr); /* write attributes back to instance */ be_return_nil(vm); } else { be_raise(vm, "value_error", "size is required"); } } if (size_arg == 0) { size_arg = BYTES_DEFAULT_SIZE; } /* if not specified, use default size */ /* compute actual size to be reserved */ if (size_arg >= 0) { size_arg += BYTES_HEADROOM; /* add automatic headroom to slightly overallocate */ if (size_arg > attr.size) { attr.size = size_arg; } } else { attr.size = -size_arg; /* set the size to a fixed negative value */ attr.fixed = btrue; } size_arg = attr.size; /* if arg1 is string, we convert hex */ if (argc > 1 && be_isstring(vm, 2)) { hex_in = be_tostring(vm, 2); if (hex_in) { size_arg = strlen(hex_in) / 2; /* allocate headroom */ } /* check if fixed size that we have the right size */ if (size_arg > attr.size) { if (attr.fixed) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } else { attr.size = size_arg; } } } /* allocate */ bytes_realloc(vm, &attr, attr.size); /* allocate new buffer */ if (!attr.bufptr) { be_throw(vm, BE_MALLOC_FAIL); } if (hex_in) { buf_add_hex(&attr, hex_in, strlen(hex_in)); } /* if fixed size, set len to size */ if (attr.fixed) { buf_set_len(&attr, attr.size); } m_write_attributes(vm, 1, &attr); /* write attributes back to instance */ be_return_nil(vm); } /* deallocate buffer */ static int m_deinit(bvm *vm) { buf_impl attr = m_read_attributes(vm, 1); if (attr.bufptr != NULL && !attr.mapped) { be_realloc(vm, attr.bufptr, attr.size, 0); } attr.size = 0; attr.len = 0; attr.bufptr = NULL; m_write_attributes(vm, 1, &attr); be_return_nil(vm); } /* grow or shrink to the exact value */ /* stack item 1 must contain the instance */ void _bytes_resize(bvm *vm, buf_impl * attr, size_t new_size) { bytes_realloc(vm, attr, new_size); if (!attr->bufptr) { be_throw(vm, BE_MALLOC_FAIL); } } /* grow if needed but don't shrink */ /* if grow, then add some headroom */ /* stack item 1 must contain the instance */ void bytes_resize(bvm *vm, buf_impl * attr, size_t new_size) { if (attr->mapped) { return; } /* if mapped, don't bother with allocation */ /* when resized to smaller, we introduce a new heurstic */ /* If the buffer is 64 bytes or smaller, don't shrink */ /* Shrink buffer only if target size is smaller than half the original size */ if (attr->size >= (int32_t)new_size) { /* enough room, consider if need to shrink */ if (attr->size <= 64) { return; } /* don't shrink if below 64 bytes */ if (attr->size < (int32_t)new_size * 2) { return; } } _bytes_resize(vm, attr, new_size); } buf_impl bytes_check_data(bvm *vm, size_t add_size) { buf_impl attr = m_read_attributes(vm, 1); /* check if the `size` is big enough */ if (attr.len + (int32_t)add_size > attr.size) { if (attr.fixed) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } /* it does not fit so we need to realocate the buffer */ bytes_resize(vm, &attr, attr.len + add_size); } return attr; } size_t be_bytes_tohex(char * out, size_t outsz, const uint8_t * in, size_t insz) { static const char * hex = "0123456789ABCDEF"; const uint8_t * pin = in; char * pout = out; for (; pin < in + insz; pout += 2, pin++) { pout[0] = hex[((*pin)>>4) & 0xF]; pout[1] = hex[ (*pin) & 0xF]; if (pout + 3 > out + outsz) { break; } /* check overflow */ } pout[0] = 0; /* terminating Nul char */ return pout - out; } static int m_tostring(bvm *vm) { int argc = be_top(vm); int32_t 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 attr = m_read_attributes(vm, 1); if (attr.bufptr) { /* pointer looks valid */ int32_t len = attr.len; 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 += be_bytes_tohex(&hex_out[l], hex_len - l, attr.bufptr, 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 */ be_remove(vm, -2); /* remove buffer */ } else { /* pointer is null, don't try to dereference it as it would crash */ be_pushstring(vm, "bytes()"); } be_return(vm); } static int m_tohex(bvm *vm) { buf_impl attr = m_read_attributes(vm, 1); if (attr.bufptr) { /* pointer looks valid */ int32_t len = attr.len; size_t hex_len = len * 2 + 1; char * hex_out = be_pushbuffer(vm, hex_len); size_t l = be_bytes_tohex(hex_out, hex_len, attr.bufptr, len); be_pushnstring(vm, hex_out, l); /* make escape string from buffer */ be_remove(vm, -2); /* remove buffer */ } else { /* pointer is null, don't try to dereference it as it would crash */ be_pushstring(vm, ""); } be_return(vm); } /* * Copy the buffer into a string without any changes */ static int m_asstring(bvm *vm) { buf_impl attr = bytes_check_data(vm, 0); check_ptr(vm, &attr); be_pushnstring(vm, (const char*) attr.bufptr, attr.len); be_return(vm); } static int m_fromstring(bvm *vm) { int argc = be_top(vm); if (argc >= 2 && be_isstring(vm, 2)) { const char *s = be_tostring(vm, 2); int32_t len = be_strlen(vm, 2); /* calling be_strlen to support null chars in string */ buf_impl attr = bytes_check_data(vm, 0); check_ptr_modifiable(vm, &attr); if (attr.fixed && attr.len != len) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } bytes_resize(vm, &attr, len); /* resize if needed */ if (len > attr.size) { len = attr.size; } /* avoid overflow */ memmove(attr.bufptr, s, len); attr.len = len; be_pop(vm, 1); /* remove arg to leave instance */ m_write_attributes(vm, 1, &attr); /* update attributes */ be_return(vm); } be_raise(vm, "type_error", "operand must be a string"); be_return_nil(vm); } /* * Add an int made of 1, 2 or 4 bytes, in little or big endian * `add(value:int[, size:int = 1]) -> instance` * * size: may be 1, 2, 4 (little endian), or -1, -2, -4 (big endian) * obvisouly -1 is idntical to 1 * size==0 does nothing */ static int m_add(bvm *vm) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 4); /* we reserve 4 bytes anyways */ check_ptr_modifiable(vm, &attr); if (attr.fixed) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } if (argc >= 2 && be_isint(vm, 2)) { int32_t v = be_toint(vm, 2); int vsize = 1; if (argc >= 3 && be_isint(vm, 3)) { vsize = be_toint(vm, 3); } switch (vsize) { case 0: break; case -1: /* fallback below */ case 1: buf_add1(&attr, v); break; case 2: buf_add2_le(&attr, v); break; case 4: buf_add4_le(&attr, v); break; case -2: buf_add2_be(&attr, v); break; case -4: buf_add4_be(&attr, v); break; default: be_raise(vm, "type_error", "size must be -4, -2, -1, 0, 1, 2 or 4."); } be_pop(vm, argc - 1); m_write_attributes(vm, 1, &attr); /* update attributes */ be_return(vm); } be_return_nil(vm); } /* * Get an int made of 1, 2 or 4 bytes, in little or big endian * `get(index:int[, size:int = 1]) -> int` * * size: may be 1, 2, 4 (little endian), or -1, -2, -4 (big endian) * obvisouly -1 is identical to 1 * 0 returns nil */ static int m_get(bvm *vm, bbool sign) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ check_ptr(vm, &attr); if (argc >=2 && be_isint(vm, 2)) { int32_t idx = be_toint(vm, 2); int vsize = 1; if (argc >= 3 && be_isint(vm, 3)) { vsize = be_toint(vm, 3); } if (idx < 0) { idx = attr.len + idx; /* if index is negative, count from end */ } if (idx < 0) { vsize = 0; /* if still negative, then invalid, return 0 */ } int ret = 0; switch (vsize) { case 0: break; case -1: /* fallback below */ case 1: ret = buf_get1(&attr, idx); if (sign) { ret = (int8_t)(uint8_t) ret; } break; case 2: ret = buf_get2_le(&attr, idx); if (sign) { ret = (int16_t)(uint16_t) ret; } break; case 3: ret = buf_get3_le(&attr, idx); if (sign & (ret & 0x800000)) { ret = ret | 0xFF000000; } break; case 4: ret = buf_get4_le(&attr, idx); break; case -2: ret = buf_get2_be(&attr, idx); if (sign) { ret = (int16_t)(uint16_t) ret; } break; case -3: ret = buf_get3_be(&attr, idx); if (sign & (ret & 0x800000)) { ret = ret | 0xFF000000; } break; case -4: ret = buf_get4_be(&attr, idx); break; default: be_raise(vm, "type_error", "size must be -4, -3, -2, -1, 0, 1, 2, 3 or 4."); } be_pop(vm, argc - 1); be_pushint(vm, ret); be_return(vm); } be_return_nil(vm); } /* * Get a float (32 bits) * `getfloat(index:int [, big_endian:bool]) -> real` */ static int m_getfloat(bvm *vm) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ check_ptr(vm, &attr); if (argc >=2 && be_isint(vm, 2)) { int32_t idx = be_toint(vm, 2); float ret_f = 0; if (idx < 0) { idx = attr.len + idx; /* if index is negative, count from end */ } if (idx >= 0) { bbool be = bfalse; /* little endian? */ if (argc >= 3) { be = be_tobool(vm, 3); } int32_t ret_i = be ? buf_get4_be(&attr, idx) : buf_get4_le(&attr, idx); ret_f = *(float*) &ret_i; } be_pop(vm, argc - 1); be_pushreal(vm, ret_f); be_return(vm); } be_return_nil(vm); } /* signed int */ static int m_geti(bvm *vm) { return m_get(vm, 1); } /* unsigned int */ static int m_getu(bvm *vm) { return m_get(vm, 0); } /* * Set an int made of 1, 2 or 4 bytes, in little or big endian * `set(index:int, value:int[, size:int = 1]) -> nil` * * size: may be 1, 2, 4 (little endian), or -1, -2, -4 (big endian) * obvisouly -1 is identical to 1 * 0 returns nil */ static int m_set(bvm *vm) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ check_ptr_modifiable(vm, &attr); if (argc >=3 && be_isint(vm, 2) && be_isint(vm, 3)) { int32_t idx = be_toint(vm, 2); int32_t value = be_toint(vm, 3); int vsize = 1; if (argc >= 4 && be_isint(vm, 4)) { vsize = be_toint(vm, 4); } if (idx < 0) { idx = attr.len + idx; /* if index is negative, count from end */ } if (idx < 0) { vsize = 0; /* if still negative, then invalid, do nothing */ } switch (vsize) { case 0: break; case -1: /* fallback below */ case 1: buf_set1(&attr, idx, value); break; case 2: buf_set2_le(&attr, idx, value); break; case 3: buf_set3_le(&attr, idx, value); break; case 4: buf_set4_le(&attr, idx, value); break; case -2: buf_set2_be(&attr, idx, value); break; case -3: buf_set3_be(&attr, idx, value); break; case -4: buf_set4_be(&attr, idx, value); break; default: be_raise(vm, "type_error", "size must be -4, -3, -2, -1, 0, 1, 2, 3 or 4."); } be_pop(vm, argc - 1); // m_write_attributes(vm, 1, &attr); /* update attributes */ be_return_nil(vm); } be_return_nil(vm); } /* * Set a 32 bits float * `setfloat(index:int, value:real or int [, big_endian:bool]) -> nil` * */ static int m_setfloat(bvm *vm) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ check_ptr_modifiable(vm, &attr); if (argc >=3 && be_isint(vm, 2) && (be_isint(vm, 3) || be_isreal(vm, 3))) { int32_t idx = be_toint(vm, 2); if (idx < 0) { idx = attr.len + idx; /* if index is negative, count from end */ } if (idx >= 0) { float val_f = (float) be_toreal(vm, 3); int32_t* val_i = (int32_t*) &val_f; bbool be = bfalse; if (argc >= 4) { be = be_tobool(vm, 4); } if (be) { buf_set4_be(&attr, idx, *val_i); } else { buf_set4_le(&attr, idx, *val_i); } be_pop(vm, argc - 1); // m_write_attributes(vm, 1, &attr); /* update attributes */ } be_return_nil(vm); } be_return_nil(vm); } /* * Add a 32 bits float * `addfloat(value:real or int [, big_endian:bool]) -> instance` * */ static int m_addfloat(bvm *vm) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 4); /* we reserve 4 bytes anyways */ check_ptr_modifiable(vm, &attr); if (attr.fixed) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } if (argc >=2 && (be_isint(vm, 2) || be_isreal(vm, 2))) { float val_f = (float) be_toreal(vm, 2); int32_t* val_i = (int32_t*) &val_f; bbool be = bfalse; if (argc >= 3) { be = be_tobool(vm, 3); } if (be) { buf_add4_be(&attr, *val_i); } else { buf_add4_le(&attr, *val_i); } be_pop(vm, argc - 1); m_write_attributes(vm, 1, &attr); /* update attributes */ be_return(vm); } be_return_nil(vm); } /* * Fills a buffer with another buffer. * * This is meant to be very flexible and avoid loops * * `setbytes(index:int, fill:bytes [, from:int, len:int]) -> nil` * */ static int m_setbytes(bvm *vm) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ check_ptr_modifiable(vm, &attr); if (argc >=3 && be_isint(vm, 2) && (be_isbytes(vm, 3))) { int32_t idx = be_toint(vm, 2); size_t from_len_total; const uint8_t* buf_ptr = (const uint8_t*) be_tobytes(vm, 3, &from_len_total); if (idx < 0) { idx = attr.len + idx; /* if index is negative, count from end */ } if (idx < 0) { idx = 0; } /* if still negative, start from offset 0 */ if (idx >= attr.len) { idx = attr.len; } int32_t from_byte = 0; if (argc >= 4 && be_isint(vm, 4)) { from_byte = be_toint(vm, 4); if (from_byte < 0) { from_byte = 0; } if ((size_t)from_byte >= from_len_total) { from_byte = from_len_total; } } int32_t from_len = from_len_total - from_byte; if (argc >= 5 && be_isint(vm, 5)) { from_len = be_toint(vm, 5); if (from_len < 0) { from_len = 0; } if (from_len >= (int32_t)from_len_total) { from_len = from_len_total; } } if (idx + from_len >= attr.len) { from_len = attr.len - idx; } // all parameters ok if (from_len > 0) { memmove(attr.bufptr + idx, buf_ptr + from_byte, from_len); } } be_return_nil(vm); } /* * Reverses in-place a sub-buffer composed of groups of n-bytes packets * * This is useful for pixel manipulation when displaying RGB pixels * * `reverse([index:int, len:int, grouplen:int]) -> self` * */ static int m_reverse(bvm *vm) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ check_ptr_modifiable(vm, &attr); int32_t idx = 0; /* start from index 0 */ int32_t len = attr.len; /* entire len */ int32_t grouplen = 1; /* default to 1-byte group */ if (argc >= 2 && be_isint(vm, 2)) { idx = be_toint(vm, 2); if (idx < 0) { idx = attr.len + idx; } /* if negative, count from end */ if (idx < 0) { idx = 0; } /* railguards */ if (idx > attr.len) { idx = attr.len; } } if (argc >= 3 && be_isint(vm, 3)) { len = be_toint(vm, 3); if (len < 0) { len = attr.len - idx; } /* negative len means */ } if (idx + len >= attr.len) { len = attr.len - idx; } // truncate len to multiple of grouplen if (argc >= 4 && be_isint(vm, 4)) { grouplen = be_toint(vm, 4); if (grouplen <= 0) { grouplen = 1; } } len = len - (len % grouplen); // apply reverse if (len > 0) { if (grouplen == 1) { /* fast version if simple byte inversion */ for (int32_t i = idx, j = idx + len -1; i < j; i++, j--) { uint8_t temp = attr.bufptr[i]; attr.bufptr[i] = attr.bufptr[j]; attr.bufptr[j] = temp; } } else { for (int32_t i = idx, j = idx + len - grouplen; i < j; i += grouplen, j -= grouplen) { for (int32_t k = 0; k < grouplen; k++) { uint8_t temp = attr.bufptr[i+k]; attr.bufptr[i+k] = attr.bufptr[j+k]; attr.bufptr[j+k] = temp; } } } } be_pushvalue(vm, 1); /* push bytes object */ be_return(vm); } static int m_setitem(bvm *vm) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ check_ptr_modifiable(vm, &attr); if (argc >=3 && be_isint(vm, 2) && be_isint(vm, 3)) { int index = be_toint(vm, 2); int val = be_toint(vm, 3); if (index < 0) { index += attr.len; /* if index is negative, count from end */ } if (index >= 0 && index < attr.len) { buf_set1(&attr, index, val); // m_write_attributes(vm, 1, &attr); /* update attributes */ be_return_nil(vm); } } be_raise(vm, "index_error", "bytes index out of range or value non int"); be_return_nil(vm); } static int m_item(bvm *vm) { int argc = be_top(vm); buf_impl attr = bytes_check_data(vm, 0); /* we reserve 4 bytes anyways */ check_ptr(vm, &attr); if (argc >=2 && be_isint(vm, 2)) { /* single byte */ int index = be_toint(vm,2); if (index < 0) { index += attr.len; } if (index >= 0 && index < attr.len) { be_pushint(vm, buf_get1(&attr, index)); be_return(vm); } } if (argc >= 2 && be_isinstance(vm, 2)) { const char *cname = be_classname(vm, 2); if (!strcmp(cname, "range")) { bint lower, upper; bint size = attr.len; /* get index range */ be_getmember(vm, 2, "__lower__"); lower = be_toint(vm, -1); be_pop(vm, 1); be_getmember(vm, 2, "__upper__"); upper = be_toint(vm, -1); be_pop(vm, 1); /* handle negative limits */ if (upper < 0) { upper += attr.len; } if (lower < 0) { lower += attr.len; } /* protection scope */ upper = upper < size ? upper : size - 1; lower = lower < 0 ? 0 : lower; /* construction result list instance */ bytes_new_object(vm, upper > lower ? upper-lower : 0); buf_impl attr2 = m_read_attributes(vm, -1); for (; lower <= upper; ++lower) { buf_add1(&attr2, attr.bufptr[lower]); } m_write_attributes(vm, -1, &attr2); /* update instance */ be_return(vm); } } be_raise(vm, "index_error", "bytes index out of range"); be_return_nil(vm); } static int m_size(bvm *vm) { buf_impl attr = m_read_attributes(vm, 1); be_pushint(vm, attr.len); be_return(vm); } static int m_tobool(bvm *vm) { buf_impl attr = m_read_attributes(vm, 1); be_pushbool(vm, attr.len > 0 ? 1 : 0); be_return(vm); } static int m_resize(bvm *vm) { int argc = be_top(vm); buf_impl attr = m_read_attributes(vm, 1); check_ptr_modifiable(vm, &attr); if (argc <= 1 || !be_isint(vm, 2)) { be_raise(vm, "type_error", "size must be of type 'int'"); } int new_len = be_toint(vm, 2); if (new_len < 0) { new_len = 0; } if (attr.fixed && attr.len != new_len) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } bytes_resize(vm, &attr, new_len); buf_set_len(&attr, new_len); be_pop(vm, 1); m_write_attributes(vm, 1, &attr); /* update instance */ be_return(vm); } static int m_clear(bvm *vm) { buf_impl attr = m_read_attributes(vm, 1); check_ptr_modifiable(vm, &attr); if (attr.fixed) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } attr.len = 0; m_write_attributes(vm, 1, &attr); /* update instance */ be_return_nil(vm); } static int m_merge(bvm *vm) { int argc = be_top(vm); buf_impl attr = m_read_attributes(vm, 1); /* no resize yet */ check_ptr(vm, &attr); if (argc >= 2 && (be_isbytes(vm, 2) || be_isstring(vm, 2))) { const uint8_t * buf; int32_t buf_len; if (be_isbytes(vm, 2)) { buf_impl attr2 = m_read_attributes(vm, 2); check_ptr(vm, &attr2); buf = attr2.bufptr; buf_len = attr2.len; } else { buf = (const uint8_t *)be_tostring(vm, 2); buf_len = strlen((const char *)buf); } /* allocate new object */ bytes_new_object(vm, attr.len + buf_len); buf_impl attr3 = m_read_attributes(vm, -1); check_ptr(vm, &attr3); buf_add_buf(&attr3, &attr); buf_add_raw(&attr3, buf, buf_len); m_write_attributes(vm, -1, &attr3); /* update instance */ be_return(vm); /* return self */ } be_raise(vm, "type_error", "operand must be bytes"); be_return_nil(vm); /* return self */ } static int m_copy(bvm *vm) { buf_impl attr = m_read_attributes(vm, 1); check_ptr(vm, &attr); bytes_new_object(vm, attr.len); buf_impl attr2 = m_read_attributes(vm, -1); check_ptr(vm, &attr2); buf_add_buf(&attr2, &attr); m_write_attributes(vm, -1, &attr2); /* update instance */ be_return(vm); /* return self */ } /* accept bytes or int as operand */ static int m_connect(bvm *vm) { int argc = be_top(vm); buf_impl attr = m_read_attributes(vm, 1); check_ptr_modifiable(vm, &attr); if (attr.fixed) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } if (argc >= 2 && (be_isbytes(vm, 2) || be_isint(vm, 2) || be_isstring(vm, 2) || be_isnil(vm, 2))) { if (be_isnil(vm, 2)) { // do nothing } else if (be_isint(vm, 2)) { bytes_resize(vm, &attr, attr.len + 1); /* resize */ buf_add1(&attr, be_toint(vm, 2)); m_write_attributes(vm, 1, &attr); /* update instance */ } else if (be_isstring(vm, 2)) { const char *str = be_tostring(vm, 2); size_t str_len = strlen(str); if (str_len > 0) { bytes_resize(vm, &attr, attr.len + str_len); /* resize */ buf_add_raw(&attr, str, str_len); m_write_attributes(vm, 1, &attr); /* update instance */ } } else { buf_impl attr2 = m_read_attributes(vm, 2); check_ptr(vm, &attr2); bytes_resize(vm, &attr, attr.len + attr2.len); /* resize buf1 for total size */ buf_add_buf(&attr, &attr2); m_write_attributes(vm, 1, &attr); /* update instance */ } be_pushvalue(vm, 1); be_return(vm); /* return self */ } be_raise(vm, "type_error", "operand must be bytes or int or string"); be_return_nil(vm); /* return self */ } static int bytes_equal(bvm *vm, bbool iseq) { bbool ret; buf_impl attr1 = m_read_attributes(vm, 1); if (!be_isbytes(vm, 2)) { ret = !iseq; } else { buf_impl attr2 = m_read_attributes(vm, 2); if (buf_equals(&attr1, &attr2)) { ret = iseq; } else { ret = !iseq; } } be_pushbool(vm, ret); be_return(vm); } static int m_equal(bvm *vm) { return bytes_equal(vm, btrue); } 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 attr = m_read_attributes(vm, 1); check_ptr(vm, &attr); int32_t len = attr.len; int32_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(attr.bufptr, len, (unsigned char*)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); int32_t bin_len = decode_base64_length((unsigned char*)s); /* do a first pass to calculate the buffer size */ buf_impl attr = m_read_attributes(vm, 1); check_ptr_modifiable(vm, &attr); if (attr.fixed && attr.len != bin_len) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } bytes_resize(vm, &attr, bin_len); /* resize if needed */ if (bin_len > attr.size) { /* avoid overflow */ be_raise(vm, "memory_error", "cannot allocate buffer"); } int32_t bin_len_final = decode_base64((unsigned char*)s, attr.bufptr); /* decode */ attr.len = bin_len_final; be_pop(vm, 1); /* remove arg to leave instance */ m_write_attributes(vm, 1, &attr); /* update instance */ be_return(vm); } be_raise(vm, "type_error", "operand must be a string"); be_return_nil(vm); } /* * Converts hex to bytes() * * `bytes().fromhexx() -> bytes()` */ static int m_fromhex(bvm *vm) { int argc = be_top(vm); if (argc >= 2 && be_isstring(vm, 2)) { int32_t from = 0; // skip x chars if (argc >= 3 && be_isint(vm, 3)) { from = be_toint(vm, 3); } const char *s = be_tostring(vm, 2); int32_t s_len = strlen(s); if (from < 0) { from = 0; } if (from > s_len) { from = s_len; } int32_t bin_len = (s_len - from) / 2; buf_impl attr = m_read_attributes(vm, 1); check_ptr_modifiable(vm, &attr); if (attr.fixed && attr.len != bin_len) { be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); } bytes_resize(vm, &attr, bin_len); /* resize if needed */ if (bin_len > attr.size) { /* avoid overflow */ be_raise(vm, "memory_error", "cannot allocate buffer"); } attr.len = 0; buf_add_hex(&attr, s + from, s_len - from); be_pop(vm, 1); /* remove arg to leave instance */ m_write_attributes(vm, 1, &attr); /* update instance */ be_pop(vm, be_top(vm) - 1); /* leave instance on stack */ be_return(vm); } be_raise(vm, "type_error", "operand must be a string"); be_return_nil(vm); } /* * Advanced API */ /* * Retrieve the memory address of the raw buffer * to be used in C functions. * * Note: the address is guaranteed not to move unless you * resize the buffer * * `_buffer() -> comptr` */ static int m_buffer(bvm *vm) { buf_impl attr = m_read_attributes(vm, 1); be_pushcomptr(vm, attr.bufptr); be_return(vm); } /* * Returns `btrue` if the buffer is mapped to memory * or `bfalse` if memory was allocated by us. * * `ismapped() -> bool` */ static int m_is_mapped(bvm *vm) { buf_impl attr = m_read_attributes(vm, 1); bbool mapped = (attr.mapped || (attr.bufptr == NULL)); be_pushbool(vm, mapped); be_return(vm); } /* * Returns `btrue` if the buffer is solidified and read only * * `isreadonly() -> bool` */ static int m_is_readonly(bvm *vm) { buf_impl attr = m_read_attributes(vm, 1); be_pushbool(vm, attr.solidified); be_return(vm); } /* * Change the pointer to a mapped buffer. * * This call does nothing if the buffer is not mapped (i.e. memory is managed externally) * * It is typically used to reuse existing Berry object and avoid a complete reallocation * * `_change_buffer(comptr) -> comptr` */ static int m_change_buffer(bvm *vm) { int argc = be_top(vm); if (argc >= 2 && be_iscomptr(vm, 2)) { buf_impl attr = m_read_attributes(vm, 1); if (attr.solidified) { be_raise(vm, "value_error", BYTES_READ_ONLY_MESSAGE); } if (!attr.mapped) { be_raise(vm, "type_error", "bytes() object must be mapped"); } attr.bufptr = be_tocomptr(vm, 2); m_write_attributes(vm, 1, &attr); /* write attributes back to instance */ be_pushcomptr(vm, attr.bufptr); be_return(vm); } be_raise(vm, "type_error", "operand must be a comptr"); be_return_nil(vm); } /* * External API */ BERRY_API void * be_pushbytes(bvm *vm, const void * bytes, size_t len) { bytes_new_object(vm, len); buf_impl attr = m_read_attributes(vm, -1); check_ptr(vm, &attr); if ((int32_t)len > attr.size) { len = attr.size; } /* double check if the buffer allocated was smaller */ if (bytes) { /* if bytes is null, buffer is filled with zeros */ memmove((void*)attr.bufptr, bytes, len); } else { memset((void*)attr.bufptr, 0, len); } attr.len = len; m_write_attributes(vm, -1, &attr); /* update instance */ /* bytes instance is on top of stack */ return (void*)attr.bufptr; } BERRY_API const void *be_tobytes(bvm *vm, int rel_index, size_t *len) { int index = be_absindex(vm, rel_index); if (be_isbytes(vm, index)) { buf_impl attr = m_read_attributes(vm, index); check_ptr(vm, &attr); if (len) { *len = attr.len; } return (void*) attr.bufptr; } if (len) { *len = 0; } return NULL; } BERRY_API bbool be_isbytes(bvm *vm, int rel_index) { bbool ret = bfalse; int index = be_absindex(vm, rel_index); if (be_isinstance(vm, index)) { be_getbuiltin(vm, "bytes"); if (be_isderived(vm, index)) { ret = btrue; } be_pop(vm, 1); } return ret; } /* Helper code to compile bytecode class Bytes : bytes #------------------------------------------------------------- #- 'getbits' function #- #- Reads a bit-field in a `bytes()` object #- #- Input: #- offset_bits (int): bit number to start reading from (0 = LSB) #- len_bits (int): how many bits to read #- Output: #- valuer (int) #-------------------------------------------------------------# def getbits(offset_bits, len_bits) if len_bits <= 0 || len_bits > 32 raise "value_error", "length in bits must be between 0 and 32" end var ret = 0 var offset_bytes = offset_bits >> 3 offset_bits = offset_bits % 8 var bit_shift = 0 #- bit number to write to -# while (len_bits > 0) var block_bits = 8 - offset_bits # how many bits to read in the current block (block = byte) -# if block_bits > len_bits block_bits = len_bits end var mask = ( (1<> offset_bits) << bit_shift) #- move the input window -# bit_shift += block_bits len_bits -= block_bits offset_bits = 0 #- start at full next byte -# offset_bytes += 1 end return ret end #------------------------------------------------------------- #- 'setbits' function #- #- Writes a bit-field in a `bytes()` object #- #- Input: #- offset_bits (int): bit number to start writing to (0 = LSB) #- len_bits (int): how many bits to write #- val (int): value to set #-------------------------------------------------------------# def setbits(offset_bits, len_bits, val) if len_bits < 0 || len_bits > 32 raise "value_error", "length in bits must be between 0 and 32" end val = int(val) #- convert bool or others to int -# var offset_bytes = offset_bits >> 3 offset_bits = offset_bits % 8 while (len_bits > 0) var block_bits = 8 - offset_bits #- how many bits to write in the current block (block = byte) -# if block_bits > len_bits block_bits = len_bits end var mask_val = (1<>= block_bits len_bits -= block_bits offset_bits = 0 #- start at full next byte -# offset_bytes += 1 end return self end end */ /******************************************************************** ** Solidified function: getbits ********************************************************************/ be_local_closure(getbits, /* name */ be_nested_proto( 9, /* nstack */ 3, /* argc */ 0, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ ( &(const bvalue[ 5]) { /* constants */ /* K0 */ be_const_int(0), /* K1 */ be_nested_str(value_error), /* K2 */ be_nested_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032), /* K3 */ be_const_int(3), /* K4 */ be_const_int(1), }), &be_const_str_getbits, &be_const_str_solidified, ( &(const binstruction[32]) { /* code */ 0x180C0500, // 0000 LE R3 R2 K0 0x740E0002, // 0001 JMPT R3 #0005 0x540E001F, // 0002 LDINT R3 32 0x240C0403, // 0003 GT R3 R2 R3 0x780E0000, // 0004 JMPF R3 #0006 0xB0060302, // 0005 RAISE 1 K1 K2 0x580C0000, // 0006 LDCONST R3 K0 0x3C100303, // 0007 SHR R4 R1 K3 0x54160007, // 0008 LDINT R5 8 0x10040205, // 0009 MOD R1 R1 R5 0x58140000, // 000A LDCONST R5 K0 0x24180500, // 000B GT R6 R2 K0 0x781A0011, // 000C JMPF R6 #001F 0x541A0007, // 000D LDINT R6 8 0x04180C01, // 000E SUB R6 R6 R1 0x241C0C02, // 000F GT R7 R6 R2 0x781E0000, // 0010 JMPF R7 #0012 0x5C180400, // 0011 MOVE R6 R2 0x381E0806, // 0012 SHL R7 K4 R6 0x041C0F04, // 0013 SUB R7 R7 K4 0x381C0E01, // 0014 SHL R7 R7 R1 0x94200004, // 0015 GETIDX R8 R0 R4 0x2C201007, // 0016 AND R8 R8 R7 0x3C201001, // 0017 SHR R8 R8 R1 0x38201005, // 0018 SHL R8 R8 R5 0x300C0608, // 0019 OR R3 R3 R8 0x00140A06, // 001A ADD R5 R5 R6 0x04080406, // 001B SUB R2 R2 R6 0x58040000, // 001C LDCONST R1 K0 0x00100904, // 001D ADD R4 R4 K4 0x7001FFEB, // 001E JMP #000B 0x80040600, // 001F RET 1 R3 }) ) ); /*******************************************************************/ /******************************************************************** ** Solidified function: setbits ********************************************************************/ be_local_closure(setbits, /* name */ be_nested_proto( 10, /* nstack */ 4, /* argc */ 0, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ ( &(const bvalue[ 5]) { /* constants */ /* K0 */ be_const_int(0), /* K1 */ be_nested_str(value_error), /* K2 */ be_nested_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032), /* K3 */ be_const_int(3), /* K4 */ be_const_int(1), }), &be_const_str_setbits, &be_const_str_solidified, ( &(const binstruction[37]) { /* code */ 0x14100500, // 0000 LT R4 R2 K0 0x74120002, // 0001 JMPT R4 #0005 0x5412001F, // 0002 LDINT R4 32 0x24100404, // 0003 GT R4 R2 R4 0x78120000, // 0004 JMPF R4 #0006 0xB0060302, // 0005 RAISE 1 K1 K2 0x60100009, // 0006 GETGBL R4 G9 0x5C140600, // 0007 MOVE R5 R3 0x7C100200, // 0008 CALL R4 1 0x5C0C0800, // 0009 MOVE R3 R4 0x3C100303, // 000A SHR R4 R1 K3 0x54160007, // 000B LDINT R5 8 0x10040205, // 000C MOD R1 R1 R5 0x24140500, // 000D GT R5 R2 K0 0x78160014, // 000E JMPF R5 #0024 0x54160007, // 000F LDINT R5 8 0x04140A01, // 0010 SUB R5 R5 R1 0x24180A02, // 0011 GT R6 R5 R2 0x781A0000, // 0012 JMPF R6 #0014 0x5C140400, // 0013 MOVE R5 R2 0x381A0805, // 0014 SHL R6 K4 R5 0x04180D04, // 0015 SUB R6 R6 K4 0x541E00FE, // 0016 LDINT R7 255 0x38200C01, // 0017 SHL R8 R6 R1 0x041C0E08, // 0018 SUB R7 R7 R8 0x94200004, // 0019 GETIDX R8 R0 R4 0x2C201007, // 001A AND R8 R8 R7 0x2C240606, // 001B AND R9 R3 R6 0x38241201, // 001C SHL R9 R9 R1 0x30201009, // 001D OR R8 R8 R9 0x98000808, // 001E SETIDX R0 R4 R8 0x3C0C0605, // 001F SHR R3 R3 R5 0x04080405, // 0020 SUB R2 R2 R5 0x58040000, // 0021 LDCONST R1 K0 0x00100904, // 0022 ADD R4 R4 K4 0x7001FFE8, // 0023 JMP #000D 0x80040000, // 0024 RET 1 R0 }) ) ); /*******************************************************************/ #if !BE_USE_PRECOMPILED_OBJECT void be_load_byteslib(bvm *vm) { static const bnfuncinfo members[] = { { ".p", NULL }, { ".len", NULL }, { ".size", NULL }, { "_buffer", m_buffer }, { "_change_buffer", m_change_buffer }, { "ismapped", m_is_mapped }, { "isreadonly", m_is_readonly }, { "init", m_init }, { "deinit", m_deinit }, { "tostring", m_tostring }, { "asstring", m_asstring }, { "tobool", m_tobool }, { "fromstring", m_fromstring }, { "tob64", m_tob64 }, { "fromb64", m_fromb64 }, { "fromhex", m_fromhex }, { "tohex", m_tohex }, { "add", m_add }, { "get", m_getu }, { "geti", m_geti }, { "set", m_set }, { "seti", m_set }, // setters for signed and unsigned are identical { "setbytes", m_setbytes }, { "getfloat", m_getfloat }, { "setfloat", m_setfloat }, { "addfloat", m_addfloat }, { "item", m_item }, { "setitem", m_setitem }, { "size", m_size }, { "resize", m_resize }, { "clear", m_clear }, { "reverse", m_reverse }, { "copy", m_copy }, { "append", m_connect }, { "+", m_merge }, { "..", m_connect }, { "==", m_equal }, { "!=", m_nequal }, { NULL, (bntvfunc) BE_CLOSURE }, /* mark section for berry closures */ { "getbits", (bntvfunc) &getbits_closure }, { "setbits", (bntvfunc) &setbits_closure }, { NULL, NULL } }; be_regclass(vm, "bytes", members); } #else #include "../generate/be_const_bytes_def.h" /* @const_object_info_begin class be_class_bytes (scope: global, name: bytes) { .p, var .len, var .size, var _buffer, func(m_buffer) _change_buffer, func(m_change_buffer) ismapped, func(m_is_mapped) isreadonly, func(m_is_readonly) init, func(m_init) deinit, func(m_deinit) tostring, func(m_tostring) asstring, func(m_asstring) tobool, func(m_tobool) fromstring, func(m_fromstring) tob64, func(m_tob64) fromb64, func(m_fromb64) fromhex, func(m_fromhex) tohex, func(m_tohex) add, func(m_add) get, func(m_getu) geti, func(m_geti) getfloat, func(m_getfloat) setfloat, func(m_setfloat) addfloat, func(m_addfloat) set, func(m_set) seti, func(m_set) setbytes, func(m_setbytes) item, func(m_item) setitem, func(m_setitem) size, func(m_size) resize, func(m_resize) clear, func(m_clear) reverse, func(m_reverse) copy, func(m_copy) append, func(m_connect) +, func(m_merge) .., func(m_connect) ==, func(m_equal) !=, func(m_nequal) getbits, closure(getbits_closure) setbits, closure(setbits_closure) } @const_object_info_end */ #include "../generate/be_fixed_be_class_bytes.h" #endif