summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/-test-/memory_view/memory_view.c20
-rw-r--r--ext/fiddle/conversions.c44
-rw-r--r--ext/fiddle/depend167
-rw-r--r--ext/fiddle/extconf.rb7
-rw-r--r--ext/fiddle/extlibs1
-rw-r--r--ext/fiddle/fiddle.c88
-rw-r--r--ext/fiddle/fiddle.gemspec14
-rw-r--r--ext/fiddle/fiddle.h92
-rw-r--r--ext/fiddle/function.c65
-rw-r--r--ext/fiddle/lib/fiddle/cparser.rb115
-rw-r--r--ext/fiddle/lib/fiddle/function.rb5
-rw-r--r--ext/fiddle/lib/fiddle/struct.rb249
-rw-r--r--ext/fiddle/lib/fiddle/version.rb2
-rw-r--r--ext/fiddle/memory_view.c254
-rw-r--r--ext/fiddle/pointer.c138
-rw-r--r--ext/fiddle/win32/libffi-3.2.1-cygwin.patch11
-rw-r--r--test/fiddle/test_c_struct_entry.rb130
-rw-r--r--test/fiddle/test_c_union_entity.rb15
-rw-r--r--test/fiddle/test_cparser.rb113
-rw-r--r--test/fiddle/test_function.rb14
-rw-r--r--test/fiddle/test_import.rb324
-rw-r--r--test/fiddle/test_memory_view.rb115
-rw-r--r--test/fiddle/test_pointer.rb85
23 files changed, 1816 insertions, 252 deletions
diff --git a/ext/-test-/memory_view/memory_view.c b/ext/-test-/memory_view/memory_view.c
index 53656b145c..61d02464b8 100644
--- a/ext/-test-/memory_view/memory_view.c
+++ b/ext/-test-/memory_view/memory_view.c
@@ -275,15 +275,15 @@ mdview_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags)
return 0;
}
- ssize_t i, ndim = RARRAY_LEN(shape_v);
+ ssize_t ndim = RARRAY_LEN(shape_v);
+ if (!NIL_P(strides_v) && RARRAY_LEN(strides_v) != ndim) {
+ rb_raise(rb_eArgError, "strides has an invalid dimension");
+ }
+
ssize_t *shape = ALLOC_N(ssize_t, ndim);
- ssize_t *strides = NULL;
+ ssize_t *strides = ALLOC_N(ssize_t, ndim);
+ ssize_t i;
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));
@@ -293,6 +293,12 @@ mdview_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags)
for (i = 0; i < ndim; ++i) {
shape[i] = NUM2SSIZET(RARRAY_AREF(shape_v, i));
}
+
+ i = ndim - 1;
+ strides[i] = item_size;
+ for (; i > 0; --i) {
+ strides[i-1] = strides[i] * shape[i];
+ }
}
rb_memory_view_init_as_byte_array(view, obj, RSTRING_PTR(buf_v), RSTRING_LEN(buf_v), true);
diff --git a/ext/fiddle/conversions.c b/ext/fiddle/conversions.c
index fdfc3fcbf2..6e0ce36378 100644
--- a/ext/fiddle/conversions.c
+++ b/ext/fiddle/conversions.c
@@ -23,6 +23,18 @@ rb_fiddle_type_ensure(VALUE type)
#ifdef TYPE_LONG_LONG
ID long_long_id;
#endif
+#ifdef TYPE_INT8_T
+ ID int8_t_id;
+#endif
+#ifdef TYPE_INT16_T
+ ID int16_t_id;
+#endif
+#ifdef TYPE_INT32_T
+ ID int32_t_id;
+#endif
+#ifdef TYPE_INT64_T
+ ID int64_t_id;
+#endif
ID float_id;
ID double_id;
ID variadic_id;
@@ -41,6 +53,18 @@ rb_fiddle_type_ensure(VALUE type)
#ifdef TYPE_LONG_LONG
RUBY_CONST_ID(long_long_id, "long_long");
#endif
+#ifdef TYPE_INT8_T
+ RUBY_CONST_ID(int8_t_id, "int8_t");
+#endif
+#ifdef TYPE_INT16_T
+ RUBY_CONST_ID(int16_t_id, "int16_t");
+#endif
+#ifdef TYPE_INT32_T
+ RUBY_CONST_ID(int32_t_id, "int32_t");
+#endif
+#ifdef TYPE_INT64_T
+ RUBY_CONST_ID(int64_t_id, "int64_t");
+#endif
RUBY_CONST_ID(float_id, "float");
RUBY_CONST_ID(double_id, "double");
RUBY_CONST_ID(variadic_id, "variadic");
@@ -73,6 +97,26 @@ rb_fiddle_type_ensure(VALUE type)
return INT2NUM(TYPE_LONG_LONG);
}
#endif
+#ifdef TYPE_INT8_T
+ else if (type_id == int8_t_id) {
+ return INT2NUM(TYPE_INT8_T);
+ }
+#endif
+#ifdef TYPE_INT16_T
+ else if (type_id == int16_t_id) {
+ return INT2NUM(TYPE_INT16_T);
+ }
+#endif
+#ifdef TYPE_INT32_T
+ else if (type_id == int32_t_id) {
+ return INT2NUM(TYPE_INT32_T);
+ }
+#endif
+#ifdef TYPE_INT64_T
+ else if (type_id == int64_t_id) {
+ return INT2NUM(TYPE_INT64_T);
+ }
+#endif
else if (type_id == float_id) {
return INT2NUM(TYPE_FLOAT);
}
diff --git a/ext/fiddle/depend b/ext/fiddle/depend
index 6087aa856f..6afc09dd67 100644
--- a/ext/fiddle/depend
+++ b/ext/fiddle/depend
@@ -879,6 +879,172 @@ handle.o: conversions.h
handle.o: fiddle.h
handle.o: function.h
handle.o: handle.c
+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: closure.h
+memory_view.o: conversions.h
+memory_view.o: fiddle.h
+memory_view.o: function.h
+memory_view.o: memory_view.c
pinned.o: $(RUBY_EXTCONF_H)
pinned.o: $(arch_hdrdir)/ruby/config.h
pinned.o: $(hdrdir)/ruby.h
@@ -1202,6 +1368,7 @@ pointer.o: $(hdrdir)/ruby/internal/variable.h
pointer.o: $(hdrdir)/ruby/internal/warning_push.h
pointer.o: $(hdrdir)/ruby/internal/xmalloc.h
pointer.o: $(hdrdir)/ruby/io.h
+pointer.o: $(hdrdir)/ruby/memory_view.h
pointer.o: $(hdrdir)/ruby/missing.h
pointer.o: $(hdrdir)/ruby/onigmo.h
pointer.o: $(hdrdir)/ruby/oniguruma.h
diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
index 9836087455..7e92d6a2f7 100644
--- a/ext/fiddle/extconf.rb
+++ b/ext/fiddle/extconf.rb
@@ -172,14 +172,19 @@ types.each do |type, signed|
if signed
check_signedness(type.downcase, "stddef.h")
end
+ else
+ check_signedness(type.downcase, "stddef.h")
end
end
+if have_header("ruby/memory_view.h")
+ have_type("rb_memory_view_t", ["ruby/memory_view.h"])
+end
+
if libffi
$LOCAL_LIBS.prepend("./#{libffi.a} ").strip! # to exts.mk
$INCFLAGS.gsub!(/-I#{libffi.dir}/, '-I$(LIBFFI_DIR)')
end
-$INCFLAGS << " -I$(top_srcdir)"
create_makefile 'fiddle' do |conf|
if !libffi
next conf << "LIBFFI_CLEAN = none\n"
diff --git a/ext/fiddle/extlibs b/ext/fiddle/extlibs
index b991f3bf46..68dac46a95 100644
--- a/ext/fiddle/extlibs
+++ b/ext/fiddle/extlibs
@@ -6,7 +6,6 @@ https://ftp.osuosl.org/pub/blfs/conglomeration/libffi/$(pkg).tar.gz \
sha512:980ca30a8d76f963fca722432b1fe5af77d7a4e4d2eac5144fbc5374d4c596609a293440573f4294207e1bdd9fda80ad1e1cafb2ffb543df5a275bc3bd546483 \
#
win32/$(pkg)-mswin.patch -p0
- win32/$(pkg)-cygwin.patch -p0
$(pkg)/config.guess -> /tool/config.guess
$(pkg)/config.sub -> /tool/config.sub
diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c
index 546b1dc451..dd819a298c 100644
--- a/ext/fiddle/fiddle.c
+++ b/ext/fiddle/fiddle.c
@@ -7,6 +7,10 @@ VALUE rb_eFiddleError;
void Init_fiddle_pointer(void);
void Init_fiddle_pinned(void);
+#ifdef FIDDLE_MEMORY_VIEW
+void Init_fiddle_memory_view(void);
+#endif
+
/*
* call-seq: Fiddle.malloc(size)
*
@@ -204,6 +208,38 @@ Init_fiddle(void)
rb_define_const(mFiddle, "TYPE_LONG_LONG", INT2NUM(TYPE_LONG_LONG));
#endif
+#ifdef TYPE_INT8_T
+ /* Document-const: TYPE_INT8_T
+ *
+ * C type - int8_t
+ */
+ rb_define_const(mFiddle, "TYPE_INT8_T", INT2NUM(TYPE_INT8_T));
+#endif
+
+#ifdef TYPE_INT16_T
+ /* Document-const: TYPE_INT16_T
+ *
+ * C type - int16_t
+ */
+ rb_define_const(mFiddle, "TYPE_INT16_T", INT2NUM(TYPE_INT16_T));
+#endif
+
+#ifdef TYPE_INT32_T
+ /* Document-const: TYPE_INT32_T
+ *
+ * C type - int32_t
+ */
+ rb_define_const(mFiddle, "TYPE_INT32_T", INT2NUM(TYPE_INT32_T));
+#endif
+
+#ifdef TYPE_INT64_T
+ /* Document-const: TYPE_INT64_T
+ *
+ * C type - int64_t
+ */
+ rb_define_const(mFiddle, "TYPE_INT64_T", INT2NUM(TYPE_INT64_T));
+#endif
+
/* Document-const: TYPE_FLOAT
*
* C type - float
@@ -298,6 +334,30 @@ Init_fiddle(void)
rb_define_const(mFiddle, "ALIGN_LONG_LONG", INT2NUM(ALIGN_LONG_LONG));
#endif
+ /* Document-const: ALIGN_INT8_T
+ *
+ * The alignment size of a int8_t
+ */
+ rb_define_const(mFiddle, "ALIGN_INT8_T", INT2NUM(ALIGN_INT8_T));
+
+ /* Document-const: ALIGN_INT16_T
+ *
+ * The alignment size of a int16_t
+ */
+ rb_define_const(mFiddle, "ALIGN_INT16_T", INT2NUM(ALIGN_INT16_T));
+
+ /* Document-const: ALIGN_INT32_T
+ *
+ * The alignment size of a int32_t
+ */
+ rb_define_const(mFiddle, "ALIGN_INT32_T", INT2NUM(ALIGN_INT32_T));
+
+ /* Document-const: ALIGN_INT64_T
+ *
+ * The alignment size of a int64_t
+ */
+ rb_define_const(mFiddle, "ALIGN_INT64_T", INT2NUM(ALIGN_INT64_T));
+
/* Document-const: ALIGN_FLOAT
*
* The alignment size of a float
@@ -388,6 +448,30 @@ Init_fiddle(void)
rb_define_const(mFiddle, "SIZEOF_LONG_LONG", INT2NUM(sizeof(LONG_LONG)));
#endif
+ /* Document-const: SIZEOF_INT8_T
+ *
+ * size of a int8_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_INT8_T", INT2NUM(sizeof(int8_t)));
+
+ /* Document-const: SIZEOF_INT16_T
+ *
+ * size of a int16_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_INT16_T", INT2NUM(sizeof(int16_t)));
+
+ /* Document-const: SIZEOF_INT32_T
+ *
+ * size of a int32_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_INT32_T", INT2NUM(sizeof(int32_t)));
+
+ /* Document-const: SIZEOF_INT64_T
+ *
+ * size of a int64_t
+ */
+ rb_define_const(mFiddle, "SIZEOF_INT64_T", INT2NUM(sizeof(int64_t)));
+
/* Document-const: SIZEOF_FLOAT
*
* size of a float
@@ -461,5 +545,9 @@ Init_fiddle(void)
Init_fiddle_handle();
Init_fiddle_pointer();
Init_fiddle_pinned();
+
+#ifdef FIDDLE_MEMORY_VIEW
+ Init_fiddle_memory_view();
+#endif
}
/* vim: set noet sws=4 sw=4: */
diff --git a/ext/fiddle/fiddle.gemspec b/ext/fiddle/fiddle.gemspec
index a9514399f0..a47f795f59 100644
--- a/ext/fiddle/fiddle.gemspec
+++ b/ext/fiddle/fiddle.gemspec
@@ -1,18 +1,13 @@
# frozen_string_literal: true
-source_version = ["", "ext/fiddle/"].find do |dir|
- begin
- break File.open(File.join(__dir__, "#{dir}lib/fiddle/version.rb")) {|f|
- f.gets("\n VERSION = ")
- f.gets[/\s*"(.+)"/, 1]
- }
- rescue Errno::ENOENT
- end
+version_module = Module.new do
+ version_rb = File.join(__dir__, "lib/fiddle/version.rb")
+ module_eval(File.read(version_rb), version_rb, __LINE__)
end
Gem::Specification.new do |spec|
spec.name = "fiddle"
- spec.version = source_version
+ spec.version = version_module::Fiddle::VERSION
spec.authors = ["Aaron Patterson", "SHIBATA Hiroshi"]
spec.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org"]
@@ -39,6 +34,7 @@ Gem::Specification.new do |spec|
"ext/fiddle/function.c",
"ext/fiddle/function.h",
"ext/fiddle/handle.c",
+ "ext/fiddle/memory_view.c",
"ext/fiddle/pinned.c",
"ext/fiddle/pointer.c",
"ext/fiddle/win32/fficonfig.h",
diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h
index 46f5a1d737..dbad910d07 100644
--- a/ext/fiddle/fiddle.h
+++ b/ext/fiddle/fiddle.h
@@ -58,38 +58,38 @@
# error "CHAR_BIT not supported"
#endif
-# if SIZEOF_SHORT == 2
-# define ffi_type_ushort ffi_type_uint16
-# define ffi_type_sshort ffi_type_sint16
-# elif SIZEOF_SHORT == 4
-# define ffi_type_ushort ffi_type_uint32
-# define ffi_type_sshort ffi_type_sint32
-# else
-# error "short size not supported"
-# endif
+#if SIZEOF_SHORT == 2
+# define ffi_type_ushort ffi_type_uint16
+# define ffi_type_sshort ffi_type_sint16
+#elif SIZEOF_SHORT == 4
+# define ffi_type_ushort ffi_type_uint32
+# define ffi_type_sshort ffi_type_sint32
+#else
+# error "short size not supported"
+#endif
-# if SIZEOF_INT == 2
-# define ffi_type_uint ffi_type_uint16
-# define ffi_type_sint ffi_type_sint16
-# elif SIZEOF_INT == 4
-# define ffi_type_uint ffi_type_uint32
-# define ffi_type_sint ffi_type_sint32
-# elif SIZEOF_INT == 8
-# define ffi_type_uint ffi_type_uint64
-# define ffi_type_sint ffi_type_sint64
-# else
-# error "int size not supported"
-# endif
+#if SIZEOF_INT == 2
+# define ffi_type_uint ffi_type_uint16
+# define ffi_type_sint ffi_type_sint16
+#elif SIZEOF_INT == 4
+# define ffi_type_uint ffi_type_uint32
+# define ffi_type_sint ffi_type_sint32
+#elif SIZEOF_INT == 8
+# define ffi_type_uint ffi_type_uint64
+# define ffi_type_sint ffi_type_sint64
+#else
+# error "int size not supported"
+#endif
-# if SIZEOF_LONG == 4
-# define ffi_type_ulong ffi_type_uint32
-# define ffi_type_slong ffi_type_sint32
-# elif SIZEOF_LONG == 8
-# define ffi_type_ulong ffi_type_uint64
-# define ffi_type_slong ffi_type_sint64
-# else
-# error "long size not supported"
-# endif
+#if SIZEOF_LONG == 4
+# define ffi_type_ulong ffi_type_uint32
+# define ffi_type_slong ffi_type_sint32
+#elif SIZEOF_LONG == 8
+# define ffi_type_ulong ffi_type_uint64
+# define ffi_type_slong ffi_type_sint64
+#else
+# error "long size not supported"
+#endif
#if HAVE_LONG_LONG
# if SIZEOF_LONG_LONG == 8
@@ -118,6 +118,27 @@
#define TYPE_VARIADIC 9
#define TYPE_CONST_STRING 10
+#define TYPE_INT8_T TYPE_CHAR
+#if SIZEOF_SHORT == 2
+# define TYPE_INT16_T TYPE_SHORT
+#elif SIZEOF_INT == 2
+# define TYPE_INT16_T TYPE_INT
+#endif
+#if SIZEOF_SHORT == 4
+# define TYPE_INT32_T TYPE_SHORT
+#elif SIZEOF_INT == 4
+# define TYPE_INT32_T TYPE_INT
+#elif SIZEOF_LONG == 4
+# define TYPE_INT32_T TYPE_LONG
+#endif
+#if SIZEOF_INT == 8
+# define TYPE_INT64_T TYPE_INT
+#elif SIZEOF_LONG == 8
+# define TYPE_INT64_T TYPE_LONG
+#elif defined(TYPE_LONG_LONG)
+# define TYPE_INT64_T TYPE_LONG_LONG
+#endif
+
#ifndef TYPE_SSIZE_T
# if SIZEOF_SIZE_T == SIZEOF_INT
# define TYPE_SSIZE_T TYPE_INT
@@ -153,8 +174,8 @@
#define ALIGN_OF(type) offsetof(struct {char align_c; type align_x;}, align_x)
#define ALIGN_VOIDP ALIGN_OF(void*)
-#define ALIGN_SHORT ALIGN_OF(short)
#define ALIGN_CHAR ALIGN_OF(char)
+#define ALIGN_SHORT ALIGN_OF(short)
#define ALIGN_INT ALIGN_OF(int)
#define ALIGN_LONG ALIGN_OF(long)
#if HAVE_LONG_LONG
@@ -163,6 +184,15 @@
#define ALIGN_FLOAT ALIGN_OF(float)
#define ALIGN_DOUBLE ALIGN_OF(double)
+#define ALIGN_INT8_T ALIGN_OF(int8_t)
+#define ALIGN_INT16_T ALIGN_OF(int16_t)
+#define ALIGN_INT32_T ALIGN_OF(int32_t)
+#define ALIGN_INT64_T ALIGN_OF(int64_t)
+
+#ifdef HAVE_TYPE_RB_MEMORY_VIEW_T
+# define FIDDLE_MEMORY_VIEW
+#endif
+
extern VALUE mFiddle;
extern VALUE rb_eFiddleDLError;
diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c
index 437dbb624a..1d82bc8a3e 100644
--- a/ext/fiddle/function.c
+++ b/ext/fiddle/function.c
@@ -77,18 +77,6 @@ rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
return rb_class_new_instance(3, argv, cFiddleFunction);
}
-static int
-parse_keyword_arg_i(VALUE key, VALUE value, VALUE self)
-{
- if (key == ID2SYM(rb_intern("name"))) {
- rb_iv_set(self, "@name", value);
- } else {
- rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE,
- RB_OBJ_STRING(key));
- }
- return ST_CONTINUE;
-}
-
static VALUE
normalize_argument_types(const char *name,
VALUE arg_types,
@@ -134,15 +122,40 @@ static VALUE
initialize(int argc, VALUE argv[], VALUE self)
{
ffi_cif * cif;
- VALUE ptr, arg_types, ret_type, abi, kwds;
+ VALUE ptr, arg_types, ret_type, abi, kwargs;
+ VALUE name = Qnil;
+ VALUE need_gvl = Qfalse;
int c_ret_type;
bool is_variadic = false;
ffi_abi c_ffi_abi;
void *cfunc;
- rb_scan_args(argc, argv, "31:", &ptr, &arg_types, &ret_type, &abi, &kwds);
+ rb_scan_args(argc, argv, "31:", &ptr, &arg_types, &ret_type, &abi, &kwargs);
rb_iv_set(self, "@closure", ptr);
+ if (!NIL_P(kwargs)) {
+ enum {
+ kw_name,
+ kw_need_gvl,
+ kw_max_,
+ };
+ static ID kw[kw_max_];
+ VALUE args[kw_max_];
+ if (!kw[0]) {
+ kw[kw_name] = rb_intern_const("name");
+ kw[kw_need_gvl] = rb_intern_const("need_gvl");
+ }
+ rb_get_kwargs(kwargs, kw, 0, kw_max_, args);
+ if (args[kw_name] != Qundef) {
+ name = args[kw_name];
+ }
+ if (args[kw_need_gvl] != Qundef) {
+ need_gvl = args[kw_need_gvl];
+ }
+ }
+ rb_iv_set(self, "@name", name);
+ rb_iv_set(self, "@need_gvl", need_gvl);
+
ptr = rb_Integer(ptr);
cfunc = NUM2PTR(ptr);
PTR2NUM(cfunc);
@@ -170,8 +183,6 @@ initialize(int argc, VALUE argv[], VALUE self)
rb_iv_set(self, "@abi", abi);
rb_iv_set(self, "@is_variadic", is_variadic ? Qtrue : Qfalse);
- if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self);
-
TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
cif->arg_types = NULL;
@@ -205,6 +216,7 @@ function_call(int argc, VALUE argv[], VALUE self)
VALUE arg_types;
VALUE cPointer;
VALUE is_variadic;
+ VALUE need_gvl;
int n_arg_types;
int n_fixed_args = 0;
int n_call_args = 0;
@@ -218,6 +230,7 @@ function_call(int argc, VALUE argv[], VALUE self)
arg_types = rb_iv_get(self, "@argument_types");
cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
is_variadic = rb_iv_get(self, "@is_variadic");
+ need_gvl = rb_iv_get(self, "@need_gvl");
n_arg_types = RARRAY_LENINT(arg_types);
n_fixed_args = n_arg_types;
@@ -355,7 +368,12 @@ function_call(int argc, VALUE argv[], VALUE self)
args.values[i_call] = NULL;
args.fn = (void(*)(void))NUM2PTR(cfunc);
- (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);
+ if (RTEST(need_gvl)) {
+ ffi_call(args.cif, args.fn, &(args.retval), args.values);
+ }
+ else {
+ (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);
+ }
rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
#if defined(_WIN32)
@@ -433,6 +451,10 @@ Init_fiddle_function(void)
* Caller must ensure the underlying function is called in a
* thread-safe manner if running in a multi-threaded process.
*
+ * Note that it is not thread-safe to use this method to
+ * directly or indirectly call many Ruby C-extension APIs unless
+ * you don't pass +need_gvl: true+ to Fiddle::Function#new.
+ *
* For an example see Fiddle::Function
*
*/
@@ -440,13 +462,20 @@ Init_fiddle_function(void)
/*
* Document-method: new
- * call-seq: new(ptr, args, ret_type, abi = DEFAULT)
+ * call-seq: new(ptr,
+ * args,
+ * ret_type,
+ * abi = DEFAULT,
+ * name: nil,
+ * need_gvl: false)
*
* Constructs a Function object.
* * +ptr+ is a referenced function, of a Fiddle::Handle
* * +args+ is an Array of arguments, passed to the +ptr+ function
* * +ret_type+ is the return type of the function
* * +abi+ is the ABI of the function
+ * * +name+ is the name of the function
+ * * +need_gvl+ is whether GVL is needed to call the function
*
*/
rb_define_method(cFiddleFunction, "initialize", initialize, -1);
diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb
index cd0a64fef5..8a269393c6 100644
--- a/ext/fiddle/lib/fiddle/cparser.rb
+++ b/ext/fiddle/lib/fiddle/cparser.rb
@@ -35,12 +35,37 @@ module Fiddle
def parse_struct_signature(signature, tymap=nil)
if signature.is_a?(String)
signature = split_arguments(signature, /[,;]/)
+ elsif signature.is_a?(Hash)
+ signature = [signature]
end
mems = []
tys = []
signature.each{|msig|
- msig = compact(msig)
+ msig = compact(msig) if msig.is_a?(String)
case msig
+ when Hash
+ msig.each do |struct_name, struct_signature|
+ struct_name = struct_name.to_s if struct_name.is_a?(Symbol)
+ struct_name = compact(struct_name)
+ struct_count = nil
+ if struct_name =~ /^([\w\*\s]+)\[(\d+)\]$/
+ struct_count = $2.to_i
+ struct_name = $1
+ end
+ if struct_signature.respond_to?(:entity_class)
+ struct_type = struct_signature
+ else
+ parsed_struct = parse_struct_signature(struct_signature, tymap)
+ struct_type = CStructBuilder.create(CStruct, *parsed_struct)
+ end
+ if struct_count
+ ty = [struct_type, struct_count]
+ else
+ ty = struct_type
+ end
+ mems.push([struct_name, struct_type.members])
+ tys.push(ty)
+ end
when /^[\w\*\s]+[\*\s](\w+)$/
mems.push($1)
tys.push(parse_ctype(msig, tymap))
@@ -128,50 +153,90 @@ module Fiddle
return [parse_ctype(ty[0], tymap), ty[1]]
when 'void'
return TYPE_VOID
- when /^(?:(?:signed\s+)?long\s+long(?:\s+int\s+)?|int64_t)(?:\s+\w+)?$/
- if( defined?(TYPE_LONG_LONG) )
- return TYPE_LONG_LONG
- else
+ when /\A(?:(?:signed\s+)?long\s+long(?:\s+int\s+)?|int64_t)(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_LONG_LONG)
raise(RuntimeError, "unsupported type: #{ty}")
end
- when /^(?:unsigned\s+long\s+long(?:\s+int\s+)?|uint64_t)(?:\s+\w+)?$/
- if( defined?(TYPE_LONG_LONG) )
- return -TYPE_LONG_LONG
- else
+ return TYPE_LONG_LONG
+ when /\A(?:unsigned\s+long\s+long(?:\s+int\s+)?|uint64_t)(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_LONG_LONG)
raise(RuntimeError, "unsupported type: #{ty}")
end
- when /^(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?$/
+ return -TYPE_LONG_LONG
+ when /\A(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?\z/
return TYPE_LONG
- when /^unsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?$/
+ when /\Aunsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?\z/
return -TYPE_LONG
- when /^(?:signed\s+)?int(?:\s+\w+)?$/
+ when /\A(?:signed\s+)?int(?:\s+\w+)?\z/
return TYPE_INT
- when /^(?:unsigned\s+int|uint)(?:\s+\w+)?$/
+ when /\A(?:unsigned\s+int|uint)(?:\s+\w+)?\z/
return -TYPE_INT
- when /^(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?$/
+ when /\A(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?\z/
return TYPE_SHORT
- when /^unsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?$/
+ when /\Aunsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?\z/
return -TYPE_SHORT
- when /^(?:signed\s+)?char(?:\s+\w+)?$/
+ when /\A(?:signed\s+)?char(?:\s+\w+)?\z/
return TYPE_CHAR
- when /^unsigned\s+char(?:\s+\w+)?$/
+ when /\Aunsigned\s+char(?:\s+\w+)?\z/
return -TYPE_CHAR
- when /^float(?:\s+\w+)?$/
+ when /\Aint8_t(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_INT8_T)
+ raise(RuntimeError, "unsupported type: #{ty}")
+ end
+ return TYPE_INT8_T
+ when /\Auint8_t(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_INT8_T)
+ raise(RuntimeError, "unsupported type: #{ty}")
+ end
+ return -TYPE_INT8_T
+ when /\Aint16_t(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_INT16_T)
+ raise(RuntimeError, "unsupported type: #{ty}")
+ end
+ return TYPE_INT16_T
+ when /\Auint16_t(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_INT16_T)
+ raise(RuntimeError, "unsupported type: #{ty}")
+ end
+ return -TYPE_INT16_T
+ when /\Aint32_t(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_INT32_T)
+ raise(RuntimeError, "unsupported type: #{ty}")
+ end
+ return TYPE_INT32_T
+ when /\Auint32_t(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_INT32_T)
+ raise(RuntimeError, "unsupported type: #{ty}")
+ end
+ return -TYPE_INT32_T
+ when /\Aint64_t(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_INT64_T)
+ raise(RuntimeError, "unsupported type: #{ty}")
+ end
+ return TYPE_INT64_T
+ when /\Auint64_t(?:\s+\w+)?\z/
+ unless Fiddle.const_defined?(:TYPE_INT64_T)
+ raise(RuntimeError, "unsupported type: #{ty}")
+ end
+ return -TYPE_INT64_T
+ when /\Afloat(?:\s+\w+)?\z/
return TYPE_FLOAT
- when /^double(?:\s+\w+)?$/
+ when /\Adouble(?:\s+\w+)?\z/
return TYPE_DOUBLE
- when /^size_t(?:\s+\w+)?$/
+ when /\Asize_t(?:\s+\w+)?\z/
return TYPE_SIZE_T
- when /^ssize_t(?:\s+\w+)?$/
+ when /\Assize_t(?:\s+\w+)?\z/
return TYPE_SSIZE_T
- when /^ptrdiff_t(?:\s+\w+)?$/
+ when /\Aptrdiff_t(?:\s+\w+)?\z/
return TYPE_PTRDIFF_T
- when /^intptr_t(?:\s+\w+)?$/
+ when /\Aintptr_t(?:\s+\w+)?\z/
return TYPE_INTPTR_T
- when /^uintptr_t(?:\s+\w+)?$/
+ when /\Auintptr_t(?:\s+\w+)?\z/
return TYPE_UINTPTR_T
when /\*/, /\[[\s\d]*\]/
return TYPE_VOIDP
+ when "..."
+ return TYPE_VARIADIC
else
ty = ty.split(' ', 2)[0]
if( tymap[ty] )
@@ -186,7 +251,7 @@ module Fiddle
def split_arguments(arguments, sep=',')
return [] if arguments.strip == 'void'
- arguments.scan(/([\w\*\s]+\(\*\w*\)\(.*?\)|[\w\*\s\[\]]+)(?:#{sep}\s*|$)/).collect {|m| m[0]}
+ arguments.scan(/([\w\*\s]+\(\*\w*\)\(.*?\)|[\w\*\s\[\]]+|\.\.\.)(?:#{sep}\s*|\z)/).collect {|m| m[0]}
end
def compact(signature)
diff --git a/ext/fiddle/lib/fiddle/function.rb b/ext/fiddle/lib/fiddle/function.rb
index dd5e04e417..0f9913adeb 100644
--- a/ext/fiddle/lib/fiddle/function.rb
+++ b/ext/fiddle/lib/fiddle/function.rb
@@ -10,6 +10,11 @@ module Fiddle
# The name of this function
attr_reader :name
+ # Whether GVL is needed to call this function
+ def need_gvl?
+ @need_gvl
+ end
+
# The integer memory location of this function
def to_i
ptr.to_i
diff --git a/ext/fiddle/lib/fiddle/struct.rb b/ext/fiddle/lib/fiddle/struct.rb
index 259903d25c..a766eba83b 100644
--- a/ext/fiddle/lib/fiddle/struct.rb
+++ b/ext/fiddle/lib/fiddle/struct.rb
@@ -4,15 +4,72 @@ require 'fiddle/value'
require 'fiddle/pack'
module Fiddle
- # C struct shell
+ # A base class for objects representing a C structure
class CStruct
+ include Enumerable
+
# accessor to Fiddle::CStructEntity
def CStruct.entity_class
CStructEntity
end
+
+ def each
+ return enum_for(__function__) unless block_given?
+
+ self.class.members.each do |name,|
+ yield(self[name])
+ end
+ end
+
+ def each_pair
+ return enum_for(__function__) unless block_given?
+
+ self.class.members.each do |name,|
+ yield(name, self[name])
+ end
+ end
+
+ def to_h
+ hash = {}
+ each_pair do |name, value|
+ hash[name] = unstruct(value)
+ end
+ hash
+ end
+
+ def replace(another)
+ if another.nil?
+ self.class.members.each do |name,|
+ self[name] = nil
+ end
+ elsif another.respond_to?(:each_pair)
+ another.each_pair do |name, value|
+ self[name] = value
+ end
+ else
+ another.each do |name, value|
+ self[name] = value
+ end
+ end
+ self
+ end
+
+ private
+ def unstruct(value)
+ case value
+ when CStruct
+ value.to_h
+ when Array
+ value.collect do |v|
+ unstruct(v)
+ end
+ else
+ value
+ end
+ end
end
- # C union shell
+ # A base class for objects representing a C union
class CUnion
# accessor to Fiddle::CUnionEntity
def CUnion.entity_class
@@ -27,10 +84,14 @@ module Fiddle
def initialize(ptr, type, initial_values)
@ptr = ptr
@type = type
- @align = PackInfo::ALIGN_MAP[type]
- @size = Fiddle::PackInfo::SIZE_MAP[type]
- @pack_format = Fiddle::PackInfo::PACK_MAP[type]
- super(initial_values.collect { |v| unsigned_value(v, type) })
+ @is_struct = @type.respond_to?(:entity_class)
+ if @is_struct
+ super(initial_values)
+ else
+ @size = Fiddle::PackInfo::SIZE_MAP[type]
+ @pack_format = Fiddle::PackInfo::PACK_MAP[type]
+ super(initial_values.collect { |v| unsigned_value(v, type) })
+ end
end
def to_ptr
@@ -42,8 +103,12 @@ module Fiddle
raise IndexError, 'index %d outside of array bounds 0...%d' % [index, size]
end
- to_ptr[index * @size, @size] = [value].pack(@pack_format)
- super(index, value)
+ if @is_struct
+ self[index].replace(value)
+ else
+ to_ptr[index * @size, @size] = [value].pack(@pack_format)
+ super(index, value)
+ end
end
end
@@ -62,7 +127,7 @@ module Fiddle
# Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
# easy-to-use manner.
#
- # Example:
+ # Examples:
#
# require 'fiddle/struct'
# require 'fiddle/cparser'
@@ -73,6 +138,17 @@ module Fiddle
#
# MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
#
+ # MyStruct.malloc(Fiddle::RUBY_FREE) do |obj|
+ # ...
+ # end
+ #
+ # obj = MyStruct.malloc(Fiddle::RUBY_FREE)
+ # begin
+ # ...
+ # ensure
+ # obj.call_free
+ # end
+ #
# obj = MyStruct.malloc
# begin
# ...
@@ -82,45 +158,78 @@ module Fiddle
#
def create(klass, types, members)
new_class = Class.new(klass){
- define_method(:initialize){|addr|
- @entity = klass.entity_class.new(addr, types)
+ define_method(:initialize){|addr, func = nil|
+ if addr.is_a?(self.class.entity_class)
+ @entity = addr
+ else
+ @entity = self.class.entity_class.new(addr, types, func)
+ end
@entity.assign_names(members)
}
define_method(:[]) { |*args| @entity.send(:[], *args) }
define_method(:[]=) { |*args| @entity.send(:[]=, *args) }
define_method(:to_ptr){ @entity }
define_method(:to_i){ @entity.to_i }
+ define_singleton_method(:types) { types }
+ define_singleton_method(:members) { members }
members.each{|name|
+ name = name[0] if name.is_a?(Array) # name is a nested struct
+ next if method_defined?(name)
define_method(name){ @entity[name] }
define_method(name + "="){|val| @entity[name] = val }
}
- }
- size = klass.entity_class.size(types)
- new_class.module_eval(<<-EOS, __FILE__, __LINE__+1)
- def new_class.size()
- #{size}
- end
- def new_class.malloc()
- addr = Fiddle.malloc(#{size})
- new(addr)
+ entity_class = klass.entity_class
+ alignment = entity_class.alignment(types)
+ size = entity_class.size(types)
+ define_singleton_method(:alignment) { alignment }
+ define_singleton_method(:size) { size }
+ define_singleton_method(:malloc) do |func=nil, &block|
+ if block
+ entity_class.malloc(types, func, size) do |entity|
+ block.call(new(entity))
+ end
+ else
+ new(entity_class.malloc(types, func, size))
+ end
end
- EOS
+ }
return new_class
end
module_function :create
end
- # A C struct wrapper
+ # A pointer to a C structure
class CStructEntity < Fiddle::Pointer
include PackInfo
include ValueUtil
+ def CStructEntity.alignment(types)
+ max = 1
+ types.each do |type, count = 1|
+ if type.respond_to?(:entity_class)
+ n = type.alignment
+ else
+ n = ALIGN_MAP[type]
+ end
+ max = n if n > max
+ end
+ max
+ end
+
# Allocates a C struct with the +types+ provided.
#
# See Fiddle::Pointer.malloc for memory management issues.
- def CStructEntity.malloc(types, func = nil)
- addr = Fiddle.malloc(CStructEntity.size(types))
- CStructEntity.new(addr, types, func)
+ def CStructEntity.malloc(types, func = nil, size = size(types), &block)
+ if block_given?
+ super(size, func) do |struct|
+ struct.set_ctypes types
+ yield struct
+ end
+ else
+ struct = super(size, func)
+ struct.set_ctypes types
+ struct
+ end
end
# Returns the offset for the packed sizes for the given +types+.
@@ -136,9 +245,15 @@ module Fiddle
max_align = types.map { |type, count = 1|
last_offset = offset
- align = PackInfo::ALIGN_MAP[type]
+ if type.respond_to?(:entity_class)
+ align = type.alignment
+ type_size = type.size
+ else
+ align = PackInfo::ALIGN_MAP[type]
+ type_size = PackInfo::SIZE_MAP[type]
+ end
offset = PackInfo.align(last_offset, align) +
- (PackInfo::SIZE_MAP[type] * count)
+ (type_size * count)
align
}.max
@@ -152,13 +267,37 @@ module Fiddle
#
# See also Fiddle::Pointer.new
def initialize(addr, types, func = nil)
+ if func && addr.is_a?(Pointer) && addr.free
+ raise ArgumentError, 'free function specified on both underlying struct Pointer and when creating a CStructEntity - who do you want to free this?'
+ end
set_ctypes(types)
super(addr, @size, func)
end
# Set the names of the +members+ in this C struct
def assign_names(members)
- @members = members
+ @members = []
+ @nested_structs = {}
+ members.each_with_index do |member, index|
+ if member.is_a?(Array) # nested struct
+ member_name = member[0]
+ struct_type, struct_count = @ctypes[index]
+ if struct_count.nil?
+ struct = struct_type.new(to_i + @offset[index])
+ else
+ structs = struct_count.times.map do |i|
+ struct_type.new(to_i + @offset[index] + i * struct_type.size)
+ end
+ struct = StructArray.new(to_i + @offset[index],
+ struct_type,
+ structs)
+ end
+ @nested_structs[member_name] = struct
+ else
+ member_name = member
+ end
+ @members << member_name
+ end
end
# Calculates the offsets and sizes for the given +types+ in the struct.
@@ -169,12 +308,18 @@ module Fiddle
max_align = types.map { |type, count = 1|
orig_offset = offset
- align = ALIGN_MAP[type]
+ if type.respond_to?(:entity_class)
+ align = type.alignment
+ type_size = type.size
+ else
+ align = ALIGN_MAP[type]
+ type_size = SIZE_MAP[type]
+ end
offset = PackInfo.align(orig_offset, align)
@offset << offset
- offset += (SIZE_MAP[type] * count)
+ offset += (type_size * count)
align
}.max
@@ -203,7 +348,13 @@ module Fiddle
end
ty = @ctypes[idx]
if( ty.is_a?(Array) )
- r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1])
+ if ty.first.respond_to?(:entity_class)
+ return @nested_structs[name]
+ else
+ r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1])
+ end
+ elsif ty.respond_to?(:entity_class)
+ return @nested_structs[name]
else
r = super(@offset[idx], SIZE_MAP[ty.abs])
end
@@ -243,6 +394,24 @@ module Fiddle
def []=(*args)
return super(*args) if args.size > 2
name, val = *args
+ name = name.to_s if name.is_a?(Symbol)
+ nested_struct = @nested_structs[name]
+ if nested_struct
+ if nested_struct.is_a?(StructArray)
+ if val.nil?
+ nested_struct.each do |s|
+ s.replace(nil)
+ end
+ else
+ val.each_with_index do |v, i|
+ nested_struct[i] = v
+ end
+ end
+ else
+ nested_struct.replace(val)
+ end
+ return val
+ end
idx = @members.index(name)
if( idx.nil? )
raise(ArgumentError, "no such member: #{name}")
@@ -261,23 +430,16 @@ module Fiddle
end
end
+ undef_method :size=
def to_s() # :nodoc:
super(@size)
end
end
- # A C union wrapper
+ # A pointer to a C union
class CUnionEntity < CStructEntity
include PackInfo
- # Allocates a C union the +types+ provided.
- #
- # See Fiddle::Pointer.malloc for memory management issues.
- def CUnionEntity.malloc(types, func=nil)
- addr = Fiddle.malloc(CUnionEntity.size(types))
- CUnionEntity.new(addr, types, func)
- end
-
# Returns the size needed for the union with the given +types+.
#
# Fiddle::CUnionEntity.size(
@@ -287,7 +449,11 @@ module Fiddle
# Fiddle::TYPE_VOIDP ]) #=> 8
def CUnionEntity.size(types)
types.map { |type, count = 1|
- PackInfo::SIZE_MAP[type] * count
+ if type.respond_to?(:entity_class)
+ type.size * count
+ else
+ PackInfo::SIZE_MAP[type] * count
+ end
}.max
end
@@ -300,4 +466,3 @@ module Fiddle
end
end
end
-
diff --git a/ext/fiddle/lib/fiddle/version.rb b/ext/fiddle/lib/fiddle/version.rb
index 40d8300d2f..d8aef1a71b 100644
--- a/ext/fiddle/lib/fiddle/version.rb
+++ b/ext/fiddle/lib/fiddle/version.rb
@@ -1,3 +1,3 @@
module Fiddle
- VERSION = "1.0.2"
+ VERSION = "1.0.4"
end
diff --git a/ext/fiddle/memory_view.c b/ext/fiddle/memory_view.c
new file mode 100644
index 0000000000..819a8bd95d
--- /dev/null
+++ b/ext/fiddle/memory_view.c
@@ -0,0 +1,254 @@
+#include <stdbool.h>
+#include <ruby/ruby.h>
+
+#ifdef HAVE_RUBY_MEMORY_VIEW_H
+# include <ruby/memory_view.h>
+#endif
+
+#if SIZEOF_INTPTR_T == SIZEOF_LONG_LONG
+# define INTPTR2NUM LL2NUM
+# define UINTPTR2NUM ULL2NUM
+#elif SIZEOF_INTPTR_T == SIZEOF_LONG
+# define INTPTR2NUM LONG2NUM
+# define UINTPTR2NUM ULONG2NUM
+#else
+# define INTPTR2NUM INT2NUM
+# define UINTPTR2NUM UINT2NUM
+#endif
+
+#include <fiddle.h>
+
+#ifdef FIDDLE_MEMORY_VIEW
+VALUE rb_cMemoryView = Qnil;
+
+struct memview_data {
+ rb_memory_view_t view;
+ rb_memory_view_item_component_t *members;
+ size_t n_members;
+};
+
+static void
+fiddle_memview_mark(void *ptr)
+{
+ const struct memview_data *data = ptr;
+ rb_gc_mark(data->view.obj);
+}
+
+static void
+fiddle_memview_free(void *ptr)
+{
+ struct memview_data *data = ptr;
+ rb_memory_view_release(&data->view);
+ if (data->members)
+ xfree(data->members);
+ xfree(ptr);
+}
+
+static size_t
+fiddle_memview_memsize(const void *ptr)
+{
+ const struct memview_data *data = ptr;
+ return sizeof(*data) + sizeof(rb_memory_view_item_component_t)*data->n_members + (size_t)data->view.len;
+}
+
+static const rb_data_type_t fiddle_memview_data_type = {
+ "fiddle/memory_view",
+ {fiddle_memview_mark, fiddle_memview_free, fiddle_memview_memsize,},
+};
+
+static VALUE
+rb_fiddle_memview_s_allocate(VALUE klass)
+{
+ struct memview_data *data;
+ VALUE obj = TypedData_Make_Struct(klass, struct memview_data, &fiddle_memview_data_type, data);
+ data->view.obj = Qnil;
+ data->members = NULL;
+ data->n_members = 0;
+ return obj;
+}
+
+static VALUE
+rb_fiddle_memview_initialize(VALUE obj, VALUE target)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (!rb_memory_view_get(target, &data->view, 0)) {
+ rb_raise(rb_eArgError, "Unable to get a memory view from %+"PRIsVALUE, target);
+ }
+
+ return Qnil;
+}
+
+static VALUE
+rb_fiddle_memview_get_obj(VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ return data->view.obj;
+}
+
+static VALUE
+rb_fiddle_memview_get_length(VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (NIL_P(data->view.obj)) return Qnil;
+ return SSIZET2NUM(data->view.len);
+}
+
+static VALUE
+rb_fiddle_memview_get_readonly(VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (NIL_P(data->view.obj)) return Qnil;
+ return data->view.readonly ? Qtrue : Qfalse;
+}
+
+static VALUE
+rb_fiddle_memview_get_format(VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (NIL_P(data->view.obj)) return Qnil;
+ return data->view.format == NULL ? Qnil : rb_str_new_cstr(data->view.format);
+}
+
+static VALUE
+rb_fiddle_memview_get_item_size(VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (NIL_P(data->view.obj)) return Qnil;
+ return SSIZET2NUM(data->view.item_size);
+}
+
+static VALUE
+rb_fiddle_memview_get_ndim(VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (NIL_P(data->view.obj)) return Qnil;
+ return SSIZET2NUM(data->view.ndim);
+}
+
+static VALUE
+rb_fiddle_memview_get_shape(VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (NIL_P(data->view.obj)) return Qnil;
+ if (data->view.shape == NULL) return Qnil;
+
+ const ssize_t ndim = data->view.ndim;
+ VALUE shape = rb_ary_new_capa(ndim);
+ ssize_t i;
+ for (i = 0; i < ndim; ++i) {
+ rb_ary_push(shape, SSIZET2NUM(data->view.shape[i]));
+ }
+ return shape;
+}
+
+static VALUE
+rb_fiddle_memview_get_strides(VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (NIL_P(data->view.obj)) return Qnil;
+ if (data->view.strides == NULL) return Qnil;
+
+ const ssize_t ndim = data->view.ndim;
+ VALUE strides = rb_ary_new_capa(ndim);
+ ssize_t i;
+ for (i = 0; i < ndim; ++i) {
+ rb_ary_push(strides, SSIZET2NUM(data->view.strides[i]));
+ }
+ return strides;
+}
+
+static VALUE
+rb_fiddle_memview_get_sub_offsets(VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (NIL_P(data->view.obj)) return Qnil;
+ if (data->view.sub_offsets == NULL) return Qnil;
+
+ const ssize_t ndim = data->view.ndim;
+ VALUE sub_offsets = rb_ary_new_capa(ndim);
+ ssize_t i;
+ for (i = 0; i < ndim; ++i) {
+ rb_ary_push(sub_offsets, SSIZET2NUM(data->view.sub_offsets[i]));
+ }
+ return sub_offsets;
+}
+
+static VALUE
+rb_fiddle_memview_aref(int argc, VALUE *argv, VALUE obj)
+{
+ struct memview_data *data;
+ TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data);
+
+ if (NIL_P(data->view.obj)) return Qnil;
+
+ const ssize_t ndim = data->view.ndim;
+ if (argc != ndim) {
+ rb_raise(rb_eIndexError, "wrong number of index (%d for %"PRIdSIZE")", argc, ndim);
+ }
+
+ VALUE indices_v = 0;
+ ssize_t *indices = ALLOCV_N(ssize_t, indices_v, ndim);
+
+ ssize_t i;
+ for (i = 0; i < ndim; ++i) {
+ ssize_t x = NUM2SSIZET(argv[i]);
+ indices[i] = x;
+ }
+
+ uint8_t *ptr = rb_memory_view_get_item_pointer(&data->view, indices);
+ ALLOCV_END(indices_v);
+
+ if (data->view.format == NULL) {
+ return INT2FIX(*ptr);
+ }
+
+ if (!data->members) {
+ const char *err;
+ if (rb_memory_view_parse_item_format(data->view.format, &data->members, &data->n_members, &err) < 0) {
+ rb_raise(rb_eRuntimeError, "Unable to recognize item format at %"PRIdSIZE" in \"%s\"",
+ err - data->view.format, data->view.format);
+ }
+ }
+
+ return rb_memory_view_extract_item_members(ptr, data->members, data->n_members);
+}
+
+void
+Init_fiddle_memory_view(void)
+{
+ rb_cMemoryView = rb_define_class_under(mFiddle, "MemoryView", rb_cObject);
+ rb_define_alloc_func(rb_cMemoryView, rb_fiddle_memview_s_allocate);
+ rb_define_method(rb_cMemoryView, "initialize", rb_fiddle_memview_initialize, 1);
+ rb_define_method(rb_cMemoryView, "obj", rb_fiddle_memview_get_obj, 0);
+ rb_define_method(rb_cMemoryView, "length", rb_fiddle_memview_get_length, 0);
+ rb_define_method(rb_cMemoryView, "readonly?", rb_fiddle_memview_get_readonly, 0);
+ rb_define_method(rb_cMemoryView, "format", rb_fiddle_memview_get_format, 0);
+ rb_define_method(rb_cMemoryView, "item_size", rb_fiddle_memview_get_item_size, 0);
+ rb_define_method(rb_cMemoryView, "ndim", rb_fiddle_memview_get_ndim, 0);
+ rb_define_method(rb_cMemoryView, "shape", rb_fiddle_memview_get_shape, 0);
+ rb_define_method(rb_cMemoryView, "strides", rb_fiddle_memview_get_strides, 0);
+ rb_define_method(rb_cMemoryView, "sub_offsets", rb_fiddle_memview_get_sub_offsets, 0);
+ rb_define_method(rb_cMemoryView, "[]", rb_fiddle_memview_aref, -1);
+}
+
+#endif /* FIDDLE_MEMORY_VIEW */
diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c
index b531befd6e..ca1d59ff5f 100644
--- a/ext/fiddle/pointer.c
+++ b/ext/fiddle/pointer.c
@@ -2,8 +2,14 @@
* $Id$
*/
+#include <stdbool.h>
#include <ruby/ruby.h>
#include <ruby/io.h>
+
+#ifdef HAVE_RUBY_MEMORY_VIEW_H
+# include <ruby/memory_view.h>
+#endif
+
#include <ctype.h>
#include <fiddle.h>
@@ -24,6 +30,7 @@ struct ptr_data {
void *ptr;
long size;
freefunc_t free;
+ bool freed;
VALUE wrap[2];
};
@@ -57,14 +64,19 @@ fiddle_ptr_mark(void *ptr)
}
static void
-fiddle_ptr_free(void *ptr)
+fiddle_ptr_free_ptr(void *ptr)
{
struct ptr_data *data = ptr;
- if (data->ptr) {
- if (data->free) {
- (*(data->free))(data->ptr);
- }
+ if (data->ptr && data->free && !data->freed) {
+ data->freed = true;
+ (*(data->free))(data->ptr);
}
+}
+
+static void
+fiddle_ptr_free(void *ptr)
+{
+ fiddle_ptr_free_ptr(ptr);
xfree(ptr);
}
@@ -80,6 +92,38 @@ static const rb_data_type_t fiddle_ptr_data_type = {
{fiddle_ptr_mark, fiddle_ptr_free, fiddle_ptr_memsize,},
};
+#ifdef FIDDLE_MEMORY_VIEW
+static struct ptr_data *
+fiddle_ptr_check_memory_view(VALUE obj)
+{
+ struct ptr_data *data;
+ TypedData_Get_Struct(obj, struct ptr_data, &fiddle_ptr_data_type, data);
+ if (data->ptr == NULL || data->size == 0) return NULL;
+ return data;
+}
+
+static int
+fiddle_ptr_memory_view_available_p(VALUE obj)
+{
+ return fiddle_ptr_check_memory_view(obj) != NULL;
+}
+
+static int
+fiddle_ptr_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags)
+{
+ struct ptr_data *data = fiddle_ptr_check_memory_view(obj);
+ rb_memory_view_init_as_byte_array(view, obj, data->ptr, data->size, true);
+
+ return 1;
+}
+
+static const rb_memory_view_entry_t fiddle_ptr_memory_view_entry = {
+ fiddle_ptr_get_memory_view,
+ NULL,
+ fiddle_ptr_memory_view_available_p
+};
+#endif
+
static VALUE
rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func)
{
@@ -89,6 +133,7 @@ rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func)
val = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data);
data->ptr = ptr;
data->free = func;
+ data->freed = false;
data->size = size;
return val;
@@ -101,13 +146,13 @@ rb_fiddle_ptr_new(void *ptr, long size, freefunc_t func)
}
static VALUE
-rb_fiddle_ptr_malloc(long size, freefunc_t func)
+rb_fiddle_ptr_malloc(VALUE klass, long size, freefunc_t func)
{
void *ptr;
ptr = ruby_xmalloc((size_t)size);
memset(ptr,0,(size_t)size);
- return rb_fiddle_ptr_new(ptr, size, func);
+ return rb_fiddle_ptr_new2(klass, ptr, size, func);
}
static void *
@@ -140,6 +185,7 @@ rb_fiddle_ptr_s_allocate(VALUE klass)
data->ptr = 0;
data->size = 0;
data->free = 0;
+ data->freed = false;
return obj;
}
@@ -191,17 +237,31 @@ rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self)
return Qnil;
}
+static VALUE
+rb_fiddle_ptr_call_free(VALUE self);
+
/*
* call-seq:
* Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance
+ * Fiddle::Pointer.malloc(size, freefunc) { |pointer| ... } => ...
*
* == Examples
*
+ * # Automatically freeing the pointer when the block is exited - recommended
+ * Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) do |pointer|
+ * ...
+ * end
+ *
+ * # Manually freeing but relying on the garbage collector otherwise
+ * pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE)
+ * ...
+ * pointer.call_free
+ *
* # Relying on the garbage collector - may lead to unlimited memory allocated before freeing any, but safe
* pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE)
* ...
*
- * # Manual freeing
+ * # Only manually freeing
* pointer = Fiddle::Pointer.malloc(size)
* begin
* ...
@@ -214,13 +274,16 @@ rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self)
* ...
*
* Allocate +size+ bytes of memory and associate it with an optional
- * +freefunc+ that will be called when the pointer is garbage collected.
- * +freefunc+ must be an address pointing to a function or an instance of
- * +Fiddle::Function+. Using +freefunc+ may lead to unlimited memory being
- * allocated before any is freed as the native memory the pointer references
- * does not contribute to triggering the Ruby garbage collector. Consider
- * manually freeing the memory as illustrated above. You cannot combine
- * the techniques as this may lead to a double-free.
+ * +freefunc+.
+ *
+ * If a block is supplied, the pointer will be yielded to the block instead of
+ * being returned, and the return value of the block will be returned. A
+ * +freefunc+ must be supplied if a block is.
+ *
+ * If a +freefunc+ is supplied it will be called once, when the pointer is
+ * garbage collected or when the block is left if a block is supplied or
+ * when the user calls +call_free+, whichever happens first. +freefunc+ must be
+ * an address pointing to a function or an instance of +Fiddle::Function+.
*/
static VALUE
rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)
@@ -242,10 +305,17 @@ rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)
rb_bug("rb_fiddle_ptr_s_malloc");
}
- obj = rb_fiddle_ptr_malloc(s,f);
+ obj = rb_fiddle_ptr_malloc(klass, s,f);
if (wrap) RPTR_DATA(obj)->wrap[1] = wrap;
- return obj;
+ if (rb_block_given_p()) {
+ if (!f) {
+ rb_raise(rb_eArgError, "a free function must be supplied to Fiddle::Pointer.malloc when it is called with a block");
+ }
+ return rb_ensure(rb_yield, obj, rb_fiddle_ptr_call_free, obj);
+ } else {
+ return obj;
+ }
}
/*
@@ -371,6 +441,34 @@ rb_fiddle_ptr_free_get(VALUE self)
}
/*
+ * call-seq: call_free => nil
+ *
+ * Call the free function for this pointer. Calling more than once will do
+ * nothing. Does nothing if there is no free function attached.
+ */
+static VALUE
+rb_fiddle_ptr_call_free(VALUE self)
+{
+ struct ptr_data *pdata;
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata);
+ fiddle_ptr_free_ptr(pdata);
+ return Qnil;
+}
+
+/*
+ * call-seq: freed? => bool
+ *
+ * Returns if the free function for this pointer has been called.
+ */
+static VALUE
+rb_fiddle_ptr_freed_p(VALUE self)
+{
+ struct ptr_data *pdata;
+ TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata);
+ return pdata->freed ? Qtrue : Qfalse;
+}
+
+/*
* call-seq:
*
* ptr.to_s => string
@@ -711,6 +809,8 @@ Init_fiddle_pointer(void)
rb_define_method(rb_cPointer, "initialize", rb_fiddle_ptr_initialize, -1);
rb_define_method(rb_cPointer, "free=", rb_fiddle_ptr_free_set, 1);
rb_define_method(rb_cPointer, "free", rb_fiddle_ptr_free_get, 0);
+ rb_define_method(rb_cPointer, "call_free", rb_fiddle_ptr_call_free, 0);
+ rb_define_method(rb_cPointer, "freed?", rb_fiddle_ptr_freed_p, 0);
rb_define_method(rb_cPointer, "to_i", rb_fiddle_ptr_to_i, 0);
rb_define_method(rb_cPointer, "to_int", rb_fiddle_ptr_to_i, 0);
rb_define_method(rb_cPointer, "to_value", rb_fiddle_ptr_to_value, 0);
@@ -732,6 +832,10 @@ Init_fiddle_pointer(void)
rb_define_method(rb_cPointer, "size", rb_fiddle_ptr_size_get, 0);
rb_define_method(rb_cPointer, "size=", rb_fiddle_ptr_size_set, 1);
+#ifdef FIDDLE_MEMORY_VIEW
+ rb_memory_view_register(rb_cPointer, &fiddle_ptr_memory_view_entry);
+#endif
+
/* Document-const: NULL
*
* A NULL pointer
diff --git a/ext/fiddle/win32/libffi-3.2.1-cygwin.patch b/ext/fiddle/win32/libffi-3.2.1-cygwin.patch
deleted file mode 100644
index 743be274f4..0000000000
--- a/ext/fiddle/win32/libffi-3.2.1-cygwin.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- libffi-3.2.1/src/closures.c 2014-11-08 21:47:24.000000000 +0900
-+++ libffi-3.2.1/src/closures.c 2020-05-26 19:09:26.334088215 +0900
-@@ -26,7 +26,7 @@
- DEALINGS IN THE SOFTWARE.
- ----------------------------------------------------------------------- */
-
--#if defined __linux__ && !defined _GNU_SOURCE
-+#if (defined __linux__ || defined __CYGWIN__) && !defined _GNU_SOURCE
- #define _GNU_SOURCE 1
- #endif
-
diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb
index 33bfee6218..9fd16d7101 100644
--- a/test/fiddle/test_c_struct_entry.rb
+++ b/test/fiddle/test_c_struct_entry.rb
@@ -43,35 +43,123 @@ module Fiddle
end
def test_set_ctypes
- union = CStructEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE
- union.assign_names %w[int long]
+ CStructEntity.malloc([TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE) do |struct|
+ struct.assign_names %w[int long]
- # this test is roundabout because the stored ctypes are not accessible
- union['long'] = 1
- union['int'] = 2
+ # this test is roundabout because the stored ctypes are not accessible
+ struct['long'] = 1
+ struct['int'] = 2
- assert_equal 1, union['long']
- assert_equal 2, union['int']
+ assert_equal 1, struct['long']
+ assert_equal 2, struct['int']
+ end
end
def test_aref_pointer_array
- team = CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE)
- team.assign_names(["names"])
- alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE)
- alice[0, 6] = "Alice\0"
- bob = Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE)
- bob[0, 4] = "Bob\0"
- team["names"] = [alice, bob]
- assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
+ CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE) do |team|
+ team.assign_names(["names"])
+ Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice|
+ alice[0, 6] = "Alice\0"
+ Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE) do |bob|
+ bob[0, 4] = "Bob\0"
+ team["names"] = [alice, bob]
+ assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
+ end
+ end
+ end
end
def test_aref_pointer
- user = CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE)
- user.assign_names(["name"])
- alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE)
- alice[0, 6] = "Alice\0"
- user["name"] = alice
- assert_equal("Alice", user["name"].to_s)
+ CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE) do |user|
+ user.assign_names(["name"])
+ Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice|
+ alice[0, 6] = "Alice\0"
+ user["name"] = alice
+ assert_equal("Alice", user["name"].to_s)
+ end
+ end
+ end
+
+ def test_new_double_free
+ types = [TYPE_INT]
+ Pointer.malloc(CStructEntity.size(types), Fiddle::RUBY_FREE) do |pointer|
+ assert_raise ArgumentError do
+ CStructEntity.new(pointer, types, Fiddle::RUBY_FREE)
+ end
+ end
+ end
+
+ def test_malloc_block
+ escaped_struct = nil
+ returned = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct|
+ assert_equal Fiddle::SIZEOF_INT, struct.size
+ assert_equal Fiddle::RUBY_FREE, struct.free.to_i
+ escaped_struct = struct
+ :returned
+ end
+ assert_equal :returned, returned
+ assert escaped_struct.freed?
+ end
+
+ def test_malloc_block_no_free
+ assert_raise ArgumentError do
+ CStructEntity.malloc([TYPE_INT]) { |struct| }
+ end
+ end
+
+ def test_free
+ struct = CStructEntity.malloc([TYPE_INT])
+ begin
+ assert_nil struct.free
+ ensure
+ Fiddle.free struct
+ end
+ end
+
+ def test_free_with_func
+ struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE)
+ refute struct.freed?
+ struct.call_free
+ assert struct.freed?
+ struct.call_free # you can safely run it again
+ assert struct.freed?
+ GC.start # you can safely run the GC routine
+ assert struct.freed?
+ end
+
+ def test_free_with_no_func
+ struct = CStructEntity.malloc([TYPE_INT])
+ refute struct.freed?
+ struct.call_free
+ refute struct.freed?
+ struct.call_free # you can safely run it again
+ refute struct.freed?
+ end
+
+ def test_freed?
+ struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE)
+ refute struct.freed?
+ struct.call_free
+ assert struct.freed?
+ end
+
+ def test_null?
+ struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE)
+ refute struct.null?
+ end
+
+ def test_size
+ CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct|
+ assert_equal Fiddle::SIZEOF_INT, struct.size
+ end
+ end
+
+ def test_size=
+ CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct|
+ assert_raise NoMethodError do
+ struct.size = 1
+ end
+ end
end
end
end if defined?(Fiddle)
diff --git a/test/fiddle/test_c_union_entity.rb b/test/fiddle/test_c_union_entity.rb
index 9310084733..e0a3757562 100644
--- a/test/fiddle/test_c_union_entity.rb
+++ b/test/fiddle/test_c_union_entity.rb
@@ -21,15 +21,16 @@ module Fiddle
end
def test_set_ctypes
- union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE
- union.assign_names %w[int long]
+ CUnionEntity.malloc([TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE) do |union|
+ union.assign_names %w[int long]
- # this test is roundabout because the stored ctypes are not accessible
- union['long'] = 1
- assert_equal 1, union['long']
+ # this test is roundabout because the stored ctypes are not accessible
+ union['long'] = 1
+ assert_equal 1, union['long']
- union['int'] = 1
- assert_equal 1, union['int']
+ union['int'] = 1
+ assert_equal 1, union['int']
+ end
end
end
end if defined?(Fiddle)
diff --git a/test/fiddle/test_cparser.rb b/test/fiddle/test_cparser.rb
index 5d9ac3c815..ef8cec5daa 100644
--- a/test/fiddle/test_cparser.rb
+++ b/test/fiddle/test_cparser.rb
@@ -2,6 +2,7 @@
begin
require_relative 'helper'
require 'fiddle/cparser'
+ require 'fiddle/import'
rescue LoadError
end
@@ -68,6 +69,19 @@ module Fiddle
assert_equal(-TYPE_LONG, parse_ctype('DWORD', {"DWORD" => "unsigned long"}))
end
+ def expand_struct_types(types)
+ types.collect do |type|
+ case type
+ when Class
+ [expand_struct_types(type.types)]
+ when Array
+ [expand_struct_types([type[0]])[0][0], type[1]]
+ else
+ type
+ end
+ end
+ end
+
def test_struct_basic
assert_equal [[TYPE_INT, TYPE_CHAR], ['i', 'c']], parse_struct_signature(['int i', 'char c'])
end
@@ -76,6 +90,93 @@ module Fiddle
assert_equal [[[TYPE_CHAR,80],[TYPE_INT,5]], ['buffer','x']], parse_struct_signature(['char buffer[80]', 'int[5] x'])
end
+ def test_struct_nested_struct
+ types, members = parse_struct_signature([
+ 'int x',
+ {inner: ['int i', 'char c']},
+ ])
+ assert_equal([[TYPE_INT, [[TYPE_INT, TYPE_CHAR]]],
+ ['x', ['inner', ['i', 'c']]]],
+ [expand_struct_types(types),
+ members])
+ end
+
+ def test_struct_nested_defined_struct
+ inner = Fiddle::Importer.struct(['int i', 'char c'])
+ assert_equal([[TYPE_INT, inner],
+ ['x', ['inner', ['i', 'c']]]],
+ parse_struct_signature([
+ 'int x',
+ {inner: inner},
+ ]))
+ end
+
+ def test_struct_double_nested_struct
+ types, members = parse_struct_signature([
+ 'int x',
+ {
+ outer: [
+ 'int y',
+ {inner: ['int i', 'char c']},
+ ],
+ },
+ ])
+ assert_equal([[TYPE_INT, [[TYPE_INT, [[TYPE_INT, TYPE_CHAR]]]]],
+ ['x', ['outer', ['y', ['inner', ['i', 'c']]]]]],
+ [expand_struct_types(types),
+ members])
+ end
+
+ def test_struct_nested_struct_array
+ types, members = parse_struct_signature([
+ 'int x',
+ {
+ 'inner[2]' => [
+ 'int i',
+ 'char c',
+ ],
+ },
+ ])
+ assert_equal([[TYPE_INT, [[TYPE_INT, TYPE_CHAR], 2]],
+ ['x', ['inner', ['i', 'c']]]],
+ [expand_struct_types(types),
+ members])
+ end
+
+ def test_struct_double_nested_struct_inner_array
+ types, members = parse_struct_signature(outer: [
+ 'int x',
+ {
+ 'inner[2]' => [
+ 'int i',
+ 'char c',
+ ],
+ },
+ ])
+ assert_equal([[[[TYPE_INT, [[TYPE_INT, TYPE_CHAR], 2]]]],
+ [['outer', ['x', ['inner', ['i', 'c']]]]]],
+ [expand_struct_types(types),
+ members])
+ end
+
+ def test_struct_double_nested_struct_outer_array
+ types, members = parse_struct_signature([
+ 'int x',
+ {
+ 'outer[2]' => {
+ inner: [
+ 'int i',
+ 'char c',
+ ],
+ },
+ },
+ ])
+ assert_equal([[TYPE_INT, [[[[TYPE_INT, TYPE_CHAR]]], 2]],
+ ['x', ['outer', [['inner', ['i', 'c']]]]]],
+ [expand_struct_types(types),
+ members])
+ end
+
def test_struct_array_str
assert_equal [[[TYPE_CHAR,80],[TYPE_INT,5]], ['buffer','x']], parse_struct_signature('char buffer[80], int[5] x')
end
@@ -179,6 +280,18 @@ module Fiddle
assert_equal [TYPE_VOIDP, TYPE_INT, TYPE_INT], args
end
+ def test_signature_variadic_arguments
+ unless Fiddle.const_defined?("TYPE_VARIADIC")
+ skip "libffi doesn't support variadic arguments"
+ end
+ assert_equal([
+ "printf",
+ TYPE_INT,
+ [TYPE_VOIDP, TYPE_VARIADIC],
+ ],
+ parse_signature('int printf(const char *format, ...)'))
+ end
+
def test_signature_return_pointer
func, ret, args = parse_signature('void* malloc(size_t)')
assert_equal 'malloc', func
diff --git a/test/fiddle/test_function.rb b/test/fiddle/test_function.rb
index 0bb66f9b8d..742615a56b 100644
--- a/test/fiddle/test_function.rb
+++ b/test/fiddle/test_function.rb
@@ -21,6 +21,17 @@ module Fiddle
assert_equal 'sin', func.name
end
+ def test_need_gvl?
+ libruby = Fiddle.dlopen(nil)
+ rb_str_dup = Function.new(libruby['rb_str_dup'],
+ [:voidp],
+ :voidp,
+ need_gvl: true)
+ assert(rb_str_dup.need_gvl?)
+ assert_equal('Hello',
+ Fiddle.dlunwrap(rb_str_dup.call(Fiddle.dlwrap('Hello'))))
+ end
+
def test_argument_errors
assert_raise(TypeError) do
Function.new(@libm['sin'], TYPE_DOUBLE, TYPE_DOUBLE)
@@ -111,8 +122,7 @@ module Fiddle
n1 = f.call(nil, 0, msec)
n2 = th.value
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
- delta = EnvUtil.apply_timeout_scale(180)
- assert_in_delta(msec, t1 - t0, delta, 'slept amount of time')
+ assert_in_delta(msec, t1 - t0, 180, 'slept amount of time')
assert_equal(0, n1, perror("poll(2) in main-thread"))
assert_equal(0, n2, perror("poll(2) in sub-thread"))
end
diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb
index 4afd8e5562..afa8df9e00 100644
--- a/test/fiddle/test_import.rb
+++ b/test/fiddle/test_import.rb
@@ -36,6 +36,29 @@ module Fiddle
"char c",
"unsigned char buff[7]",
]
+ StructNestedStruct = struct [
+ {
+ "vertices[2]" => {
+ position: ["float x", "float y", "float z"],
+ texcoord: ["float u", "float v"]
+ },
+ object: ["int id", "void *user_data"],
+ },
+ "int id"
+ ]
+ UnionNestedStruct = union [
+ {
+ keyboard: [
+ 'unsigned int state',
+ 'char key'
+ ],
+ mouse: [
+ 'unsigned int button',
+ 'unsigned short x',
+ 'unsigned short y'
+ ]
+ }
+ ]
CallCallback = bind("void call_callback(void*, void*)"){ | ptr1, ptr2|
f = Function.new(ptr1.to_i, [TYPE_VOIDP], TYPE_VOID)
@@ -56,22 +79,18 @@ module Fiddle
def test_struct_memory_access()
# check memory operations performed directly on struct
- my_struct = Fiddle::Importer.struct(['int id']).malloc
- begin
+ Fiddle::Importer.struct(['int id']).malloc(Fiddle::RUBY_FREE) do |my_struct|
my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
assert_equal 0x01010101, my_struct.id
my_struct.id = 0
assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT]
- ensure
- Fiddle.free my_struct.to_ptr
end
end
def test_struct_ptr_array_subscript_multiarg()
# check memory operations performed on struct#to_ptr
- struct = Fiddle::Importer.struct([ 'int x' ]).malloc
- begin
+ Fiddle::Importer.struct([ 'int x' ]).malloc(Fiddle::RUBY_FREE) do |struct|
ptr = struct.to_ptr
struct.x = 0x02020202
@@ -79,35 +98,25 @@ module Fiddle
ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
assert_equal 0x01010101, struct.x
- ensure
- Fiddle.free struct.to_ptr
end
end
def test_malloc()
- s1 = LIBC::Timeval.malloc()
- begin
- s2 = LIBC::Timeval.malloc()
- begin
+ LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |s1|
+ LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |s2|
refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i)
- ensure
- Fiddle.free s2.to_ptr
end
- ensure
- Fiddle.free s1.to_ptr
end
end
def test_sizeof()
assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*"))
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct))
- my_struct = LIBC::MyStruct.malloc()
- begin
+ LIBC::MyStruct.malloc(Fiddle::RUBY_FREE) do |my_struct|
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(my_struct))
- ensure
- Fiddle.free my_struct.to_ptr
end
assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long")) if defined?(SIZEOF_LONG_LONG)
+ assert_equal(LIBC::StructNestedStruct.size(), LIBC.sizeof(LIBC::StructNestedStruct))
end
Fiddle.constants.grep(/\ATYPE_(?!VOID|VARIADIC\z)(.*)/) do
@@ -158,8 +167,7 @@ module Fiddle
end
def test_struct_array_assignment()
- instance = Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc
- begin
+ Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc(Fiddle::RUBY_FREE) do |instance|
instance.stages[0] = 1024
instance.stages[1] = 10
instance.stages[2] = 100
@@ -170,39 +178,279 @@ module Fiddle
instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT]
assert_raise(IndexError) { instance.stages[-1] = 5 }
assert_raise(IndexError) { instance.stages[3] = 5 }
- ensure
- Fiddle.free instance.to_ptr
+ end
+ end
+
+ def test_nested_struct_reusing_other_structs()
+ position_struct = Fiddle::Importer.struct(['float x', 'float y', 'float z'])
+ texcoord_struct = Fiddle::Importer.struct(['float u', 'float v'])
+ vertex_struct = Fiddle::Importer.struct(position: position_struct, texcoord: texcoord_struct)
+ mesh_struct = Fiddle::Importer.struct([
+ {
+ "vertices[2]" => vertex_struct,
+ object: [
+ "int id",
+ "void *user_data",
+ ],
+ },
+ "int id",
+ ])
+ assert_equal LIBC::StructNestedStruct.size, mesh_struct.size
+
+
+ keyboard_event_struct = Fiddle::Importer.struct(['unsigned int state', 'char key'])
+ mouse_event_struct = Fiddle::Importer.struct(['unsigned int button', 'unsigned short x', 'unsigned short y'])
+ event_union = Fiddle::Importer.union([{ keboard: keyboard_event_struct, mouse: mouse_event_struct}])
+ assert_equal LIBC::UnionNestedStruct.size, event_union.size
+ end
+
+ def test_nested_struct_alignment_is_not_its_size()
+ inner = Fiddle::Importer.struct(['int x', 'int y', 'int z', 'int w'])
+ outer = Fiddle::Importer.struct(['char a', { 'nested' => inner }, 'char b'])
+ outer.malloc(Fiddle::RUBY_FREE) do |instance|
+ offset = instance.to_ptr.instance_variable_get(:"@offset")
+ assert_equal Fiddle::SIZEOF_INT * 5, offset.last
+ assert_equal Fiddle::SIZEOF_INT * 6, outer.size
+ assert_equal instance.to_ptr.size, outer.size
+ end
+ end
+
+ def test_struct_nested_struct_members()
+ LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s|
+ Fiddle::Pointer.malloc(24, Fiddle::RUBY_FREE) do |user_data|
+ s.vertices[0].position.x = 1
+ s.vertices[0].position.y = 2
+ s.vertices[0].position.z = 3
+ s.vertices[0].texcoord.u = 4
+ s.vertices[0].texcoord.v = 5
+ s.vertices[1].position.x = 6
+ s.vertices[1].position.y = 7
+ s.vertices[1].position.z = 8
+ s.vertices[1].texcoord.u = 9
+ s.vertices[1].texcoord.v = 10
+ s.object.id = 100
+ s.object.user_data = user_data
+ s.id = 101
+ assert_equal({
+ "vertices" => [
+ {
+ "position" => {
+ "x" => 1,
+ "y" => 2,
+ "z" => 3,
+ },
+ "texcoord" => {
+ "u" => 4,
+ "v" => 5,
+ },
+ },
+ {
+ "position" => {
+ "x" => 6,
+ "y" => 7,
+ "z" => 8,
+ },
+ "texcoord" => {
+ "u" => 9,
+ "v" => 10,
+ },
+ },
+ ],
+ "object" => {
+ "id" => 100,
+ "user_data" => user_data,
+ },
+ "id" => 101,
+ },
+ s.to_h)
+ end
+ end
+ end
+
+ def test_union_nested_struct_members()
+ LIBC::UnionNestedStruct.malloc(Fiddle::RUBY_FREE) do |s|
+ s.keyboard.state = 100
+ s.keyboard.key = 101
+ assert_equal(100, s.mouse.button)
+ refute_equal( 0, s.mouse.x)
+ end
+ end
+
+ def test_struct_nested_struct_replace_array_element()
+ LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s|
+ s.vertices[0].position.x = 5
+
+ vertex_struct = Fiddle::Importer.struct [{
+ position: ["float x", "float y", "float z"],
+ texcoord: ["float u", "float v"]
+ }]
+ vertex_struct.malloc(Fiddle::RUBY_FREE) do |vertex|
+ vertex.position.x = 100
+ s.vertices[0] = vertex
+
+ # make sure element was copied by value, but things like memory address
+ # should not be changed
+ assert_equal(100, s.vertices[0].position.x)
+ refute_equal(vertex.object_id, s.vertices[0].object_id)
+ refute_equal(vertex.to_ptr, s.vertices[0].to_ptr)
+ end
+ end
+ end
+
+ def test_struct_nested_struct_replace_array_element_nil()
+ LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s|
+ s.vertices[0].position.x = 5
+ s.vertices[0] = nil
+ assert_equal({
+ "position" => {
+ "x" => 0.0,
+ "y" => 0.0,
+ "z" => 0.0,
+ },
+ "texcoord" => {
+ "u" => 0.0,
+ "v" => 0.0,
+ },
+ },
+ s.vertices[0].to_h)
+ end
+ end
+
+ def test_struct_nested_struct_replace_array_element_hash()
+ LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s|
+ s.vertices[0] = {
+ position: {
+ x: 10,
+ y: 100,
+ }
+ }
+ assert_equal({
+ "position" => {
+ "x" => 10.0,
+ "y" => 100.0,
+ "z" => 0.0,
+ },
+ "texcoord" => {
+ "u" => 0.0,
+ "v" => 0.0,
+ },
+ },
+ s.vertices[0].to_h)
+ end
+ end
+
+ def test_struct_nested_struct_replace_entire_array()
+ LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s|
+ vertex_struct = Fiddle::Importer.struct [{
+ position: ["float x", "float y", "float z"],
+ texcoord: ["float u", "float v"]
+ }]
+
+ vertex_struct.malloc(Fiddle::RUBY_FREE) do |same0|
+ vertex_struct.malloc(Fiddle::RUBY_FREE) do |same1|
+ same = [same0, same1]
+ same[0].position.x = 1; same[1].position.x = 6
+ same[0].position.y = 2; same[1].position.y = 7
+ same[0].position.z = 3; same[1].position.z = 8
+ same[0].texcoord.u = 4; same[1].texcoord.u = 9
+ same[0].texcoord.v = 5; same[1].texcoord.v = 10
+ s.vertices = same
+ assert_equal([
+ {
+ "position" => {
+ "x" => 1.0,
+ "y" => 2.0,
+ "z" => 3.0,
+ },
+ "texcoord" => {
+ "u" => 4.0,
+ "v" => 5.0,
+ },
+ },
+ {
+ "position" => {
+ "x" => 6.0,
+ "y" => 7.0,
+ "z" => 8.0,
+ },
+ "texcoord" => {
+ "u" => 9.0,
+ "v" => 10.0,
+ },
+ }
+ ],
+ s.vertices.collect(&:to_h))
+ end
+ end
+ end
+ end
+
+ def test_struct_nested_struct_replace_entire_array_with_different_struct()
+ LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s|
+ different_struct_same_size = Fiddle::Importer.struct [{
+ a: ['float i', 'float j', 'float k'],
+ b: ['float l', 'float m']
+ }]
+
+ different_struct_same_size.malloc(Fiddle::RUBY_FREE) do |different0|
+ different_struct_same_size.malloc(Fiddle::RUBY_FREE) do |different1|
+ different = [different0, different1]
+ different[0].a.i = 11; different[1].a.i = 16
+ different[0].a.j = 12; different[1].a.j = 17
+ different[0].a.k = 13; different[1].a.k = 18
+ different[0].b.l = 14; different[1].b.l = 19
+ different[0].b.m = 15; different[1].b.m = 20
+ s.vertices[0][0, s.vertices[0].class.size] = different[0].to_ptr
+ s.vertices[1][0, s.vertices[1].class.size] = different[1].to_ptr
+ assert_equal([
+ {
+ "position" => {
+ "x" => 11.0,
+ "y" => 12.0,
+ "z" => 13.0,
+ },
+ "texcoord" => {
+ "u" => 14.0,
+ "v" => 15.0,
+ },
+ },
+ {
+ "position" => {
+ "x" => 16.0,
+ "y" => 17.0,
+ "z" => 18.0,
+ },
+ "texcoord" => {
+ "u" => 19.0,
+ "v" => 20.0,
+ },
+ }
+ ],
+ s.vertices.collect(&:to_h))
+ end
+ end
end
end
def test_struct()
- s = LIBC::MyStruct.malloc()
- begin
+ LIBC::MyStruct.malloc(Fiddle::RUBY_FREE) do |s|
s.num = [0,1,2,3,4]
s.c = ?a.ord
s.buff = "012345\377"
assert_equal([0,1,2,3,4], s.num)
assert_equal(?a.ord, s.c)
assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff)
- ensure
- Fiddle.free s.to_ptr
end
end
def test_gettimeofday()
if( defined?(LIBC.gettimeofday) )
- timeval = LIBC::Timeval.malloc()
- begin
- timezone = LIBC::Timezone.malloc()
- begin
+ LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |timeval|
+ LIBC::Timezone.malloc(Fiddle::RUBY_FREE) do |timezone|
LIBC.gettimeofday(timeval, timezone)
- ensure
- Fiddle.free timezone.to_ptr
end
cur = Time.now()
assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i)
- ensure
- Fiddle.free timeval.to_ptr
end
end
end
@@ -227,9 +475,5 @@ module Fiddle
r = LIBC.atof("12.34")
assert_includes(12.00..13.00, r)
end
-
- def test_no_message_with_debug
- assert_in_out_err(%w[--debug --disable=gems -rfiddle/import], 'p Fiddle::Importer', ['Fiddle::Importer'])
- end
end
end if defined?(Fiddle)
diff --git a/test/fiddle/test_memory_view.rb b/test/fiddle/test_memory_view.rb
new file mode 100644
index 0000000000..3c310c2d68
--- /dev/null
+++ b/test/fiddle/test_memory_view.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+begin
+ require '-test-/memory_view'
+rescue LoadError
+end
+
+module Fiddle
+ class TestMemoryView < TestCase
+ def setup
+ skip "MemoryView is unavailable" unless defined? Fiddle::MemoryView
+ end
+
+ def test_null_ptr
+ assert_raise(ArgumentError) do
+ MemoryView.new(Fiddle::NULL)
+ end
+ end
+
+ def test_memory_view_from_unsupported_obj
+ obj = Object.new
+ assert_raise(ArgumentError) do
+ MemoryView.new(obj)
+ end
+ end
+
+ def test_memory_view_from_pointer
+ str = Marshal.load(Marshal.dump("hello world"))
+ ptr = Pointer[str]
+ mview = MemoryView.new(ptr)
+ assert_same(ptr, mview.obj)
+ assert_equal(str.length, mview.length)
+ assert_equal(true, mview.readonly?)
+ assert_equal(nil, mview.format)
+ assert_equal(1, mview.item_size)
+ assert_equal(1, mview.ndim)
+ assert_equal(nil, mview.shape)
+ assert_equal(nil, mview.strides)
+ assert_equal(nil, mview.sub_offsets)
+
+ codes = str.codepoints
+ assert_equal(codes, (0...str.length).map {|i| mview[i] })
+ end
+
+ def test_memory_view_multi_dimensional
+ skip "MemoryViewTestUtils is unavailable" unless defined? MemoryViewTestUtils
+
+ buf = [ 1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12 ].pack("l!*")
+ shape = [3, 4]
+ md = MemoryViewTestUtils::MultiDimensionalView.new(buf, "l!", shape, nil)
+ mview = Fiddle::MemoryView.new(md)
+ assert_equal(buf.length, mview.length)
+ assert_equal("l!", mview.format)
+ assert_equal(Fiddle::SIZEOF_LONG, mview.item_size)
+ assert_equal(2, mview.ndim)
+ assert_equal(shape, mview.shape)
+ assert_equal([Fiddle::SIZEOF_LONG*4, Fiddle::SIZEOF_LONG], mview.strides)
+ assert_equal(nil, mview.sub_offsets)
+ assert_equal(1, mview[0, 0])
+ assert_equal(4, mview[0, 3])
+ assert_equal(6, mview[1, 1])
+ assert_equal(10, mview[2, 1])
+ end
+
+ def test_memory_view_multi_dimensional_with_strides
+ skip "MemoryViewTestUtils is unavailable" unless defined? MemoryViewTestUtils
+
+ buf = [ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16 ].pack("l!*")
+ shape = [2, 8]
+ strides = [4*Fiddle::SIZEOF_LONG*2, Fiddle::SIZEOF_LONG*2]
+ md = MemoryViewTestUtils::MultiDimensionalView.new(buf, "l!", shape, strides)
+ mview = Fiddle::MemoryView.new(md)
+ assert_equal("l!", mview.format)
+ assert_equal(Fiddle::SIZEOF_LONG, mview.item_size)
+ assert_equal(buf.length, mview.length)
+ assert_equal(2, mview.ndim)
+ assert_equal(shape, mview.shape)
+ assert_equal(strides, mview.strides)
+ assert_equal(nil, mview.sub_offsets)
+ assert_equal(1, mview[0, 0])
+ assert_equal(5, mview[0, 2])
+ assert_equal(9, mview[1, 0])
+ assert_equal(15, mview[1, 3])
+ end
+
+ def test_memory_view_multi_dimensional_with_multiple_members
+ skip "MemoryViewTestUtils is unavailable" unless defined? MemoryViewTestUtils
+
+ buf = [ 1, 2, 3, 4, 5, 6, 7, 8,
+ -1, -2, -3, -4, -5, -6, -7, -8].pack("s*")
+ shape = [2, 4]
+ strides = [4*Fiddle::SIZEOF_SHORT*2, Fiddle::SIZEOF_SHORT*2]
+ md = MemoryViewTestUtils::MultiDimensionalView.new(buf, "ss", shape, strides)
+ mview = Fiddle::MemoryView.new(md)
+ assert_equal("ss", mview.format)
+ assert_equal(Fiddle::SIZEOF_SHORT*2, mview.item_size)
+ assert_equal(buf.length, mview.length)
+ assert_equal(2, mview.ndim)
+ assert_equal(shape, mview.shape)
+ assert_equal(strides, mview.strides)
+ assert_equal(nil, mview.sub_offsets)
+ assert_equal([1, 2], mview[0, 0])
+ assert_equal([5, 6], mview[0, 2])
+ assert_equal([-1, -2], mview[1, 0])
+ assert_equal([-7, -8], mview[1, 3])
+ end
+ end
+end
diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb
index c69e4f7142..e685fea5dc 100644
--- a/test/fiddle/test_pointer.rb
+++ b/test/fiddle/test_pointer.rb
@@ -32,6 +32,31 @@ module Fiddle
assert_equal free.to_i, ptr.free.to_i
end
+ def test_malloc_block
+ escaped_ptr = nil
+ returned = Pointer.malloc(10, Fiddle::RUBY_FREE) do |ptr|
+ assert_equal 10, ptr.size
+ assert_equal Fiddle::RUBY_FREE, ptr.free.to_i
+ escaped_ptr = ptr
+ :returned
+ end
+ assert_equal :returned, returned
+ assert escaped_ptr.freed?
+ end
+
+ def test_malloc_block_no_free
+ assert_raise ArgumentError do
+ Pointer.malloc(10) { |ptr| }
+ end
+ end
+
+ def test_malloc_subclass
+ subclass = Class.new(Pointer)
+ subclass.malloc(10, Fiddle::RUBY_FREE) do |ptr|
+ assert ptr.is_a?(subclass)
+ end
+ end
+
def test_to_str
str = Marshal.load(Marshal.dump("hello world"))
ptr = Pointer[str]
@@ -84,17 +109,18 @@ module Fiddle
end
def test_to_ptr_io
- buf = Pointer.malloc(10, Fiddle::RUBY_FREE)
- File.open(__FILE__, 'r') do |f|
- ptr = Pointer.to_ptr f
- fread = Function.new(@libc['fread'],
- [TYPE_VOIDP, TYPE_INT, TYPE_INT, TYPE_VOIDP],
- TYPE_INT)
- fread.call(buf.to_i, Fiddle::SIZEOF_CHAR, buf.size - 1, ptr.to_i)
- end
-
- File.open(__FILE__, 'r') do |f|
- assert_equal f.read(9), buf.to_s
+ Pointer.malloc(10, Fiddle::RUBY_FREE) do |buf|
+ File.open(__FILE__, 'r') do |f|
+ ptr = Pointer.to_ptr f
+ fread = Function.new(@libc['fread'],
+ [TYPE_VOIDP, TYPE_INT, TYPE_INT, TYPE_VOIDP],
+ TYPE_INT)
+ fread.call(buf.to_i, Fiddle::SIZEOF_CHAR, buf.size - 1, ptr.to_i)
+ end
+
+ File.open(__FILE__, 'r') do |f|
+ assert_equal f.read(9), buf.to_s
+ end
end
end
@@ -170,27 +196,48 @@ module Fiddle
assert_equal free.ptr, ptr.free.ptr
end
+ def test_free_with_func
+ ptr = Pointer.malloc(4, Fiddle::RUBY_FREE)
+ refute ptr.freed?
+ ptr.call_free
+ assert ptr.freed?
+ ptr.call_free # you can safely run it again
+ assert ptr.freed?
+ GC.start # you can safely run the GC routine
+ assert ptr.freed?
+ end
+
+ def test_free_with_no_func
+ ptr = Pointer.malloc(4)
+ refute ptr.freed?
+ ptr.call_free
+ refute ptr.freed?
+ ptr.call_free # you can safely run it again
+ refute ptr.freed?
+ end
+
+ def test_freed?
+ ptr = Pointer.malloc(4, Fiddle::RUBY_FREE)
+ refute ptr.freed?
+ ptr.call_free
+ assert ptr.freed?
+ end
+
def test_null?
ptr = Pointer.new(0)
assert ptr.null?
end
def test_size
- ptr = Pointer.malloc(4)
- begin
+ Pointer.malloc(4, Fiddle::RUBY_FREE) do |ptr|
assert_equal 4, ptr.size
- ensure
- Fiddle.free ptr
end
end
def test_size=
- ptr = Pointer.malloc(4)
- begin
+ Pointer.malloc(4, Fiddle::RUBY_FREE) do |ptr|
ptr.size = 10
assert_equal 10, ptr.size
- ensure
- Fiddle.free ptr
end
end