mirror of
https://github.com/janet-lang/janet
synced 2025-08-03 20:43:55 +00:00
Add ffi/pointer-buffer for easier memory manipulation in FFI.
Added underlying buffer support for buffer instances that cannot reallocated underlying memory - useful for (small) memory mapped files and other FFI utilties.
This commit is contained in:
parent
d1eba60ba8
commit
1cadff8e58
@ -28,6 +28,13 @@
|
|||||||
#include "state.h"
|
#include "state.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Allow for managed buffers that cannot realloc/free their backing memory */
|
||||||
|
static void janet_buffer_can_realloc(JanetBuffer *buffer) {
|
||||||
|
if (buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC) {
|
||||||
|
janet_panic("buffer cannot reallocate foreign memory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize a buffer */
|
/* Initialize a buffer */
|
||||||
static JanetBuffer *janet_buffer_init_impl(JanetBuffer *buffer, int32_t capacity) {
|
static JanetBuffer *janet_buffer_init_impl(JanetBuffer *buffer, int32_t capacity) {
|
||||||
uint8_t *data = NULL;
|
uint8_t *data = NULL;
|
||||||
@ -53,7 +60,9 @@ JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
|
|||||||
|
|
||||||
/* Deinitialize a buffer (free data memory) */
|
/* Deinitialize a buffer (free data memory) */
|
||||||
void janet_buffer_deinit(JanetBuffer *buffer) {
|
void janet_buffer_deinit(JanetBuffer *buffer) {
|
||||||
janet_free(buffer->data);
|
if (!(buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC)) {
|
||||||
|
janet_free(buffer->data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize a buffer */
|
/* Initialize a buffer */
|
||||||
@ -67,6 +76,7 @@ void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth)
|
|||||||
uint8_t *new_data;
|
uint8_t *new_data;
|
||||||
uint8_t *old = buffer->data;
|
uint8_t *old = buffer->data;
|
||||||
if (capacity <= buffer->capacity) return;
|
if (capacity <= buffer->capacity) return;
|
||||||
|
janet_buffer_can_realloc(buffer);
|
||||||
int64_t big_capacity = ((int64_t) capacity) * growth;
|
int64_t big_capacity = ((int64_t) capacity) * growth;
|
||||||
capacity = big_capacity > INT32_MAX ? INT32_MAX : (int32_t) big_capacity;
|
capacity = big_capacity > INT32_MAX ? INT32_MAX : (int32_t) big_capacity;
|
||||||
janet_gcpressure(capacity - buffer->capacity);
|
janet_gcpressure(capacity - buffer->capacity);
|
||||||
@ -99,6 +109,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
|
|||||||
}
|
}
|
||||||
int32_t new_size = buffer->count + n;
|
int32_t new_size = buffer->count + n;
|
||||||
if (new_size > buffer->capacity) {
|
if (new_size > buffer->capacity) {
|
||||||
|
janet_buffer_can_realloc(buffer);
|
||||||
int32_t new_capacity = (new_size > (INT32_MAX / 2)) ? INT32_MAX : (new_size * 2);
|
int32_t new_capacity = (new_size > (INT32_MAX / 2)) ? INT32_MAX : (new_size * 2);
|
||||||
uint8_t *new_data = janet_realloc(buffer->data, new_capacity * sizeof(uint8_t));
|
uint8_t *new_data = janet_realloc(buffer->data, new_capacity * sizeof(uint8_t));
|
||||||
janet_gcpressure(new_capacity - buffer->capacity);
|
janet_gcpressure(new_capacity - buffer->capacity);
|
||||||
@ -220,6 +231,7 @@ JANET_CORE_FN(cfun_buffer_trim,
|
|||||||
"modified buffer.") {
|
"modified buffer.") {
|
||||||
janet_fixarity(argc, 1);
|
janet_fixarity(argc, 1);
|
||||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||||
|
janet_buffer_can_realloc(buffer);
|
||||||
if (buffer->count < buffer->capacity) {
|
if (buffer->count < buffer->capacity) {
|
||||||
int32_t newcap = buffer->count > 4 ? buffer->count : 4;
|
int32_t newcap = buffer->count > 4 ? buffer->count : 4;
|
||||||
uint8_t *newData = janet_realloc(buffer->data, newcap);
|
uint8_t *newData = janet_realloc(buffer->data, newcap);
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "features.h"
|
#include "features.h"
|
||||||
#include <janet.h>
|
#include <janet.h>
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "gc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef JANET_FFI
|
#ifdef JANET_FFI
|
||||||
@ -309,6 +310,7 @@ static JanetFFIPrimType decode_ffi_prim(const uint8_t *name) {
|
|||||||
if (!janet_cstrcmp(name, "void")) return JANET_FFI_TYPE_VOID;
|
if (!janet_cstrcmp(name, "void")) return JANET_FFI_TYPE_VOID;
|
||||||
if (!janet_cstrcmp(name, "bool")) return JANET_FFI_TYPE_BOOL;
|
if (!janet_cstrcmp(name, "bool")) return JANET_FFI_TYPE_BOOL;
|
||||||
if (!janet_cstrcmp(name, "ptr")) return JANET_FFI_TYPE_PTR;
|
if (!janet_cstrcmp(name, "ptr")) return JANET_FFI_TYPE_PTR;
|
||||||
|
if (!janet_cstrcmp(name, "pointer")) return JANET_FFI_TYPE_PTR;
|
||||||
if (!janet_cstrcmp(name, "string")) return JANET_FFI_TYPE_STRING;
|
if (!janet_cstrcmp(name, "string")) return JANET_FFI_TYPE_STRING;
|
||||||
if (!janet_cstrcmp(name, "float")) return JANET_FFI_TYPE_FLOAT;
|
if (!janet_cstrcmp(name, "float")) return JANET_FFI_TYPE_FLOAT;
|
||||||
if (!janet_cstrcmp(name, "double")) return JANET_FFI_TYPE_DOUBLE;
|
if (!janet_cstrcmp(name, "double")) return JANET_FFI_TYPE_DOUBLE;
|
||||||
@ -1481,25 +1483,46 @@ JANET_CORE_FN(janet_core_native_close,
|
|||||||
|
|
||||||
JANET_CORE_FN(cfun_ffi_malloc,
|
JANET_CORE_FN(cfun_ffi_malloc,
|
||||||
"(ffi/malloc size)",
|
"(ffi/malloc size)",
|
||||||
"Allocates memory directly using the system memory allocator. Memory allocated in this way must be freed manually! Returns a raw pointer, or nil if size = 0.") {
|
"Allocates memory directly using the janet memory allocator. Memory allocated in this way must be freed manually! Returns a raw pointer, or nil if size = 0.") {
|
||||||
janet_sandbox_assert(JANET_SANDBOX_FFI);
|
janet_sandbox_assert(JANET_SANDBOX_FFI);
|
||||||
janet_fixarity(argc, 1);
|
janet_fixarity(argc, 1);
|
||||||
size_t size = janet_getsize(argv, 0);
|
size_t size = janet_getsize(argv, 0);
|
||||||
if (size == 0) return janet_wrap_nil();
|
if (size == 0) return janet_wrap_nil();
|
||||||
return janet_wrap_pointer(malloc(size));
|
return janet_wrap_pointer(janet_malloc(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
JANET_CORE_FN(cfun_ffi_free,
|
JANET_CORE_FN(cfun_ffi_free,
|
||||||
"(ffi/free pointer)",
|
"(ffi/free pointer)",
|
||||||
"Free memory allocated with `ffi/malloc`.") {
|
"Free memory allocated with `ffi/malloc`. Returns nil.") {
|
||||||
janet_sandbox_assert(JANET_SANDBOX_FFI);
|
janet_sandbox_assert(JANET_SANDBOX_FFI);
|
||||||
janet_fixarity(argc, 1);
|
janet_fixarity(argc, 1);
|
||||||
if (janet_checktype(argv[0], JANET_NIL)) return janet_wrap_nil();
|
if (janet_checktype(argv[0], JANET_NIL)) return janet_wrap_nil();
|
||||||
void *pointer = janet_getpointer(argv, 0);
|
void *pointer = janet_getpointer(argv, 0);
|
||||||
free(pointer);
|
janet_free(pointer);
|
||||||
return janet_wrap_nil();
|
return janet_wrap_nil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JANET_CORE_FN(cfun_ffi_pointer_buffer,
|
||||||
|
"(ffi/pointer-buffer pointer capacity &opt count offset)",
|
||||||
|
"Create a buffer from a pointer. The underlying memory of the buffer will not be "
|
||||||
|
"reallocated or freed by the garbage collector, allowing unmanaged, mutable memory "
|
||||||
|
"to be manipulated with buffer functions. Attempts to resize or extend the buffer "
|
||||||
|
"beyond it's initial capacity will raise an error. As with many FFI functions, it is memory "
|
||||||
|
"unsafe and can potentially allow out of bounds memory access. Returns a new buffer.") {
|
||||||
|
janet_sandbox_assert(JANET_SANDBOX_FFI);
|
||||||
|
janet_arity(argc, 2, 4);
|
||||||
|
void *pointer = janet_getpointer(argv, 0);
|
||||||
|
int32_t capacity = janet_getnat(argv, 1);
|
||||||
|
int32_t count = janet_optnat(argv, argc, 2, 0);
|
||||||
|
int64_t offset = janet_optinteger64(argv, argc, 3, 0);
|
||||||
|
JanetBuffer *buffer = janet_gcalloc(JANET_MEMORY_BUFFER, sizeof(JanetBuffer));
|
||||||
|
buffer->gc.flags |= JANET_BUFFER_FLAG_NO_REALLOC;
|
||||||
|
buffer->capacity = capacity;
|
||||||
|
buffer->count = count;
|
||||||
|
buffer->data = ((uint8_t *) pointer) + offset;
|
||||||
|
return janet_wrap_buffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
void janet_lib_ffi(JanetTable *env) {
|
void janet_lib_ffi(JanetTable *env) {
|
||||||
JanetRegExt ffi_cfuns[] = {
|
JanetRegExt ffi_cfuns[] = {
|
||||||
JANET_CORE_REG("ffi/native", janet_core_raw_native),
|
JANET_CORE_REG("ffi/native", janet_core_raw_native),
|
||||||
@ -1516,6 +1539,7 @@ void janet_lib_ffi(JanetTable *env) {
|
|||||||
JANET_CORE_REG("ffi/jitfn", cfun_ffi_jitfn),
|
JANET_CORE_REG("ffi/jitfn", cfun_ffi_jitfn),
|
||||||
JANET_CORE_REG("ffi/malloc", cfun_ffi_malloc),
|
JANET_CORE_REG("ffi/malloc", cfun_ffi_malloc),
|
||||||
JANET_CORE_REG("ffi/free", cfun_ffi_free),
|
JANET_CORE_REG("ffi/free", cfun_ffi_free),
|
||||||
|
JANET_CORE_REG("ffi/pointer-buffer", cfun_ffi_pointer_buffer),
|
||||||
JANET_REG_END
|
JANET_REG_END
|
||||||
};
|
};
|
||||||
janet_core_cfuns_ext(env, NULL, ffi_cfuns);
|
janet_core_cfuns_ext(env, NULL, ffi_cfuns);
|
||||||
|
@ -1585,6 +1585,7 @@ JANET_API Janet janet_array_pop(JanetArray *array);
|
|||||||
JANET_API Janet janet_array_peek(JanetArray *array);
|
JANET_API Janet janet_array_peek(JanetArray *array);
|
||||||
|
|
||||||
/* Buffer functions */
|
/* Buffer functions */
|
||||||
|
#define JANET_BUFFER_FLAG_NO_REALLOC 0x10000
|
||||||
JANET_API JanetBuffer *janet_buffer(int32_t capacity);
|
JANET_API JanetBuffer *janet_buffer(int32_t capacity);
|
||||||
JANET_API JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity);
|
JANET_API JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity);
|
||||||
JANET_API void janet_buffer_deinit(JanetBuffer *buffer);
|
JANET_API void janet_buffer_deinit(JanetBuffer *buffer);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user