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:
parent
6eeacbbc36
commit
890bc2cdde
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
162
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
|
||||
|
|
164
ext/-test-/memory_view/depend
Normal file
164
ext/-test-/memory_view/depend
Normal 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
|
3
ext/-test-/memory_view/extconf.rb
Normal file
3
ext/-test-/memory_view/extconf.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "../auto_ext.rb"
|
||||
auto_ext(inc: true)
|
400
ext/-test-/memory_view/memory_view.c
Normal file
400
ext/-test-/memory_view/memory_view.c
Normal 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
136
include/ruby/memory_view.h
Normal 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 */
|
1
inits.c
1
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);
|
||||
|
|
506
memory_view.c
Normal file
506
memory_view.c
Normal 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__");
|
||||
}
|
249
test/ruby/test_memory_view.rb
Normal file
249
test/ruby/test_memory_view.rb
Normal 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
|
Loading…
Reference in a new issue