summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/-test-/memory_view/memory_view.c27
-rw-r--r--include/ruby/memory_view.h3
-rw-r--r--memory_view.c225
-rw-r--r--test/ruby/test_memory_view.rb51
4 files changed, 300 insertions, 6 deletions
diff --git a/ext/-test-/memory_view/memory_view.c b/ext/-test-/memory_view/memory_view.c
index f7c50900876..621614b1111 100644
--- a/ext/-test-/memory_view/memory_view.c
+++ b/ext/-test-/memory_view/memory_view.c
@@ -80,15 +80,15 @@ memory_view_parse_item_format(VALUE mod, VALUE format)
const char *err = NULL;
rb_memory_view_item_component_t *members;
- ssize_t n_members;
+ size_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;
+ VALUE ary = rb_ary_new_capa((long)n_members);
+ size_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));
@@ -231,6 +231,26 @@ memory_view_ref_count_while_exporting(VALUE mod, VALUE obj, VALUE n)
}
static VALUE
+memory_view_extract_item_members(VALUE mod, VALUE str, VALUE format)
+{
+ StringValue(str);
+ StringValue(format);
+
+ rb_memory_view_item_component_t *members;
+ size_t n_members;
+ const char *err = NULL;
+ (void)rb_memory_view_parse_item_format(RSTRING_PTR(format), &members, &n_members, &err);
+ if (err != NULL) {
+ rb_raise(rb_eArgError, "Unable to parse item format");
+ }
+
+ VALUE item = rb_memory_view_extract_item_members(RSTRING_PTR(str), members, n_members);
+ xfree(members);
+
+ return item;
+}
+
+static VALUE
expstr_initialize(VALUE obj, VALUE s)
{
if (!NIL_P(s)) {
@@ -346,6 +366,7 @@ Init_memory_view(void)
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);
rb_define_module_function(mMemoryViewTestUtils, "ref_count_while_exporting", memory_view_ref_count_while_exporting, 2);
+ rb_define_module_function(mMemoryViewTestUtils, "extract_item_members", memory_view_extract_item_members, 2);
VALUE cExportableString = rb_define_class_under(mMemoryViewTestUtils, "ExportableString", rb_cObject);
rb_define_method(cExportableString, "initialize", expstr_initialize, 1);
diff --git a/include/ruby/memory_view.h b/include/ruby/memory_view.h
index e2c5cd9a03f..7c8ed72fa28 100644
--- a/include/ruby/memory_view.h
+++ b/include/ruby/memory_view.h
@@ -128,9 +128,10 @@ RBIMPL_ATTR_NOALIAS()
int rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const bool 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);
+ size_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);
+VALUE rb_memory_view_extract_item_members(const void *ptr, const rb_memory_view_item_component_t *members, size_t n_members);
int rb_memory_view_available_p(VALUE obj);
int rb_memory_view_get(VALUE obj, rb_memory_view_t* memory_view, int flags);
diff --git a/memory_view.c b/memory_view.c
index 8b2a2c051d7..2bf3eee1079 100644
--- a/memory_view.c
+++ b/memory_view.c
@@ -12,6 +12,18 @@
#include "internal/util.h"
#include "ruby/memory_view.h"
+#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
+
+
#define STRUCT_ALIGNOF(T, result) do { \
(result) = RUBY_ALIGNOF(T); \
} while(0)
@@ -394,13 +406,13 @@ calculate_padding(ssize_t total, ssize_t alignment_size) {
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)
+ size_t *n_members, const char **err)
{
if (format == NULL) return 1;
VALUE error = Qnil;
ssize_t total = 0;
- ssize_t len = 0;
+ size_t len = 0;
bool alignment = false;
ssize_t max_alignment_size = 0;
@@ -558,6 +570,215 @@ rb_memory_view_get_item_pointer(rb_memory_view_t *view, const ssize_t *indices)
return ptr;
}
+static void
+switch_endianness(uint8_t *buf, ssize_t len)
+{
+ RUBY_ASSERT(buf != NULL);
+ RUBY_ASSERT(len >= 0);
+
+ uint8_t *p = buf;
+ uint8_t *q = buf + len - 1;
+
+ while (q - p > 0) {
+ uint8_t t = *p;
+ *p = *q;
+ *q = t;
+ ++p;
+ --q;
+ }
+}
+
+static inline VALUE
+extract_item_member(const uint8_t *ptr, const rb_memory_view_item_component_t *member, const size_t i,
+ uint8_t *buf, const size_t buf_size)
+{
+ RUBY_ASSERT(ptr != NULL);
+ RUBY_ASSERT(member != NULL);
+
+#ifdef WORDS_BIGENDIAN
+ const bool native_endian_p = !member->little_endian_p;
+#else
+ const bool native_endian_p = member->little_endian_p;
+#endif
+
+ const uint8_t *p = ptr + member->offset + i * member->size;
+
+ if (member->format == 'c') {
+ return INT2FIX(*(char *)p);
+ }
+ else if (member->format == 'C') {
+ return INT2FIX(*(unsigned char *)p);
+ }
+
+ const uint8_t *q = p;
+
+ if (!native_endian_p) {
+ RUBY_ASSERT(buf != NULL);
+ MEMCPY(buf, p, uint8_t, member->size);
+ switch_endianness(buf, member->size);
+ q = buf;
+ }
+
+ switch (member->format) {
+ case 's':
+ if (member->native_size_p) {
+ return INT2FIX(*(short *)q);
+ }
+ else {
+ return INT2FIX(*(int16_t *)q);
+ }
+
+ case 'S':
+ case 'n':
+ case 'v':
+ if (member->native_size_p) {
+ return UINT2NUM(*(unsigned short *)q);
+ }
+ else {
+ return INT2FIX(*(uint16_t *)q);
+ }
+
+ case 'i':
+ return INT2NUM(*(int *)q);
+
+ case 'I':
+ return UINT2NUM(*(unsigned int *)q);
+
+ case 'l':
+ if (member->native_size_p) {
+ return LONG2NUM(*(long *)q);
+ }
+ else {
+ return LONG2NUM(*(int32_t *)q);
+ }
+
+ case 'L':
+ case 'N':
+ case 'V':
+ if (member->native_size_p) {
+ return ULONG2NUM(*(unsigned long *)q);
+ }
+ else {
+ return ULONG2NUM(*(uint32_t *)q);
+ }
+
+ case 'f':
+ case 'e':
+ case 'g':
+ return DBL2NUM(*(float *)q);
+
+ case 'q':
+ if (member->native_size_p) {
+ return LL2NUM(*(LONG_LONG *)q);
+ }
+ else {
+#if SIZEOF_INT64_t == SIZEOF_LONG
+ return LONG2NUM(*(int64_t *)q);
+#else
+ return LL2NUM(*(int64_t *)q);
+#endif
+ }
+
+ case 'Q':
+ if (member->native_size_p) {
+ return ULL2NUM(*(unsigned LONG_LONG *)q);
+ }
+ else {
+#if SIZEOF_UINT64_t == SIZEOF_LONG
+ return ULONG2NUM(*(uint64_t *)q);
+#else
+ return ULL2NUM(*(uint64_t *)q);
+#endif
+ }
+
+ case 'd':
+ case 'E':
+ case 'G':
+ return DBL2NUM(*(double *)q);
+
+ case 'j':
+ return INTPTR2NUM(*(intptr_t *)q);
+
+ case 'J':
+ return UINTPTR2NUM(*(uintptr_t *)q);
+
+ default:
+ UNREACHABLE_RETURN(Qnil);
+ }
+}
+
+/* Return a value of the extracted member. */
+VALUE
+rb_memory_view_extract_item_member(const void *ptr, const rb_memory_view_item_component_t *member, const size_t i)
+{
+ if (ptr == NULL) return Qnil;
+ if (member == NULL) return Qnil;
+ if (i >= member->repeat) return Qnil;
+
+#ifdef WORDS_BIGENDIAN
+ const bool native_endian_p = !member->little_endian_p;
+#else
+ const bool native_endian_p = member->little_endian_p;
+#endif
+
+ VALUE buf_v = 0;
+ uint8_t *buf;
+ if (!native_endian_p) {
+ buf = ALLOCV_N(uint8_t, buf_v, member->size);
+ }
+
+ VALUE v = extract_item_member(ptr, member, i, buf, member->size);
+
+ if (buf_v) ALLOCV_END(buf_v);
+ return v;
+}
+
+/* Return a value that consists of item members.
+ * When an item is a single member, the return value is a single value.
+ * When an item consists of multiple members, an array will be returned. */
+VALUE
+rb_memory_view_extract_item_members(const void *ptr, const rb_memory_view_item_component_t *members, const size_t n_members)
+{
+ if (ptr == NULL) return Qnil;
+ if (members == NULL) return Qnil;
+ if (n_members == 0) return Qnil;
+
+ if (n_members == 1 && members[0].repeat == 1) {
+ return rb_memory_view_extract_item_member(ptr, members, 0);
+ }
+
+ size_t i, max_size = 0;
+ bool need_switch_endianness_p = false;
+ for (i = 0; i < n_members; ++i) {
+ if (max_size < members[i].size) {
+ max_size = members[i].size;
+ }
+#ifdef WORDS_BIGENDIAN
+ need_switch_endianness_p |= members[i].little_endian_p;
+#else
+ need_switch_endianness_p |= !members[i].little_endian_p;
+#endif
+ }
+
+ VALUE buf_v = 0;
+ uint8_t *buf;
+ if (need_switch_endianness_p) {
+ buf = ALLOCV_N(uint8_t, buf_v, max_size);
+ }
+
+ VALUE item = rb_ary_new();
+ for (i = 0; i < n_members; ++i) {
+ size_t j;
+ for (j = 0; j < members[i].repeat; ++j) {
+ VALUE v = extract_item_member(ptr, &members[i], j, buf, max_size);
+ rb_ary_push(item, v);
+ }
+ }
+
+ if (buf_v) ALLOCV_END(buf_v);
+ return item;
+}
+
static const rb_memory_view_entry_t *
lookup_memory_view_entry(VALUE klass)
{
diff --git a/test/ruby/test_memory_view.rb b/test/ruby/test_memory_view.rb
index baf544c2275..0150e18c726 100644
--- a/test/ruby/test_memory_view.rb
+++ b/test/ruby/test_memory_view.rb
@@ -197,6 +197,57 @@ class TestMemoryView < Test::Unit::TestCase
assert_equal(expected_result, members)
end
+ def test_rb_memory_view_extract_item_members
+ m = MemoryViewTestUtils
+ assert_equal(1, m.extract_item_members([1].pack("c"), "c"))
+ assert_equal([1, 2], m.extract_item_members([1, 2].pack("ii"), "ii"))
+ assert_equal([1, 2, 3], m.extract_item_members([1, 2, 3].pack("cls"), "cls"))
+ end
+
+ def test_rb_memory_view_extract_item_members_endianness
+ m = MemoryViewTestUtils
+ assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S>2"))
+ assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "n2"))
+ assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S<2"))
+ assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "v2"))
+ assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L>"))
+ assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "N"))
+ assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L<"))
+ assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "V"))
+ assert_equal(0x0102030405060708, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q>"))
+ assert_equal(0x0807060504030201, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q<"))
+ end
+
+ def test_rb_memory_view_extract_item_members_float
+ m = MemoryViewTestUtils
+ packed = [1.23].pack("f")
+ assert_equal(packed.unpack("f")[0], m.extract_item_members(packed, "f"))
+ end
+
+ def test_rb_memory_view_extract_item_members_float_endianness
+ m = MemoryViewTestUtils
+ hi, lo = [1.23].pack("f").unpack("L")[0].divmod(0x10000)
+ packed = [lo, hi].pack("S*")
+ assert_equal(packed.unpack("e")[0], m.extract_item_members(packed, "e"))
+ packed = [hi, lo].pack("S*")
+ assert_equal(packed.unpack("g")[0], m.extract_item_members(packed, "g"))
+ end
+
+ def test_rb_memory_view_extract_item_members_doble
+ m = MemoryViewTestUtils
+ packed = [1.23].pack("d")
+ assert_equal(1.23, m.extract_item_members(packed, "d"))
+ end
+
+ def test_rb_memory_view_extract_item_members_doble_endianness
+ m = MemoryViewTestUtils
+ hi, lo = [1.23].pack("d").unpack("Q")[0].divmod(0x10000)
+ packed = [lo, hi].pack("L*")
+ assert_equal(packed.unpack("E")[0], m.extract_item_members(packed, "E"))
+ packed = [hi, lo].pack("L*")
+ assert_equal(packed.unpack("G")[0], m.extract_item_members(packed, "G"))
+ end
+
def test_rb_memory_view_available_p
es = MemoryViewTestUtils::ExportableString.new("ruby")
assert_equal(true, MemoryViewTestUtils.available?(es))