1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Buffer protocol proposal (#3261)

* Add buffer protocol

* Modify for some review comments

* Per-object buffer availability

* Rename to MemoryView from Buffer and make compilable

* Support integral repeat count in memory view format

* Support 'x' for padding bytes

* Add rb_memory_view_parse_item_format

* Check type in rb_memory_view_register

* Update dependencies in common.mk

* Add test of MemoryView

* Add test of rb_memory_view_init_as_byte_array

* Add native size format test

* Add MemoryView test utilities

* Add test of rb_memory_view_fill_contiguous_strides

* Skip spaces in format string

* Support endianness specifiers

* Update documentation

* Support alignment

* Use RUBY_ALIGNOF

* Fix format parser to follow the pack format

* Support the _ modifier

* Parse count specifiers in get_format_size function.

* Use STRUCT_ALIGNOF

* Fix test

* Fix test

* Fix total size for the case with tail padding

* Fix rb_memory_view_get_item_pointer

* Fix rb_memory_view_parse_item_format again
This commit is contained in:
Kenta Murata 2020-09-25 20:32:02 +09:00 committed by GitHub
parent 6eeacbbc36
commit 890bc2cdde
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
Notes: git 2020-09-25 20:32:28 +09:00
Merged-By: mrkn <mrkn@ruby-lang.org>
8 changed files with 1621 additions and 0 deletions

162
common.mk
View file

@ -107,6 +107,7 @@ COMMONOBJS = array.$(OBJEXT) \
load.$(OBJEXT) \
marshal.$(OBJEXT) \
math.$(OBJEXT) \
memory_view.$(OBJEXT) \
mjit.$(OBJEXT) \
mjit_compile.$(OBJEXT) \
node.$(OBJEXT) \
@ -7998,6 +7999,167 @@ math.$(OBJEXT): {$(VPATH)}math.c
math.$(OBJEXT): {$(VPATH)}missing.h
math.$(OBJEXT): {$(VPATH)}st.h
math.$(OBJEXT): {$(VPATH)}subst.h
memory_view.$(OBJEXT): $(hdrdir)/ruby/ruby.h
memory_view.$(OBJEXT): $(top_srcdir)/internal/util.h
memory_view.$(OBJEXT): {$(VPATH)}assert.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/assume.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/bool.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/limits.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
memory_view.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
memory_view.$(OBJEXT): {$(VPATH)}config.h
memory_view.$(OBJEXT): {$(VPATH)}defines.h
memory_view.$(OBJEXT): {$(VPATH)}intern.h
memory_view.$(OBJEXT): {$(VPATH)}internal.h
memory_view.$(OBJEXT): {$(VPATH)}internal/anyargs.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
memory_view.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
memory_view.$(OBJEXT): {$(VPATH)}internal/assume.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/const.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/error.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/format.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
memory_view.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
memory_view.$(OBJEXT): {$(VPATH)}internal/cast.h
memory_view.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
memory_view.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
memory_view.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
memory_view.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
memory_view.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
memory_view.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
memory_view.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
memory_view.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
memory_view.$(OBJEXT): {$(VPATH)}internal/config.h
memory_view.$(OBJEXT): {$(VPATH)}internal/constant_p.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/robject.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
memory_view.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
memory_view.$(OBJEXT): {$(VPATH)}internal/ctype.h
memory_view.$(OBJEXT): {$(VPATH)}internal/dllexport.h
memory_view.$(OBJEXT): {$(VPATH)}internal/dosish.h
memory_view.$(OBJEXT): {$(VPATH)}internal/error.h
memory_view.$(OBJEXT): {$(VPATH)}internal/eval.h
memory_view.$(OBJEXT): {$(VPATH)}internal/event.h
memory_view.$(OBJEXT): {$(VPATH)}internal/fl_type.h
memory_view.$(OBJEXT): {$(VPATH)}internal/gc.h
memory_view.$(OBJEXT): {$(VPATH)}internal/glob.h
memory_view.$(OBJEXT): {$(VPATH)}internal/globals.h
memory_view.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
memory_view.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
memory_view.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
memory_view.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
memory_view.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
memory_view.$(OBJEXT): {$(VPATH)}internal/has/extension.h
memory_view.$(OBJEXT): {$(VPATH)}internal/has/feature.h
memory_view.$(OBJEXT): {$(VPATH)}internal/has/warning.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/array.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/class.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/error.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/file.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/gc.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/io.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/load.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/object.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/process.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/random.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/range.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/re.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/select.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/string.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/time.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
memory_view.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
memory_view.$(OBJEXT): {$(VPATH)}internal/interpreter.h
memory_view.$(OBJEXT): {$(VPATH)}internal/iterator.h
memory_view.$(OBJEXT): {$(VPATH)}internal/memory.h
memory_view.$(OBJEXT): {$(VPATH)}internal/method.h
memory_view.$(OBJEXT): {$(VPATH)}internal/module.h
memory_view.$(OBJEXT): {$(VPATH)}internal/newobj.h
memory_view.$(OBJEXT): {$(VPATH)}internal/rgengc.h
memory_view.$(OBJEXT): {$(VPATH)}internal/scan_args.h
memory_view.$(OBJEXT): {$(VPATH)}internal/special_consts.h
memory_view.$(OBJEXT): {$(VPATH)}internal/static_assert.h
memory_view.$(OBJEXT): {$(VPATH)}internal/stdalign.h
memory_view.$(OBJEXT): {$(VPATH)}internal/stdbool.h
memory_view.$(OBJEXT): {$(VPATH)}internal/symbol.h
memory_view.$(OBJEXT): {$(VPATH)}internal/token_paste.h
memory_view.$(OBJEXT): {$(VPATH)}internal/value.h
memory_view.$(OBJEXT): {$(VPATH)}internal/value_type.h
memory_view.$(OBJEXT): {$(VPATH)}internal/variable.h
memory_view.$(OBJEXT): {$(VPATH)}internal/warning_push.h
memory_view.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
memory_view.$(OBJEXT): {$(VPATH)}memory_view.c
memory_view.$(OBJEXT): {$(VPATH)}memory_view.h
memory_view.$(OBJEXT): {$(VPATH)}missing.h
memory_view.$(OBJEXT): {$(VPATH)}st.h
memory_view.$(OBJEXT): {$(VPATH)}subst.h
miniinit.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
miniinit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
miniinit.$(OBJEXT): $(CCAN_DIR)/list/list.h

View file

@ -0,0 +1,164 @@
# AUTOGENERATED DEPENDENCIES START
memory_view.o: $(RUBY_EXTCONF_H)
memory_view.o: $(arch_hdrdir)/ruby/config.h
memory_view.o: $(hdrdir)/ruby.h
memory_view.o: $(hdrdir)/ruby/assert.h
memory_view.o: $(hdrdir)/ruby/backward.h
memory_view.o: $(hdrdir)/ruby/backward/2/assume.h
memory_view.o: $(hdrdir)/ruby/backward/2/attributes.h
memory_view.o: $(hdrdir)/ruby/backward/2/bool.h
memory_view.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h
memory_view.o: $(hdrdir)/ruby/backward/2/inttypes.h
memory_view.o: $(hdrdir)/ruby/backward/2/limits.h
memory_view.o: $(hdrdir)/ruby/backward/2/long_long.h
memory_view.o: $(hdrdir)/ruby/backward/2/stdalign.h
memory_view.o: $(hdrdir)/ruby/backward/2/stdarg.h
memory_view.o: $(hdrdir)/ruby/defines.h
memory_view.o: $(hdrdir)/ruby/intern.h
memory_view.o: $(hdrdir)/ruby/internal/anyargs.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/char.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/double.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/int.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/long.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/short.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
memory_view.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
memory_view.o: $(hdrdir)/ruby/internal/assume.h
memory_view.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
memory_view.o: $(hdrdir)/ruby/internal/attr/artificial.h
memory_view.o: $(hdrdir)/ruby/internal/attr/cold.h
memory_view.o: $(hdrdir)/ruby/internal/attr/const.h
memory_view.o: $(hdrdir)/ruby/internal/attr/constexpr.h
memory_view.o: $(hdrdir)/ruby/internal/attr/deprecated.h
memory_view.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
memory_view.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
memory_view.o: $(hdrdir)/ruby/internal/attr/error.h
memory_view.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
memory_view.o: $(hdrdir)/ruby/internal/attr/forceinline.h
memory_view.o: $(hdrdir)/ruby/internal/attr/format.h
memory_view.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
memory_view.o: $(hdrdir)/ruby/internal/attr/noalias.h
memory_view.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
memory_view.o: $(hdrdir)/ruby/internal/attr/noexcept.h
memory_view.o: $(hdrdir)/ruby/internal/attr/noinline.h
memory_view.o: $(hdrdir)/ruby/internal/attr/nonnull.h
memory_view.o: $(hdrdir)/ruby/internal/attr/noreturn.h
memory_view.o: $(hdrdir)/ruby/internal/attr/pure.h
memory_view.o: $(hdrdir)/ruby/internal/attr/restrict.h
memory_view.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
memory_view.o: $(hdrdir)/ruby/internal/attr/warning.h
memory_view.o: $(hdrdir)/ruby/internal/attr/weakref.h
memory_view.o: $(hdrdir)/ruby/internal/cast.h
memory_view.o: $(hdrdir)/ruby/internal/compiler_is.h
memory_view.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
memory_view.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
memory_view.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
memory_view.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
memory_view.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
memory_view.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
memory_view.o: $(hdrdir)/ruby/internal/compiler_since.h
memory_view.o: $(hdrdir)/ruby/internal/config.h
memory_view.o: $(hdrdir)/ruby/internal/constant_p.h
memory_view.o: $(hdrdir)/ruby/internal/core.h
memory_view.o: $(hdrdir)/ruby/internal/core/rarray.h
memory_view.o: $(hdrdir)/ruby/internal/core/rbasic.h
memory_view.o: $(hdrdir)/ruby/internal/core/rbignum.h
memory_view.o: $(hdrdir)/ruby/internal/core/rclass.h
memory_view.o: $(hdrdir)/ruby/internal/core/rdata.h
memory_view.o: $(hdrdir)/ruby/internal/core/rfile.h
memory_view.o: $(hdrdir)/ruby/internal/core/rhash.h
memory_view.o: $(hdrdir)/ruby/internal/core/robject.h
memory_view.o: $(hdrdir)/ruby/internal/core/rregexp.h
memory_view.o: $(hdrdir)/ruby/internal/core/rstring.h
memory_view.o: $(hdrdir)/ruby/internal/core/rstruct.h
memory_view.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
memory_view.o: $(hdrdir)/ruby/internal/ctype.h
memory_view.o: $(hdrdir)/ruby/internal/dllexport.h
memory_view.o: $(hdrdir)/ruby/internal/dosish.h
memory_view.o: $(hdrdir)/ruby/internal/error.h
memory_view.o: $(hdrdir)/ruby/internal/eval.h
memory_view.o: $(hdrdir)/ruby/internal/event.h
memory_view.o: $(hdrdir)/ruby/internal/fl_type.h
memory_view.o: $(hdrdir)/ruby/internal/gc.h
memory_view.o: $(hdrdir)/ruby/internal/glob.h
memory_view.o: $(hdrdir)/ruby/internal/globals.h
memory_view.o: $(hdrdir)/ruby/internal/has/attribute.h
memory_view.o: $(hdrdir)/ruby/internal/has/builtin.h
memory_view.o: $(hdrdir)/ruby/internal/has/c_attribute.h
memory_view.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
memory_view.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
memory_view.o: $(hdrdir)/ruby/internal/has/extension.h
memory_view.o: $(hdrdir)/ruby/internal/has/feature.h
memory_view.o: $(hdrdir)/ruby/internal/has/warning.h
memory_view.o: $(hdrdir)/ruby/internal/intern/array.h
memory_view.o: $(hdrdir)/ruby/internal/intern/bignum.h
memory_view.o: $(hdrdir)/ruby/internal/intern/class.h
memory_view.o: $(hdrdir)/ruby/internal/intern/compar.h
memory_view.o: $(hdrdir)/ruby/internal/intern/complex.h
memory_view.o: $(hdrdir)/ruby/internal/intern/cont.h
memory_view.o: $(hdrdir)/ruby/internal/intern/dir.h
memory_view.o: $(hdrdir)/ruby/internal/intern/enum.h
memory_view.o: $(hdrdir)/ruby/internal/intern/enumerator.h
memory_view.o: $(hdrdir)/ruby/internal/intern/error.h
memory_view.o: $(hdrdir)/ruby/internal/intern/eval.h
memory_view.o: $(hdrdir)/ruby/internal/intern/file.h
memory_view.o: $(hdrdir)/ruby/internal/intern/gc.h
memory_view.o: $(hdrdir)/ruby/internal/intern/hash.h
memory_view.o: $(hdrdir)/ruby/internal/intern/io.h
memory_view.o: $(hdrdir)/ruby/internal/intern/load.h
memory_view.o: $(hdrdir)/ruby/internal/intern/marshal.h
memory_view.o: $(hdrdir)/ruby/internal/intern/numeric.h
memory_view.o: $(hdrdir)/ruby/internal/intern/object.h
memory_view.o: $(hdrdir)/ruby/internal/intern/parse.h
memory_view.o: $(hdrdir)/ruby/internal/intern/proc.h
memory_view.o: $(hdrdir)/ruby/internal/intern/process.h
memory_view.o: $(hdrdir)/ruby/internal/intern/random.h
memory_view.o: $(hdrdir)/ruby/internal/intern/range.h
memory_view.o: $(hdrdir)/ruby/internal/intern/rational.h
memory_view.o: $(hdrdir)/ruby/internal/intern/re.h
memory_view.o: $(hdrdir)/ruby/internal/intern/ruby.h
memory_view.o: $(hdrdir)/ruby/internal/intern/select.h
memory_view.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
memory_view.o: $(hdrdir)/ruby/internal/intern/signal.h
memory_view.o: $(hdrdir)/ruby/internal/intern/sprintf.h
memory_view.o: $(hdrdir)/ruby/internal/intern/string.h
memory_view.o: $(hdrdir)/ruby/internal/intern/struct.h
memory_view.o: $(hdrdir)/ruby/internal/intern/thread.h
memory_view.o: $(hdrdir)/ruby/internal/intern/time.h
memory_view.o: $(hdrdir)/ruby/internal/intern/variable.h
memory_view.o: $(hdrdir)/ruby/internal/intern/vm.h
memory_view.o: $(hdrdir)/ruby/internal/interpreter.h
memory_view.o: $(hdrdir)/ruby/internal/iterator.h
memory_view.o: $(hdrdir)/ruby/internal/memory.h
memory_view.o: $(hdrdir)/ruby/internal/method.h
memory_view.o: $(hdrdir)/ruby/internal/module.h
memory_view.o: $(hdrdir)/ruby/internal/newobj.h
memory_view.o: $(hdrdir)/ruby/internal/rgengc.h
memory_view.o: $(hdrdir)/ruby/internal/scan_args.h
memory_view.o: $(hdrdir)/ruby/internal/special_consts.h
memory_view.o: $(hdrdir)/ruby/internal/static_assert.h
memory_view.o: $(hdrdir)/ruby/internal/stdalign.h
memory_view.o: $(hdrdir)/ruby/internal/stdbool.h
memory_view.o: $(hdrdir)/ruby/internal/symbol.h
memory_view.o: $(hdrdir)/ruby/internal/token_paste.h
memory_view.o: $(hdrdir)/ruby/internal/value.h
memory_view.o: $(hdrdir)/ruby/internal/value_type.h
memory_view.o: $(hdrdir)/ruby/internal/variable.h
memory_view.o: $(hdrdir)/ruby/internal/warning_push.h
memory_view.o: $(hdrdir)/ruby/internal/xmalloc.h
memory_view.o: $(hdrdir)/ruby/memory_view.h
memory_view.o: $(hdrdir)/ruby/missing.h
memory_view.o: $(hdrdir)/ruby/ruby.h
memory_view.o: $(hdrdir)/ruby/st.h
memory_view.o: $(hdrdir)/ruby/subst.h
memory_view.o: memory_view.c
# AUTOGENERATED DEPENDENCIES END

View file

@ -0,0 +1,3 @@
# frozen_string_literal: false
require_relative "../auto_ext.rb"
auto_ext(inc: true)

View file

@ -0,0 +1,400 @@
#include "ruby.h"
#include "ruby/memory_view.h"
#define STRUCT_ALIGNOF(T, result) do { \
struct S { char _; T t; }; \
(result) = (int)offsetof(struct S, t); \
} while(0)
static ID id_str;
static VALUE sym_format;
static VALUE sym_native_size_p;
static VALUE sym_offset;
static VALUE sym_size;
static VALUE sym_repeat;
static VALUE sym_obj;
static VALUE sym_len;
static VALUE sym_readonly;
static VALUE sym_format;
static VALUE sym_item_size;
static VALUE sym_ndim;
static VALUE sym_shape;
static VALUE sym_strides;
static VALUE sym_sub_offsets;
static VALUE sym_endianness;
static VALUE sym_little_endian;
static VALUE sym_big_endian;
static VALUE exported_objects;
static int
exportable_string_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags)
{
VALUE str = rb_ivar_get(obj, id_str);
rb_memory_view_init_as_byte_array(view, obj, RSTRING_PTR(str), RSTRING_LEN(str), true);
VALUE count = rb_hash_lookup2(exported_objects, obj, INT2FIX(0));
count = rb_funcall(count, '+', 1, INT2FIX(1));
rb_hash_aset(exported_objects, obj, count);
return 1;
}
static int
exportable_string_release_memory_view(VALUE obj, rb_memory_view_t *view)
{
VALUE count = rb_hash_lookup2(exported_objects, obj, INT2FIX(0));
if (INT2FIX(1) == count) {
rb_hash_delete(exported_objects, obj);
}
else if (INT2FIX(0) == count) {
rb_raise(rb_eRuntimeError, "Duplicated releasing of a memory view has been occurred for %"PRIsVALUE, obj);
}
else {
count = rb_funcall(count, '-', 1, INT2FIX(1));
rb_hash_aset(exported_objects, obj, count);
}
return 1;
}
static int
exportable_string_memory_view_available_p(VALUE obj)
{
return Qtrue;
}
static const rb_memory_view_entry_t exportable_string_memory_view_entry = {
exportable_string_get_memory_view,
exportable_string_release_memory_view,
exportable_string_memory_view_available_p
};
static VALUE
memory_view_available_p(VALUE mod, VALUE obj)
{
return rb_memory_view_available_p(obj) ? Qtrue : Qfalse;
}
static VALUE
memory_view_register(VALUE mod, VALUE obj)
{
return rb_memory_view_register(obj, &exportable_string_memory_view_entry) ? Qtrue : Qfalse;
}
static VALUE
memory_view_item_size_from_format(VALUE mod, VALUE format)
{
const char *c_str = NULL;
if (!NIL_P(format))
c_str = StringValueCStr(format);
const char *err = NULL;
ssize_t item_size = rb_memory_view_item_size_from_format(c_str, &err);
if (!err)
return rb_assoc_new(SSIZET2NUM(item_size), Qnil);
else
return rb_assoc_new(SSIZET2NUM(item_size), rb_str_new_cstr(err));
}
static VALUE
memory_view_parse_item_format(VALUE mod, VALUE format)
{
const char *c_str = NULL;
if (!NIL_P(format))
c_str = StringValueCStr(format);
const char *err = NULL;
rb_memory_view_item_component_t *members;
ssize_t n_members;
ssize_t item_size = rb_memory_view_parse_item_format(c_str, &members, &n_members, &err);
VALUE result = rb_ary_new_capa(3);
rb_ary_push(result, SSIZET2NUM(item_size));
if (!err) {
VALUE ary = rb_ary_new_capa(n_members);
ssize_t i;
for (i = 0; i < n_members; ++i) {
VALUE member = rb_hash_new();
rb_hash_aset(member, sym_format, rb_str_new(&members[i].format, 1));
rb_hash_aset(member, sym_native_size_p, members[i].native_size_p ? Qtrue : Qfalse);
rb_hash_aset(member, sym_endianness, members[i].little_endian_p ? sym_little_endian : sym_big_endian);
rb_hash_aset(member, sym_offset, SSIZET2NUM(members[i].offset));
rb_hash_aset(member, sym_size, SSIZET2NUM(members[i].size));
rb_hash_aset(member, sym_repeat, SSIZET2NUM(members[i].repeat));
rb_ary_push(ary, member);
}
xfree(members);
rb_ary_push(result, ary);
rb_ary_push(result, Qnil);
}
else {
rb_ary_push(result, Qnil); // members
rb_ary_push(result, rb_str_new_cstr(err));
}
return result;
}
static VALUE
memory_view_get_memory_view_info(VALUE mod, VALUE obj)
{
rb_memory_view_t view;
if (!rb_memory_view_get(obj, &view, 0)) {
return Qnil;
}
VALUE hash = rb_hash_new();
rb_hash_aset(hash, sym_obj, view.obj);
rb_hash_aset(hash, sym_len, SSIZET2NUM(view.len));
rb_hash_aset(hash, sym_readonly, view.readonly ? Qtrue : Qfalse);
rb_hash_aset(hash, sym_format, view.format ? rb_str_new_cstr(view.format) : Qnil);
rb_hash_aset(hash, sym_item_size, SSIZET2NUM(view.item_size));
rb_hash_aset(hash, sym_ndim, SSIZET2NUM(view.ndim));
if (view.shape) {
VALUE shape = rb_ary_new_capa(view.ndim);
rb_hash_aset(hash, sym_shape, shape);
}
else {
rb_hash_aset(hash, sym_shape, Qnil);
}
if (view.strides) {
VALUE strides = rb_ary_new_capa(view.ndim);
rb_hash_aset(hash, sym_strides, strides);
}
else {
rb_hash_aset(hash, sym_strides, Qnil);
}
if (view.sub_offsets) {
VALUE sub_offsets = rb_ary_new_capa(view.ndim);
rb_hash_aset(hash, sym_sub_offsets, sub_offsets);
}
else {
rb_hash_aset(hash, sym_sub_offsets, Qnil);
}
rb_memory_view_release(&view);
return hash;
}
static VALUE
memory_view_fill_contiguous_strides(VALUE mod, VALUE ndim_v, VALUE item_size_v, VALUE shape_v, VALUE row_major_p)
{
int i, ndim = FIX2INT(ndim_v);
Check_Type(shape_v, T_ARRAY);
ssize_t *shape = ALLOC_N(ssize_t, ndim);
for (i = 0; i < ndim; ++i) {
shape[i] = NUM2SSIZET(RARRAY_AREF(shape_v, i));
}
ssize_t *strides = ALLOC_N(ssize_t, ndim);
rb_memory_view_fill_contiguous_strides(ndim, NUM2SSIZET(item_size_v), shape, RTEST(row_major_p), strides);
VALUE result = rb_ary_new_capa(ndim);
for (i = 0; i < ndim; ++i) {
rb_ary_push(result, SSIZET2NUM(strides[i]));
}
xfree(strides);
xfree(shape);
return result;
}
static VALUE
expstr_initialize(VALUE obj, VALUE s)
{
rb_ivar_set(obj, id_str, s);
return Qnil;
}
static int
mdview_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags)
{
VALUE buf_v = rb_ivar_get(obj, id_str);
VALUE shape_v = rb_ivar_get(obj, SYM2ID(sym_shape));
VALUE strides_v = rb_ivar_get(obj, SYM2ID(sym_strides));
ssize_t i, ndim = RARRAY_LEN(shape_v);
ssize_t *shape = ALLOC_N(ssize_t, ndim);
ssize_t *strides = NULL;
if (!NIL_P(strides_v)) {
if (RARRAY_LEN(strides_v) != ndim) {
rb_raise(rb_eArgError, "strides has an invalid dimension");
}
strides = ALLOC_N(ssize_t, ndim);
for (i = 0; i < ndim; ++i) {
shape[i] = NUM2SSIZET(RARRAY_AREF(shape_v, i));
strides[i] = NUM2SSIZET(RARRAY_AREF(strides_v, i));
}
}
else {
for (i = 0; i < ndim; ++i) {
shape[i] = NUM2SSIZET(RARRAY_AREF(shape_v, i));
}
}
rb_memory_view_init_as_byte_array(view, obj, RSTRING_PTR(buf_v), RSTRING_LEN(buf_v), true);
view->format = "l";
view->item_size = sizeof(long);
view->ndim = ndim;
view->shape = shape;
view->strides = strides;
VALUE count = rb_hash_lookup2(exported_objects, obj, INT2FIX(0));
count = rb_funcall(count, '+', 1, INT2FIX(1));
rb_hash_aset(exported_objects, obj, count);
return 1;
}
static int
mdview_release_memory_view(VALUE obj, rb_memory_view_t *view)
{
VALUE count = rb_hash_lookup2(exported_objects, obj, INT2FIX(0));
if (INT2FIX(1) == count) {
rb_hash_delete(exported_objects, obj);
}
else if (INT2FIX(0) == count) {
rb_raise(rb_eRuntimeError, "Duplicated releasing of a memory view has been occurred for %"PRIsVALUE, obj);
}
else {
count = rb_funcall(count, '-', 1, INT2FIX(1));
rb_hash_aset(exported_objects, obj, count);
}
return 1;
}
static int
mdview_memory_view_available_p(VALUE obj)
{
return true;
}
static const rb_memory_view_entry_t mdview_memory_view_entry = {
mdview_get_memory_view,
mdview_release_memory_view,
mdview_memory_view_available_p
};
static VALUE
mdview_initialize(VALUE obj, VALUE buf, VALUE shape, VALUE strides)
{
Check_Type(buf, T_STRING);
Check_Type(shape, T_ARRAY);
if (!NIL_P(strides)) Check_Type(strides, T_ARRAY);
rb_ivar_set(obj, id_str, buf);
rb_ivar_set(obj, SYM2ID(sym_shape), shape);
rb_ivar_set(obj, SYM2ID(sym_strides), strides);
return Qnil;
}
static VALUE
mdview_aref(VALUE obj, VALUE indices_v)
{
Check_Type(indices_v, T_ARRAY);
rb_memory_view_t view;
if (!rb_memory_view_get(obj, &view, 0)) {
rb_raise(rb_eRuntimeError, "rb_memory_view_get: failed");
}
if (RARRAY_LEN(indices_v) != view.ndim) {
rb_raise(rb_eKeyError, "Indices has an invalid dimension");
}
VALUE buf_indices;
ssize_t *indices = ALLOCV_N(ssize_t, buf_indices, view.ndim);
ssize_t i;
for (i = 0; i < view.ndim; ++i) {
indices[i] = NUM2SSIZET(RARRAY_AREF(indices_v, i));
}
char *ptr = rb_memory_view_get_item_pointer(&view, indices);
ALLOCV_END(buf_indices);
long x = *(long *)ptr;
VALUE result = LONG2FIX(x);
rb_memory_view_release(&view);
return result;
}
void
Init_memory_view(void)
{
VALUE mMemoryViewTestUtils = rb_define_module("MemoryViewTestUtils");
rb_define_module_function(mMemoryViewTestUtils, "available?", memory_view_available_p, 1);
rb_define_module_function(mMemoryViewTestUtils, "register", memory_view_register, 1);
rb_define_module_function(mMemoryViewTestUtils, "item_size_from_format", memory_view_item_size_from_format, 1);
rb_define_module_function(mMemoryViewTestUtils, "parse_item_format", memory_view_parse_item_format, 1);
rb_define_module_function(mMemoryViewTestUtils, "get_memory_view_info", memory_view_get_memory_view_info, 1);
rb_define_module_function(mMemoryViewTestUtils, "fill_contiguous_strides", memory_view_fill_contiguous_strides, 4);
VALUE cExportableString = rb_define_class_under(mMemoryViewTestUtils, "ExportableString", rb_cObject);
rb_define_method(cExportableString, "initialize", expstr_initialize, 1);
rb_memory_view_register(cExportableString, &exportable_string_memory_view_entry);
VALUE cMDView = rb_define_class_under(mMemoryViewTestUtils, "MultiDimensionalView", rb_cObject);
rb_define_method(cMDView, "initialize", mdview_initialize, 3);
rb_define_method(cMDView, "[]", mdview_aref, 1);
rb_memory_view_register(cMDView, &mdview_memory_view_entry);
id_str = rb_intern("__str__");
sym_format = ID2SYM(rb_intern("format"));
sym_native_size_p = ID2SYM(rb_intern("native_size_p"));
sym_offset = ID2SYM(rb_intern("offset"));
sym_size = ID2SYM(rb_intern("size"));
sym_repeat = ID2SYM(rb_intern("repeat"));
sym_obj = ID2SYM(rb_intern("obj"));
sym_len = ID2SYM(rb_intern("len"));
sym_readonly = ID2SYM(rb_intern("readonly"));
sym_format = ID2SYM(rb_intern("format"));
sym_item_size = ID2SYM(rb_intern("item_size"));
sym_ndim = ID2SYM(rb_intern("ndim"));
sym_shape = ID2SYM(rb_intern("shape"));
sym_strides = ID2SYM(rb_intern("strides"));
sym_sub_offsets = ID2SYM(rb_intern("sub_offsets"));
sym_endianness = ID2SYM(rb_intern("endianness"));
sym_little_endian = ID2SYM(rb_intern("little_endian"));
sym_big_endian = ID2SYM(rb_intern("big_endian"));
#ifdef WORDS_BIGENDIAN
rb_const_set(mMemoryViewTestUtils, rb_intern("NATIVE_ENDIAN"), sym_big_endian);
#else
rb_const_set(mMemoryViewTestUtils, rb_intern("NATIVE_ENDIAN"), sym_little_endian);
#endif
#define DEF_ALIGNMENT_CONST(type, TYPE) do { \
int alignment; \
STRUCT_ALIGNOF(type, alignment); \
rb_const_set(mMemoryViewTestUtils, rb_intern(#TYPE "_ALIGNMENT"), INT2FIX(alignment)); \
} while(0)
DEF_ALIGNMENT_CONST(short, SHORT);
DEF_ALIGNMENT_CONST(int, INT);
DEF_ALIGNMENT_CONST(long, LONG);
DEF_ALIGNMENT_CONST(LONG_LONG, LONG_LONG);
DEF_ALIGNMENT_CONST(int16_t, INT16);
DEF_ALIGNMENT_CONST(int32_t, INT32);
DEF_ALIGNMENT_CONST(int64_t, INT64);
DEF_ALIGNMENT_CONST(intptr_t, INTPTR);
DEF_ALIGNMENT_CONST(float, FLOAT);
DEF_ALIGNMENT_CONST(double, DOUBLE);
#undef DEF_ALIGNMENT_CONST
exported_objects = rb_hash_new();
rb_gc_register_mark_object(exported_objects);
}

136
include/ruby/memory_view.h Normal file
View file

@ -0,0 +1,136 @@
#ifndef RUBY_MEMORY_VIEW_H
#define RUBY_MEMORY_VIEW_H 1
/**
* @file
* @author Ruby developers <ruby-core@ruby-lang.org>
* @copyright This file is a part of the programming language Ruby.
* Permission is hereby granted, to either redistribute and/or
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
* @brief Memory View.
*/
#include "ruby/internal/dllexport.h"
#include "ruby/internal/stdbool.h"
#include "ruby/internal/value.h"
#include "ruby/intern.h"
enum ruby_memory_view_flags {
RUBY_MEMORY_VIEW_SIMPLE = 0,
RUBY_MEMORY_VIEW_WRITABLE = (1<<0),
RUBY_MEMORY_VIEW_FORMAT = (1<<1),
RUBY_MEMORY_VIEW_MULTI_DIMENSIONAL = (1<<2),
RUBY_MEMORY_VIEW_STRIDES = (1<<3) | RUBY_MEMORY_VIEW_MULTI_DIMENSIONAL,
RUBY_MEMORY_VIEW_ROW_MAJOR = (1<<4) | RUBY_MEMORY_VIEW_STRIDES,
RUBY_MEMORY_VIEW_COLUMN_MAJOR = (1<<5) | RUBY_MEMORY_VIEW_STRIDES,
RUBY_MEMORY_VIEW_ANY_CONTIGUOUS = RUBY_MEMORY_VIEW_ROW_MAJOR | RUBY_MEMORY_VIEW_COLUMN_MAJOR,
RUBY_MEMORY_VIEW_INDIRECT = (1<<6) | RUBY_MEMORY_VIEW_STRIDES,
};
typedef struct {
char format;
unsigned native_size_p: 1;
unsigned little_endian_p: 1;
size_t offset;
size_t size;
size_t repeat;
} rb_memory_view_item_component_t;
typedef struct {
/* The original object that have the memory exported via this memory view.
* The consumer of this memory view has the responsibility to call rb_gc_mark
* for preventing this obj collected by GC. */
VALUE obj;
/* The pointer to the exported memory. */
void *data;
/* The number of bytes in data. */
ssize_t len;
/* 1 for readonly memory, 0 for writable memory. */
int readonly;
/* A string to describe the format of an element, or NULL for unsigned byte.
* The format string is a sequence the following pack-template specifiers:
*
* c, C, s, s!, S, S!, n, v, i, i!, I, I!, l, l!,
* L, L!, N, V, f, e, g, d, E, G, j, J, x
*
* For example, "dd" for an element that consists of two double values,
* and "CCC" for an element that consists of three bytes, such as
* a RGB color triplet.
*
* Also, the value endianness can be explicitly specified by '<' or '>'
* following a value type specifier.
*/
const char *format;
/* The number of bytes in each element.
* item_size should equal to rb_memory_view_item_size_from_format(format). */
ssize_t item_size;
struct {
/* The array of rb_memory_view_item_component_t that describes the
* item structure. */
rb_memory_view_item_component_t *components;
/* The number of components in an item. */
ssize_t length;
} item_desc;
/* The number of dimension. */
int ndim;
/* ndim size array indicating the number of elements in each dimension.
* This can be NULL when ndim == 1. */
ssize_t *shape;
/* ndim size array indicating the number of bytes to skip to go to the
* next element in each dimension. */
ssize_t *strides;
/* The offset in each dimension when this memory view exposes a nested array.
* Or, NULL when this memory view exposes a flat array. */
ssize_t *sub_offsets;
/* the private data for managing this exported memory */
void *const private;
} rb_memory_view_t;
typedef int (* rb_memory_view_get_func_t)(VALUE obj, rb_memory_view_t *view, int flags);
typedef int (* rb_memory_view_release_func_t)(VALUE obj, rb_memory_view_t *view);
typedef int (* rb_memory_view_available_p_func_t)(VALUE obj);
typedef struct {
rb_memory_view_get_func_t get_func;
rb_memory_view_release_func_t release_func;
rb_memory_view_available_p_func_t available_p_func;
} rb_memory_view_entry_t;
RBIMPL_SYMBOL_EXPORT_BEGIN()
/* memory_view.c */
bool rb_memory_view_register(VALUE klass, const rb_memory_view_entry_t *entry);
#define rb_memory_view_is_contiguous(view) ( \
rb_memory_view_is_row_major_contiguous(view) \
|| rb_memory_view_is_column_major_contiguous(view))
int rb_memory_view_is_row_major_contiguous(const rb_memory_view_t *view);
int rb_memory_view_is_column_major_contiguous(const rb_memory_view_t *view);
void rb_memory_view_fill_contiguous_strides(const int ndim, const int item_size, const ssize_t *const shape, const int row_major_p, ssize_t *const strides);
int rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const int readonly);
ssize_t rb_memory_view_parse_item_format(const char *format,
rb_memory_view_item_component_t **members,
ssize_t *n_members, const char **err);
ssize_t rb_memory_view_item_size_from_format(const char *format, const char **err);
void *rb_memory_view_get_item_pointer(rb_memory_view_t *view, const ssize_t *indices);
int rb_memory_view_available_p(VALUE obj);
int rb_memory_view_get(VALUE obj, rb_memory_view_t* memory_view, int flags);
int rb_memory_view_release(rb_memory_view_t* memory_view);
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RUBY_BUFFER_H */

View file

@ -70,6 +70,7 @@ rb_call_inits(void)
CALL(Cont);
CALL(Rational);
CALL(Complex);
CALL(MemoryView);
CALL(version);
CALL(vm_trace);
CALL(vm_stack_canary);

506
memory_view.c Normal file
View file

@ -0,0 +1,506 @@
/**********************************************************************
memory_view.c - Memory View
Copyright (C) 2020 Kenta Murata <mrkn@mrkn.jp>
**********************************************************************/
#include "internal.h"
#include "internal/util.h"
#include "ruby/memory_view.h"
#define STRUCT_ALIGNOF(T, result) do { \
struct S { char _; T t; }; \
(result) = (int)offsetof(struct S, t); \
} while(0)
static ID id_memory_view;
static const rb_data_type_t memory_view_entry_data_type = {
"memory_view",
{
0,
0,
0,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
/* Register memory view functions for the given class */
bool
rb_memory_view_register(VALUE klass, const rb_memory_view_entry_t *entry) {
Check_Type(klass, T_CLASS);
VALUE entry_obj = rb_ivar_get(klass, id_memory_view);
if (! NIL_P(entry_obj)) {
rb_warning("Duplicated registration of memory view to %"PRIsVALUE, klass);
return 0;
}
else {
entry_obj = TypedData_Wrap_Struct(0, &memory_view_entry_data_type, (void *)entry);
rb_ivar_set(klass, id_memory_view, entry_obj);
return 1;
}
}
/* Examine whether the given memory view has row-major order strides. */
int
rb_memory_view_is_row_major_contiguous(const rb_memory_view_t *view)
{
const ssize_t ndim = view->ndim;
const ssize_t *shape = view->shape;
const ssize_t *strides = view->strides;
ssize_t n = view->item_size;
ssize_t i;
for (i = ndim - 1; i >= 0; --i) {
if (strides[i] != n) return 0;
n *= shape[i];
}
return 1;
}
/* Examine whether the given memory view has column-major order strides. */
int
rb_memory_view_is_column_major_contiguous(const rb_memory_view_t *view)
{
const ssize_t ndim = view->ndim;
const ssize_t *shape = view->shape;
const ssize_t *strides = view->strides;
ssize_t n = view->item_size;
ssize_t i;
for (i = 0; i < ndim; ++i) {
if (strides[i] != n) return 0;
n *= shape[i];
}
return 1;
}
/* Initialize strides array to represent the specified contiguous array. */
void
rb_memory_view_fill_contiguous_strides(const int ndim, const int item_size, const ssize_t *const shape, const int row_major_p, ssize_t *const strides)
{
ssize_t i, n = item_size;
if (row_major_p) {
for (i = ndim - 1; i >= 0; --i) {
strides[i] = n;
n *= shape[i];
}
}
else { // column-major
for (i = 0; i < ndim; ++i) {
strides[i] = n;
n *= shape[i];
}
}
}
/* Initialize view to expose a simple byte array */
int
rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const int readonly)
{
view->obj = obj;
view->data = data;
view->len = len;
view->readonly = readonly;
view->format = NULL;
view->item_size = 1;
view->ndim = 1;
view->shape = NULL;
view->strides = NULL;
view->sub_offsets = NULL;
*((void **)&view->private) = NULL;
return 1;
}
#ifdef HAVE_TRUE_LONG_LONG
static const char native_types[] = "sSiIlLqQjJ";
#else
static const char native_types[] = "sSiIlLjJ";
#endif
static const char endianness_types[] = "sSiIlLqQjJ";
typedef enum {
ENDIANNESS_NATIVE,
ENDIANNESS_LITTLE,
ENDIANNESS_BIG
} endianness_t;
static ssize_t
get_format_size(const char *format, bool *native_p, ssize_t *alignment, endianness_t *endianness, ssize_t *count, const char **next_format, VALUE *error)
{
RUBY_ASSERT(format != NULL);
RUBY_ASSERT(native_p != NULL);
RUBY_ASSERT(endianness != NULL);
RUBY_ASSERT(count != NULL);
RUBY_ASSERT(next_format != NULL);
*native_p = false;
*endianness = ENDIANNESS_NATIVE;
*count = 1;
const int type_char = *format;
int i = 1;
while (format[i]) {
switch (format[i]) {
case '!':
case '_':
if (strchr(native_types, type_char)) {
*native_p = true;
++i;
}
else {
if (error) {
*error = rb_exc_new_str(rb_eArgError,
rb_sprintf("Unable to specify native size for '%c'", type_char));
}
return -1;
}
continue;
case '<':
case '>':
if (!strchr(endianness_types, type_char)) {
if (error) {
*error = rb_exc_new_str(rb_eArgError,
rb_sprintf("Unable to specify endianness for '%c'", type_char));
}
return -1;
}
if (*endianness != ENDIANNESS_NATIVE) {
*error = rb_exc_new_cstr(rb_eArgError, "Unable to use both '<' and '>' multiple times");
return -1;
}
*endianness = (format[i] == '<') ? ENDIANNESS_LITTLE : ENDIANNESS_BIG;
++i;
continue;
default:
break;
}
break;
}
// parse count
int ch = format[i];
if ('0' <= ch && ch <= '9') {
ssize_t n = 0;
while ('0' <= (ch = format[i]) && ch <= '9') {
n = 10*n + ruby_digit36_to_number_table[ch];
++i;
}
*count = n;
}
*next_format = &format[i];
switch (type_char) {
case 'x': // padding
return 1;
case 'c': // signed char
case 'C': // unsigned char
return sizeof(char);
case 's': // s for int16_t, s! for signed short
case 'S': // S for uint16_t, S! for unsigned short
if (*native_p) {
STRUCT_ALIGNOF(short, *alignment);
return sizeof(short);
}
// fall through
case 'n': // n for big-endian 16bit unsigned integer
case 'v': // v for little-endian 16bit unsigned integer
STRUCT_ALIGNOF(int16_t, *alignment);
return 2;
case 'i': // i and i! for signed int
case 'I': // I and I! for unsigned int
STRUCT_ALIGNOF(int, *alignment);
return sizeof(int);
case 'l': // l for int32_t, l! for signed long
case 'L': // L for uint32_t, L! for unsigned long
if (*native_p) {
STRUCT_ALIGNOF(long, *alignment);
return sizeof(long);
}
// fall through
case 'N': // N for big-endian 32bit unsigned integer
case 'V': // V for little-endian 32bit unsigned integer
STRUCT_ALIGNOF(int32_t, *alignment);
return 4;
case 'f': // f for native float
case 'e': // e for little-endian float
case 'g': // g for big-endian float
STRUCT_ALIGNOF(float, *alignment);
return sizeof(float);
case 'q': // q for int64_t, q! for signed long long
case 'Q': // Q for uint64_t, Q! for unsigned long long
if (*native_p) {
STRUCT_ALIGNOF(LONG_LONG, *alignment);
return sizeof(LONG_LONG);
}
STRUCT_ALIGNOF(int64_t, *alignment);
return 8;
case 'd': // d for native double
case 'E': // E for little-endian double
case 'G': // G for big-endian double
STRUCT_ALIGNOF(double, *alignment);
return sizeof(double);
case 'j': // j for intptr_t
case 'J': // J for uintptr_t
STRUCT_ALIGNOF(intptr_t, *alignment);
return sizeof(intptr_t);
default:
*alignment = -1;
if (error) {
*error = rb_exc_new_str(rb_eArgError, rb_sprintf("Invalid type character '%c'", type_char));
}
return -1;
}
}
static inline ssize_t
calculate_padding(ssize_t total, ssize_t alignment_size) {
if (alignment_size > 1) {
ssize_t res = total % alignment_size;
if (res > 0) {
return alignment_size - res;
}
}
return 0;
}
ssize_t
rb_memory_view_parse_item_format(const char *format,
rb_memory_view_item_component_t **members,
ssize_t *n_members, const char **err)
{
if (format == NULL) return 1;
VALUE error = Qnil;
ssize_t total = 0;
ssize_t len = 0;
bool alignment = false;
ssize_t max_alignment_size = 0;
const char *p = format;
if (*p == '|') { // alginment specifier
alignment = true;
++format;
++p;
}
while (*p) {
const char *q = p;
// ignore spaces
if (ISSPACE(*p)) {
while (ISSPACE(*p)) ++p;
continue;
}
bool native_size_p = false;
ssize_t alignment_size = 0;
endianness_t endianness = ENDIANNESS_NATIVE;
ssize_t count = 0;
const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, &error);
if (size < 0) {
if (err) *err = q;
return -1;
}
if (max_alignment_size < alignment_size) {
max_alignment_size = alignment_size;
}
const ssize_t padding = alignment ? calculate_padding(total, alignment_size) : 0;
total += padding + size * count;
if (*q != 'x') {
++len;
}
}
// adjust total size with the alignment size of the largest element
if (alignment && max_alignment_size > 0) {
const ssize_t padding = calculate_padding(total, max_alignment_size);
total += padding;
}
if (members && n_members) {
rb_memory_view_item_component_t *buf = ALLOC_N(rb_memory_view_item_component_t, len);
ssize_t i = 0, offset = 0;
const char *p = format;
while (*p) {
const int type_char = *p;
bool native_size_p;
ssize_t alignment_size = 0;
endianness_t endianness = ENDIANNESS_NATIVE;
ssize_t count = 0;
const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, NULL);
const ssize_t padding = alignment ? calculate_padding(offset, alignment_size) : 0;
offset += padding;
if (type_char != 'x') {
#ifdef WORDS_BIGENDIAN
bool little_endian_p = (endianness == ENDIANNESS_LITTLE);
#else
bool little_endian_p = (endianness != ENDIANNESS_BIG);
#endif
switch (type_char) {
case 'e':
case 'E':
little_endian_p = true;
break;
case 'g':
case 'G':
little_endian_p = false;
break;
default:
break;
}
buf[i++] = (rb_memory_view_item_component_t){
.format = type_char,
.native_size_p = native_size_p,
.little_endian_p = little_endian_p,
.offset = offset,
.size = size,
.repeat = count
};
}
offset += size * count;
}
*members = buf;
*n_members = len;
}
return total;
}
/* Return the item size. */
ssize_t
rb_memory_view_item_size_from_format(const char *format, const char **err)
{
return rb_memory_view_parse_item_format(format, NULL, NULL, err);
}
/* Return the pointer to the item located by the given indices. */
void *
rb_memory_view_get_item_pointer(rb_memory_view_t *view, const ssize_t *indices)
{
uint8_t *ptr = view->data;
if (view->ndim == 1) {
ssize_t stride = view->strides != NULL ? view->strides[0] : view->item_size;
return ptr + indices[0] * stride;
}
assert(view->shape != NULL);
int i;
if (view->strides == NULL) {
// row-major contiguous array
ssize_t stride = view->item_size;
for (i = 0; i < view->ndim; ++i) {
stride *= view->shape[i];
}
for (i = 0; i < view->ndim; ++i) {
stride /= view->shape[i];
ptr += indices[i] * stride;
}
}
else if (view->sub_offsets == NULL) {
// flat strided array
for (i = 0; i < view->ndim; ++i) {
ptr += indices[i] * view->strides[i];
}
}
else {
// indirect strided array
for (i = 0; i < view->ndim; ++i) {
ptr += indices[i] * view->strides[i];
if (view->sub_offsets[i] >= 0) {
ptr = *(uint8_t **)ptr + view->sub_offsets[i];
}
}
}
return ptr;
}
static const rb_memory_view_entry_t *
lookup_memory_view_entry(VALUE klass)
{
VALUE entry_obj = rb_ivar_get(klass, id_memory_view);
while (NIL_P(entry_obj)) {
klass = rb_class_get_superclass(klass);
if (klass == rb_cBasicObject || klass == rb_cObject)
return NULL;
entry_obj = rb_ivar_get(klass, id_memory_view);
}
if (! rb_typeddata_is_kind_of(entry_obj, &memory_view_entry_data_type))
return NULL;
return (const rb_memory_view_entry_t *)RTYPEDDATA_DATA(entry_obj);
}
/* Examine whether the given object supports memory view. */
int
rb_memory_view_available_p(VALUE obj)
{
VALUE klass = CLASS_OF(obj);
const rb_memory_view_entry_t *entry = lookup_memory_view_entry(klass);
if (entry)
return (* entry->available_p_func)(obj);
else
return 0;
}
/* Obtain a memory view from obj, and substitute the information to view. */
int
rb_memory_view_get(VALUE obj, rb_memory_view_t* view, int flags)
{
VALUE klass = CLASS_OF(obj);
const rb_memory_view_entry_t *entry = lookup_memory_view_entry(klass);
if (entry)
return (*entry->get_func)(obj, view, flags);
else
return 0;
}
/* Release the memory view obtained from obj. */
int
rb_memory_view_release(rb_memory_view_t* view)
{
VALUE klass = CLASS_OF(view->obj);
const rb_memory_view_entry_t *entry = lookup_memory_view_entry(klass);
if (entry)
return (*entry->release_func)(view->obj, view);
else
return 0;
}
void
Init_MemoryView(void)
{
id_memory_view = rb_intern("__memory_view__");
}

View file

@ -0,0 +1,249 @@
require "-test-/memory_view"
require "rbconfig/sizeof"
class TestMemoryView < Test::Unit::TestCase
NATIVE_ENDIAN = MemoryViewTestUtils::NATIVE_ENDIAN
LITTLE_ENDIAN = :little_endian
BIG_ENDIAN = :big_endian
%I(SHORT INT INT16 INT32 INT64 INTPTR LONG LONG_LONG FLOAT DOUBLE).each do |type|
name = :"#{type}_ALIGNMENT"
const_set(name, MemoryViewTestUtils.const_get(name))
end
def test_rb_memory_view_register_duplicated
assert_warning(/Duplicated registration of memory view to/) do
MemoryViewTestUtils.register(MemoryViewTestUtils::ExportableString)
end
end
def test_rb_memory_view_register_nonclass
assert_raise(TypeError) do
MemoryViewTestUtils.register(Object.new)
end
end
def sizeof(type)
RbConfig::SIZEOF[type.to_s]
end
def test_rb_memory_view_item_size_from_format
[
[nil, 1], ['c', 1], ['C', 1],
['n', 2], ['v', 2],
['l', 4], ['L', 4], ['N', 4], ['V', 4], ['f', 4], ['e', 4], ['g', 4],
['q', 8], ['Q', 8], ['d', 8], ['E', 8], ['G', 8],
['s', sizeof(:short)], ['S', sizeof(:short)], ['s!', sizeof(:short)], ['S!', sizeof(:short)],
['i', sizeof(:int)], ['I', sizeof(:int)], ['i!', sizeof(:int)], ['I!', sizeof(:int)],
['l!', sizeof(:long)], ['L!', sizeof(:long)],
['q!', sizeof('long long')], ['Q!', sizeof('long long')],
['j', sizeof(:intptr_t)], ['J', sizeof(:intptr_t)],
].each do |format, expected|
actual, err = MemoryViewTestUtils.item_size_from_format(format)
assert_nil(err)
assert_equal(expected, actual, "rb_memory_view_item_size_from_format(#{format || 'NULL'}) == #{expected}")
end
end
def test_rb_memory_view_item_size_from_format_composed
actual, = MemoryViewTestUtils.item_size_from_format("ccc")
assert_equal(3, actual)
actual, = MemoryViewTestUtils.item_size_from_format("c3")
assert_equal(3, actual)
actual, = MemoryViewTestUtils.item_size_from_format("fd")
assert_equal(12, actual)
actual, = MemoryViewTestUtils.item_size_from_format("fx2d")
assert_equal(14, actual)
end
def test_rb_memory_view_item_size_from_format_with_spaces
# spaces should be ignored
actual, = MemoryViewTestUtils.item_size_from_format("f x2 d")
assert_equal(14, actual)
end
def test_rb_memory_view_item_size_from_format_error
assert_equal([-1, "a"], MemoryViewTestUtils.item_size_from_format("ccca"))
assert_equal([-1, "a"], MemoryViewTestUtils.item_size_from_format("ccc4a"))
end
def test_rb_memory_view_parse_item_format
total_size, members, err = MemoryViewTestUtils.parse_item_format("ccc2f3x2d4q!<")
assert_equal(58, total_size)
assert_nil(err)
assert_equal([
{format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 0, size: 1, repeat: 1},
{format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 1, size: 1, repeat: 1},
{format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 2, size: 1, repeat: 2},
{format: 'f', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 4, size: 4, repeat: 3},
{format: 'd', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 18, size: 8, repeat: 4},
{format: 'q', native_size_p: true, endianness: :little_endian, offset: 50, size: sizeof('long long'), repeat: 1}
],
members)
end
def test_rb_memory_view_parse_item_format_with_alignment_signle
[
["c", false, NATIVE_ENDIAN, 1, 1, 1],
["C", false, NATIVE_ENDIAN, 1, 1, 1],
["s", false, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1],
["S", false, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1],
["s!", true, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1],
["S!", true, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1],
["n", false, NATIVE_ENDIAN, INT16_ALIGNMENT, sizeof(:int16_t), 1],
["v", false, NATIVE_ENDIAN, INT16_ALIGNMENT, sizeof(:int16_t), 1],
["i", false, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1],
["I", false, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1],
["i!", true, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1],
["I!", true, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1],
["l", false, NATIVE_ENDIAN, INT32_ALIGNMENT, sizeof(:int32_t), 1],
["L", false, NATIVE_ENDIAN, INT32_ALIGNMENT, sizeof(:int32_t), 1],
["l!", true, NATIVE_ENDIAN, LONG_ALIGNMENT, sizeof(:long), 1],
["L!", true, NATIVE_ENDIAN, LONG_ALIGNMENT, sizeof(:long), 1],
["N", false, NATIVE_ENDIAN, INT32_ALIGNMENT, sizeof(:int32_t), 1],
["V", false, NATIVE_ENDIAN, INT32_ALIGNMENT, sizeof(:int32_t), 1],
["f", false, NATIVE_ENDIAN, FLOAT_ALIGNMENT, sizeof(:float), 1],
["e", false, :little_endian, FLOAT_ALIGNMENT, sizeof(:float), 1],
["g", false, :big_endian, FLOAT_ALIGNMENT, sizeof(:float), 1],
["q", false, NATIVE_ENDIAN, INT64_ALIGNMENT, sizeof(:int64_t), 1],
["Q", false, NATIVE_ENDIAN, INT64_ALIGNMENT, sizeof(:int64_t), 1],
["q!", true, NATIVE_ENDIAN, LONG_LONG_ALIGNMENT, sizeof("long long"), 1],
["Q!", true, NATIVE_ENDIAN, LONG_LONG_ALIGNMENT, sizeof("long long"), 1],
["d", false, NATIVE_ENDIAN, DOUBLE_ALIGNMENT, sizeof(:double), 1],
["E", false, :little_endian, DOUBLE_ALIGNMENT, sizeof(:double), 1],
["G", false, :big_endian, DOUBLE_ALIGNMENT, sizeof(:double), 1],
["j", false, NATIVE_ENDIAN, INTPTR_ALIGNMENT, sizeof(:intptr_t), 1],
["J", false, NATIVE_ENDIAN, INTPTR_ALIGNMENT, sizeof(:intptr_t), 1],
].each do |type, native_size_p, endianness, alignment, size, repeat, total_size|
total_size, members, err = MemoryViewTestUtils.parse_item_format("|c#{type}")
assert_nil(err)
padding_size = alignment - 1
expected_total_size = 1 + padding_size + size
assert_equal(expected_total_size, total_size)
expected_result = [
{format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 0, size: 1, repeat: 1},
{format: type[0], native_size_p: native_size_p, endianness: endianness, offset: alignment, size: size, repeat: repeat},
]
assert_equal(expected_result, members)
end
end
def alignment_padding(total_size, alignment)
res = total_size % alignment
if res > 0
alignment - res
else
0
end
end
def test_rb_memory_view_parse_item_format_with_alignment_total_size_with_tail_padding
total_size, members, err = MemoryViewTestUtils.parse_item_format("|lqc")
assert_nil(err)
expected_total_size = sizeof(:int32_t)
expected_total_size += alignment_padding(expected_total_size, INT32_ALIGNMENT)
expected_total_size += sizeof(:int64_t)
expected_total_size += alignment_padding(expected_total_size, INT64_ALIGNMENT)
expected_total_size += 1
expected_total_size += alignment_padding(expected_total_size, INT64_ALIGNMENT)
assert_equal(expected_total_size, total_size)
end
def test_rb_memory_view_parse_item_format_with_alignment_compound
total_size, members, err = MemoryViewTestUtils.parse_item_format("|ccc2f3x2d4cq!<")
assert_nil(err)
expected_total_size = 1 + 1 + 1*2
expected_total_size += alignment_padding(expected_total_size, FLOAT_ALIGNMENT)
expected_total_size += sizeof(:float)*3 + 1*2
expected_total_size += alignment_padding(expected_total_size, DOUBLE_ALIGNMENT)
expected_total_size += sizeof(:double)*4 + 1
expected_total_size += alignment_padding(expected_total_size, LONG_LONG_ALIGNMENT)
expected_total_size += sizeof("long long")
assert_equal(expected_total_size, total_size)
expected_result = [
{format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 0, size: 1, repeat: 1},
{format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 1, size: 1, repeat: 1},
{format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 2, size: 1, repeat: 2},
]
offset = 4
res = offset % FLOAT_ALIGNMENT
offset += FLOAT_ALIGNMENT - res if res > 0
expected_result << {format: 'f', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 4, repeat: 3}
offset += 12
offset += 2 # 2x
res = offset % DOUBLE_ALIGNMENT
offset += DOUBLE_ALIGNMENT - res if res > 0
expected_result << {format: 'd', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 8, repeat: 4}
offset += 32
expected_result << {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 1, repeat: 1}
offset += 1
res = offset % LONG_LONG_ALIGNMENT
offset += LONG_LONG_ALIGNMENT - res if res > 0
expected_result << {format: 'q', native_size_p: true, endianness: :little_endian, offset: offset, size: 8, repeat: 1}
assert_equal(expected_result, members)
end
def test_rb_memory_view_init_as_byte_array
# ExportableString's memory view is initialized by rb_memory_view_init_as_byte_array
es = MemoryViewTestUtils::ExportableString.new("ruby")
memory_view_info = MemoryViewTestUtils.get_memory_view_info(es)
assert_equal({
obj: es,
len: 4,
readonly: true,
format: nil,
item_size: 1,
ndim: 1,
shape: nil,
strides: nil,
sub_offsets: nil
},
memory_view_info)
end
def test_rb_memory_view_fill_contiguous_strides
row_major_strides = MemoryViewTestUtils.fill_contiguous_strides(3, 8, [2, 3, 4], true)
assert_equal([96, 32, 8],
row_major_strides)
column_major_strides = MemoryViewTestUtils.fill_contiguous_strides(3, 8, [2, 3, 4], false)
assert_equal([8, 16, 48],
column_major_strides)
end
def test_rb_memory_view_get_item_pointer
buf = [ 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12 ].pack("l!*")
shape = [3, 4]
mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, shape, nil)
assert_equal(1, mv[[0, 0]])
assert_equal(4, mv[[0, 3]])
assert_equal(6, mv[[1, 1]])
assert_equal(10, mv[[2, 1]])
buf = [ 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16 ].pack("l!*")
shape = [2, 8]
strides = [4*sizeof(:long)*2, sizeof(:long)*2]
mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, shape, strides)
assert_equal(1, mv[[0, 0]])
assert_equal(5, mv[[0, 2]])
assert_equal(9, mv[[1, 0]])
assert_equal(15, mv[[1, 3]])
end
end