ndav 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/README.md +4 -0
- data/ext/ndav.c +71 -54
- data/lib/ndav/ffi.rb +3 -3
- data/lib/ndav/flags.rb +9 -5
- data/lib/ndav/import.rb +9 -0
- data/lib/ndav/item_component.rb +13 -0
- data/lib/ndav/memory_viewable.rb +58 -20
- data/lib/ndav.rb +131 -32
- data/ndav.gemspec +1 -2
- data/test/test_flags.rb +28 -0
- data/test/test_ndav.rb +160 -0
- metadata +4 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ddc0e563bff86b51222b18b00f243b94b0f2e5a69dad3e7694c80a349775bf30
|
|
4
|
+
data.tar.gz: 0afbc73643e49372d2b5104358acdc7460b26d7e6cc8892d45a67e4530cbe3d9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f3a264a0204009e7f6b7505cae42667abe159e7ce7cbc5a27ffa88578c5691138b1b938795e14083b6f4cc9605dbc53b70a3990df495443ec9f5a8ebd664d5b5
|
|
7
|
+
data.tar.gz: bafa151218a7e1acb22acd915fe08acb5147228980a2f441cd928ba66ae4425fb4fcd7bf9509e8ac28f5113e555ee9927b9a71b3d13ca2064134f144a524958c
|
data/.yardopts
CHANGED
data/README.md
CHANGED
|
@@ -9,6 +9,10 @@ It provides an interoperability layer for multi-dimensional arrays which can be
|
|
|
9
9
|
|
|
10
10
|

|
|
11
11
|
|
|
12
|
+
* Homepage: https://kitaitimakoto.gitlab.io/ndav/
|
|
13
|
+
* Source Code: https://gitlab.com/KitaitiMakoto/ndav
|
|
14
|
+
* RubyGems: https://rubygems.org/gems/ndav
|
|
15
|
+
|
|
12
16
|
SYNOPSIS
|
|
13
17
|
--------
|
|
14
18
|
|
data/ext/ndav.c
CHANGED
|
@@ -11,8 +11,8 @@ static VALUE sym_readonly_p;
|
|
|
11
11
|
static VALUE sym_sub_offsets;
|
|
12
12
|
static VALUE sym_ndim;
|
|
13
13
|
static VALUE sym_item_size;
|
|
14
|
-
static
|
|
15
|
-
static VALUE
|
|
14
|
+
static ID id_new;
|
|
15
|
+
static VALUE cItemComponent;
|
|
16
16
|
|
|
17
17
|
typedef struct {
|
|
18
18
|
VALUE descriptor;
|
|
@@ -51,38 +51,14 @@ release_memview_common(rb_memory_view_t *view)
|
|
|
51
51
|
static bool
|
|
52
52
|
ndav_memory_viewable_get_memory_view(const VALUE ndav, rb_memory_view_t *view, int flags)
|
|
53
53
|
{
|
|
54
|
-
bool row_major_requested = (flags & RUBY_MEMORY_VIEW_ROW_MAJOR) == RUBY_MEMORY_VIEW_ROW_MAJOR;
|
|
55
|
-
bool column_major_requested = (flags & RUBY_MEMORY_VIEW_COLUMN_MAJOR) == RUBY_MEMORY_VIEW_COLUMN_MAJOR;
|
|
56
|
-
bool indirect_requested = (flags & RUBY_MEMORY_VIEW_INDIRECT) == RUBY_MEMORY_VIEW_INDIRECT;
|
|
57
54
|
VALUE desc = rb_funcall(ndav, id_ndav_validated_descriptor, 1, INT2NUM(flags));
|
|
58
55
|
if (!RB_TYPE_P(desc, T_HASH)) {
|
|
59
56
|
rb_warn("descriptor not Hash");
|
|
60
57
|
return false;
|
|
61
58
|
}
|
|
62
|
-
bool row_major_contiguous = RTEST(rb_hash_aref(desc, sym_row_major_contiguous_p));
|
|
63
|
-
bool column_major_contiguous = RTEST(rb_hash_aref(desc, sym_column_major_contiguous_p));
|
|
64
|
-
if (row_major_requested && column_major_requested) { // row-major OR column-major requested
|
|
65
|
-
if (!row_major_contiguous && !column_major_contiguous) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Currently, non-contiguous array not supported
|
|
70
|
-
else if (row_major_requested && !row_major_contiguous) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
// Currently, non-contiguous array not supported
|
|
74
|
-
else if (column_major_requested && !column_major_contiguous) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
bool writable_requested = (flags & RUBY_MEMORY_VIEW_WRITABLE) == RUBY_MEMORY_VIEW_WRITABLE;
|
|
78
|
-
// TODO: Handle other flags
|
|
79
59
|
|
|
80
60
|
VALUE readonly_v = rb_hash_aref(desc, sym_readonly_p);
|
|
81
61
|
bool readonly = NIL_P(readonly_v) || RTEST(readonly_v);
|
|
82
|
-
if (writable_requested && readonly) {
|
|
83
|
-
rb_warn("not writable");
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
62
|
VALUE byte_size_v = rb_hash_aref(desc, sym_byte_size);
|
|
87
63
|
ssize_t byte_size = NUM2SSIZET(byte_size_v);
|
|
88
64
|
VALUE addr = rb_hash_aref(desc, sym_addr);
|
|
@@ -94,10 +70,6 @@ ndav_memory_viewable_get_memory_view(const VALUE ndav, rb_memory_view_t *view, i
|
|
|
94
70
|
tmp.readonly = readonly;
|
|
95
71
|
tmp.obj = ndav;
|
|
96
72
|
// TODO: Commonalize
|
|
97
|
-
// TODO: Consider:
|
|
98
|
-
// * Use TypedData?
|
|
99
|
-
// * If so, calling attr readers at Ruby layer has performance disadvantage.
|
|
100
|
-
// * Freezing instance vars in #initialize and then embed them to TypedData may be a solution
|
|
101
73
|
VALUE item_size = rb_hash_aref(desc, sym_item_size);
|
|
102
74
|
tmp.item_size = NUM2SSIZET(item_size);
|
|
103
75
|
tmp.byte_size = byte_size;
|
|
@@ -108,11 +80,6 @@ ndav_memory_viewable_get_memory_view(const VALUE ndav, rb_memory_view_t *view, i
|
|
|
108
80
|
|
|
109
81
|
// TODO: Commonalize
|
|
110
82
|
VALUE shape = rb_hash_aref(desc, sym_shape);
|
|
111
|
-
if (!RB_TYPE_P(shape, T_ARRAY)) {
|
|
112
|
-
release_memview_common(&tmp);
|
|
113
|
-
rb_warn("shape is not an array");
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
83
|
ssize_t *view_shape = (ssize_t *)malloc(sizeof(ssize_t) * tmp.ndim);
|
|
117
84
|
if (!view_shape) {
|
|
118
85
|
release_memview_common(&tmp);
|
|
@@ -126,11 +93,6 @@ ndav_memory_viewable_get_memory_view(const VALUE ndav, rb_memory_view_t *view, i
|
|
|
126
93
|
tmp.shape = view_shape;
|
|
127
94
|
|
|
128
95
|
VALUE strides = rb_hash_aref(desc, sym_strides);
|
|
129
|
-
if (!RB_TYPE_P(strides, T_ARRAY)) {
|
|
130
|
-
release_memview_common(&tmp);
|
|
131
|
-
rb_warn("strides is not an array");
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
96
|
ssize_t *view_strides = (ssize_t *)malloc(sizeof(ssize_t) * tmp.ndim);
|
|
135
97
|
if (!view_strides) {
|
|
136
98
|
release_memview_common(&tmp);
|
|
@@ -144,18 +106,7 @@ ndav_memory_viewable_get_memory_view(const VALUE ndav, rb_memory_view_t *view, i
|
|
|
144
106
|
tmp.strides = view_strides;
|
|
145
107
|
|
|
146
108
|
VALUE sub_offsets = rb_hash_aref(desc, sym_sub_offsets);
|
|
147
|
-
if (NIL_P(sub_offsets)) {
|
|
148
|
-
if (indirect_requested) {
|
|
149
|
-
release_memview_common(&tmp);
|
|
150
|
-
rb_warn("indirect requested but sub_offsets is NULL");
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
tmp.sub_offsets = NULL;
|
|
154
|
-
} else if (!RB_TYPE_P(sub_offsets, T_ARRAY)) {
|
|
155
|
-
release_memview_common(&tmp);
|
|
156
|
-
rb_warn("sub_offsets is not an array");
|
|
157
|
-
return false;
|
|
158
|
-
} else {
|
|
109
|
+
if (!NIL_P(sub_offsets)) {
|
|
159
110
|
ssize_t *view_sub_offsets = (ssize_t *)malloc(sizeof(ssize_t) * tmp.ndim);
|
|
160
111
|
if (!view_sub_offsets) {
|
|
161
112
|
release_memview_common(&tmp);
|
|
@@ -228,6 +179,69 @@ ndav_memory_viewable_s_register(VALUE mod, VALUE klass)
|
|
|
228
179
|
return Qtrue;
|
|
229
180
|
}
|
|
230
181
|
|
|
182
|
+
typedef struct init_item_components_args {
|
|
183
|
+
rb_memory_view_item_component_t *members;
|
|
184
|
+
size_t n_members;
|
|
185
|
+
} init_item_components_args;
|
|
186
|
+
|
|
187
|
+
static VALUE
|
|
188
|
+
init_item_components(VALUE args_v)
|
|
189
|
+
{
|
|
190
|
+
init_item_components_args *args = (init_item_components_args *)args_v;
|
|
191
|
+
VALUE members = rb_ary_new_capa(args->n_members);
|
|
192
|
+
|
|
193
|
+
for (size_t i = 0; i < args->n_members; i++) {
|
|
194
|
+
rb_ary_push(members, rb_funcall(
|
|
195
|
+
cItemComponent,
|
|
196
|
+
id_new,
|
|
197
|
+
6,
|
|
198
|
+
rb_str_new(&args->members[i].format, 1),
|
|
199
|
+
args->members[i].native_size_p ? Qtrue : Qfalse,
|
|
200
|
+
args->members[i].little_endian_p ? Qtrue : Qfalse,
|
|
201
|
+
SIZET2NUM(args->members[i].offset),
|
|
202
|
+
SIZET2NUM(args->members[i].size),
|
|
203
|
+
SIZET2NUM(args->members[i].repeat)
|
|
204
|
+
));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return members;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Want to be implemented in Fiddle gem
|
|
211
|
+
static VALUE
|
|
212
|
+
ndav_s_parse_item_format(VALUE klass, VALUE format)
|
|
213
|
+
{
|
|
214
|
+
rb_memory_view_item_component_t *members;
|
|
215
|
+
size_t n_members;
|
|
216
|
+
const char *err;
|
|
217
|
+
|
|
218
|
+
ssize_t byte_size = rb_memory_view_parse_item_format(
|
|
219
|
+
StringValueCStr(format),
|
|
220
|
+
&members,
|
|
221
|
+
&n_members,
|
|
222
|
+
&err
|
|
223
|
+
);
|
|
224
|
+
if (byte_size < 0) {
|
|
225
|
+
rb_raise(rb_eRuntimeError, "Parse error at: %s", err);
|
|
226
|
+
}
|
|
227
|
+
if (n_members == 0) {
|
|
228
|
+
xfree(members);
|
|
229
|
+
return rb_ary_new();
|
|
230
|
+
}
|
|
231
|
+
init_item_components_args args = {
|
|
232
|
+
.members = members,
|
|
233
|
+
.n_members = n_members
|
|
234
|
+
};
|
|
235
|
+
int state = 0;
|
|
236
|
+
VALUE members_v = rb_protect(init_item_components, (VALUE)&args, &state);
|
|
237
|
+
xfree(members);
|
|
238
|
+
if (state != 0) {
|
|
239
|
+
rb_jump_tag(state);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return members_v;
|
|
243
|
+
}
|
|
244
|
+
|
|
231
245
|
void
|
|
232
246
|
Init_ndav(void)
|
|
233
247
|
{
|
|
@@ -241,10 +255,13 @@ Init_ndav(void)
|
|
|
241
255
|
sym_sub_offsets = ID2SYM(rb_intern("sub_offsets"));
|
|
242
256
|
sym_ndim = ID2SYM(rb_intern("ndim"));
|
|
243
257
|
sym_item_size = ID2SYM(rb_intern("item_size"));
|
|
244
|
-
|
|
245
|
-
sym_column_major_contiguous_p = ID2SYM(rb_intern("column_major_contiguous?"));
|
|
258
|
+
id_new = rb_intern("new");
|
|
246
259
|
|
|
247
260
|
VALUE cNDAV = rb_define_class("NDAV", rb_cObject);
|
|
248
261
|
VALUE mMemoryViewable = rb_define_module_under(cNDAV, "MemoryViewable");
|
|
249
262
|
rb_define_singleton_method(mMemoryViewable, "register", ndav_memory_viewable_s_register, 1);
|
|
263
|
+
|
|
264
|
+
rb_require("ndav/item_component");
|
|
265
|
+
cItemComponent = rb_path2class("NDAV::ItemComponent");
|
|
266
|
+
rb_define_singleton_method(cNDAV, "parse_item_format", ndav_s_parse_item_format, 1);
|
|
250
267
|
}
|
data/lib/ndav/ffi.rb
CHANGED
|
@@ -20,7 +20,7 @@ class NDAV
|
|
|
20
20
|
module ToNDAV
|
|
21
21
|
def to_ndav(format: TYPE_SIZE_TO_FORMAT[type_size], lifetime: self, **)
|
|
22
22
|
shape = [size / type_size]
|
|
23
|
-
byte_size = shape.reduce(ITEM_SIZES[format], :*)
|
|
23
|
+
byte_size = shape.reduce(::NDAV::ITEM_SIZES[format], :*)
|
|
24
24
|
ptr = ::Fiddle::Pointer.new(address, byte_size)
|
|
25
25
|
::NDAV.new(ptr, shape:, format:, lifetime:, **)
|
|
26
26
|
end
|
|
@@ -30,7 +30,7 @@ class NDAV
|
|
|
30
30
|
def ndav_descriptor(**)
|
|
31
31
|
shape = [size / type_size]
|
|
32
32
|
format = TYPE_SIZE_TO_FORMAT[type_size]
|
|
33
|
-
item_size = ITEM_SIZES[format]
|
|
33
|
+
item_size = ::NDAV::ITEM_SIZES[format]
|
|
34
34
|
byte_size = shape.reduce(item_size, :*)
|
|
35
35
|
ptr = ::Fiddle::Pointer.new(address, byte_size)
|
|
36
36
|
|
|
@@ -69,7 +69,7 @@ class NDAV
|
|
|
69
69
|
|
|
70
70
|
module ToNDAV
|
|
71
71
|
def to_ndav(shape:, format: TYPE_SIZE_TO_FORMAT[type_size], lifetime: self, **)
|
|
72
|
-
byte_size = shape.reduce(ITEM_SIZES[format], :*)
|
|
72
|
+
byte_size = shape.reduce(::NDAV::ITEM_SIZES[format], :*)
|
|
73
73
|
ptr = ::Fiddle::Pointer.new(address, byte_size)
|
|
74
74
|
::NDAV.new(ptr, shape:, format:, lifetime:, **)
|
|
75
75
|
end
|
data/lib/ndav/flags.rb
CHANGED
|
@@ -15,11 +15,15 @@ class NDAV
|
|
|
15
15
|
|
|
16
16
|
def decode(flags)
|
|
17
17
|
FLAGS.each_with_object({}) {|(name, value), state|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
state[name] = case name
|
|
19
|
+
when :simple
|
|
20
|
+
flags == value
|
|
21
|
+
when :any_contiguous
|
|
22
|
+
(flags & FLAGS[:row_major]) == FLAGS[:row_major] ||
|
|
23
|
+
(flags & FLAGS[:column_major]) == FLAGS[:column_major]
|
|
24
|
+
else
|
|
25
|
+
flags & value == value
|
|
26
|
+
end
|
|
23
27
|
}
|
|
24
28
|
end
|
|
25
29
|
end
|
data/lib/ndav/import.rb
ADDED
data/lib/ndav/memory_viewable.rb
CHANGED
|
@@ -2,7 +2,7 @@ class NDAV
|
|
|
2
2
|
# +include+ this module and define +#ndav_descriptor+ method in your class,
|
|
3
3
|
# and then the class exports a MemoryView.
|
|
4
4
|
#
|
|
5
|
-
# +#ndav_descriptor+'s arguments are flags of MemoryView including
|
|
5
|
+
# +#ndav_descriptor+ 's keyword arguments are flags of MemoryView including
|
|
6
6
|
# +simple+, +writable+, +format+, +multi_dimensional+, +strides+, +row_major+, +column_major+, +any_contiguous+, +indirect+.
|
|
7
7
|
# For the meaning of them, see MemoryView document.
|
|
8
8
|
#
|
|
@@ -21,15 +21,15 @@ class NDAV
|
|
|
21
21
|
# include NDAV::MemoryViewable
|
|
22
22
|
# # :
|
|
23
23
|
# def ndav_descriptor(
|
|
24
|
-
# simple
|
|
25
|
-
# writable
|
|
26
|
-
# format
|
|
27
|
-
# multi_dimensional
|
|
28
|
-
# strides
|
|
29
|
-
# row_major
|
|
30
|
-
# column_major
|
|
31
|
-
# any_contiguous
|
|
32
|
-
# indirect:
|
|
24
|
+
# simple:,
|
|
25
|
+
# writable:,
|
|
26
|
+
# format:,
|
|
27
|
+
# multi_dimensional:,
|
|
28
|
+
# strides:,
|
|
29
|
+
# row_major:,
|
|
30
|
+
# column_major:,
|
|
31
|
+
# any_contiguous:,
|
|
32
|
+
# indirect:
|
|
33
33
|
# )
|
|
34
34
|
# {
|
|
35
35
|
# data:,
|
|
@@ -46,31 +46,69 @@ class NDAV
|
|
|
46
46
|
module MemoryViewable
|
|
47
47
|
class << self
|
|
48
48
|
def included(base)
|
|
49
|
-
|
|
49
|
+
register base
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
private
|
|
54
54
|
|
|
55
55
|
def ndav_validated_descriptor(flags = ::NDAV::Flags::FLAGS[:simple])
|
|
56
|
-
|
|
56
|
+
flags = ::NDAV::Flags.decode(flags)
|
|
57
|
+
desc = ndav_descriptor(**flags).to_h
|
|
58
|
+
desc => {data:, shape:, strides:, format:, byte_size:, readonly?: readonly}
|
|
59
|
+
sub_offsets = desc[:sub_offsets]
|
|
60
|
+
|
|
57
61
|
[:data, :shape, :strides, :format, :byte_size, :readonly?].each do |key|
|
|
58
|
-
|
|
59
|
-
warn ":#{key}
|
|
62
|
+
if desc[key].nil?
|
|
63
|
+
warn ":#{key} is nil"
|
|
64
|
+
return false
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
[:shape, :strides].each do |key|
|
|
68
|
+
unless desc[key].instance_of? Array
|
|
69
|
+
warn "#{key} not Array"
|
|
60
70
|
return false
|
|
61
71
|
end
|
|
62
72
|
end
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
if sub_offsets && !sub_offsets.instance_of?(Array)
|
|
74
|
+
warn "sub_offsets not Array"
|
|
75
|
+
return false
|
|
76
|
+
end
|
|
77
|
+
item_size = ::NDAV::ITEM_SIZES[format]
|
|
78
|
+
unless shape.reduce(item_size, :*) == byte_size
|
|
65
79
|
warn "item size from format and shape not match byte_size"
|
|
66
80
|
return false
|
|
67
81
|
end
|
|
82
|
+
|
|
83
|
+
if flags[:writable] && readonly
|
|
84
|
+
warn "not writable"
|
|
85
|
+
return false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# TODO: Research the meaning of MULTI_DIMENSIONAL and validate it
|
|
89
|
+
|
|
90
|
+
if flags[:indirect] && !sub_offsets
|
|
91
|
+
warn "indirect requested but sub_offsets is NULL"
|
|
92
|
+
return false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
row_major_contiguous = ::NDAV.row_major_contiguous?(format:, shape:, strides:)
|
|
96
|
+
column_major_contiguous = ::NDAV.column_major_contiguous?(format:, shape:, strides:)
|
|
97
|
+
if flags[:row_major] && !row_major_contiguous
|
|
98
|
+
return false
|
|
99
|
+
end
|
|
100
|
+
if flags[:column_major] && !column_major_contiguous
|
|
101
|
+
return false
|
|
102
|
+
end
|
|
103
|
+
if flags[:any_contiguous] && !row_major_contiguous && !column_major_contiguous
|
|
104
|
+
return false
|
|
105
|
+
end
|
|
106
|
+
|
|
68
107
|
desc.merge(
|
|
69
|
-
addr: ::Fiddle::Pointer.to_ptr(
|
|
70
|
-
ndim:
|
|
108
|
+
addr: ::Fiddle::Pointer.to_ptr(data).to_i,
|
|
109
|
+
ndim: shape.length,
|
|
71
110
|
item_size:,
|
|
72
|
-
|
|
73
|
-
column_major_contiguous?: ::NDAV.column_major_contiguous?(**desc)
|
|
111
|
+
sub_offsets:
|
|
74
112
|
)
|
|
75
113
|
rescue => err
|
|
76
114
|
warn err
|
data/lib/ndav.rb
CHANGED
|
@@ -3,19 +3,52 @@ require "ndav.so"
|
|
|
3
3
|
require "ndav/flags"
|
|
4
4
|
require "ndav/converter"
|
|
5
5
|
require "ndav/memory_viewable"
|
|
6
|
+
require "ndav/import"
|
|
6
7
|
|
|
7
|
-
# TODO: Manage flags of internal data object
|
|
8
8
|
class NDAV
|
|
9
9
|
include MemoryViewable
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
ITEM_SIZES = Hash.new {|sizes, format| sizes[format] = item_size_from_format(format)}
|
|
12
|
+
ITEM_DESCS = Hash.new {|descs, format| descs[format] = parse_item_format(format)}
|
|
13
|
+
DEFAULT_FORMATS = {
|
|
14
|
+
1 => "C",
|
|
15
|
+
2 => "s",
|
|
16
|
+
4 => "l",
|
|
17
|
+
8 => "q"
|
|
18
|
+
}
|
|
15
19
|
|
|
16
|
-
attr_reader :ndim, :shape, :format, :item_size, :byte_size, :strides, :to_s, :lifetime
|
|
20
|
+
attr_reader :ndim, :shape, :format, :item_size, :byte_size, :strides, :sub_offsets, :to_s, :lifetime
|
|
17
21
|
|
|
18
22
|
class << self
|
|
23
|
+
# * Doesn't modify byte_size
|
|
24
|
+
# * Keyword arguments override obj's MemoryView members
|
|
25
|
+
# * Overrides obj's members as required by keyword arguments
|
|
26
|
+
# * ex) format: "C" requires item_size = 1 even if obj's item_size isn't it
|
|
27
|
+
# * Raises exception if keyword arguments conflict
|
|
28
|
+
alias new_override new
|
|
29
|
+
|
|
30
|
+
# * Raises exception when a keyword argument is not nil and obj's corresponding member is not nil
|
|
31
|
+
def new(obj, shape: nil, strides: nil, format: nil, lifetime: nil)
|
|
32
|
+
is_fmv = obj.kind_of? Fiddle::MemoryView
|
|
33
|
+
fmv = is_fmv ? obj : Fiddle::MemoryView.new(obj)
|
|
34
|
+
|
|
35
|
+
if shape && fmv.shape
|
|
36
|
+
raise ArgumentError, "shape is already set. Cannot override."
|
|
37
|
+
end
|
|
38
|
+
if strides && fmv.strides
|
|
39
|
+
raise ArgumentError, "strides is already set. Cannot override."
|
|
40
|
+
end
|
|
41
|
+
if format && fmv.format
|
|
42
|
+
raise ArgumentError, "format is already set. Cannot override."
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
new_override(obj, shape:, strides:, format:, lifetime:)
|
|
46
|
+
ensure
|
|
47
|
+
unless is_fmv
|
|
48
|
+
fmv&.release
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
19
52
|
def default_strides(shape:, item_size:, row_major: true)
|
|
20
53
|
each_shape = row_major ? shape.reverse_each : shape.each
|
|
21
54
|
each_shape.reduce([[], item_size]) {|(strides, stride), s|
|
|
@@ -57,50 +90,78 @@ class NDAV
|
|
|
57
90
|
|
|
58
91
|
true
|
|
59
92
|
end
|
|
93
|
+
|
|
94
|
+
def item_size_from_format(format)
|
|
95
|
+
err = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
|
|
96
|
+
size = Import.rb_memory_view_item_size_from_format(format, err)
|
|
97
|
+
if size < 0
|
|
98
|
+
raise ArgumentError, "Invalid format character: #{err.ptr.to_s}"
|
|
99
|
+
else
|
|
100
|
+
size
|
|
101
|
+
end
|
|
102
|
+
ensure
|
|
103
|
+
err&.free
|
|
104
|
+
end
|
|
60
105
|
end
|
|
61
106
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
107
|
+
def initialize(obj, readonly: nil, format: nil, item_size: nil, shape: nil, strides: nil, sub_offsets: nil, lifetime: nil)
|
|
108
|
+
# We cannot determine flags until ndav_descriptor is called but some MemoryView producer like Fiddle::Pointer and IO::Buffer determines some members by flags.
|
|
109
|
+
# For instance, Fiddle::Pointer's MemoryView is readonly when writable flag is not passed and is not readonly when writable flag is passed.
|
|
110
|
+
# How should we solve this mismatching? Delaying to determine the flags?
|
|
111
|
+
# Considers when it becomes actual problem.
|
|
112
|
+
@fmv = obj.kind_of?(::Fiddle::MemoryView) ? obj : ::Fiddle::MemoryView.new(obj)
|
|
65
113
|
# Fiddle::MemoryView#to_s is zero-copy
|
|
66
114
|
# Fiddle::MemoryView#to_s embeds obj into the String, so we can prevent GC from collecting it
|
|
67
|
-
@to_s = fmv.to_s
|
|
115
|
+
@to_s = @fmv.to_s
|
|
68
116
|
@lifetime = lifetime
|
|
117
|
+
@byte_size = @fmv.byte_size
|
|
69
118
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
119
|
+
case [format, item_size]
|
|
120
|
+
in [nil, nil]
|
|
121
|
+
@format = @fmv.format || "C"
|
|
122
|
+
@item_size = @fmv.item_size
|
|
123
|
+
in [_, nil]
|
|
124
|
+
@format = format
|
|
125
|
+
@item_size = ITEM_SIZES[@format]
|
|
126
|
+
in [nil, _]
|
|
127
|
+
@item_size = item_size
|
|
128
|
+
@format = DEFAULT_FORMATS[@item_size] || @fmv.format
|
|
129
|
+
else
|
|
130
|
+
@format = format
|
|
131
|
+
@item_size = item_size
|
|
132
|
+
end
|
|
74
133
|
|
|
75
|
-
|
|
76
|
-
@item_size = obj.kind_of?(::Fiddle::Pointer) ? item_size_by_format : fmv.item_size || item_size_by_format
|
|
77
|
-
@shape = shape || fmv.shape
|
|
78
|
-
case [@shape, @byte_size]
|
|
134
|
+
case [shape, @byte_size]
|
|
79
135
|
in [nil, nil]
|
|
80
|
-
|
|
136
|
+
@shape = @fmv.shape
|
|
137
|
+
@byte_size = @shape.reduce(@item_size, :*)
|
|
81
138
|
in [_, nil]
|
|
139
|
+
@shape = shape
|
|
82
140
|
@byte_size = @shape.reduce(@item_size, :*)
|
|
83
141
|
in [nil, _]
|
|
84
|
-
|
|
85
|
-
raise ArgumentError, "byte_size must be n-times of item_size" unless r.zero?
|
|
86
|
-
@shape = [n]
|
|
142
|
+
@shape = @fmv.shape || [@byte_size / @item_size]
|
|
87
143
|
else
|
|
88
|
-
|
|
144
|
+
@shape = shape
|
|
89
145
|
end
|
|
146
|
+
|
|
90
147
|
@ndim = @shape.size
|
|
91
|
-
@readonly = fmv.readonly?
|
|
92
|
-
@strides = strides || fmv.strides || self.class.default_strides(shape: @shape, item_size: @item_size)
|
|
93
|
-
@sub_offsets = fmv.sub_offsets
|
|
148
|
+
@readonly = readonly.nil? ? @fmv.readonly? : readonly
|
|
149
|
+
@strides = strides || @fmv.strides || self.class.default_strides(shape: @shape, item_size: @item_size)
|
|
150
|
+
@sub_offsets = sub_offsets || @fmv.sub_offsets
|
|
94
151
|
@row_major_contiguous = self.class.row_major_contiguous?(format: @format, shape: @shape, strides: @strides)
|
|
95
152
|
@column_major_contiguous = self.class.column_major_contiguous?(format: @format, shape: @shape, strides: @strides)
|
|
96
153
|
|
|
97
|
-
validate
|
|
154
|
+
validate
|
|
98
155
|
end
|
|
99
156
|
|
|
100
157
|
def readonly?
|
|
101
158
|
@readonly
|
|
102
159
|
end
|
|
103
160
|
|
|
161
|
+
def item_desc
|
|
162
|
+
ITEM_DESCS[@format]
|
|
163
|
+
end
|
|
164
|
+
|
|
104
165
|
def row_major_contiguous?
|
|
105
166
|
@row_major_contiguous
|
|
106
167
|
end
|
|
@@ -114,9 +175,16 @@ class NDAV
|
|
|
114
175
|
end
|
|
115
176
|
alias to_fiddle_pointer to_ptr
|
|
116
177
|
|
|
178
|
+
def release
|
|
179
|
+
@fmv.release
|
|
180
|
+
@fmv = @ndim = @readonly = @shape = @format = @item_size = @byte_size = @strides = @sub_offsets = @lifetime = @to_s = @row_major_contiguous = @column_major_contiguous = nil
|
|
181
|
+
end
|
|
182
|
+
|
|
117
183
|
private
|
|
118
184
|
|
|
119
185
|
def ndav_descriptor(**)
|
|
186
|
+
return false unless @fmv
|
|
187
|
+
|
|
120
188
|
{
|
|
121
189
|
data: @to_s,
|
|
122
190
|
shape: @shape,
|
|
@@ -128,16 +196,47 @@ class NDAV
|
|
|
128
196
|
}
|
|
129
197
|
end
|
|
130
198
|
|
|
131
|
-
def validate
|
|
199
|
+
def validate
|
|
200
|
+
raise ArgumentError, "byte_size must be present" unless @byte_size
|
|
201
|
+
|
|
132
202
|
unless @item_size == ITEM_SIZES[@format]
|
|
133
|
-
raise ArgumentError, "item_size does not match format"
|
|
203
|
+
raise ArgumentError, "item_size does not match format. item_size: #{@item_size.inspect}, format: #{@format.inspect}"
|
|
134
204
|
end
|
|
135
205
|
|
|
206
|
+
r = @byte_size % @item_size
|
|
207
|
+
raise ArgumentError, "byte_size must be n-times of item_size" unless r.zero?
|
|
208
|
+
|
|
209
|
+
raise ArgumentError, "shape length doen't match ndim" unless @shape.length == @ndim
|
|
136
210
|
if @row_major_contiguous || @column_major_contiguous
|
|
137
|
-
byte_size
|
|
138
|
-
|
|
139
|
-
|
|
211
|
+
unless @byte_size == @shape.reduce(@item_size, :*)
|
|
212
|
+
raise ArgumentError, "shape not match byte_size"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
raise ArgumentError, "strides length doen't match ndim" unless @strides.length == @ndim
|
|
217
|
+
if @row_major_contiguous
|
|
218
|
+
stride = @item_size
|
|
219
|
+
@strides.reverse_each.with_index do |s, i|
|
|
220
|
+
raise ArgumentError, "invalid strides" unless s == stride
|
|
221
|
+
stride *= @shape[@ndim - i - 1]
|
|
222
|
+
end
|
|
223
|
+
else
|
|
224
|
+
max_offset = 0
|
|
225
|
+
min_offset = 0
|
|
226
|
+
@shape.zip(@strides) do |s, stride|
|
|
227
|
+
next if s == 0
|
|
228
|
+
|
|
229
|
+
extent = (s - 1) * stride
|
|
230
|
+
max_offset += [extent, 0].max
|
|
231
|
+
min_offset += [extent, 0].min
|
|
232
|
+
end
|
|
233
|
+
if min_offset < 0
|
|
234
|
+
# NDAV uses Fiddle::MemoryView#to_s and it doesn't allow negative min offset
|
|
235
|
+
# Some MemoryView might allow negative min offset
|
|
236
|
+
# Remove this check if such the MemoryView producer is found
|
|
237
|
+
raise ArgumentError, "min offset must be positive"
|
|
140
238
|
end
|
|
239
|
+
raise ArgumentError, "max offset too large" if max_offset + @item_size > @byte_size
|
|
141
240
|
end
|
|
142
241
|
end
|
|
143
242
|
end
|
data/ndav.gemspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = "ndav"
|
|
3
|
-
s.version = "0.0.
|
|
3
|
+
s.version = "0.0.7"
|
|
4
4
|
s.summary = "N-Dimensional Array View"
|
|
5
5
|
s.authors = ["Kitaiti Makoto"]
|
|
6
6
|
s.licenses = ["BSD-2-Clause"]
|
|
@@ -22,5 +22,4 @@ Gem::Specification.new do |s|
|
|
|
22
22
|
s.add_development_dependency "red-arrow"
|
|
23
23
|
s.add_development_dependency "ffi"
|
|
24
24
|
s.add_development_dependency "yard"
|
|
25
|
-
s.add_development_dependency "commonmarker"
|
|
26
25
|
end
|
data/test/test_flags.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require_relative "helper"
|
|
2
|
+
|
|
3
|
+
class TestFlags < TestBase
|
|
4
|
+
include NDAV::Flags
|
|
5
|
+
|
|
6
|
+
def test_any_contiguous
|
|
7
|
+
decoded = decode(combine_flags(:any_contiguous))
|
|
8
|
+
assert_true decoded[:row_major]
|
|
9
|
+
assert_true decoded[:column_major]
|
|
10
|
+
assert_true decoded[:any_contiguous]
|
|
11
|
+
|
|
12
|
+
decoded = decode(combine_flags(:row_major))
|
|
13
|
+
assert_true decoded[:row_major]
|
|
14
|
+
assert_false decoded[:column_major]
|
|
15
|
+
assert_true decoded[:any_contiguous]
|
|
16
|
+
|
|
17
|
+
decoded = decode(combine_flags(:column_major))
|
|
18
|
+
assert_false decoded[:row_major]
|
|
19
|
+
assert_true decoded[:column_major]
|
|
20
|
+
assert_true decoded[:any_contiguous]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def combine_flags(*flags)
|
|
26
|
+
flags.reduce(0) {|composed, name| composed | FLAGS[name]}
|
|
27
|
+
end
|
|
28
|
+
end
|
data/test/test_ndav.rb
CHANGED
|
@@ -36,6 +36,19 @@ class TestNDAV < TestBase
|
|
|
36
36
|
assert_int_array ndav
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def test_item_descs
|
|
40
|
+
assert_equal [
|
|
41
|
+
NDAV::ItemComponent.new(
|
|
42
|
+
format: "s",
|
|
43
|
+
native_size_p: false,
|
|
44
|
+
little_endian_p: true,
|
|
45
|
+
offset: 0,
|
|
46
|
+
size: 2,
|
|
47
|
+
repeat: 1
|
|
48
|
+
)
|
|
49
|
+
], NDAV.new(@array).item_desc
|
|
50
|
+
end
|
|
51
|
+
|
|
39
52
|
def test_column_major
|
|
40
53
|
str = [1, 2, 3,
|
|
41
54
|
4, 5, 6].pack("s*")
|
|
@@ -43,4 +56,151 @@ class TestNDAV < TestBase
|
|
|
43
56
|
|
|
44
57
|
assert_true ndav.column_major_contiguous?
|
|
45
58
|
end
|
|
59
|
+
|
|
60
|
+
def test_release
|
|
61
|
+
ndav = NDAV.new(@array)
|
|
62
|
+
|
|
63
|
+
ndav.release
|
|
64
|
+
[:ndim, :shape, :format, :item_size, :byte_size, :strides, :sub_offsets, :lifetime, :to_s].each do |attr|
|
|
65
|
+
assert_nil ndav.send(attr)
|
|
66
|
+
end
|
|
67
|
+
assert_nil ndav.readonly?
|
|
68
|
+
assert_nil ndav.row_major_contiguous?
|
|
69
|
+
assert_nil ndav.column_major_contiguous?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_parse_item_format
|
|
73
|
+
assert_equal [
|
|
74
|
+
NDAV::ItemComponent.new("f", false, IO::Buffer::HOST_ENDIAN == IO::Buffer::LITTLE_ENDIAN, 0, 4, 1)
|
|
75
|
+
], NDAV.parse_item_format("f")
|
|
76
|
+
|
|
77
|
+
assert_equal [
|
|
78
|
+
NDAV::ItemComponent.new("s", false, true, 0, 2, 1)
|
|
79
|
+
], NDAV.parse_item_format("s<")
|
|
80
|
+
|
|
81
|
+
assert_equal [
|
|
82
|
+
NDAV::ItemComponent.new("C", false, true, 0, 1, 1),
|
|
83
|
+
NDAV::ItemComponent.new("C", false, true, 1, 1, 1),
|
|
84
|
+
NDAV::ItemComponent.new("C", false, true, 2, 1, 1),
|
|
85
|
+
], NDAV.parse_item_format("CCC")
|
|
86
|
+
|
|
87
|
+
assert_equal [
|
|
88
|
+
NDAV::ItemComponent.new("C", false, true, 0, 1, 3)
|
|
89
|
+
], NDAV.parse_item_format("C3")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class TestOverwrite < self
|
|
93
|
+
def test_format
|
|
94
|
+
assert_raise ArgumentError do
|
|
95
|
+
NDAV.new(@array, format: "C")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
ndav = NDAV.new_override(@array, format: "C")
|
|
99
|
+
Fiddle::MemoryView.export ndav do |memview|
|
|
100
|
+
assert_equal "C", memview.format
|
|
101
|
+
assert_equal 1, memview.item_size
|
|
102
|
+
assert_equal 6, memview.byte_size
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
assert_raise ArgumentError do
|
|
106
|
+
NDAV.new(@array, format: "f")
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_item_size
|
|
111
|
+
assert_raise ArgumentError do
|
|
112
|
+
NDAV.new(@array, item_size: 1)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
ndav = NDAV.new_override(@array, item_size: 1)
|
|
116
|
+
Fiddle::MemoryView.export ndav do |memview|
|
|
117
|
+
assert_equal 1, memview.item_size
|
|
118
|
+
assert_equal "C", memview.format
|
|
119
|
+
assert_equal 6, memview.byte_size
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
assert_raise ArgumentError do
|
|
123
|
+
NDAV.new_override(@array, item_size: 4)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def test_format_and_item_size
|
|
128
|
+
assert_raise ArgumentError do
|
|
129
|
+
NDAV.new(@array, format: "C", item_size: 1)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
ndav = NDAV.new_override(@array, format: "C", item_size: 1)
|
|
133
|
+
Fiddle::MemoryView.export ndav do |memview|
|
|
134
|
+
assert_equal "C", memview.format
|
|
135
|
+
assert_equal 1, memview.item_size
|
|
136
|
+
assert_equal 6, memview.byte_size
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
assert_raise ArgumentError do
|
|
140
|
+
NDAV.new_override(@array, format: "C", item_size: 2)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def test_shape
|
|
145
|
+
array_with_shape = NDAV.new(@array)
|
|
146
|
+
assert_raise ArgumentError do
|
|
147
|
+
NDAV.new(array_with_shape, shape: [3, 1])
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
ndav = NDAV.new_override(@array, shape: [3, 1])
|
|
151
|
+
Fiddle::MemoryView.export ndav do |memview|
|
|
152
|
+
assert_equal [3, 1], memview.shape
|
|
153
|
+
assert_equal 2, memview.ndim
|
|
154
|
+
assert_equal 6, memview.byte_size
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
assert_raise ArgumentError do
|
|
158
|
+
NDAV.new_override(@array, shape: [2, 3, 4])
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def test_strides
|
|
163
|
+
array_with_shape = NDAV.new(@array)
|
|
164
|
+
assert_raise ArgumentError do
|
|
165
|
+
NDAV.new(array_with_shape, strides: [2])
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
ndav = NDAV.new_override(@array, strides: [2])
|
|
169
|
+
Fiddle::MemoryView.export ndav do |memview|
|
|
170
|
+
assert_equal [2], memview.strides
|
|
171
|
+
assert_equal [3], memview.shape
|
|
172
|
+
assert_equal 2, memview.item_size
|
|
173
|
+
assert_equal 6, memview.byte_size
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
assert_raise ArgumentError do
|
|
177
|
+
NDAV.new_override(@array, strides: [4])
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
assert_raise ArgumentError do
|
|
181
|
+
NDAV.new_override(@array, strides: [6])
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def test_shape_and_strides
|
|
186
|
+
ndav = NDAV.new_override(@array, shape: [1, 3], strides: [6, 2])
|
|
187
|
+
Fiddle::MemoryView.export ndav do |memview|
|
|
188
|
+
assert_equal [1, 3], memview.shape
|
|
189
|
+
assert_equal [6, 2], memview.strides
|
|
190
|
+
assert_equal 6, memview.byte_size
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def test_overwrite_readonly?
|
|
195
|
+
assert_raise ArgumentError do
|
|
196
|
+
NDAV.new(@array, readonly: false)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
ndav = NDAV.new_override(@array, readonly: false)
|
|
200
|
+
Fiddle::MemoryView.export ndav do |memview|
|
|
201
|
+
assert_false memview.readonly?
|
|
202
|
+
assert_equal 6, memview.byte_size
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
46
206
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ndav
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kitaiti Makoto
|
|
@@ -163,20 +163,6 @@ dependencies:
|
|
|
163
163
|
- - ">="
|
|
164
164
|
- !ruby/object:Gem::Version
|
|
165
165
|
version: '0'
|
|
166
|
-
- !ruby/object:Gem::Dependency
|
|
167
|
-
name: commonmarker
|
|
168
|
-
requirement: !ruby/object:Gem::Requirement
|
|
169
|
-
requirements:
|
|
170
|
-
- - ">="
|
|
171
|
-
- !ruby/object:Gem::Version
|
|
172
|
-
version: '0'
|
|
173
|
-
type: :development
|
|
174
|
-
prerelease: false
|
|
175
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
176
|
-
requirements:
|
|
177
|
-
- - ">="
|
|
178
|
-
- !ruby/object:Gem::Version
|
|
179
|
-
version: '0'
|
|
180
166
|
executables: []
|
|
181
167
|
extensions:
|
|
182
168
|
- ext/extconf.rb
|
|
@@ -195,12 +181,15 @@ files:
|
|
|
195
181
|
- lib/ndav/converter.rb
|
|
196
182
|
- lib/ndav/ffi.rb
|
|
197
183
|
- lib/ndav/flags.rb
|
|
184
|
+
- lib/ndav/import.rb
|
|
185
|
+
- lib/ndav/item_component.rb
|
|
198
186
|
- lib/ndav/memory_viewable.rb
|
|
199
187
|
- ndav.gemspec
|
|
200
188
|
- test/helper.rb
|
|
201
189
|
- test/test_converter.rb
|
|
202
190
|
- test/test_export.rb
|
|
203
191
|
- test/test_ffi.rb
|
|
192
|
+
- test/test_flags.rb
|
|
204
193
|
- test/test_ndav.rb
|
|
205
194
|
- test/test_package.rb
|
|
206
195
|
- test/test_ractor.rb
|