diff --git a/examples/tarray.janet b/examples/tarray.janet new file mode 100644 index 00000000..b55fa58b --- /dev/null +++ b/examples/tarray.janet @@ -0,0 +1,83 @@ +# naive matrix implementation for testing typed array + +(defmacro printf [& xs] ['print ['string/format (splice xs)]]) + +(defn matrix [nrow ncol] {:nrow nrow :ncol ncol :array (tarray/new :float64 (* nrow ncol))}) + +(defn matrix/row [mat i] + (def {:nrow nrow :ncol ncol :array array} mat) + (tarray/new :float64 ncol 1 (* i ncol) array)) + +(defn matrix/column [mat j] + (def {:nrow nrow :ncol ncol :array array} mat) + (tarray/new :float64 nrow ncol j array)) + +(defn matrix/set [mat i j value] + (def {:nrow nrow :ncol ncol :array array} mat) + (set (array (+ (* i ncol) j)) value)) + +(defn matrix/get [mat i j value] + (def {:nrow nrow :ncol ncol :array array} mat) + (array (+ (* i ncol) j))) + + +# other variants to test rows and cols views + +(defn matrix/set* [mat i j value] + (set ((matrix/row mat i) j) value)) + +(defn matrix/set** [mat i j value] + (set ((matrix/column mat j) i) value)) + + +(defn matrix/get* [mat i j value] + ((matrix/row mat i) j)) + +(defn matrix/get** [mat i j value] + ((matrix/column j) i)) + + +(defn tarray/print [array] + (def size (tarray/length array)) + (def buf @"") + (buffer/format buf "[%2i]" size) + (for i 0 size + (buffer/format buf " %+6.3f " (array i))) + (print buf)) + +(defn matrix/print [mat] + (def {:nrow nrow :ncol ncol :array tarray} mat) + (printf "matrix %iX%i %p" nrow ncol tarray) + (for i 0 nrow + (tarray/print (matrix/row mat i)))) + + +(def nr 5) +(def nc 4) +(def A (matrix nr nc)) + +(loop (i :range (0 nr) j :range (0 nc)) + (matrix/set A i j i)) +(matrix/print A) + +(loop (i :range (0 nr) j :range (0 nc)) + (matrix/set* A i j i)) +(matrix/print A) + +(loop (i :range (0 nr) j :range (0 nc)) + (matrix/set** A i j i)) +(matrix/print A) + + +(printf "properties:\n%p" (tarray/properties (A :array))) +(for i 0 nr + (printf "row properties:[%i]\n%p" i (tarray/properties (matrix/row A i)))) +(for i 0 nc + (printf "col properties:[%i]\n%p" i (tarray/properties (matrix/column A i)))) + + + + + + + diff --git a/src/core/corelib.c b/src/core/corelib.c index 04b2547a..af186b0a 100644 --- a/src/core/corelib.c +++ b/src/core/corelib.c @@ -787,6 +787,10 @@ JanetTable *janet_core_env(void) { #ifdef JANET_ASSEMBLER janet_lib_asm(env); #endif +#ifdef JANET_TYPED_ARRAY + janet_lib_typed_array(env); +#endif + #ifdef JANET_BOOTSTRAP /* Run bootstrap source */ diff --git a/src/core/io.c b/src/core/io.c index 44904517..0a6534d4 100644 --- a/src/core/io.c +++ b/src/core/io.c @@ -56,6 +56,8 @@ JanetAbstractType cfun_io_filetype = { cfun_io_gc, NULL, io_file_get, + NULL, + NULL, NULL }; diff --git a/src/core/marsh.c b/src/core/marsh.c index 52dbe922..f91db881 100644 --- a/src/core/marsh.c +++ b/src/core/marsh.c @@ -266,6 +266,47 @@ static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) { marshal_one(st, janet_wrap_fiber(fiber->child), flags + 1); } + +void janet_marshal_int(JanetMarshalContext *ctx, int32_t value) { + MarshalState *st = (MarshalState *)(ctx->m_state); + pushint(st, value); +}; + +void janet_marshal_byte(JanetMarshalContext *ctx, uint8_t value) { + MarshalState *st = (MarshalState *)(ctx->m_state); + pushbyte(st, value); +}; + +void janet_marshal_bytes(JanetMarshalContext *ctx, const uint8_t *bytes, int32_t len) { + MarshalState *st = (MarshalState *)(ctx->m_state); + pushbytes(st, bytes, len); +} + +void janet_marshal_janet(JanetMarshalContext *ctx, Janet x) { + MarshalState *st = (MarshalState *)(ctx->m_state); + marshal_one(st, x, ctx->flags + 1); +} + +#define MARK_SEEN() \ + janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)) + + +static void marshal_one_abstract(MarshalState *st, Janet x, int flags) { + void *abstract = janet_unwrap_abstract(x); + const JanetAbstractType *at = janet_abstract_type(abstract); + if (at->marshal) { + MARK_SEEN(); + JanetMarshalContext context = {st, NULL, flags, NULL}; + pushbyte(st, LB_ABSTRACT); + marshal_one(st, janet_ckeywordv(at->name), flags + 1); + pushint(st, janet_abstract_size(abstract)); + at->marshal(abstract, &context); + } else { + janet_panicf("try to marshal unregistered abstract type, cannot marshal %p", x); + } +} + + /* The main body of the marshaling function. Is the main * entry point for the mutually recursive functions. */ static void marshal_one(MarshalState *st, Janet x, int flags) { @@ -291,8 +332,6 @@ static void marshal_one(MarshalState *st, Janet x, int flags) { } } -#define MARK_SEEN() \ - janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)) /* Check reference and registry value */ { @@ -423,6 +462,10 @@ static void marshal_one(MarshalState *st, Janet x, int flags) { MARK_SEEN(); return; } + case JANET_ABSTRACT: { + marshal_one_abstract(st, x, flags); + return; + } case JANET_FUNCTION: { pushbyte(st, LB_FUNCTION); JanetFunction *func = janet_unwrap_function(x); @@ -476,6 +519,7 @@ typedef struct { const uint8_t *end; } UnmarshalState; + #define MARSH_EOS(st, data) do { \ if ((data) >= (st)->end) janet_panic("unexpected end of source");\ } while (0) @@ -856,6 +900,61 @@ static const uint8_t *unmarshal_one_fiber( return data; } + +void janet_unmarshal_int(JanetMarshalContext *ctx, int32_t *i) { + UnmarshalState *st = (UnmarshalState *)(ctx->u_state); + *i = readint(st, &(ctx->data)); +}; + +void janet_unmarshal_uint(JanetMarshalContext *ctx, uint32_t *i) { + UnmarshalState *st = (UnmarshalState *)(ctx->u_state); + *i = (uint32_t)readint(st, &(ctx->data)); +}; + +void janet_unmarshal_size(JanetMarshalContext *ctx, size_t *i) { + UnmarshalState *st = (UnmarshalState *)(ctx->u_state); + *i = (size_t)readint(st, &(ctx->data)); +}; + + + +void janet_unmarshal_byte(JanetMarshalContext *ctx, uint8_t *b) { + UnmarshalState *st = (UnmarshalState *)(ctx->u_state); + MARSH_EOS(st, ctx->data); + *b = *(ctx->data++); +}; + +void janet_unmarshal_bytes(JanetMarshalContext *ctx, uint8_t *dest, int32_t len) { + UnmarshalState *st = (UnmarshalState *)(ctx->u_state); + MARSH_EOS(st, ctx->data + len - 1); + memcpy(dest, ctx->data, len); + ctx->data += len; +} + +void janet_unmarshal_janet(JanetMarshalContext *ctx, Janet *out) { + UnmarshalState *st = (UnmarshalState *)(ctx->u_state); + ctx->data = unmarshal_one(st, ctx->data, out, ctx->flags); +} + +static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *data, Janet *out, int flags) { + Janet key; + data = unmarshal_one(st, data, &key, flags + 1); + const JanetAbstractType *at = janet_get_abstract_type(key); + if (at == NULL) return NULL; + if (at->unmarshal) { + void *p = janet_abstract(at, readint(st, &data)); + JanetMarshalContext context = {NULL, st, flags, data}; + at->unmarshal(p, &context); + *out = janet_wrap_abstract(p); + return data; + } + return NULL; +} + + + + + static const uint8_t *unmarshal_one( UnmarshalState *st, const uint8_t *data, @@ -965,6 +1064,10 @@ static const uint8_t *unmarshal_one( } return data; } + case LB_ABSTRACT: { + data++; + return unmarshal_one_abstract(st, data, out, flags); + } case LB_REFERENCE: case LB_ARRAY: case LB_TUPLE: diff --git a/src/core/parse.c b/src/core/parse.c index 24ec333a..e4d87914 100644 --- a/src/core/parse.c +++ b/src/core/parse.c @@ -630,6 +630,8 @@ static JanetAbstractType janet_parse_parsertype = { parsergc, parsermark, parserget, + NULL, + NULL, NULL }; diff --git a/src/core/peg.c b/src/core/peg.c index f34eb96c..46d786dd 100644 --- a/src/core/peg.c +++ b/src/core/peg.c @@ -984,6 +984,8 @@ static JanetAbstractType peg_type = { NULL, peg_mark, NULL, + NULL, + NULL, NULL }; diff --git a/src/core/typedarray.c b/src/core/typedarray.c new file mode 100644 index 00000000..fb802ecf --- /dev/null +++ b/src/core/typedarray.c @@ -0,0 +1,598 @@ +/* +* Copyright (c) 2019 Calvin Rose & contributors +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +/* Compiler feature test macros for things */ +#define _DEFAULT_SOURCE +#define _BSD_SOURCE + +#ifndef JANET_AMALG +#include +#include "util.h" +#endif + +typedef uint8_t ta_uint8_t; +typedef int8_t ta_int8_t; +typedef uint16_t ta_uint16_t; +typedef int16_t ta_int16_t; +typedef uint32_t ta_uint32_t; +typedef int32_t ta_int32_t; +typedef uint64_t ta_uint64_t; +typedef int64_t ta_int64_t; +typedef float ta_float32_t; +typedef double ta_float64_t; + + +static char *ta_type_names[] = { + "uint8", + "int8", + "uint16", + "int16", + "uint32", + "int32", + "uint64", + "int64", + "float32", + "float64", + "any", +}; + +static size_t ta_type_sizes[] = { + sizeof(ta_uint8_t), + sizeof(ta_int8_t), + sizeof(ta_uint16_t), + sizeof(ta_int16_t), + sizeof(ta_uint32_t), + sizeof(ta_int32_t), + sizeof(ta_uint64_t), + sizeof(ta_int64_t), + sizeof(ta_float32_t), + sizeof(ta_float64_t), + 0, +}; +#define TA_COUNT_TYPES (JANET_TARRAY_TYPE_float64 + 1) +#define TA_ATOM_MAXSIZE 8 +#define TA_FLAG_BIG_ENDIAN 1 + +static JanetTArrayType get_ta_type_by_name(const uint8_t *name) { + size_t nt = sizeof(ta_type_names) / sizeof(char *); + for (size_t i = 0; i < nt; i++) { + if (!janet_cstrcmp(name, ta_type_names[i])) + return i; + } + return 0; +} + + + +static JanetTArrayBuffer *ta_buffer_init(JanetTArrayBuffer *buf, size_t size) { + buf->data = NULL; + if (size > 0) { + buf->data = (uint8_t *)calloc(size, sizeof(uint8_t)); + if (buf->data == NULL) { + JANET_OUT_OF_MEMORY; + } + } + buf->size = size; +#ifdef JANET_BIG_ENDIAN + buf->flags = TA_FLAG_BIG_ENDIAN; +#else + buf->flags = 0; +#endif + return buf; +} + +static int ta_buffer_gc(void *p, size_t s) { + (void) s; + JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p; + free(buf->data); + return 0; +} + +static void ta_buffer_marshal(void *p, JanetMarshalContext *ctx) { + JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p; + janet_marshal_int(ctx, buf->size); + janet_marshal_int(ctx, buf->flags); + janet_marshal_bytes(ctx, buf->data, buf->size); +} + +static void ta_buffer_unmarshal(void *p, JanetMarshalContext *ctx) { + JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p; + uint32_t size; + janet_unmarshal_uint(ctx, &size); + ta_buffer_init(buf, size); // warning if indianess <> platform ?? + janet_unmarshal_uint(ctx, &(buf->flags)); + janet_unmarshal_bytes(ctx, buf->data, buf->size); +} + + +static const JanetAbstractType ta_buffer_type = { + "ta/buffer", + ta_buffer_gc, + NULL, + NULL, + NULL, + ta_buffer_marshal, + ta_buffer_unmarshal, +}; + + + + +static int ta_mark(void *p, size_t s) { + (void) s; + JanetTArrayView *view = (JanetTArrayView *)p; + janet_mark(janet_wrap_abstract(view->buffer)); + return 0; +} + +static void ta_view_marshal(void *p, JanetMarshalContext *ctx) { + JanetTArrayView *view = (JanetTArrayView *)p; + size_t offset = (view->buffer->data - (uint8_t *)(view->data)); + janet_marshal_int(ctx, view->size); + janet_marshal_int(ctx, view->stride); + janet_marshal_int(ctx, view->type); + janet_marshal_int(ctx, offset); + janet_marshal_janet(ctx, janet_wrap_abstract(view->buffer)); +} + + +static void ta_view_unmarshal(void *p, JanetMarshalContext *ctx) { + JanetTArrayView *view = (JanetTArrayView *)p; + size_t offset; + Janet buffer; + janet_unmarshal_size(ctx, &(view->size)); + janet_unmarshal_size(ctx, &(view->stride)); + janet_unmarshal_uint(ctx, &(view->type)); + janet_unmarshal_size(ctx, &offset); + janet_unmarshal_janet(ctx, &buffer); + view->buffer = (JanetTArrayBuffer *)janet_unwrap_abstract(buffer); + size_t buf_need_size = offset + (janet_tarray_type_size(view->type)) * ((view->size - 1) * view->stride + 1); + if (view->buffer->size < buf_need_size) + janet_panic("bad typed array offset in marshalled data"); + view->data = view->buffer->data + offset; +} + + + + + +#define DEFINE_VIEW_TYPE(thetype) \ + typedef struct { \ + JanetTArrayBuffer * buffer; \ + ta_##thetype##_t * data; \ + size_t size; \ + size_t stride; \ + JanetTArrayType type; \ + } TA_View_##thetype ; + + +#define DEFINE_VIEW_GETTER(type) \ +static Janet ta_get_##type(void *p, Janet key) { \ + Janet value; \ + size_t index; \ + if (!janet_checkint(key)) \ + janet_panic("expected integer key"); \ + index = (size_t)janet_unwrap_integer(key);\ + TA_View_##type * array=(TA_View_##type *)p; \ + if (index >= array->size) { \ + value = janet_wrap_nil(); \ + } else { \ + value = janet_wrap_number(array->data[index*array->stride]); \ + } \ + return value; \ +} + +#define DEFINE_VIEW_SETTER(type) \ +void ta_put_##type(void *p, Janet key,Janet value) { \ + size_t index;\ + if (!janet_checkint(key))\ + janet_panic("expected integer key"); \ + if (!janet_checktype(value,JANET_NUMBER)) \ + janet_panic("expected number value"); \ + index = (size_t)janet_unwrap_integer(key); \ + TA_View_##type * array=(TA_View_##type *)p; \ + if (index >= array->size) { \ + janet_panic("typed array out of bounds"); \ + } \ + array->data[index*array->stride]=(ta_##type##_t)janet_unwrap_number(value); \ +} + +#define DEFINE_VIEW_INITIALIZER(thetype) \ + static JanetTArrayView * ta_init_##thetype(JanetTArrayView * view,JanetTArrayBuffer * buf,size_t size,size_t offset,size_t stride) { \ + if ((stride<1) || (size <1)) { \ + janet_panic("stride and size should be > 0"); \ + }; \ + TA_View_##thetype * tview=(TA_View_##thetype *) view; \ + size_t buf_size=offset+(sizeof(ta_##thetype##_t))*((size-1)*stride+1); \ + if (buf==NULL) { \ + buf=(JanetTArrayBuffer *)janet_abstract(&ta_buffer_type,sizeof(JanetTArrayBuffer)); \ + ta_buffer_init(buf,buf_size); \ + } \ + if (buf->sizesize,buf_size); \ + } \ + tview->buffer=buf; \ + tview->stride=stride; \ + tview->size=size; \ + tview->data=(ta_##thetype##_t *)(buf->data+offset); \ + tview->type=JANET_TARRAY_TYPE_##thetype; \ + return view; \ +}; + + + +#define BUILD_TYPE(type) \ +DEFINE_VIEW_TYPE(type) \ +DEFINE_VIEW_GETTER(type) \ +DEFINE_VIEW_SETTER(type) \ +DEFINE_VIEW_INITIALIZER(type) + +BUILD_TYPE(uint8) +BUILD_TYPE(int8) +BUILD_TYPE(uint16) +BUILD_TYPE(int16) +BUILD_TYPE(uint32) +BUILD_TYPE(int32) +BUILD_TYPE(uint64) +BUILD_TYPE(int64) +BUILD_TYPE(float32) +BUILD_TYPE(float64) + +#undef DEFINE_VIEW_TYPE +#undef DEFINE_VIEW_GETTER +#undef DEFINE_VIEW_SETTER +#undef DEFINE_VIEW_INITIALIZER + + +#define DEFINE_VIEW_ABSTRACT_TYPE(type) \ +{ \ + "ta/"#type, \ + NULL, \ + ta_mark, \ + ta_get_##type, \ + ta_put_##type, \ + ta_view_marshal, \ + ta_view_unmarshal \ +} + +static const JanetAbstractType ta_array_types[] = { + DEFINE_VIEW_ABSTRACT_TYPE(uint8), + DEFINE_VIEW_ABSTRACT_TYPE(int8), + DEFINE_VIEW_ABSTRACT_TYPE(uint16), + DEFINE_VIEW_ABSTRACT_TYPE(int16), + DEFINE_VIEW_ABSTRACT_TYPE(uint32), + DEFINE_VIEW_ABSTRACT_TYPE(int32), + DEFINE_VIEW_ABSTRACT_TYPE(uint64), + DEFINE_VIEW_ABSTRACT_TYPE(int64), + DEFINE_VIEW_ABSTRACT_TYPE(float32), + DEFINE_VIEW_ABSTRACT_TYPE(float64) +}; + +#undef DEFINE_VIEW_ABSTRACT_TYPE + + + +static int is_ta_anytype(Janet x) { + if (janet_checktype(x, JANET_ABSTRACT)) { + const JanetAbstractType *at = janet_abstract_type(janet_unwrap_abstract(x)); + for (size_t i = 0; i < TA_COUNT_TYPES; i++) { + if (at == ta_array_types + i) return 1; + } + } + return 0; +} + +static int is_ta_type(Janet x, JanetTArrayType type) { + return (janet_checktype(x, JANET_ABSTRACT) && (type < TA_COUNT_TYPES) && + (janet_abstract_type(janet_unwrap_abstract(x)) == &ta_array_types[type])) ? 1 : 0; +} + + +#define CASE_TYPE_INITIALIZE(type) case JANET_TARRAY_TYPE_##type : ta_init_##type(view,buffer,size,offset,stride); break + +JanetTArrayBuffer *janet_tarray_buffer(size_t size) { + JanetTArrayBuffer *buf = (JanetTArrayBuffer *)janet_abstract(&ta_buffer_type, sizeof(JanetTArrayBuffer)); + ta_buffer_init(buf, size); + return buf; +} + +JanetTArrayView *janet_tarray_view(JanetTArrayType type, size_t size, size_t stride, size_t offset, JanetTArrayBuffer *buffer) { + JanetTArrayView *view = janet_abstract(&ta_array_types[type], sizeof(JanetTArrayView)); + switch (type) { + CASE_TYPE_INITIALIZE(uint8); + CASE_TYPE_INITIALIZE(int8); + CASE_TYPE_INITIALIZE(uint16); + CASE_TYPE_INITIALIZE(int16); + CASE_TYPE_INITIALIZE(uint32); + CASE_TYPE_INITIALIZE(int32); + CASE_TYPE_INITIALIZE(uint64); + CASE_TYPE_INITIALIZE(int64); + CASE_TYPE_INITIALIZE(float32); + CASE_TYPE_INITIALIZE(float64); + default : + janet_panic("bad typed array type"); + + } + return view; +} + +#undef CASE_TYPE_INITIALIZE + +JanetTArrayBuffer *janet_gettarray_buffer(const Janet *argv, int32_t n) { + return (JanetTArrayBuffer *)janet_getabstract(argv, n, &ta_buffer_type); +} + +int janet_is_tarray_view(Janet x, JanetTArrayType type) { + return (type == JANET_TARRAY_TYPE_any) ? is_ta_anytype(x) : is_ta_type(x, type); +} + +int janet_tarray_type_size(JanetTArrayType type) { + return (type < TA_COUNT_TYPES) ? ta_type_sizes[type] : 0 ; +} + +JanetTArrayView *janet_gettarray_view(const Janet *argv, int32_t n, JanetTArrayType type) { + if (janet_is_tarray_view(argv[n], type)) { + return (JanetTArrayView *)janet_unwrap_abstract(argv[n]); + } else { + janet_panicf("bad slot #%d, expected typed array of type %s, got %v", + n, (type <= JANET_TARRAY_TYPE_any) ? ta_type_names[type] : "?", argv[n]); + return NULL; + } +} + +static Janet cfun_typed_array_new(int32_t argc, Janet *argv) { + janet_arity(argc, 2, 5); + size_t offset = 0; + size_t stride = 1; + JanetTArrayBuffer *buffer = NULL; + const uint8_t *keyw = janet_getkeyword(argv, 0); + JanetTArrayType type = get_ta_type_by_name(keyw); + size_t size = (size_t)janet_getinteger(argv, 1); + if (argc > 2) + stride = (size_t)janet_getinteger(argv, 2); + if (argc > 3) + offset = (size_t)janet_getinteger(argv, 3); + if (argc > 4) { + if (is_ta_anytype(argv[4])) { + JanetTArrayView *view = (JanetTArrayView *)janet_unwrap_abstract(argv[4]); + offset = (view->buffer->data - (uint8_t *)(view->data)) + offset * ta_type_sizes[view->type]; + stride *= view->stride; + buffer = view->buffer; + } else { + buffer = (JanetTArrayBuffer *)janet_getabstract(argv, 4, &ta_buffer_type); + } + } + JanetTArrayView *view = janet_tarray_view(type, size, stride, offset, buffer); + return janet_wrap_abstract(view); +} + + +static Janet cfun_typed_array_buffer(int32_t argc, Janet *argv) { + janet_fixarity(argc, 1); + if (is_ta_anytype(argv[0])) { + JanetTArrayView *view = (JanetTArrayView *)janet_unwrap_abstract(argv[0]); + return janet_wrap_abstract(view->buffer); + } + size_t size = (size_t)janet_getinteger(argv, 0); + JanetTArrayBuffer *buf = janet_tarray_buffer(size); + return janet_wrap_abstract(buf); +} + +static Janet cfun_typed_array_size(int32_t argc, Janet *argv) { + janet_fixarity(argc, 1); + if (is_ta_anytype(argv[0])) { + JanetTArrayView *view = (JanetTArrayView *)janet_unwrap_abstract(argv[0]); + return janet_wrap_number(view->size); + } + JanetTArrayBuffer *buf = (JanetTArrayBuffer *)janet_getabstract(argv, 0, &ta_buffer_type); + return janet_wrap_number(buf->size); +} + +static Janet cfun_typed_array_properties(int32_t argc, Janet *argv) { + janet_fixarity(argc, 1); + if (is_ta_anytype(argv[0])) { + JanetTArrayView *view = (JanetTArrayView *)janet_unwrap_abstract(argv[0]); + JanetKV *props = janet_struct_begin(6); + janet_struct_put(props, janet_ckeywordv("size"), janet_wrap_number(view->size)); + janet_struct_put(props, janet_ckeywordv("byte-offset"), janet_wrap_number((uint8_t *)(view->data) - view->buffer->data)); + janet_struct_put(props, janet_ckeywordv("stride"), janet_wrap_number(view->stride)); + janet_struct_put(props, janet_ckeywordv("type"), janet_ckeywordv(ta_type_names[view->type])); + janet_struct_put(props, janet_ckeywordv("type-size"), janet_wrap_number(ta_type_sizes[view->type])); + janet_struct_put(props, janet_ckeywordv("buffer"), janet_wrap_abstract(view->buffer)); + return janet_wrap_struct(janet_struct_end(props)); + } else { + JanetTArrayBuffer *buffer = janet_gettarray_buffer(argv, 0); + JanetKV *props = janet_struct_begin(3); + janet_struct_put(props, janet_ckeywordv("size"), janet_wrap_number(buffer->size)); + janet_struct_put(props, janet_ckeywordv("big-endian"), janet_wrap_boolean(buffer->flags & TA_FLAG_BIG_ENDIAN)); + return janet_wrap_struct(janet_struct_end(props)); + } + +} + +/* TODO move it , it's not the good place for this function */ +static Janet cfun_abstract_properties(int32_t argc, Janet *argv) { + janet_fixarity(argc, 1); + const uint8_t *key = janet_getkeyword(argv, 0); + const JanetAbstractType *at = janet_get_abstract_type(janet_wrap_keyword(key)); + if (at == NULL) { + return janet_wrap_nil(); + } + JanetKV *props = janet_struct_begin(2); + janet_struct_put(props, janet_ckeywordv("name"), janet_ckeywordv(at->name)); + janet_struct_put(props, janet_ckeywordv("marshal"), janet_wrap_boolean((at->marshal != NULL) && (at->unmarshal != NULL))); + return janet_wrap_struct(janet_struct_end(props)); +} + +static Janet cfun_typed_array_slice(int32_t argc, Janet *argv) { + janet_arity(argc, 1, 3); + JanetTArrayView *src = janet_gettarray_view(argv, 0, JANET_TARRAY_TYPE_any); + const JanetAbstractType *at = janet_abstract_type(janet_unwrap_abstract(argv[0])); + JanetRange range; + int32_t length = (int32_t)src->size; + if (argc == 1) { + range.start = 0; + range.end = length; + } else if (argc == 2) { + range.start = janet_gethalfrange(argv, 1, length, "start"); + range.end = length; + } else { + range.start = janet_gethalfrange(argv, 1, length, "start"); + range.end = janet_gethalfrange(argv, 2, length, "end"); + if (range.end < range.start) + range.end = range.start; + } + JanetArray *array = janet_array(range.end - range.start); + if (array->data) { + for (int32_t i = range.start; i < range.end; i++) { + array->data[i - range.start] = at->get(src, janet_wrap_number(i)); + } + } + array->count = range.end - range.start; + return janet_wrap_array(array); +} + + +static Janet cfun_typed_array_copy_bytes(int32_t argc, Janet *argv) { + janet_arity(argc, 4, 5); + JanetTArrayView *src = janet_gettarray_view(argv, 0, JANET_TARRAY_TYPE_any); + size_t index_src = (size_t)janet_getinteger(argv, 1); + JanetTArrayView *dst = janet_gettarray_view(argv, 2, JANET_TARRAY_TYPE_any); + size_t index_dst = (size_t)janet_getinteger(argv, 3); + size_t count = (argc == 5) ? (size_t)janet_getinteger(argv, 4) : 1; + size_t src_atom_size = ta_type_sizes[src->type]; + size_t dst_atom_size = ta_type_sizes[dst->type]; + size_t step_src = src->stride * src_atom_size; + size_t step_dst = dst->stride * dst_atom_size; + size_t pos_src = ((uint8_t *)(src->data) - src->buffer->data) + (index_src * step_src); + size_t pos_dst = ((uint8_t *)(dst->data) - dst->buffer->data) + (index_dst * step_dst); + uint8_t *ps = src->buffer->data + pos_src, * pd = dst->buffer->data + pos_dst; + if ((pos_dst + (count - 1)*step_dst + src_atom_size <= dst->buffer->size) && + (pos_src + (count - 1)*step_src + src_atom_size <= src->buffer->size)) { + for (size_t i = 0; i < count; i++) { + memmove(pd, ps, src_atom_size); + pd += step_dst; + ps += step_src; + } + } else { + janet_panic("typed array copy out of bounds"); + } + return janet_wrap_nil(); +} + +static Janet cfun_typed_array_swap_bytes(int32_t argc, Janet *argv) { + janet_arity(argc, 4, 5); + JanetTArrayView *src = janet_gettarray_view(argv, 0, JANET_TARRAY_TYPE_any); + size_t index_src = (size_t)janet_getinteger(argv, 1); + JanetTArrayView *dst = janet_gettarray_view(argv, 2, JANET_TARRAY_TYPE_any); + size_t index_dst = (size_t)janet_getinteger(argv, 3); + size_t count = (argc == 5) ? (size_t)janet_getinteger(argv, 4) : 1; + size_t src_atom_size = ta_type_sizes[src->type]; + size_t dst_atom_size = ta_type_sizes[dst->type]; + size_t step_src = src->stride * src_atom_size; + size_t step_dst = dst->stride * dst_atom_size; + size_t pos_src = ((uint8_t *)(src->data) - src->buffer->data) + (index_src * step_src); + size_t pos_dst = ((uint8_t *)(dst->data) - dst->buffer->data) + (index_dst * step_dst); + uint8_t *ps = src->buffer->data + pos_src, * pd = dst->buffer->data + pos_dst; + uint8_t temp[TA_ATOM_MAXSIZE]; + if ((pos_dst + (count - 1)*step_dst + src_atom_size <= dst->buffer->size) && + (pos_src + (count - 1)*step_src + src_atom_size <= src->buffer->size)) { + for (size_t i = 0; i < count; i++) { + memcpy(temp, ps, src_atom_size); + memcpy(ps, pd, src_atom_size); + memcpy(pd, temp, src_atom_size); + pd += step_dst; + ps += step_src; + } + } else { + janet_panic("typed array swap out of bounds"); + } + return janet_wrap_nil(); +} + + + +static const JanetReg ta_cfuns[] = { + { + "tarray/new", cfun_typed_array_new, + JDOC("(tarray/new type size [stride = 1 [offset = 0 [tarray | buffer]]] )\n\n" + "Create new typed array") + }, + { + "tarray/buffer", cfun_typed_array_buffer, + JDOC("(tarray/buffer (array | size) )\n\n" + "return typed array buffer or create a new buffer ") + }, + { + "tarray/length", cfun_typed_array_size, + JDOC("(tarray/length (array | buffer) )\n\n" + "return typed array or buffer size ") + }, + { + "tarray/properties", cfun_typed_array_properties, + JDOC("(tarray/properties array )\n\n" + "return typed array properties as a struct") + }, + { + "tarray/copy-bytes", cfun_typed_array_copy_bytes, + JDOC("(tarray/copy-bytes src sindex dst dindex [count=1])\n\n" + "copy count elements of src array from index sindex \n" + "to dst array at position dindex \n" + "memory can overlap" + ) + }, + { + "tarray/swap-bytes", cfun_typed_array_swap_bytes, + JDOC("(tarray/swap-bytes src sindex dst dindex [count=1])\n\n" + "swap count elements between src array from index sindex \n" + "and dst array at position dindex \n" + "memory can overlap" + ) + }, + { + "tarray/slice", cfun_typed_array_slice, + JDOC("(tarray/slice tarr [, start=0 [, end=(size tarr)]])\n\n" + "Takes a slice of typed array from start to end. The range is half" + "open, [start, end). Indexes can also be negative, indicating indexing" + "from the end of the end of the typed array. By default, start is 0 and end is" + "the size of the typed array. Returns a new janet array.") + }, + { + "abstract/properties", cfun_abstract_properties, + JDOC("(abstract/properties tag)\n\n" + "return abstract type properties as a struct") + }, + + {NULL, NULL, NULL} +}; + + + + +/* Module entry point */ +void janet_lib_typed_array(JanetTable *env) { + janet_core_cfuns(env, NULL, ta_cfuns); + janet_register_abstract_type(&ta_buffer_type); + for (size_t i = 0; i < TA_COUNT_TYPES; i++) { + janet_register_abstract_type(ta_array_types + i); + } +} diff --git a/src/core/util.c b/src/core/util.c index c51a7cd5..0351a929 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -284,6 +284,38 @@ void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns) } } +/* Abstract type introspection */ + +static const JanetAbstractType type_wrap = {"core/type_info", NULL, NULL, NULL, NULL, NULL, NULL}; + +typedef struct { + const JanetAbstractType *at; +} JanetAbstractTypeWrap; + + +void janet_register_abstract_type(const JanetAbstractType *at) { + JanetAbstractTypeWrap *abstract = (JanetAbstractTypeWrap *)janet_abstract(&type_wrap, sizeof(JanetAbstractTypeWrap)); + abstract->at = at; + if (!(janet_checktype(janet_table_get(janet_vm_registry, janet_ckeywordv(at->name)), JANET_NIL))) { + janet_panic("Register abstract type fail, a type with same name exists"); + } + janet_table_put(janet_vm_registry, janet_ckeywordv(at->name), janet_wrap_abstract(abstract)); +} + + +const JanetAbstractType *janet_get_abstract_type(Janet key) { + Janet twrap = janet_table_get(janet_vm_registry, key); + if (janet_checktype(twrap, JANET_NIL)) { + return NULL; + } + if (!janet_checktype(twrap, JANET_ABSTRACT) || (janet_abstract_type(janet_unwrap_abstract(twrap)) != &type_wrap)) { + janet_panic("expected abstract type"); + } + JanetAbstractTypeWrap *w = (JanetAbstractTypeWrap *)janet_unwrap_abstract(twrap); + return w->at; +} + + #ifndef JANET_BOOTSTRAP void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p) { (void) p; diff --git a/src/core/util.h b/src/core/util.h index ab8d887f..42058dc2 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -89,5 +89,10 @@ void janet_lib_debug(JanetTable *env); #ifdef JANET_PEG void janet_lib_peg(JanetTable *env); #endif +#ifdef JANET_TYPED_ARRAY +void janet_lib_typed_array(JanetTable *env); +#endif + + #endif diff --git a/src/include/janet.h b/src/include/janet.h index 0adc5f67..d1e811e1 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -128,6 +128,12 @@ extern "C" { #define JANET_PEG #endif +/* Enable or disable the typedarray module */ +#ifndef JANET_NO_TYPED_ARRAY +#define JANET_TYPED_ARRAY +#endif + + /* How to export symbols */ #ifndef JANET_API #ifdef JANET_WINDOWS @@ -798,6 +804,13 @@ struct JanetParser { int lookback; }; +typedef struct { + void *m_state; /* void* to not expose MarshalState ?*/ + void *u_state; + int flags; + const uint8_t *data; +} JanetMarshalContext; + /* Defines an abstract type */ struct JanetAbstractType { const char *name; @@ -805,6 +818,8 @@ struct JanetAbstractType { int (*gcmark)(void *data, size_t len); Janet(*get)(void *data, Janet key); void (*put)(void *data, Janet key, Janet value); + void (*marshal)(void *p, JanetMarshalContext *ctx); + void (*unmarshal)(void *p, JanetMarshalContext *ctx); }; struct JanetReg { @@ -1235,6 +1250,63 @@ JANET_API JanetRange janet_getslice(int32_t argc, const Janet *argv); JANET_API int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const char *which); JANET_API int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which); + +/* Marshal API */ +JANET_API void janet_marshal_int(JanetMarshalContext *ctx, int32_t value); +JANET_API void janet_marshal_byte(JanetMarshalContext *ctx, uint8_t value); +JANET_API void janet_marshal_bytes(JanetMarshalContext *ctx, const uint8_t *bytes, int32_t len); +JANET_API void janet_marshal_janet(JanetMarshalContext *ctx, Janet x); + +JANET_API void janet_unmarshal_int(JanetMarshalContext *ctx, int32_t *i); +JANET_API void janet_unmarshal_uint(JanetMarshalContext *ctx, uint32_t *i); +JANET_API void janet_unmarshal_size(JanetMarshalContext *ctx, size_t *i); +JANET_API void janet_unmarshal_byte(JanetMarshalContext *ctx, uint8_t *b); +JANET_API void janet_unmarshal_bytes(JanetMarshalContext *ctx, uint8_t *dest, int32_t len); +JANET_API void janet_unmarshal_janet(JanetMarshalContext *ctx, Janet *out); + +JANET_API void janet_register_abstract_type(const JanetAbstractType *at); +JANET_API const JanetAbstractType *janet_get_abstract_type(Janet key); + +#ifdef JANET_TYPED_ARRAY + +typedef enum { + JANET_TARRAY_TYPE_uint8, + JANET_TARRAY_TYPE_int8, + JANET_TARRAY_TYPE_uint16, + JANET_TARRAY_TYPE_int16, + JANET_TARRAY_TYPE_uint32, + JANET_TARRAY_TYPE_int32, + JANET_TARRAY_TYPE_uint64, + JANET_TARRAY_TYPE_int64, + JANET_TARRAY_TYPE_float32, + JANET_TARRAY_TYPE_float64, + JANET_TARRAY_TYPE_any, +} JanetTArrayType; + +typedef struct { + uint8_t *data; + size_t size; + uint32_t flags; +} JanetTArrayBuffer; + +typedef struct { + JanetTArrayBuffer *buffer; + void *data; /* pointer inside buffer->data */ + size_t size; + size_t stride; + JanetTArrayType type; +} JanetTArrayView; + +JANET_API JanetTArrayBuffer *janet_tarray_buffer(size_t size); +JANET_API JanetTArrayView *janet_tarray_view(JanetTArrayType type, size_t size, size_t stride, size_t offset, JanetTArrayBuffer *buffer); +JANET_API int janet_is_tarray_view(Janet x, JanetTArrayType type); +JANET_API int janet_tarray_type_size(JanetTArrayType type); +JANET_API JanetTArrayBuffer *janet_gettarray_buffer(const Janet *argv, int32_t n); +JANET_API JanetTArrayView *janet_gettarray_view(const Janet *argv, int32_t n, JanetTArrayType type); + +#endif + + /***** END SECTION MAIN *****/ #ifdef __cplusplus diff --git a/test/suite5.janet b/test/suite5.janet new file mode 100644 index 00000000..083e9e54 --- /dev/null +++ b/test/suite5.janet @@ -0,0 +1,55 @@ +# Copyright (c) 2019 Calvin Rose & contributors +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +(import test/helper :prefix "" :exit true) +(start-suite 5) +# some tests typed array + +(assert-no-error + "create some typed array" + (do + (def a (tarray/new :float64 10)) + (def b (tarray/new :float64 5 2 0 a)) + (def c (tarray/new :uint32 20)) + )) + +(assert-no-error + "create some typed array from buffer" + (do + (def buf (tarray/buffer (+ 64 (* (+ 1 (* (- 10 1) 2)) 8)))) + (def b (tarray/new :float64 10 2 64 buf)))) + +(def a (tarray/new :float64 10)) +(def b (tarray/new :float64 5 2 0 a)) + +(assert-no-error + "fill tarray" + (for i 0 (tarray/length a) + (set (a i) i))) + +(assert (= (tarray/buffer a) (tarray/buffer b)) "tarray views pointing same buffer") +(assert (= (a 2) (b 1) ) "tarray views pointing same buffer") +(assert (= ((tarray/slice b) 3) (b 3) (a 6) 6) "tarray slice") +(assert (= ((tarray/slice b 1) 2) (b 3) (a 6) 6) "tarray slice") + +(assert (= ((unmarshal (marshal b)) 3) (b 3)) "marshal") + +(end-suite) +