ndav 0.0.4 → 0.0.6
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 +2 -1
- data/README.md +27 -9
- data/ext/ndav.c +192 -87
- data/lib/ndav/converter.rb +13 -0
- data/lib/ndav/ffi.rb +19 -0
- data/lib/ndav/flags.rb +26 -0
- data/lib/ndav/memory_viewable.rb +80 -0
- data/lib/ndav.rb +92 -37
- data/ndav.gemspec +2 -1
- data/test/test_ffi.rb +8 -0
- data/test/test_ndav.rb +8 -0
- data/test/test_ractor.rb +79 -0
- metadata +19 -4
- data/NDAV.png +0 -0
- data/ext/ndav.h +0 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0194ddf5f19c3e973cb1ef947c125b598295db76eeca5998776612a592c40067'
|
|
4
|
+
data.tar.gz: 59f077dfdf2cdb48c675cde2e9addd7094a01417210fe28f8d786b93213ff8f8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e0ff36126d7192ace3dba90a706ec0f45032c590dd5085c0ca1e4166c6a6708b518ad7077ef9af54a6def14911e3a62c3bbda0e0d904cea0dd51ab46a9fb25dd
|
|
7
|
+
data.tar.gz: 97026defa9b86987290f46a5f04ae0133ca280a87bea04989954fb49055ccc230a5f33d5ac90a8d68c046a05dcfaa97c46c7d6f93fda90f4234f7bf074ae1293
|
data/.yardopts
CHANGED
data/README.md
CHANGED
|
@@ -7,7 +7,7 @@ A thin wrapper around [MemoryView][] ("buffer protocol" for Ruby).
|
|
|
7
7
|
|
|
8
8
|
It provides an interoperability layer for multi-dimensional arrays which can be shared between libraries.
|
|
9
9
|
|
|
10
|
-

|
|
11
11
|
|
|
12
12
|
SYNOPSIS
|
|
13
13
|
--------
|
|
@@ -57,7 +57,9 @@ It might not be difficult, but a little bit cumbersome. Additionally, Red Arrow
|
|
|
57
57
|
USAGE
|
|
58
58
|
-----
|
|
59
59
|
|
|
60
|
-
`ndav` gem is just a base library. You need to install bridges as well.
|
|
60
|
+
`ndav` gem is just a base library. You need to install bridges as well.
|
|
61
|
+
|
|
62
|
+
Say, assume you want to make conversions between Numo::NArray each other.
|
|
61
63
|
|
|
62
64
|
require "numo/narray"
|
|
63
65
|
require "ndav"
|
|
@@ -70,7 +72,7 @@ USAGE
|
|
|
70
72
|
ndav = NDAV.from_numo_narray(numo) # => NDAV
|
|
71
73
|
|
|
72
74
|
include NDAV::Converter
|
|
73
|
-
numo = NumoNArray(ndav)
|
|
75
|
+
numo = NumoNArray(ndav) # => Numo::SFloat
|
|
74
76
|
ndav = NDAV(numo) # => NDAV
|
|
75
77
|
|
|
76
78
|
For `Torch::Tensor` and `OnnxRuntime::OrtValue`, you can do the same operation, therefore you may convert them to each other, like this:
|
|
@@ -100,12 +102,18 @@ NDAV can be initialized *directly* from libraries which export [MemoryView][] su
|
|
|
100
102
|
arrow = Arrow::Int16Array.new([1, 2, 3])
|
|
101
103
|
ndav = NDAV.new(arrow)
|
|
102
104
|
|
|
103
|
-
On the other hand, it also exports MemoryView. You can pass NDAV arrays *directly* to methods which accept MemoryView such as whispercpp without bridge libraries:
|
|
105
|
+
On the other hand, it also exports MemoryView. You can pass NDAV arrays *directly* to methods which accept MemoryView such as [whispercpp][] without bridge libraries:
|
|
104
106
|
|
|
105
107
|
waveform, sample_rate = TorchAudio.load("path/to/audio.wav")
|
|
106
108
|
samples = waveform.to_ndav
|
|
107
109
|
whisper.full(params, samples)
|
|
108
110
|
|
|
111
|
+
Additinally, classes that `include` `NDAV::MemoryViewable`, including `Numo::NArray`, `OnnxRuntime::OrtValue` and `Torch::Tensor`, export MemoryViews:
|
|
112
|
+
|
|
113
|
+
memory_view = Fiddle::MemoryView.new(numo)
|
|
114
|
+
memory_view = Fiddle::MemoryView.new(ort_value)
|
|
115
|
+
memory_view = Fiddle::MemoryView.new(torch_tensor)
|
|
116
|
+
|
|
109
117
|
### Notice On Memory Sharing ###
|
|
110
118
|
|
|
111
119
|
Notice that NDAV is just a memory view and libraries share a memory address. If you change source data destructively, it affects converted data.
|
|
@@ -128,12 +136,18 @@ BRIDGES
|
|
|
128
136
|
|
|
129
137
|
There are some bridges using NDAV:
|
|
130
138
|
|
|
131
|
-
*
|
|
139
|
+
* {file:ndav/ffi ndav/ffi}: [`FFI::MemoryPointer`][FFI Pointer], [`FFI::Pointer`][FFI Pointer] <-> `NDAV`
|
|
132
140
|
* [ndav-numo-narray][]: [`Numo::NArray`][Numo::NArray] <-> `NDAV`
|
|
133
141
|
* [ndav-ort_value][]: [`OnnxRuntime::OrtValue`][ONNX Runtime Ruby] <-> `NDAV`
|
|
134
142
|
* [ndav-torch-tensor][]: [`Torch::Tensor`][Torch.rb] <-> `NDAV`
|
|
135
143
|
* [onnxruntime-torch-tensor][]: [`Torch::Tensor`][Torch.rb] <-> [`OnnxRuntime::OrtValue`][ONNX Runtime Ruby] via `NDAV`
|
|
136
144
|
|
|
145
|
+
APPLICATIONS AND LIBRARIES WHICH USE NDAV
|
|
146
|
+
-----------------------------------------
|
|
147
|
+
|
|
148
|
+
* [GTCRN][] - An audio speech enhancement (noise reduction) library. It uses [TorchAudio][] for loading and preprocessing audio before passing it to ONNX Runtime, then performs post-processing on the result and writes it back to a file.
|
|
149
|
+
* [Itak][] - An audio processing tool for podcasters. After reducing noise with the GTCRN mentioned above, it uses the VAD function of [whispercpp][] to remove silent periods. Although whispercpp does not accept existing tensor libraries, it does accept MemoryView, so it can be passed via NDAV.
|
|
150
|
+
|
|
137
151
|
CREATING BRIDGES
|
|
138
152
|
----------------
|
|
139
153
|
|
|
@@ -141,8 +155,8 @@ Refer to existing bridge implementations listed above to create your bridge.
|
|
|
141
155
|
|
|
142
156
|
The points are:
|
|
143
157
|
|
|
144
|
-
* Implement `FromNDAV#from_ndav`, `ToNDAV#to_ndav
|
|
145
|
-
* When initializing NDAV object from your object, use `lifetime` keyword argument for
|
|
158
|
+
* Implement `FromNDAV#from_ndav`, `MemoryViewable#ndav_descriptor`, optionally `ToNDAV#to_ndav`, {NDAV.register register} them, and `NDAV.from_your_data` and `NDAV#to_your_data` are automatically derived and the array become to able to export a MemoryView
|
|
159
|
+
* When initializing NDAV object from your object, use `lifetime` keyword argument for {NDAV#initialize} effectively to prevent Ruby from GCing your object, which would lead to a dangling pointer
|
|
146
160
|
* When initializing your object from NDAV object, keep NDAV object alive to prevent Ruby from GCing NDAV object, which would lead to a dangling pointer, [ndav-numo-narray][], for instance, embeds the NDAV object in an instance variable
|
|
147
161
|
|
|
148
162
|
An advantage of NDAV over raw MemoryView is that you can write bridges in pure Ruby in most cases. It helps prototyping and experimentation. As an exception, I had to write C code for ndav-numo-narray because Numo::NArray only provides methods that access data by copying and does not directly expose its raw data pointer to Ruby API. However, in even such case, pure Ruby bridge remains a viable option for prototyping and experiments where a single initial copy is acceptable.
|
|
@@ -157,7 +171,7 @@ If [MemoryView][] gets popular enough in the Ruby ecosystem, this library will e
|
|
|
157
171
|
LICENSE
|
|
158
172
|
-------
|
|
159
173
|
|
|
160
|
-
BSD-2-Clause license. See LICENSE.txt file.
|
|
174
|
+
BSD-2-Clause license. See {file:LICENSE.txt} file.
|
|
161
175
|
|
|
162
176
|
[MemoryView]: https://docs.ruby-lang.org/en/master/contributing/memory_view_md.html
|
|
163
177
|
[Numo::NArray]: https://ruby-numo.github.io/narray/
|
|
@@ -165,9 +179,13 @@ BSD-2-Clause license. See LICENSE.txt file.
|
|
|
165
179
|
[ONNX Runtime Ruby]: https://github.com/ankane/onnxruntime-ruby
|
|
166
180
|
[Red Arrow]: https://github.com/apache/arrow/tree/main/ruby
|
|
167
181
|
[Red Arrow Numo::NArray]: https://github.com/red-data-tools/red-arrow-numo-narray
|
|
168
|
-
[ndav/ffi]: https://gitlab.com/KitaitiMakoto/ndav/-/blob/main/lib/ndav/ffi.rb
|
|
182
|
+
[ndav/ffi]: https://gitlab.com/KitaitiMakoto/ndav/-/blob/main/lib/ndav/ffi.rb
|
|
169
183
|
[FFI Pointer]: https://github.com/ffi/ffi/wiki/Pointers
|
|
184
|
+
[whispercpp]: https://github.com/ggml-org/whisper.cpp/tree/master/bindings/ruby
|
|
170
185
|
[ndav-numo-narray]: https://gitlab.com/KitaitiMakoto/ndav-numo-narray
|
|
171
186
|
[ndav-ort_value]: https://gitlab.com/KitaitiMakoto/ndav-ort_value
|
|
172
187
|
[ndav-torch-tensor]: https://gitlab.com/KitaitiMakoto/ndav-torch-tensor
|
|
173
188
|
[onnxruntime-torch-tensor]: https://gitlab.com/KitaitiMakoto/onnxruntime-torch-tensor
|
|
189
|
+
[GTCRN]: https://gitlab.com/KitaitiMakoto/gtcrn
|
|
190
|
+
[TorchAudio]: https://github.com/ankane/torchaudio-ruby
|
|
191
|
+
[Itak]: https://gitlab.com/KitaitiMakoto/itak
|
data/ext/ndav.c
CHANGED
|
@@ -1,145 +1,250 @@
|
|
|
1
|
-
#include
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <ruby/memory_view.h>
|
|
3
|
+
|
|
4
|
+
static ID id_ndav_validated_descriptor;
|
|
5
|
+
static VALUE sym_addr;
|
|
6
|
+
static VALUE sym_shape;
|
|
7
|
+
static VALUE sym_strides;
|
|
8
|
+
static VALUE sym_format;
|
|
9
|
+
static VALUE sym_byte_size;
|
|
10
|
+
static VALUE sym_readonly_p;
|
|
11
|
+
static VALUE sym_sub_offsets;
|
|
12
|
+
static VALUE sym_ndim;
|
|
13
|
+
static VALUE sym_item_size;
|
|
14
|
+
static VALUE sym_row_major_contiguous_p;
|
|
15
|
+
static VALUE sym_column_major_contiguous_p;
|
|
16
|
+
|
|
17
|
+
typedef struct {
|
|
18
|
+
VALUE descriptor;
|
|
19
|
+
} private_data_t;
|
|
2
20
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
typedef struct fill_size_array_args_t {
|
|
6
|
-
VALUE src;
|
|
7
|
-
ssize_t *dest;
|
|
8
|
-
ssize_t size;
|
|
9
|
-
} fill_size_array_args_t;
|
|
10
|
-
|
|
11
|
-
static VALUE
|
|
12
|
-
fill_size_array(VALUE rb_args)
|
|
21
|
+
static bool
|
|
22
|
+
fill_size_array(VALUE src, ssize_t *dest, ssize_t size)
|
|
13
23
|
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
for (ssize_t i = 0; i < size; i++) {
|
|
25
|
+
VALUE val = rb_ary_entry(src, i);
|
|
26
|
+
if (!RB_INTEGER_TYPE_P(val)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
dest[i] = NUM2SSIZET(val);
|
|
18
30
|
}
|
|
19
|
-
return
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static void
|
|
35
|
+
release_memview_common(rb_memory_view_t *view)
|
|
36
|
+
{
|
|
37
|
+
if (view->shape) {
|
|
38
|
+
free((void *)view->shape);
|
|
39
|
+
view->shape = NULL;
|
|
40
|
+
}
|
|
41
|
+
if (view->strides) {
|
|
42
|
+
free((void *)view->strides);
|
|
43
|
+
view->strides = NULL;
|
|
44
|
+
}
|
|
45
|
+
if (view->sub_offsets) {
|
|
46
|
+
free((void *)view->sub_offsets);
|
|
47
|
+
view->sub_offsets = NULL;
|
|
48
|
+
}
|
|
20
49
|
}
|
|
21
50
|
|
|
22
51
|
static bool
|
|
23
|
-
|
|
52
|
+
ndav_memory_viewable_get_memory_view(const VALUE ndav, rb_memory_view_t *view, int flags)
|
|
24
53
|
{
|
|
25
54
|
bool row_major_requested = (flags & RUBY_MEMORY_VIEW_ROW_MAJOR) == RUBY_MEMORY_VIEW_ROW_MAJOR;
|
|
26
55
|
bool column_major_requested = (flags & RUBY_MEMORY_VIEW_COLUMN_MAJOR) == RUBY_MEMORY_VIEW_COLUMN_MAJOR;
|
|
27
56
|
bool indirect_requested = (flags & RUBY_MEMORY_VIEW_INDIRECT) == RUBY_MEMORY_VIEW_INDIRECT;
|
|
28
|
-
|
|
57
|
+
VALUE desc = rb_funcall(ndav, id_ndav_validated_descriptor, 1, INT2NUM(flags));
|
|
58
|
+
if (!RB_TYPE_P(desc, T_HASH)) {
|
|
59
|
+
rb_warn("descriptor not Hash");
|
|
29
60
|
return false;
|
|
30
61
|
}
|
|
31
|
-
bool
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (NIL_P(readonly)) {
|
|
36
|
-
if (writable_requested) {
|
|
37
|
-
rb_warn("not writable");
|
|
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) {
|
|
38
66
|
return false;
|
|
39
67
|
}
|
|
40
|
-
view->readonly = true;
|
|
41
|
-
} else {
|
|
42
|
-
view->readonly = RTEST(readonly);
|
|
43
68
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
+
|
|
80
|
+
VALUE readonly_v = rb_hash_aref(desc, sym_readonly_p);
|
|
81
|
+
bool readonly = NIL_P(readonly_v) || RTEST(readonly_v);
|
|
82
|
+
if (writable_requested && readonly) {
|
|
83
|
+
rb_warn("not writable");
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
VALUE byte_size_v = rb_hash_aref(desc, sym_byte_size);
|
|
87
|
+
ssize_t byte_size = NUM2SSIZET(byte_size_v);
|
|
88
|
+
VALUE addr = rb_hash_aref(desc, sym_addr);
|
|
89
|
+
rb_memory_view_t tmp = {0};
|
|
90
|
+
if (!rb_memory_view_init_as_byte_array(&tmp, ndav, NUM2PTR(addr), byte_size, readonly)) {
|
|
91
|
+
rb_warn("failed to initialize");
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
tmp.readonly = readonly;
|
|
95
|
+
tmp.obj = ndav;
|
|
48
96
|
// TODO: Commonalize
|
|
49
97
|
// TODO: Consider:
|
|
50
98
|
// * Use TypedData?
|
|
51
99
|
// * If so, calling attr readers at Ruby layer has performance disadvantage.
|
|
52
100
|
// * Freezing instance vars in #initialize and then embed them to TypedData may be a solution
|
|
53
|
-
VALUE item_size =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
view->format = StringValueCStr(format);
|
|
61
|
-
|
|
62
|
-
int state;
|
|
101
|
+
VALUE item_size = rb_hash_aref(desc, sym_item_size);
|
|
102
|
+
tmp.item_size = NUM2SSIZET(item_size);
|
|
103
|
+
tmp.byte_size = byte_size;
|
|
104
|
+
VALUE ndim = rb_hash_aref(desc, sym_ndim);
|
|
105
|
+
tmp.ndim = NUM2SSIZET(ndim);
|
|
106
|
+
VALUE format = rb_hash_aref(desc, sym_format);
|
|
107
|
+
tmp.format = StringValueCStr(format);
|
|
63
108
|
|
|
64
109
|
// TODO: Commonalize
|
|
65
|
-
VALUE shape =
|
|
110
|
+
VALUE shape = rb_hash_aref(desc, sym_shape);
|
|
66
111
|
if (!RB_TYPE_P(shape, T_ARRAY)) {
|
|
67
|
-
|
|
112
|
+
release_memview_common(&tmp);
|
|
113
|
+
rb_warn("shape is not an array");
|
|
68
114
|
return false;
|
|
69
115
|
}
|
|
70
|
-
ssize_t *view_shape =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
view_shape,
|
|
74
|
-
view->ndim,
|
|
75
|
-
};
|
|
76
|
-
rb_protect(fill_size_array, (VALUE)&shape_args, &state);
|
|
77
|
-
if (state) {
|
|
78
|
-
xfree(view_shape);
|
|
79
|
-
rb_jump_tag(state);
|
|
116
|
+
ssize_t *view_shape = (ssize_t *)malloc(sizeof(ssize_t) * tmp.ndim);
|
|
117
|
+
if (!view_shape) {
|
|
118
|
+
release_memview_common(&tmp);
|
|
80
119
|
return false;
|
|
81
120
|
}
|
|
82
|
-
|
|
121
|
+
if (!fill_size_array(shape, view_shape, tmp.ndim)) {
|
|
122
|
+
free(view_shape);
|
|
123
|
+
release_memview_common(&tmp);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
tmp.shape = view_shape;
|
|
83
127
|
|
|
84
|
-
VALUE strides =
|
|
128
|
+
VALUE strides = rb_hash_aref(desc, sym_strides);
|
|
85
129
|
if (!RB_TYPE_P(strides, T_ARRAY)) {
|
|
86
|
-
|
|
87
|
-
|
|
130
|
+
release_memview_common(&tmp);
|
|
131
|
+
rb_warn("strides is not an array");
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
ssize_t *view_strides = (ssize_t *)malloc(sizeof(ssize_t) * tmp.ndim);
|
|
135
|
+
if (!view_strides) {
|
|
136
|
+
release_memview_common(&tmp);
|
|
88
137
|
return false;
|
|
89
138
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
view_strides,
|
|
94
|
-
view->ndim
|
|
95
|
-
};
|
|
96
|
-
rb_protect(fill_size_array, (VALUE)&strides_args, &state);
|
|
97
|
-
if (state) {
|
|
98
|
-
xfree((void *)view_shape);
|
|
99
|
-
xfree((void *)view_strides);
|
|
100
|
-
rb_jump_tag(state);
|
|
139
|
+
if (!fill_size_array(strides, view_strides, tmp.ndim)) {
|
|
140
|
+
free(view_strides);
|
|
141
|
+
release_memview_common(&tmp);
|
|
101
142
|
return false;
|
|
102
143
|
}
|
|
103
|
-
|
|
144
|
+
tmp.strides = view_strides;
|
|
104
145
|
|
|
105
|
-
|
|
146
|
+
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 {
|
|
159
|
+
ssize_t *view_sub_offsets = (ssize_t *)malloc(sizeof(ssize_t) * tmp.ndim);
|
|
160
|
+
if (!view_sub_offsets) {
|
|
161
|
+
release_memview_common(&tmp);
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (!fill_size_array(sub_offsets, view_sub_offsets, tmp.ndim)) {
|
|
165
|
+
free(view_sub_offsets);
|
|
166
|
+
release_memview_common(&tmp);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
tmp.sub_offsets = view_sub_offsets;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private_data_t *private_data = malloc(sizeof(private_data_t));
|
|
173
|
+
if (!private_data) {
|
|
174
|
+
release_memview_common(&tmp);
|
|
175
|
+
rb_warn("failed to alloc private_data");
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
private_data->descriptor = Qnil;
|
|
179
|
+
rb_gc_register_address(&private_data->descriptor);
|
|
180
|
+
private_data->descriptor = desc;
|
|
181
|
+
tmp.private_data = private_data;
|
|
182
|
+
|
|
183
|
+
*view = tmp;
|
|
106
184
|
|
|
107
185
|
return true;
|
|
108
186
|
}
|
|
109
187
|
|
|
110
188
|
static bool
|
|
111
|
-
|
|
189
|
+
ndav_memory_viewable_release_memory_view(const VALUE ndav, rb_memory_view_t *view)
|
|
112
190
|
{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
view->strides = NULL;
|
|
191
|
+
release_memview_common(view);
|
|
192
|
+
if (view->private_data) {
|
|
193
|
+
private_data_t *private_data = (private_data_t *)view->private_data;
|
|
194
|
+
rb_gc_unregister_address(&private_data->descriptor);
|
|
195
|
+
free(private_data);
|
|
196
|
+
view->private_data = NULL;
|
|
120
197
|
}
|
|
121
198
|
|
|
122
199
|
return true;
|
|
123
200
|
}
|
|
124
201
|
|
|
125
202
|
static bool
|
|
126
|
-
|
|
203
|
+
ndav_memory_viewable_memory_view_available_p(const VALUE obj)
|
|
127
204
|
{
|
|
128
|
-
VALUE
|
|
129
|
-
|
|
205
|
+
VALUE descriptor = rb_funcall(obj, id_ndav_validated_descriptor, 0);
|
|
206
|
+
if (RB_TYPE_P(descriptor, T_HASH)) {
|
|
207
|
+
return true;
|
|
208
|
+
} else {
|
|
209
|
+
rb_warn("descriptor not Hash");
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
130
212
|
}
|
|
131
213
|
|
|
132
214
|
const struct rb_memory_view_entry ndav_view_entry = {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
215
|
+
ndav_memory_viewable_get_memory_view,
|
|
216
|
+
ndav_memory_viewable_release_memory_view,
|
|
217
|
+
ndav_memory_viewable_memory_view_available_p
|
|
136
218
|
};
|
|
137
219
|
|
|
220
|
+
static VALUE
|
|
221
|
+
ndav_memory_viewable_s_register(VALUE mod, VALUE klass)
|
|
222
|
+
{
|
|
223
|
+
if (!rb_memory_view_register(klass, &ndav_view_entry)) {
|
|
224
|
+
// error message output in rb_memory_view_register()
|
|
225
|
+
rb_raise(rb_eArgError, "");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return Qtrue;
|
|
229
|
+
}
|
|
230
|
+
|
|
138
231
|
void
|
|
139
232
|
Init_ndav(void)
|
|
140
233
|
{
|
|
141
|
-
|
|
234
|
+
id_ndav_validated_descriptor = rb_intern("ndav_validated_descriptor");
|
|
235
|
+
sym_addr = ID2SYM(rb_intern("addr"));
|
|
236
|
+
sym_shape = ID2SYM(rb_intern("shape"));
|
|
237
|
+
sym_strides = ID2SYM(rb_intern("strides"));
|
|
238
|
+
sym_format = ID2SYM(rb_intern("format"));
|
|
239
|
+
sym_byte_size = ID2SYM(rb_intern("byte_size"));
|
|
240
|
+
sym_readonly_p = ID2SYM(rb_intern("readonly?"));
|
|
241
|
+
sym_sub_offsets = ID2SYM(rb_intern("sub_offsets"));
|
|
242
|
+
sym_ndim = ID2SYM(rb_intern("ndim"));
|
|
243
|
+
sym_item_size = ID2SYM(rb_intern("item_size"));
|
|
244
|
+
sym_row_major_contiguous_p = ID2SYM(rb_intern("row_major_contiguous?"));
|
|
245
|
+
sym_column_major_contiguous_p = ID2SYM(rb_intern("column_major_contiguous?"));
|
|
142
246
|
|
|
143
247
|
VALUE cNDAV = rb_define_class("NDAV", rb_cObject);
|
|
144
|
-
|
|
248
|
+
VALUE mMemoryViewable = rb_define_module_under(cNDAV, "MemoryViewable");
|
|
249
|
+
rb_define_singleton_method(mMemoryViewable, "register", ndav_memory_viewable_s_register, 1);
|
|
145
250
|
}
|
data/lib/ndav/converter.rb
CHANGED
|
@@ -17,6 +17,19 @@ class NDAV
|
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
if mdl.const_defined?(:MemoryViewable)
|
|
21
|
+
cls.include ::NDAV::MemoryViewable
|
|
22
|
+
cls.include mdl::MemoryViewable
|
|
23
|
+
|
|
24
|
+
unless mdl.const_defined?(:ToNDAV)
|
|
25
|
+
mdl.const_set(:ToNDAV, Module.new {
|
|
26
|
+
def to_ndav(lifetime: self, **)
|
|
27
|
+
::NDAV.new(self, lifetime:, **)
|
|
28
|
+
end
|
|
29
|
+
})
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
20
33
|
if mdl.const_defined?(:ToNDAV)
|
|
21
34
|
cls.include mdl::ToNDAV
|
|
22
35
|
|
data/lib/ndav/ffi.rb
CHANGED
|
@@ -26,6 +26,25 @@ class NDAV
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
module MemoryViewable
|
|
30
|
+
def ndav_descriptor(**)
|
|
31
|
+
shape = [size / type_size]
|
|
32
|
+
format = TYPE_SIZE_TO_FORMAT[type_size]
|
|
33
|
+
item_size = ITEM_SIZES[format]
|
|
34
|
+
byte_size = shape.reduce(item_size, :*)
|
|
35
|
+
ptr = ::Fiddle::Pointer.new(address, byte_size)
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
data: Fiddle::MemoryView.new(ptr).to_s,
|
|
39
|
+
shape:,
|
|
40
|
+
strides: ::NDAV.default_strides(shape:, item_size:),
|
|
41
|
+
format:,
|
|
42
|
+
byte_size:,
|
|
43
|
+
readonly?: true
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
29
48
|
module Converter
|
|
30
49
|
def FFIMemoryPointer(array, *, **)
|
|
31
50
|
if ::FFI::MemoryPointer === array
|
data/lib/ndav/flags.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class NDAV
|
|
2
|
+
module Flags
|
|
3
|
+
FLAGS = {}
|
|
4
|
+
FLAGS[:simple] = 0
|
|
5
|
+
FLAGS[:writable] = (1<<0)
|
|
6
|
+
FLAGS[:format] = (1<<1)
|
|
7
|
+
FLAGS[:multi_dimensional] = (1<<2)
|
|
8
|
+
FLAGS[:strides] = (1<<3) | FLAGS[:multi_dimensional]
|
|
9
|
+
FLAGS[:row_major] = (1<<4) | FLAGS[:strides]
|
|
10
|
+
FLAGS[:column_major] = (1<<5) | FLAGS[:strides]
|
|
11
|
+
FLAGS[:any_contiguous] = FLAGS[:row_major] | FLAGS[:column_major]
|
|
12
|
+
FLAGS[:indirect] = (1<<6) | FLAGS[:strides]
|
|
13
|
+
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def decode(flags)
|
|
17
|
+
FLAGS.each_with_object({}) {|(name, value), state|
|
|
18
|
+
if name == :simple
|
|
19
|
+
state[name] = flags == 0
|
|
20
|
+
else
|
|
21
|
+
state[name] = flags & value == value
|
|
22
|
+
end
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
class NDAV
|
|
2
|
+
# +include+ this module and define +#ndav_descriptor+ method in your class,
|
|
3
|
+
# and then the class exports a MemoryView.
|
|
4
|
+
#
|
|
5
|
+
# +#ndav_descriptor+'s arguments are flags of MemoryView including
|
|
6
|
+
# +simple+, +writable+, +format+, +multi_dimensional+, +strides+, +row_major+, +column_major+, +any_contiguous+, +indirect+.
|
|
7
|
+
# For the meaning of them, see MemoryView document.
|
|
8
|
+
#
|
|
9
|
+
# It must return a Hash whose keys includes:
|
|
10
|
+
#
|
|
11
|
+
# * +:data+: Required. Binary +String+, pointer address +Integer+ or +Fiddle::Pointer+. This is passed to +Fiddle::Pointer.to_ptr+, so should be valid for the method.
|
|
12
|
+
# * +:shape+: Required. See MemoryView document.
|
|
13
|
+
# * +:strides+: Required. See MemoryView document.
|
|
14
|
+
# * +:format+: Required. See MemoryView document.
|
|
15
|
+
# * +:byte_size+: Required. See MemoryView document.
|
|
16
|
+
# * +:readonly?+: Required. See MemoryView document.
|
|
17
|
+
# * +:sub_offsets+: Optional. See MemoryView document.
|
|
18
|
+
#
|
|
19
|
+
# @example
|
|
20
|
+
# class YourClass
|
|
21
|
+
# include NDAV::MemoryViewable
|
|
22
|
+
# # :
|
|
23
|
+
# def ndav_descriptor(
|
|
24
|
+
# simple: true,
|
|
25
|
+
# writable: false,
|
|
26
|
+
# format: false,
|
|
27
|
+
# multi_dimensional: false,
|
|
28
|
+
# strides: false,
|
|
29
|
+
# row_major: false,
|
|
30
|
+
# column_major: false,
|
|
31
|
+
# any_contiguous: false,
|
|
32
|
+
# indirect: false
|
|
33
|
+
# )
|
|
34
|
+
# {
|
|
35
|
+
# data:,
|
|
36
|
+
# shape:,
|
|
37
|
+
# strides:,
|
|
38
|
+
# format:,
|
|
39
|
+
# byte_size:,
|
|
40
|
+
# readonly?:,
|
|
41
|
+
# sub_offsets:
|
|
42
|
+
# }
|
|
43
|
+
# end
|
|
44
|
+
# # :
|
|
45
|
+
# end
|
|
46
|
+
module MemoryViewable
|
|
47
|
+
class << self
|
|
48
|
+
def included(base)
|
|
49
|
+
::NDAV::MemoryViewable.register base
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def ndav_validated_descriptor(flags = ::NDAV::Flags::FLAGS[:simple])
|
|
56
|
+
desc = ndav_descriptor(**NDAV::Flags.decode(flags)).to_h
|
|
57
|
+
[:data, :shape, :strides, :format, :byte_size, :readonly?].each do |key|
|
|
58
|
+
unless desc.key? key
|
|
59
|
+
warn ":#{key} not in descriptor"
|
|
60
|
+
return false
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
item_size = NDAV::ITEM_SIZES[desc[:format]]
|
|
64
|
+
unless desc[:shape].reduce(item_size, :*) == desc[:byte_size]
|
|
65
|
+
warn "item size from format and shape not match byte_size"
|
|
66
|
+
return false
|
|
67
|
+
end
|
|
68
|
+
desc.merge(
|
|
69
|
+
addr: ::Fiddle::Pointer.to_ptr(desc[:data]).to_i,
|
|
70
|
+
ndim: desc[:shape].length,
|
|
71
|
+
item_size:,
|
|
72
|
+
row_major_contiguous?: ::NDAV.row_major_contiguous?(**desc),
|
|
73
|
+
column_major_contiguous?: ::NDAV.column_major_contiguous?(**desc)
|
|
74
|
+
)
|
|
75
|
+
rescue => err
|
|
76
|
+
warn err
|
|
77
|
+
return false
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/ndav.rb
CHANGED
|
@@ -1,88 +1,143 @@
|
|
|
1
1
|
require "fiddle"
|
|
2
2
|
require "ndav.so"
|
|
3
|
+
require "ndav/flags"
|
|
3
4
|
require "ndav/converter"
|
|
5
|
+
require "ndav/memory_viewable"
|
|
4
6
|
|
|
5
7
|
# TODO: Manage flags of internal data object
|
|
6
8
|
class NDAV
|
|
9
|
+
include MemoryViewable
|
|
10
|
+
|
|
7
11
|
# TODO: Want to use return value of rb_memory_view_parse_item_format
|
|
8
12
|
ITEM_SIZES = ["c", "C", "s", "S", "l", "L", "q", "Q", "f", "d", "s!", "S!", "n", "v", "i", "i!", "I", "I!", "l!", "L!", "N", "V", "e", "g", "q!", "Q!", "E", "G"].collect {|format|
|
|
9
13
|
[format, [0].pack(format).bytesize]
|
|
10
14
|
}.to_h
|
|
11
15
|
|
|
12
|
-
attr_reader :ndim, :shape, :format, :item_size, :byte_size, :strides, :lifetime
|
|
16
|
+
attr_reader :ndim, :shape, :format, :item_size, :byte_size, :strides, :to_s, :lifetime
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def default_strides(shape:, item_size:, row_major: true)
|
|
20
|
+
each_shape = row_major ? shape.reverse_each : shape.each
|
|
21
|
+
each_shape.reduce([[], item_size]) {|(strides, stride), s|
|
|
22
|
+
if row_major
|
|
23
|
+
strides.unshift stride
|
|
24
|
+
else
|
|
25
|
+
strides << stride
|
|
26
|
+
end
|
|
27
|
+
[strides, stride * s]
|
|
28
|
+
}[0]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Algorithm stolen from memory_view.c
|
|
32
|
+
def row_major_contiguous?(format:, shape:, strides:, **)
|
|
33
|
+
ndim = shape.length
|
|
34
|
+
n = ITEM_SIZES[format]
|
|
35
|
+
return strides[0] == n if ndim == 1
|
|
36
|
+
|
|
37
|
+
(ndim - 1).downto 0 do |i|
|
|
38
|
+
return false unless strides[i] == n
|
|
39
|
+
|
|
40
|
+
n *= shape[i]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Algorithm stolen from memory_view.c
|
|
47
|
+
def column_major_contiguous?(format:, shape:, strides:, **)
|
|
48
|
+
ndim = shape.length
|
|
49
|
+
n = ITEM_SIZES[format]
|
|
50
|
+
return strides[0] == n if ndim == 1
|
|
51
|
+
|
|
52
|
+
0.upto(ndim - 1) do |i|
|
|
53
|
+
return false unless strides[i] == n
|
|
54
|
+
|
|
55
|
+
n *= shape[i]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
true
|
|
59
|
+
end
|
|
60
|
+
end
|
|
13
61
|
|
|
14
62
|
# TODO: More options to trick for the case of MemoryView export is wrong but developer don't fix it
|
|
15
|
-
def initialize(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
63
|
+
def initialize(obj, shape: nil, strides: nil, format: nil, lifetime: nil)
|
|
64
|
+
fmv = obj.kind_of?(::Fiddle::MemoryView) ? obj : ::Fiddle::MemoryView.new(obj)
|
|
65
|
+
# Fiddle::MemoryView#to_s is zero-copy
|
|
66
|
+
# Fiddle::MemoryView#to_s embeds obj into the String, so we can prevent GC from collecting it
|
|
67
|
+
@to_s = fmv.to_s
|
|
68
|
+
@lifetime = lifetime
|
|
19
69
|
|
|
20
|
-
@format = format ||
|
|
70
|
+
@format = format || fmv.format
|
|
21
71
|
raise ArgumentError, %Q|unsupported format: #{@format.to_s.dump}, currently supported: #{ITEM_SIZES.keys}| unless ITEM_SIZES.key?(@format)
|
|
22
72
|
|
|
23
73
|
item_size_by_format = ITEM_SIZES[@format]
|
|
24
74
|
|
|
25
|
-
@byte_size =
|
|
26
|
-
@item_size =
|
|
27
|
-
@shape = shape ||
|
|
75
|
+
@byte_size = fmv.byte_size
|
|
76
|
+
@item_size = obj.kind_of?(::Fiddle::Pointer) ? item_size_by_format : fmv.item_size || item_size_by_format
|
|
77
|
+
@shape = shape || fmv.shape
|
|
28
78
|
case [@shape, @byte_size]
|
|
29
79
|
in [nil, nil]
|
|
30
|
-
raise ArgumentError, "either shape or byte_size must be present in
|
|
31
|
-
in [
|
|
80
|
+
raise ArgumentError, "either shape or byte_size must be present in obj or arguments"
|
|
81
|
+
in [_, nil]
|
|
32
82
|
@byte_size = @shape.reduce(@item_size, :*)
|
|
33
|
-
in [nil,
|
|
83
|
+
in [nil, _]
|
|
34
84
|
n, r = @byte_size.divmod(@item_size)
|
|
35
85
|
raise ArgumentError, "byte_size must be n-times of item_size" unless r.zero?
|
|
36
86
|
@shape = [n]
|
|
37
87
|
else
|
|
38
88
|
# noop
|
|
39
89
|
end
|
|
40
|
-
@strides = @fmv.strides || @shape.reverse_each.reduce([[], @item_size]) {|(strides, stride), s|
|
|
41
|
-
strides.unshift(stride)
|
|
42
|
-
[strides, stride * s]
|
|
43
|
-
}[0]
|
|
44
90
|
@ndim = @shape.size
|
|
45
|
-
@readonly =
|
|
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
|
|
94
|
+
@row_major_contiguous = self.class.row_major_contiguous?(format: @format, shape: @shape, strides: @strides)
|
|
95
|
+
@column_major_contiguous = self.class.column_major_contiguous?(format: @format, shape: @shape, strides: @strides)
|
|
46
96
|
|
|
47
|
-
validate
|
|
97
|
+
validate fmv
|
|
48
98
|
end
|
|
49
99
|
|
|
50
100
|
def readonly?
|
|
51
101
|
@readonly
|
|
52
102
|
end
|
|
53
103
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
104
|
+
def row_major_contiguous?
|
|
105
|
+
@row_major_contiguous
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def column_major_contiguous?
|
|
109
|
+
@column_major_contiguous
|
|
59
110
|
end
|
|
60
111
|
|
|
61
112
|
def to_ptr
|
|
62
|
-
::Fiddle::Pointer[to_s]
|
|
113
|
+
::Fiddle::Pointer[@to_s]
|
|
63
114
|
end
|
|
64
115
|
alias to_fiddle_pointer to_ptr
|
|
65
116
|
|
|
66
117
|
private
|
|
67
118
|
|
|
68
|
-
def
|
|
119
|
+
def ndav_descriptor(**)
|
|
120
|
+
{
|
|
121
|
+
data: @to_s,
|
|
122
|
+
shape: @shape,
|
|
123
|
+
strides: @strides,
|
|
124
|
+
format: @format,
|
|
125
|
+
byte_size: @byte_size,
|
|
126
|
+
readonly?: @readonly,
|
|
127
|
+
sub_offsets: @sub_offsets
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def validate(fmv)
|
|
69
132
|
unless @item_size == ITEM_SIZES[@format]
|
|
70
133
|
raise ArgumentError, "item_size does not match format"
|
|
71
134
|
end
|
|
72
135
|
|
|
73
|
-
if @
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
stride = @item_size
|
|
78
|
-
@shape.reverse_each.with_index do |s, i|
|
|
79
|
-
unless @strides[@ndim - 1 - i] == stride
|
|
80
|
-
raise ArgumentError, "only row-major contiguous strides is supported"
|
|
136
|
+
if @row_major_contiguous || @column_major_contiguous
|
|
137
|
+
byte_size = @shape.reduce(@item_size, :*)
|
|
138
|
+
unless byte_size == @byte_size
|
|
139
|
+
raise ArgumentError, "strides not match byte_size"
|
|
81
140
|
end
|
|
82
|
-
stride *= s
|
|
83
|
-
end
|
|
84
|
-
unless stride == @byte_size
|
|
85
|
-
raise ArgumentError, "strides not match byte_size"
|
|
86
141
|
end
|
|
87
142
|
end
|
|
88
143
|
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.6"
|
|
4
4
|
s.summary = "N-Dimensional Array View"
|
|
5
5
|
s.authors = ["Kitaiti Makoto"]
|
|
6
6
|
s.licenses = ["BSD-2-Clause"]
|
|
@@ -22,4 +22,5 @@ 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"
|
|
25
26
|
end
|
data/test/test_ffi.rb
CHANGED
|
@@ -48,6 +48,14 @@ class TestFFI < TestBase
|
|
|
48
48
|
assert_equal ndav.to_ptr.to_i, ptr.address
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
def test_memory_view_from_ffi_memory_pointer
|
|
52
|
+
ptr = ::FFI::MemoryPointer.new(:int16, 3)
|
|
53
|
+
ptr.write_array_of_int16([1, 2, 3])
|
|
54
|
+
fmv = Fiddle::MemoryView.new(ptr)
|
|
55
|
+
|
|
56
|
+
assert_int_array fmv
|
|
57
|
+
end
|
|
58
|
+
|
|
51
59
|
class TestConverter < self
|
|
52
60
|
def test_ndav_from_ffi_memory_pointer
|
|
53
61
|
ptr = ::FFI::MemoryPointer.new(:int16, 3)
|
data/test/test_ndav.rb
CHANGED
|
@@ -35,4 +35,12 @@ class TestNDAV < TestBase
|
|
|
35
35
|
|
|
36
36
|
assert_int_array ndav
|
|
37
37
|
end
|
|
38
|
+
|
|
39
|
+
def test_column_major
|
|
40
|
+
str = [1, 2, 3,
|
|
41
|
+
4, 5, 6].pack("s*")
|
|
42
|
+
ndav = NDAV.from_string(str, format: "s", shape: [2, 3], strides: [2, 4])
|
|
43
|
+
|
|
44
|
+
assert_true ndav.column_major_contiguous?
|
|
45
|
+
end
|
|
38
46
|
end
|
data/test/test_ractor.rb
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require_relative "helper"
|
|
2
|
+
|
|
3
|
+
class TestRactor < TestBase
|
|
4
|
+
ractor
|
|
5
|
+
|
|
6
|
+
def test_ractor
|
|
7
|
+
ractor = Ractor.new {
|
|
8
|
+
Ractor.receive
|
|
9
|
+
}
|
|
10
|
+
ndav = NDAV.new(Arrow::Int16Array.new([1, 2, 3]))
|
|
11
|
+
ractor << ndav
|
|
12
|
+
result = ractor.value
|
|
13
|
+
|
|
14
|
+
assert_not_same ndav, result
|
|
15
|
+
assert_int_array result
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_ractor_from_string
|
|
19
|
+
ractor = Ractor.new {
|
|
20
|
+
Ractor.receive
|
|
21
|
+
}
|
|
22
|
+
ndav = NDAV.from_string([1, 2, 3].pack("s*"), format: "s")
|
|
23
|
+
ractor << ndav
|
|
24
|
+
result = ractor.value
|
|
25
|
+
|
|
26
|
+
assert_int_array result
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_ractor_share
|
|
30
|
+
ractor = Ractor.new {
|
|
31
|
+
Ractor.receive
|
|
32
|
+
}
|
|
33
|
+
ndav = NDAV.new(Arrow::Int16Array.new([1, 2, 3]))
|
|
34
|
+
Ractor.make_shareable(ndav)
|
|
35
|
+
ractor << ndav
|
|
36
|
+
result = ractor.value
|
|
37
|
+
|
|
38
|
+
assert_same ndav, result
|
|
39
|
+
assert_int_array result
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_port
|
|
43
|
+
ractor = Ractor.new {
|
|
44
|
+
ndav, port = Ractor.receive
|
|
45
|
+
port << ndav
|
|
46
|
+
}
|
|
47
|
+
ndav = NDAV.new(Arrow::Int16Array.new([1, 2, 3]))
|
|
48
|
+
port = Ractor::Port.new
|
|
49
|
+
ractor << [ndav, port]
|
|
50
|
+
result = port.receive
|
|
51
|
+
|
|
52
|
+
assert_not_same ndav, result
|
|
53
|
+
assert_int_array result
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_port_share
|
|
57
|
+
ractor = Ractor.new {
|
|
58
|
+
ndav, port = Ractor.receive
|
|
59
|
+
port << ndav
|
|
60
|
+
}
|
|
61
|
+
ndav = NDAV.new(Arrow::Int16Array.new([1, 2, 3]))
|
|
62
|
+
Ractor.make_shareable(ndav)
|
|
63
|
+
port = Ractor::Port.new
|
|
64
|
+
ractor << [ndav, port]
|
|
65
|
+
result = port.receive
|
|
66
|
+
|
|
67
|
+
assert_same ndav, result
|
|
68
|
+
assert_int_array result
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_ractor_not_initializable
|
|
72
|
+
ractor = Ractor.new {
|
|
73
|
+
NDAV.new(Arrow::Int16Array.new([1, 2, 3]))
|
|
74
|
+
}
|
|
75
|
+
assert_raise Ractor::RemoteError do
|
|
76
|
+
ractor.value
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
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.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kitaiti Makoto
|
|
@@ -163,6 +163,20 @@ 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'
|
|
166
180
|
executables: []
|
|
167
181
|
extensions:
|
|
168
182
|
- ext/extconf.rb
|
|
@@ -173,15 +187,15 @@ files:
|
|
|
173
187
|
- ".yardopts"
|
|
174
188
|
- Gemfile
|
|
175
189
|
- LICENSE.txt
|
|
176
|
-
- NDAV.png
|
|
177
190
|
- README.md
|
|
178
191
|
- Rakefile
|
|
179
192
|
- ext/extconf.rb
|
|
180
193
|
- ext/ndav.c
|
|
181
|
-
- ext/ndav.h
|
|
182
194
|
- lib/ndav.rb
|
|
183
195
|
- lib/ndav/converter.rb
|
|
184
196
|
- lib/ndav/ffi.rb
|
|
197
|
+
- lib/ndav/flags.rb
|
|
198
|
+
- lib/ndav/memory_viewable.rb
|
|
185
199
|
- ndav.gemspec
|
|
186
200
|
- test/helper.rb
|
|
187
201
|
- test/test_converter.rb
|
|
@@ -189,6 +203,7 @@ files:
|
|
|
189
203
|
- test/test_ffi.rb
|
|
190
204
|
- test/test_ndav.rb
|
|
191
205
|
- test/test_package.rb
|
|
206
|
+
- test/test_ractor.rb
|
|
192
207
|
homepage: https://kitaitimakoto.gitlab.io/ndav
|
|
193
208
|
licenses:
|
|
194
209
|
- BSD-2-Clause
|
|
@@ -208,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
208
223
|
- !ruby/object:Gem::Version
|
|
209
224
|
version: '0'
|
|
210
225
|
requirements: []
|
|
211
|
-
rubygems_version: 4.0.
|
|
226
|
+
rubygems_version: 4.0.16
|
|
212
227
|
specification_version: 4
|
|
213
228
|
summary: N-Dimensional Array View
|
|
214
229
|
test_files: []
|
data/NDAV.png
DELETED
|
Binary file
|
data/ext/ndav.h
DELETED