summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common.mk162
-rw-r--r--ext/-test-/memory_view/depend164
-rw-r--r--ext/-test-/memory_view/extconf.rb3
-rw-r--r--ext/-test-/memory_view/memory_view.c400
-rw-r--r--include/ruby/memory_view.h136
-rw-r--r--inits.c1
-rw-r--r--memory_view.c506
-rw-r--r--test/ruby/test_memory_view.rb249
8 files changed, 1621 insertions, 0 deletions
diff --git a/common.mk b/common.mk
index 8a1864dbd8..cf4c6398ab 100644
--- a/common.mk
+++ b/common.mk
@@ -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
diff --git a/ext/-test-/memory_view/depend b/ext/-test-/memory_view/depend
new file mode 100644
index 0000000000..bcbd98d41f
--- /dev/null
+++ b/ext/-test-/memory_view/depend
@@ -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
diff --git a/ext/-test-/memory_view/extconf.rb b/ext/-test-/memory_view/extconf.rb
new file mode 100644
index 0000000000..d786b15db9
--- /dev/null
+++ b/ext/-test-/memory_view/extconf.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: false
+require_relative "../auto_ext.rb"
+auto_ext(inc: true)
diff --git a/ext/-test-/memory_view/memory_view.c b/ext/-test-/memory_view/memory_view.c
new file mode 100644
index 0000000000..33124e2551
--- /dev/null
+++ b/ext/-test-/memory_view/memory_view.c
@@ -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);
+}
diff --git a/include/ruby/memory_view.h b/include/ruby/memory_view.h
new file mode 100644
index 0000000000..dc6c971593
--- /dev/null
+++ b/include/ruby/memory_view.h
@@ -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 */
diff --git a/inits.c b/inits.c
index b36b162a42..f636748101 100644
--- a/inits.c
+++ b/inits.c
@@ -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);
diff --git a/memory_view.c b/memory_view.c
new file mode 100644
index 0000000000..e45cbeb796
--- /dev/null
+++ b/memory_view.c
@@ -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__");
+}
diff --git a/test/ruby/test_memory_view.rb b/test/ruby/test_memory_view.rb
new file mode 100644
index 0000000000..b9bcae9008
--- /dev/null
+++ b/test/ruby/test_memory_view.rb
@@ -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