Skip to content

Commit 7e69296

Browse files
authored
memory_view.c: Add rb_memory_view_extract_item_members (ruby#3855)
1 parent ca76337 commit 7e69296

File tree

4 files changed

+285
-6
lines changed

4 files changed

+285
-6
lines changed

ext/-test-/memory_view/memory_view.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,15 @@ memory_view_parse_item_format(VALUE mod, VALUE format)
8080
const char *err = NULL;
8181

8282
rb_memory_view_item_component_t *members;
83-
ssize_t n_members;
83+
size_t n_members;
8484
ssize_t item_size = rb_memory_view_parse_item_format(c_str, &members, &n_members, &err);
8585

8686
VALUE result = rb_ary_new_capa(3);
8787
rb_ary_push(result, SSIZET2NUM(item_size));
8888

8989
if (!err) {
90-
VALUE ary = rb_ary_new_capa(n_members);
91-
ssize_t i;
90+
VALUE ary = rb_ary_new_capa((long)n_members);
91+
size_t i;
9292
for (i = 0; i < n_members; ++i) {
9393
VALUE member = rb_hash_new();
9494
rb_hash_aset(member, sym_format, rb_str_new(&members[i].format, 1));
@@ -230,6 +230,26 @@ memory_view_ref_count_while_exporting(VALUE mod, VALUE obj, VALUE n)
230230
return memory_view_ref_count_while_exporting_i(obj, FIX2LONG(n));
231231
}
232232

233+
static VALUE
234+
memory_view_extract_item_members(VALUE mod, VALUE str, VALUE format)
235+
{
236+
StringValue(str);
237+
StringValue(format);
238+
239+
rb_memory_view_item_component_t *members;
240+
size_t n_members;
241+
const char *err = NULL;
242+
(void)rb_memory_view_parse_item_format(RSTRING_PTR(format), &members, &n_members, &err);
243+
if (err != NULL) {
244+
rb_raise(rb_eArgError, "Unable to parse item format");
245+
}
246+
247+
VALUE item = rb_memory_view_extract_item_members(RSTRING_PTR(str), members, n_members);
248+
xfree(members);
249+
250+
return item;
251+
}
252+
233253
static VALUE
234254
expstr_initialize(VALUE obj, VALUE s)
235255
{
@@ -346,6 +366,7 @@ Init_memory_view(void)
346366
rb_define_module_function(mMemoryViewTestUtils, "get_memory_view_info", memory_view_get_memory_view_info, 1);
347367
rb_define_module_function(mMemoryViewTestUtils, "fill_contiguous_strides", memory_view_fill_contiguous_strides, 4);
348368
rb_define_module_function(mMemoryViewTestUtils, "ref_count_while_exporting", memory_view_ref_count_while_exporting, 2);
369+
rb_define_module_function(mMemoryViewTestUtils, "extract_item_members", memory_view_extract_item_members, 2);
349370

350371
VALUE cExportableString = rb_define_class_under(mMemoryViewTestUtils, "ExportableString", rb_cObject);
351372
rb_define_method(cExportableString, "initialize", expstr_initialize, 1);

include/ruby/memory_view.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,10 @@ RBIMPL_ATTR_NOALIAS()
128128
int rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const bool readonly);
129129
ssize_t rb_memory_view_parse_item_format(const char *format,
130130
rb_memory_view_item_component_t **members,
131-
ssize_t *n_members, const char **err);
131+
size_t *n_members, const char **err);
132132
ssize_t rb_memory_view_item_size_from_format(const char *format, const char **err);
133133
void *rb_memory_view_get_item_pointer(rb_memory_view_t *view, const ssize_t *indices);
134+
VALUE rb_memory_view_extract_item_members(const void *ptr, const rb_memory_view_item_component_t *members, const size_t n_members);
134135

135136
int rb_memory_view_available_p(VALUE obj);
136137
int rb_memory_view_get(VALUE obj, rb_memory_view_t* memory_view, int flags);

memory_view.c

Lines changed: 208 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@
1212
#include "internal/util.h"
1313
#include "ruby/memory_view.h"
1414

15+
#if SIZEOF_INTPTR_T == SIZEOF_LONG_LONG
16+
# define INTPTR2NUM LL2NUM
17+
# define UINTPTR2NUM ULL2NUM
18+
#elif SIZEOF_INTPTR_T == SIZEOF_LONG
19+
# define INTPTR2NUM LONG2NUM
20+
# define UINTPTR2NUM ULONG2NUM
21+
#else
22+
# define INTPTR2NUM INT2NUM
23+
# define UINTPTR2NUM UINT2NUM
24+
#endif
25+
26+
1527
#define STRUCT_ALIGNOF(T, result) do { \
1628
(result) = RUBY_ALIGNOF(T); \
1729
} while(0)
@@ -394,13 +406,13 @@ calculate_padding(ssize_t total, ssize_t alignment_size) {
394406
ssize_t
395407
rb_memory_view_parse_item_format(const char *format,
396408
rb_memory_view_item_component_t **members,
397-
ssize_t *n_members, const char **err)
409+
size_t *n_members, const char **err)
398410
{
399411
if (format == NULL) return 1;
400412

401413
VALUE error = Qnil;
402414
ssize_t total = 0;
403-
ssize_t len = 0;
415+
size_t len = 0;
404416
bool alignment = false;
405417
ssize_t max_alignment_size = 0;
406418

@@ -558,6 +570,200 @@ rb_memory_view_get_item_pointer(rb_memory_view_t *view, const ssize_t *indices)
558570
return ptr;
559571
}
560572

573+
static void
574+
switch_endianness(uint8_t *buf, ssize_t len)
575+
{
576+
RUBY_ASSERT(buf != NULL);
577+
RUBY_ASSERT(len >= 0);
578+
579+
uint8_t *p = buf;
580+
uint8_t *q = buf + len - 1;
581+
582+
while (q - p > 0) {
583+
uint8_t t = *p;
584+
*p = *q;
585+
*q = t;
586+
++p;
587+
--q;
588+
}
589+
}
590+
591+
static inline VALUE
592+
extract_item_member(const uint8_t *ptr, const rb_memory_view_item_component_t *member, const size_t i)
593+
{
594+
RUBY_ASSERT(ptr != NULL);
595+
RUBY_ASSERT(member != NULL);
596+
RUBY_ASSERT(i < member->repeat);
597+
598+
#ifdef WORDS_BIGENDIAN
599+
const bool native_endian_p = !member->little_endian_p;
600+
#else
601+
const bool native_endian_p = member->little_endian_p;
602+
#endif
603+
604+
const uint8_t *p = ptr + member->offset + i * member->size;
605+
606+
if (member->format == 'c') {
607+
return INT2FIX(*(char *)p);
608+
}
609+
else if (member->format == 'C') {
610+
return INT2FIX(*(unsigned char *)p);
611+
}
612+
613+
union {
614+
short s;
615+
unsigned short us;
616+
int i;
617+
unsigned int ui;
618+
long l;
619+
unsigned long ul;
620+
LONG_LONG ll;
621+
unsigned LONG_LONG ull;
622+
int16_t i16;
623+
uint16_t u16;
624+
int32_t i32;
625+
uint32_t u32;
626+
int64_t i64;
627+
uint64_t u64;
628+
intptr_t iptr;
629+
uintptr_t uptr;
630+
float f;
631+
double d;
632+
} val;
633+
634+
if (!native_endian_p) {
635+
MEMCPY(&val, p, uint8_t, member->size);
636+
switch_endianness((uint8_t *)&val, member->size);
637+
}
638+
else {
639+
MEMCPY(&val, p, uint8_t, member->size);
640+
}
641+
642+
switch (member->format) {
643+
case 's':
644+
if (member->native_size_p) {
645+
return INT2FIX(val.s);
646+
}
647+
else {
648+
return INT2FIX(val.i16);
649+
}
650+
651+
case 'S':
652+
case 'n':
653+
case 'v':
654+
if (member->native_size_p) {
655+
return UINT2NUM(val.us);
656+
}
657+
else {
658+
return INT2FIX(val.u16);
659+
}
660+
661+
case 'i':
662+
return INT2NUM(val.i);
663+
664+
case 'I':
665+
return UINT2NUM(val.ui);
666+
667+
case 'l':
668+
if (member->native_size_p) {
669+
return LONG2NUM(val.l);
670+
}
671+
else {
672+
return LONG2NUM(val.i32);
673+
}
674+
675+
case 'L':
676+
case 'N':
677+
case 'V':
678+
if (member->native_size_p) {
679+
return ULONG2NUM(val.ul);
680+
}
681+
else {
682+
return ULONG2NUM(val.u32);
683+
}
684+
685+
case 'f':
686+
case 'e':
687+
case 'g':
688+
return DBL2NUM(val.f);
689+
690+
case 'q':
691+
if (member->native_size_p) {
692+
return LL2NUM(val.ll);
693+
}
694+
else {
695+
#if SIZEOF_INT64_t == SIZEOF_LONG
696+
return LONG2NUM(val.i64);
697+
#else
698+
return LL2NUM(val.i64);
699+
#endif
700+
}
701+
702+
case 'Q':
703+
if (member->native_size_p) {
704+
return ULL2NUM(val.ull);
705+
}
706+
else {
707+
#if SIZEOF_UINT64_t == SIZEOF_LONG
708+
return ULONG2NUM(val.u64);
709+
#else
710+
return ULL2NUM(val.u64);
711+
#endif
712+
}
713+
714+
case 'd':
715+
case 'E':
716+
case 'G':
717+
return DBL2NUM(val.d);
718+
719+
case 'j':
720+
return INTPTR2NUM(val.iptr);
721+
722+
case 'J':
723+
return UINTPTR2NUM(val.uptr);
724+
725+
default:
726+
UNREACHABLE_RETURN(Qnil);
727+
}
728+
}
729+
730+
/* Return a value of the extracted member. */
731+
VALUE
732+
rb_memory_view_extract_item_member(const void *ptr, const rb_memory_view_item_component_t *member, const size_t i)
733+
{
734+
if (ptr == NULL) return Qnil;
735+
if (member == NULL) return Qnil;
736+
if (i >= member->repeat) return Qnil;
737+
738+
return extract_item_member(ptr, member, i);
739+
}
740+
741+
/* Return a value that consists of item members.
742+
* When an item is a single member, the return value is a single value.
743+
* When an item consists of multiple members, an array will be returned. */
744+
VALUE
745+
rb_memory_view_extract_item_members(const void *ptr, const rb_memory_view_item_component_t *members, const size_t n_members)
746+
{
747+
if (ptr == NULL) return Qnil;
748+
if (members == NULL) return Qnil;
749+
if (n_members == 0) return Qnil;
750+
751+
if (n_members == 1 && members[0].repeat == 1) {
752+
return rb_memory_view_extract_item_member(ptr, members, 0);
753+
}
754+
755+
size_t i, j;
756+
VALUE item = rb_ary_new();
757+
for (i = 0; i < n_members; ++i) {
758+
for (j = 0; j < members[i].repeat; ++j) {
759+
VALUE v = extract_item_member(ptr, &members[i], j);
760+
rb_ary_push(item, v);
761+
}
762+
}
763+
764+
return item;
765+
}
766+
561767
static const rb_memory_view_entry_t *
562768
lookup_memory_view_entry(VALUE klass)
563769
{

test/ruby/test_memory_view.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,57 @@ def test_rb_memory_view_parse_item_format_with_alignment_compound
197197
assert_equal(expected_result, members)
198198
end
199199

200+
def test_rb_memory_view_extract_item_members
201+
m = MemoryViewTestUtils
202+
assert_equal(1, m.extract_item_members([1].pack("c"), "c"))
203+
assert_equal([1, 2], m.extract_item_members([1, 2].pack("ii"), "ii"))
204+
assert_equal([1, 2, 3], m.extract_item_members([1, 2, 3].pack("cls"), "cls"))
205+
end
206+
207+
def test_rb_memory_view_extract_item_members_endianness
208+
m = MemoryViewTestUtils
209+
assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S>2"))
210+
assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "n2"))
211+
assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S<2"))
212+
assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "v2"))
213+
assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L>"))
214+
assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "N"))
215+
assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L<"))
216+
assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "V"))
217+
assert_equal(0x0102030405060708, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q>"))
218+
assert_equal(0x0807060504030201, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q<"))
219+
end
220+
221+
def test_rb_memory_view_extract_item_members_float
222+
m = MemoryViewTestUtils
223+
packed = [1.23].pack("f")
224+
assert_equal(packed.unpack("f")[0], m.extract_item_members(packed, "f"))
225+
end
226+
227+
def test_rb_memory_view_extract_item_members_float_endianness
228+
m = MemoryViewTestUtils
229+
hi, lo = [1.23].pack("f").unpack("L")[0].divmod(0x10000)
230+
packed = [lo, hi].pack("S*")
231+
assert_equal(packed.unpack("e")[0], m.extract_item_members(packed, "e"))
232+
packed = [hi, lo].pack("S*")
233+
assert_equal(packed.unpack("g")[0], m.extract_item_members(packed, "g"))
234+
end
235+
236+
def test_rb_memory_view_extract_item_members_doble
237+
m = MemoryViewTestUtils
238+
packed = [1.23].pack("d")
239+
assert_equal(1.23, m.extract_item_members(packed, "d"))
240+
end
241+
242+
def test_rb_memory_view_extract_item_members_doble_endianness
243+
m = MemoryViewTestUtils
244+
hi, lo = [1.23].pack("d").unpack("Q")[0].divmod(0x10000)
245+
packed = [lo, hi].pack("L*")
246+
assert_equal(packed.unpack("E")[0], m.extract_item_members(packed, "E"))
247+
packed = [hi, lo].pack("L*")
248+
assert_equal(packed.unpack("G")[0], m.extract_item_members(packed, "G"))
249+
end
250+
200251
def test_rb_memory_view_available_p
201252
es = MemoryViewTestUtils::ExportableString.new("ruby")
202253
assert_equal(true, MemoryViewTestUtils.available?(es))

0 commit comments

Comments
 (0)