Merge pull request #698 from laxsjo/master

add option for buf-resize to create a copy of the given array
This commit is contained in:
Benjamin Vedder 2024-01-03 21:55:59 +01:00 committed by GitHub
commit 36ed818e97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 369 additions and 61 deletions

View File

@ -4552,7 +4552,7 @@ The last byte in `seq` will be ignored as that is the null-terminator if `seq` i
| ESC, Express | 6.05+ |
```clj
(buf-resize arr delta-size opt-absolute-size)
(buf-resize arr delta-size opt-absolute-size opt-copy-symbol)
```
Change the length of array `arr` in bytes. A reference to `arr` is returned.
@ -4567,6 +4567,14 @@ This extension can be used in two modes:
Passing `nil` to `delta-size` while not passing any value for
`opt-absolute-size` will result in an `eval_error`.
You can optionally pass the symbol `'copy` to `opt-copy-symbol` to specify that
`arr` should be left unchanged and that a copy should instead be made. Don't
worry about the exact position of the argument, the only important part is that
`opt-copy-symbol` is last. So you can give a value for `opt-copy-symbol` even
when `opt-absolute-size` isn't passed. You can also for completeness pass the
symbol `'mut` to specify that the standard behaviour of modifying `arr` in place
should remain in effect.
When growing the length of the array a new range will be allocated and the old
data copied over. The new bytes will be initialised to zero. If the new length
of the array is smaller than the previous the allocated range will simply be
@ -4576,7 +4584,7 @@ It is possible to shrink an array to a length of zero.
**Note**
The array will be resized in place. The returned reference to `arr` is just for
convenience.
convenience. (Unless `opt-copy-symbol` is `'copy` of course.)
Example where we remove the terminating null byte from a string buffer:
```clj
@ -4593,6 +4601,18 @@ Example where we increase the length of `buf` to 5:
> [1 2 3 4 5]
```
Example where we create a copy of `name` with the terminating null byte
removed.
```clj
(def name "name")
(def name-array (buf-resize name -1 'copy))
(print name)
> "name"
(print name-array)
> [110 97 109 101]
```
---
## Import Files

148
lispBM/lbm_vesc_utils.c Normal file
View File

@ -0,0 +1,148 @@
/*
Copyright 2023 Rasmus Söderhielm rasmus.soderhielm@gmail.com
This file is part of the VESC firmware.
The VESC firmware is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The VESC firmware is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include "symrepr.h"
#include "lbm_defines.h"
#include "lbm_types.h"
#include "heap.h"
#include "eval_cps.h"
#include "lbm_flat_value.h"
#include "commands.h"
#include "lbm_vesc_utils.h"
bool lbm_add_symbol_const_if_new(char *name, lbm_uint *id) {
if (!lbm_get_symbol_by_name(name, id) && !lbm_add_symbol_const(name, id)) {
return false;
}
return true;
}
lbm_array_header_t *lbm_dec_array_header(lbm_value value) {
if (!lbm_is_array_r(value)) {
return NULL;
}
return (lbm_array_header_t *)lbm_car(value);
}
void *lbm_dec_array_data(lbm_value value) {
if (!lbm_is_array_r(value)) {
return NULL;
}
lbm_array_header_t *header = lbm_dec_array_header(value);
if (!header->data) {
return NULL;
}
return header->data;
}
lbm_value lbm_allocate_empty_list(lbm_uint len) {
lbm_value res = ENC_SYM_NIL;
for (lbm_uint i = 0; i < len; i++) {
res = lbm_cons(ENC_SYM_NIL, res);
if (res == ENC_SYM_MERROR) {
return ENC_SYM_MERROR;
}
}
return res;
}
lbm_value lbm_allocate_empty_list_grid(lbm_uint height, lbm_uint width) {
lbm_value outer = ENC_SYM_NIL;
for (lbm_uint i = 0; i < height; i++) {
lbm_value inner = ENC_SYM_NIL;
for (lbm_uint j = 0; j < width; j++) {
inner = lbm_cons(ENC_SYM_NIL, inner);
if (inner == ENC_SYM_MERROR) {
return ENC_SYM_MERROR;
}
}
outer = lbm_cons(inner, outer);
if (outer == ENC_SYM_MERROR) {
return ENC_SYM_MERROR;
}
}
return outer;
}
bool lbm_memory_shrink_bytes(void *array, lbm_uint size_bytes) {
lbm_uint size_words = size_bytes / LBM_WORD_SIZE;
if (size_bytes % LBM_WORD_SIZE != 0) {
size_words += 1;
}
return lbm_memory_shrink((lbm_uint *)array, size_words) > 0;
}
bool lbm_array_shrink(lbm_value array, lbm_uint new_size) {
if (!lbm_is_array_rw(array)) {
return false;
}
lbm_array_header_t *header = (lbm_array_header_t *)lbm_car(array);
if (!lbm_memory_shrink_bytes(header->data, new_size)) {
return false;
}
header->size = new_size;
return true;
}
extern const char *lbm_error_str_num_args;
bool lbm_check_argn_range(lbm_uint argn, lbm_uint n_min, lbm_uint n_max) {
if (!(n_min <= argn && argn <= n_max)) {
lbm_set_error_reason((char *)lbm_error_str_num_args);
return false;
}
return true;
}
bool lbm_check_argn_least(lbm_uint argn, lbm_uint n_min) {
if (!(n_min <= argn)) {
lbm_set_error_reason((char *)lbm_error_str_num_args);
return false;
}
return true;
}
bool f_pack_array(lbm_flat_value_t *result, void *data, size_t size) {
if (!lbm_start_flatten(result, 5 + size)) {
return false;
}
if (!f_lbm_array(result, size, data)) {
return false;
}
if (!lbm_finish_flatten(result)) {
return false;
}
return true;
}

111
lispBM/lbm_vesc_utils.h Normal file
View File

@ -0,0 +1,111 @@
/*
Copyright 2023 Rasmus Söderhielm rasmus.soderhielm@gmail.com
This file is part of the VESC firmware.
The VESC firmware is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The VESC firmware is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include "lbm_types.h"
#include "lbm_defines.h"
#include "heap.h"
#include "lbm_flat_value.h"
// Is this the right place to define this?
/**
* Bytes per word in the LBM memory.
*/
#define LBM_WORD_SIZE 4
/**
* Extract the array header struct from a lbm value array.
*
* The type of the value is checked to be at least be a readable array (doesn't
* have to be writeable though).
*
* @param value The lbm value to convert to an array header struct.
* @return The extracted pointer to the array header struct if value was a
* readable array. Null is returned otherwise. (@note: Null is also returned if
* the lbm_value contained a null pointer, @todo: unsure exactly what layer that would
* be right now though...)
*/
lbm_array_header_t *lbm_dec_array_header(lbm_value value);
/**
* Wrapper around lbm_memory_shrink that takes number of bytes instead of number
* of words. Shrinks the size of an pointer allocated in LBM memory to the
* smallest possible size while still having capacity for the specified amount
* of bytes.
*
* @param ptr Pointer to the allocated segment in LBM memory. Should have been
* obtained through lbm_malloc or other similar way at some point.
* @param size_bytes The new capacity of the allocation in bytes. Must be
* smaller or equal to the previous capacity.
* @return If the operation succeeded. The return value of lbm_memory_shrink is
* directly passed through, that is: false is returned either if ptr didn't
* point into the LBM memory/didn't point to the start of an allocated segment
* or if the new size was larger than the previous (note that since this
* function converts bytes to words, a larger size in bytes might not cause it
* to fail, as the size in words could still be the same). Otherwise true is
* returned.
*/
bool lbm_memory_shrink_bytes(void *ptr, lbm_uint size_bytes);
/**
* Shrink an lbm array to the new specified size.
*
* @param lbm_value The array to shrink. Should hold an LBM value that
* corresponds to an array.
* @param new_size The new smaller array size.
* @return Bool indicating if the array was successfully shrunk. False is
* returned if array didn't hold a byte array value, or if new_len was larger
* than the original size.
*/
bool lbm_array_shrink(lbm_value array, lbm_uint new_size);
/**
* Check if the number of arguments is the specified range. Sets error-reason if
* result is false.
*
* The range specified is inclusive!
*
* @param argn Number of arguments.
* @param n_min Minimum number of arguments.
* @param n_max Maximum number of arguments.
*
*/
bool lbm_check_argn_range(lbm_uint argn, lbm_uint n_min, lbm_uint n_max);
/**
* Check if the number of arguments is at large as long as specified. Sets
* error-reason if result is false.
*
* The (open) range specified is inclusive!
*
* @param argn Number of arguments.
* @param n_min Minimum number of arguments.
*
*/
bool lbm_check_argn_least(lbm_uint argn, lbm_uint n_min);
#define LBM_CHECK_ARGN_RANGE(min, max) \
if (!lbm_check_argn_range(argn, (min), (max))) { \
return ENC_SYM_EERROR; \
}
#define LBM_CHECK_ARGN_LEAST(min) \
if (!lbm_check_argn_least(argn, (min))) { \
return ENC_SYM_EERROR; \
}

View File

@ -25,7 +25,8 @@ LISPBMSRC = $(LISPBM)/src/env.c \
lispBM/lispif.c \
lispBM/lispif_vesc_extensions.c \
lispBM/lispif_vesc_dynamic_loader.c \
lispBM/lispif_c_lib.c
lispBM/lispif_c_lib.c \
lispBM/lbm_vesc_utils.c
LISPBMINC = lispBM \
$(LISPBM) \

View File

@ -24,6 +24,7 @@
#include "extensions/math_extensions.h"
#include "extensions/string_extensions.h"
#include "lbm_constants.h"
#include "lbm_vesc_utils.h"
#include "commands.h"
#include "mc_interface.h"
@ -222,6 +223,10 @@ typedef struct {
lbm_uint rate_400k;
lbm_uint rate_700k;
// Arrays
lbm_uint copy;
lbm_uint mut;
// Other
lbm_uint half_duplex;
} vesc_syms;
@ -548,6 +553,12 @@ static bool compare_symbol(lbm_uint sym, lbm_uint *comp) {
get_add_symbol("rate-700k", comp);
}
else if (comp == &syms_vesc.copy) {
get_add_symbol("copy", comp);
} else if (comp == &syms_vesc.mut) {
get_add_symbol("mut", comp);
}
else if (comp == &syms_vesc.half_duplex) {
get_add_symbol("half-duplex", comp);
}
@ -564,33 +575,6 @@ static bool is_symbol_true_false(lbm_value v) {
return res;
}
/**
* Wrapper around lbm_memory_shrink that takes number of bytes instead of number
* of words. Shrinks the size of an pointer allocated in LBM memory to the
* smallest possible size while still having capacity for the specified amount
* of bytes.
*
* @param ptr Pointer to the allocated segment in LBM memory. Should have been
* obtained through lbm_malloc or other similar way at some point.
* @param size_bytes The new capacity of the allocation in bytes. Must be
* smaller or equal to the previous capacity.
* @return If the operation succeeded. The return value of lbm_memory_shrink is
* directly passed through, that is: false is returned either if ptr didn't
* point into the LBM memory/didn't point to the start of an allocated segment
* or if the new size was larger than the previous (note that since this
* function converts bytes to words, a larger size in bytes might not cause it
* to fail, as the size in words could still be the same). Otherwise true is
* returned.
*/
static bool lbm_memory_shrink_bytes(void *array, lbm_uint size_bytes) {
lbm_uint size_words = size_bytes / LBM_WORD_SIZE;
if (size_bytes % LBM_WORD_SIZE != 0) {
size_words += 1;
}
return lbm_memory_shrink((lbm_uint *)array, size_words) > 0;
}
// Various commands
static lbm_value ext_print(lbm_value *args, lbm_uint argn) {
@ -4664,15 +4648,41 @@ static lbm_value ext_buf_find(lbm_value *args, lbm_uint argn) {
* reference only for convenience.
*/
static lbm_value ext_buf_resize(lbm_value *args, lbm_uint argn) {
if ((argn != 2 && argn != 3) || !lbm_is_array_rw(args[0])
|| (!lbm_is_number(args[1]) && !lbm_is_symbol_nil(args[1]))
|| (argn == 3 && !lbm_is_number(args[2]))) {
lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
LBM_CHECK_ARGN_RANGE(2, 4);
bool should_copy = false;
if (argn > 2 && lbm_is_symbol(args[argn - 1])) {
lbm_uint sym = lbm_dec_sym(args[argn - 1]);
if (compare_symbol(sym, &syms_vesc.copy)) {
should_copy = true;
} else if (compare_symbol(sym, &syms_vesc.mut)) {
should_copy = false;
} else {
lbm_set_error_suspect(args[argn - 1]);
return ENC_SYM_TERROR;
}
}
if ((!should_copy && !lbm_is_array_rw(args[0]))
|| (should_copy && !lbm_is_array_r(args[0]))) {
lbm_set_error_suspect(args[0]);
return ENC_SYM_TERROR;
}
bool delta_size_passed = !lbm_is_symbol_nil(args[1]);
bool new_size_passed = argn == 3;
bool new_size_passed = argn > 2 && lbm_is_number(args[2]);
if (delta_size_passed && !lbm_is_number(args[1])) {
lbm_set_error_suspect(args[1]);
return ENC_SYM_TERROR;
}
if (argn == 4 && !lbm_is_number(args[2])) {
// The case where argn is 3 is covered by the first check.
lbm_set_error_suspect(args[2]);
return ENC_SYM_TERROR;
}
if (!delta_size_passed && !new_size_passed) {
lbm_set_error_reason(
"delta-size (arg 2) was nil while new-size wasn't provided (arg 3)"
@ -4680,7 +4690,7 @@ static lbm_value ext_buf_resize(lbm_value *args, lbm_uint argn) {
return ENC_SYM_EERROR;
}
lbm_array_header_t *header = (lbm_array_header_t *)lbm_car(args[0]);
lbm_array_header_t *header = lbm_dec_array_header(args[0]);
if (header == NULL) {
// Should be impossible, unless it contained null pointer to header.
return ENC_SYM_FATAL_ERROR;
@ -4702,35 +4712,53 @@ static lbm_value ext_buf_resize(lbm_value *args, lbm_uint argn) {
new_size = (uint32_t)new_size_signed;
}
if (new_size == header->size) {
return args[0];
} else if (new_size < header->size) {
uint32_t allocated_size = new_size;
if (new_size == 0) {
// arrays of size 0 still need some memory allocated for them.
allocated_size = 1;
}
// We sadly can't trust the return value, as it fails if the allocation
// was previously a single word long. So we just throw it away.
lbm_memory_shrink_bytes(header->data, allocated_size);
header->size = new_size;
return args[0];
} else {
void *buffer = lbm_malloc_reserve(new_size);
if (buffer == NULL) {
if (should_copy) {
void *buffer = lbm_malloc(new_size);
if (!buffer) {
return ENC_SYM_MERROR;
}
memcpy(buffer, header->data, header->size);
memset(buffer + header->size, 0, new_size - header->size);
memcpy(buffer, header->data, MIN(header->size, new_size));
if (new_size > header->size) {
memset(buffer + header->size, 0, new_size - header->size);
}
lbm_memory_free(header->data);
header->data = buffer;
header->size = new_size;
lbm_value result;
if (!lbm_lift_array(&result, buffer, new_size)) {
return ENC_SYM_MERROR;
}
return result;
} else {
if (new_size == header->size) {
return args[0];
} else if (new_size < header->size) {
uint32_t allocated_size = new_size;
if (new_size == 0) {
// arrays of size 0 still need some memory allocated for them.
allocated_size = 1;
}
// We sadly can't trust the return value, as it fails if the allocation
// was previously a single word long. So we just throw it away.
lbm_memory_shrink_bytes(header->data, allocated_size);
return args[0];
header->size = new_size;
return args[0];
} else {
void *buffer = lbm_malloc(new_size);
if (buffer == NULL) {
return ENC_SYM_MERROR;
}
memcpy(buffer, header->data, header->size);
memset(buffer + header->size, 0, new_size - header->size);
lbm_memory_free(header->data);
header->data = buffer;
header->size = new_size;
return args[0];
}
}
}