8000 Buffer protocol proposal by mrkn · Pull Request #3261 · ruby/ruby · GitHub
[go: up one dir, main page]

Skip to content

Buffer protocol proposal #3261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Sep 25, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ff82d64
Add buffer protocol
mrkn Jun 26, 2020
add4635
Modify for some review comments
mrkn Jul 16, 2020
0a40fd8
Per-object buffer availability
mrkn Aug 24, 2020
185476f
Rename to MemoryView from Buffer and make compilable
mrkn Aug 24, 2020
12bc6ea
Support integral repeat count in memory view format
mrkn Aug 24, 2020
c97f9b8
Support 'x' for padding bytes
mrkn Aug 24, 2020
12dd500
Add rb_memory_view_parse_item_format
mrkn Aug 24, 2020
11426bc
Check type in rb_memory_view_register
mrkn Aug 24, 2020
ccbb0c9
Update dependencies in common.mk
mrkn Aug 25, 2020
a3b750e
Add test of MemoryView
mrkn Aug 25, 2020
aa6aaf1
Add test of rb_memory_view_init_as_byte_array
mrkn Aug 25, 2020
08c54b7
Add native size format test
mrkn Aug 25, 2020
0166ec6
Add MemoryView test utilities
mrkn Aug 28, 2020
51ee869
Add test of rb_memory_view_fill_contiguous_strides
mrkn Aug 28, 2020
934edd7
Skip spaces in format string
mrkn Sep 1, 2020
1b842a1
Support endianness specifiers
mrkn Sep 1, 2020
39a2a3f
Update documentation
mrkn Sep 1, 2020
01c3e0f
Support alignment
mrkn Sep 1, 2020
71800fc
Use RUBY_ALIGNOF
mrkn Sep 20, 2020
bdb94a0
Fix format parser to follow the pack format
mrkn Sep 20, 2020
72fee2b
Support the _ modifier
mrkn Sep 20, 2020
3d9bdce
Parse count specifiers in get_format_size function.
mrkn Sep 20, 2020
bff9105
Use STRUCT_ALIGNOF
mrkn Sep 21, 2020
cfad9fe
Fix test
mrkn Sep 22, 2020
be8a1ce
Fix test
mrkn Sep 23, 2020
13de9f7
Fix total size for the case with tail padding
mrkn Sep 23, 2020
184fbef
Fix rb_memory_view_get_item_pointer
mrkn Sep 24, 2020
d264358
Fix rb_memory_view_parse_item_format again
mrkn Sep 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add rb_memory_view_parse_item_format
  • Loading branch information
mrkn committed Sep 25, 2020
commit 12dd5005e56545bd80cf4afb3a57ca6529036cde
23 changes: 21 additions & 2 deletions include/ruby/memory_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ enum ruby_memory_view_flags {
RUBY_MEMORY_VIEW_INDIRECT = (1<<6) | RUBY_MEMORY_VIEW_STRIDES,
};

typedef struct {
char format;
int native_size_p;
size_t offset;
size_t size;
size_t repeat;
} rb_memory_view_item_component_t;

typedef struct {
/* The original object that have the memory exported via this memory view.
* The consumer of this memory view has the responsibility to call rb_gc_mark
Expand All @@ -50,14 +58,22 @@ typedef struct {
*
* For example, "dd" for an element that consists of two double values,
* and "CCC" for an element that consists of three bytes, such as
* a RGB color triplet.
*/
* a RGB color triplet. */
const char *format;

/* The number of bytes in each element.
* item_size should equal to rb_memory_view_item_size_from_format(format). */
ssize_t item_size;

struct {
/* The array of rb_memory_view_item_component_t that describes the
* item structure. */
rb_memory_view_item_component_t *components;

/* The number of components in an item. */
ssize_t length;
} item_desc;

/* The number of dimension. */
int ndim;

Expand Down Expand Up @@ -100,6 +116,9 @@ int rb_memory_view_is_row_major_contiguous(const rb_memory_view_t *view);
int rb_memory_view_is_column_major_contiguous(const rb_memory_view_t *view);
void rb_memory_view_fill_contiguous_strides(const int ndim, const int item_size, const ssize_t *const shape, ssize_t *const strides, const int row_major_p);
int rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const int readonly);
ssize_t rb_memory_view_parse_item_format(const char *format,
rb_memory_view_item_component_t **members,
ssize_t *n_members, const char **err);
ssize_t rb_memory_view_item_size_from_format(const char *format, const char **err);
void *rb_memory_view_get_item_pointer(rb_memory_view_t *view, const ssize_t *indices);

Expand Down
217 changes: 137 additions & 80 deletions memory_view.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,106 +107,163 @@ rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data,
return 1;
}

/* Return the item size. */
static ssize_t
get_format_size(const char *format, bool *native_p)
{
RUBY_ASSERT(format != NULL);
RUBY_ASSERT(native_p != NULL);

*native_p = false;

switch (*format) {
case 'x': // padding
return 1;

case 'c': // signed char
case 'C': // unsigned char
return sizeof(char);

case 's': // s for int16_t, s! for signed short
case 'S': // S for uint16_t, S! for unsigned short
if (format[1] == '!') {
*native_p = true;
return sizeof(short);
}
// fall through

case 'n': // n for big-endian 16bit unsigned integer
case 'v': // v for little-endian 16bit unsigned integer
return 2;

case 'i': // i and i! for signed int
case 'I': // I and I! for unsigned int
*native_p = (format[1] == '!');
return sizeof(int);

case 'l': // l for int32_t, l! for signed long
case 'L': // L for uint32_t, L! for unsigned long
if (format[1] == '!') {
*native_p = true;
return sizeof(long);
}
// fall through

case 'N': // N for big-endian 32bit unsigned integer
case 'V': // V for little-endian 32bit unsigned integer
case 'f': // f for native float
case 'e': // e for little-endian float
case 'g': // g for big-endian float
return 4;

case 'q': // q for int64_t, q! for signed long long
case 'Q': // Q for uint64_t, Q! for unsigned long long
if (format[1] == '!') {
*native_p = true;
return sizeof(LONG_LONG);
}
// fall through

case 'd': // d for native double
case 'E': // E for little-endian double
case 'G': // G for big-endian double
return 8;

case 'j': // j for intptr_t
case 'J': // J for uintptr_t
return sizeof(intptr_t);

default:
return -1;
}
}

ssize_t
rb_memory_view_item_size_from_format(const char *format, const char **err)
rb_memory_view_parse_item_format(const char *format,
rb_memory_view_item_component_t **members,
ssize_t *n_members, const char **err)
{
if (format == NULL) return 1;

ssize_t size = 0;
while (*format) {
const char *s = format;
ssize_t total = 0;
ssize_t len = 0;

const char *p = format;
while (*p) {
const char *q = p;
ssize_t count = 0;

int ch = *format;
int ch = *p;
if ('0' <= ch && ch <= '9') {
while ('0' <= (ch = *format) && ch <= '9') {
while ('0' <= (ch = *p) && ch <= '9') {
count = 10*count + ruby_digit36_to_number_table[ch];
++format;
++p;
}
}
else {
count = 1;
}

ssize_t n = 0;
switch (*format) {
case 'x': // padding
n += count;
break;

case 'c': // signed char
case 'C': // unsigned char
n += count * sizeof(char);
break;

case 's': // s for int16_t, s! for signed short
case 'S': // S for uint16_t, S! for unsigned short
if (format[1] == '!') {
++format;
n += count * sizeof(short);
break;
}
// fall through

case 'n': // n for big-endian 16bit unsigned integer
case 'v': // v for little-endian 16bit unsigned integer
n += count * 2;
break;

case 'i': // i and i! for signed int
case 'I': // I and I! for unsigned int
if (format[1] == '!') ++format;
n += count * sizeof(int);
break;

case 'l': // l for int32_t, l! for signed long
case 'L': // L for uint32_t, L! for unsigned long
if (format[1] == '!') {
++format;
n += count * sizeof(long);
break;
}
// fall through

case 'N': // N for big-endian 32bit unsigned integer
case 'V': // V for little-endian 32bit unsigned integer
case 'f': // f for native float
case 'e': // e for little-endian float
case 'g': // g for big-endian float
n += 4;
break;

case 'q': // q for int64_t, q! for signed long long
case 'Q': // Q for uint64_t, Q! for unsigned long long
if (format[1] == '!') {
++format;
n += count * sizeof(LONG_LONG);
break;
}
// fall through
bool native_p;
ssize_t size = get_format_size(p, &native_p);
if (size < 0) {
if (err) *err = q;
return -1;
}

case 'd': // d for native double
case 'E': // E for little-endian double
case 'G': // G for big-endian double
n += count * 8;
break;
if (ch != 'x') {
++len;
}

case 'j': // j for intptr_t
case 'J': // J for uintptr_t
n += count * sizeof(intptr_t);
break;
total += size * count;

default:
if (err) *err = s;
return -1;
}
p += 1 + (int)native_p;
}

size += n;
++format;
if (members && n_members) {
rb_memory_view_item_component_t *buf = ALLOC_N(rb_memory_view_item_component_t, len);

ssize_t i = 0, offset = 0;
const char *p = format;
while (*p) {
ssize_t count = 0;
int ch = *p;
if ('0' <= ch && ch <= '9') {
while ('0' <= (ch = *p) && ch <= '9') {
count = 10*count + ruby_digit36_to_number_table[ch];
++p;
}
}
else {
count = 1;
}

bool native_p;
ssize_t size = get_format_size(p, &native_p);

if (ch != 'x') {
buf[i++] = (rb_memory_view_item_component_t){
.format = ch,
.native_size_p = native_p,
.offset = offset,
.size = size,
.repeat = count
};
}
offset += size * count;
p += 1 + (int)native_p;
}
*members = buf;
*n_members = len;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems no padding after the last component.
I guess that the size of "lc" would be 5.
But it should be 8 to align next item.

return size;
return total;
}

/* Return the item size. */
ssize_t
rb_memory_view_item_size_from_format(const char *format, const char **err)
{
return rb_memory_view_parse_item_format(format, NULL, NULL, err);
}

/* Return the pointer to the item located by the given indices. */
Expand Down
0