ndav 0.0.3 → 0.0.5
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/.gitignore +2 -0
- data/.gitlab-ci.yml +12 -0
- data/.yardopts +4 -0
- data/README.md +176 -2
- data/Rakefile +4 -2
- data/ext/ndav.c +155 -64
- data/lib/ndav/converter.rb +17 -0
- data/lib/ndav/ffi.rb +19 -0
- data/lib/ndav/flags.rb +26 -0
- data/lib/ndav/memory_viewable.rb +47 -0
- data/lib/ndav.rb +92 -37
- data/ndav.gemspec +5 -2
- data/test/test_ffi.rb +8 -0
- data/test/test_ndav.rb +15 -0
- data/test/test_ractor.rb +79 -0
- metadata +37 -5
- 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: '0648fef27ca47ec51a45cacb90330e5fb2de53071d4223604752c04746ad9c2d'
|
|
4
|
+
data.tar.gz: 4af1dacda9bfa1258552bc271d773136b06ea414cce61c75c194c590f1361850
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b81a17db0cc3814963b47e590e2f3e021e86792326a98ad1f246df8f887c4f1e49aec42808c82aeceb522e28f05f7dac9d3a6eb65417fbfc9ff785cb75284723
|
|
7
|
+
data.tar.gz: 798ed5c4f2f84fa73b262af460e0054454a78d6f8a9eeacbb191f0b866c6c4e98ae72ed296af23b7a67d98d6894a988cc22dfc23484267d1773bd91ed7043a63
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
data/.yardopts
ADDED
data/README.md
CHANGED
|
@@ -1,9 +1,183 @@
|
|
|
1
1
|
NDAV - N-Dimensional Array View
|
|
2
2
|
===============================
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
[](https://badge.fury.io/rb/ndav)
|
|
5
|
+
|
|
6
|
+
A thin wrapper around [MemoryView][] ("buffer protocol" for Ruby).
|
|
7
|
+
|
|
8
|
+
It provides an interoperability layer for multi-dimensional arrays which can be shared between libraries.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
SYNOPSIS
|
|
13
|
+
--------
|
|
14
|
+
|
|
15
|
+
waveform, sample_rate = TorchAudio.load("path/to/audio.wav")
|
|
16
|
+
|
|
17
|
+
# Convert Torch::Tensor to NDAV
|
|
18
|
+
# so that you can convert it to OrtValue,
|
|
19
|
+
# a data format for ONNX Runtime
|
|
20
|
+
input = waveform
|
|
21
|
+
.to_ndav
|
|
22
|
+
.to_ort_value
|
|
23
|
+
|
|
24
|
+
# Make ONNX Runtime return result as OrtValue
|
|
25
|
+
outputs = OnnxRuntime::Session.new("path/to/model.onnx")
|
|
26
|
+
.run(
|
|
27
|
+
[:output_name],
|
|
28
|
+
{input_name: input},
|
|
29
|
+
output_type: :ort_value
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# You may convert OrtValue to Torch::Tensor via NDAV
|
|
33
|
+
output_tensor = outputs[0]
|
|
34
|
+
.to_ndav
|
|
35
|
+
.to_torch_tensor # converts back to Torch::Tensor
|
|
36
|
+
|
|
37
|
+
TorchAudio.save("path/to/output.wav", output_tensor, sample_rate)
|
|
38
|
+
|
|
39
|
+
ABSTRACT
|
|
40
|
+
--------
|
|
41
|
+
|
|
42
|
+
NDAV acts as an interoperability layer between multi-dimensional arrays including images, audio and tensors such as [Numo::NArray][], [Torch.rb][]'s `Torch::Tensor`, [ONNX Runtime Ruby][]'s `OnnxRuntime::OrtValue`, [Red Arrow][]'s `Arrow::Array` and so on.
|
|
43
|
+
|
|
44
|
+
It allows data to be shared without copying.
|
|
45
|
+
|
|
46
|
+
BACKGROUND
|
|
47
|
+
----------
|
|
48
|
+
|
|
49
|
+
In the modern Ruby community, [Numo::NArray][] is often used for data conversion. But, data are copied when converting to and from Numo::NArray. In addition, Numo::NArray neither exports nor accepts MemoryView.
|
|
50
|
+
|
|
51
|
+
[Red Arrow][] is also used and it can export MemoryView from `Arrow::Array` (not from `Arrow::Tensor`, though). It also can be converted to and from Numo::NArray using [Red Arrow Numo::NArray][]. But, in real-world usage, we need, for example, to convert data with many hops:
|
|
52
|
+
|
|
53
|
+
Torch::Tensor -> Numo::NArray -> Red Arrow -> MemoryView -> some process...
|
|
54
|
+
|
|
55
|
+
It might not be difficult, but a little bit cumbersome. Additionally, Red Arrow doesn't accept MemoryView.
|
|
56
|
+
|
|
57
|
+
USAGE
|
|
58
|
+
-----
|
|
59
|
+
|
|
60
|
+
`ndav` gem is just a base library. You need to install bridges as well. Say, assume you want to make conversions between Numo::NArray each other.
|
|
61
|
+
|
|
62
|
+
require "numo/narray"
|
|
63
|
+
require "ndav"
|
|
64
|
+
require "ndav/numo/narray"
|
|
65
|
+
|
|
66
|
+
numo = Numo::SFloat.new(3, 5).seq # => Numo::SFloat
|
|
67
|
+
ndav = numo.to_ndav # => NDAV
|
|
68
|
+
|
|
69
|
+
numo = Numo::SFloat.from_ndav(ndav) # => Numo::SFloat
|
|
70
|
+
ndav = NDAV.from_numo_narray(numo) # => NDAV
|
|
71
|
+
|
|
72
|
+
include NDAV::Converter
|
|
73
|
+
numo = NumoNArray(ndav) # => Numo::SFloat
|
|
74
|
+
ndav = NDAV(numo) # => NDAV
|
|
75
|
+
|
|
76
|
+
For `Torch::Tensor` and `OnnxRuntime::OrtValue`, you can do the same operation, therefore you may convert them to each other, like this:
|
|
77
|
+
|
|
78
|
+
numo
|
|
79
|
+
.to_ndav
|
|
80
|
+
.to_torch_tensor
|
|
81
|
+
.then {|torch_tensor| some_process(torch_tensor)}
|
|
82
|
+
.to_ndav
|
|
83
|
+
.to_ort_value
|
|
84
|
+
.then {|ort_value|
|
|
85
|
+
OnnxRuntime::Session.new("model.onnx")
|
|
86
|
+
.run(
|
|
87
|
+
[:output],
|
|
88
|
+
{input: ort_value},
|
|
89
|
+
output_type: :ort_value
|
|
90
|
+
)[0]
|
|
91
|
+
}
|
|
92
|
+
.to_ndav
|
|
93
|
+
.to_torch_tensor
|
|
94
|
+
.then {|torch_tensor| TorchAudio.save(torch_tensor, sample_rate)}
|
|
95
|
+
|
|
96
|
+
### Working With MemoryView ###
|
|
97
|
+
|
|
98
|
+
NDAV can be initialized *directly* from libraries which export [MemoryView][] such as [Red Arrow][], without any bridge library:
|
|
99
|
+
|
|
100
|
+
arrow = Arrow::Int16Array.new([1, 2, 3])
|
|
101
|
+
ndav = NDAV.new(arrow)
|
|
102
|
+
|
|
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:
|
|
104
|
+
|
|
105
|
+
waveform, sample_rate = TorchAudio.load("path/to/audio.wav")
|
|
106
|
+
samples = waveform.to_ndav
|
|
107
|
+
whisper.full(params, samples)
|
|
108
|
+
|
|
109
|
+
### Notice On Memory Sharing ###
|
|
110
|
+
|
|
111
|
+
Notice that NDAV is just a memory view and libraries share a memory address. If you change source data destructively, it affects converted data.
|
|
112
|
+
|
|
113
|
+
Additionally, you potentially encounter odd data corruption or segmentation fault. These might be bugs in bridge libraries such as [ndav-numo-narray][]. As a user, you don't need to worry about this kind of memory management, but it's worth knowing such situations may occur.
|
|
114
|
+
|
|
115
|
+
INSTALLATION
|
|
116
|
+
------------
|
|
117
|
+
|
|
118
|
+
% gem install ndav
|
|
119
|
+
|
|
120
|
+
or,
|
|
121
|
+
|
|
122
|
+
% bundle add ndav
|
|
123
|
+
|
|
124
|
+
But, you need bridges for real-world use. See each bridge's documentation for individual requirements.
|
|
125
|
+
|
|
126
|
+
BRIDGES
|
|
127
|
+
-------
|
|
128
|
+
|
|
129
|
+
There are some bridges using NDAV:
|
|
130
|
+
|
|
131
|
+
* {file:ndav/ffi ndav/ffi}: [`FFI::MemoryPointer`][FFI Pointer], [`FFI::Pointer`][FFI Pointer] <-> `NDAV`
|
|
132
|
+
* [ndav-numo-narray][]: [`Numo::NArray`][Numo::NArray] <-> `NDAV`
|
|
133
|
+
* [ndav-ort_value][]: [`OnnxRuntime::OrtValue`][ONNX Runtime Ruby] <-> `NDAV`
|
|
134
|
+
* [ndav-torch-tensor][]: [`Torch::Tensor`][Torch.rb] <-> `NDAV`
|
|
135
|
+
* [onnxruntime-torch-tensor][]: [`Torch::Tensor`][Torch.rb] <-> [`OnnxRuntime::OrtValue`][ONNX Runtime Ruby] via `NDAV`
|
|
136
|
+
|
|
137
|
+
APPLICATIONS AND LIBRARIES WHICH USE NDAV
|
|
138
|
+
-----------------------------------------
|
|
139
|
+
|
|
140
|
+
* [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.
|
|
141
|
+
* [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.
|
|
142
|
+
|
|
143
|
+
CREATING BRIDGES
|
|
144
|
+
----------------
|
|
145
|
+
|
|
146
|
+
Refer to existing bridge implementations listed above to create your bridge.
|
|
147
|
+
|
|
148
|
+
The points are:
|
|
149
|
+
|
|
150
|
+
* Implement `FromNDAV#from_ndav`, `ToNDAV#to_ndav`, {NDAV.register register} them, and `NDAV.from_your_data` and `NDAV#to_your_data` are automatically derived
|
|
151
|
+
* 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
|
|
152
|
+
* 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
|
|
153
|
+
|
|
154
|
+
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.
|
|
155
|
+
|
|
156
|
+
If you are a library author, I want you to consider making your library work with [MemoryView][] instead of creating an NDAV bridge.
|
|
157
|
+
|
|
158
|
+
FUTURE
|
|
159
|
+
------
|
|
160
|
+
|
|
161
|
+
If [MemoryView][] gets popular enough in the Ruby ecosystem, this library will end its role and will no longer be needed. I hope such future.
|
|
5
162
|
|
|
6
163
|
LICENSE
|
|
7
164
|
-------
|
|
8
165
|
|
|
9
|
-
BSD-2-Clause license. See LICENSE.txt file.
|
|
166
|
+
BSD-2-Clause license. See {file:LICENSE.txt} file.
|
|
167
|
+
|
|
168
|
+
[MemoryView]: https://docs.ruby-lang.org/en/master/contributing/memory_view_md.html
|
|
169
|
+
[Numo::NArray]: https://ruby-numo.github.io/narray/
|
|
170
|
+
[Torch.rb]: https://github.com/ankane/torch.rb
|
|
171
|
+
[ONNX Runtime Ruby]: https://github.com/ankane/onnxruntime-ruby
|
|
172
|
+
[Red Arrow]: https://github.com/apache/arrow/tree/main/ruby
|
|
173
|
+
[Red Arrow Numo::NArray]: https://github.com/red-data-tools/red-arrow-numo-narray
|
|
174
|
+
[ndav/ffi]: https://gitlab.com/KitaitiMakoto/ndav/-/blob/main/lib/ndav/ffi.rb
|
|
175
|
+
[FFI Pointer]: https://github.com/ffi/ffi/wiki/Pointers
|
|
176
|
+
[whispercpp]: https://github.com/ggml-org/whisper.cpp/tree/master/bindings/ruby
|
|
177
|
+
[ndav-numo-narray]: https://gitlab.com/KitaitiMakoto/ndav-numo-narray
|
|
178
|
+
[ndav-ort_value]: https://gitlab.com/KitaitiMakoto/ndav-ort_value
|
|
179
|
+
[ndav-torch-tensor]: https://gitlab.com/KitaitiMakoto/ndav-torch-tensor
|
|
180
|
+
[onnxruntime-torch-tensor]: https://gitlab.com/KitaitiMakoto/onnxruntime-torch-tensor
|
|
181
|
+
[GTCRN]: https://gitlab.com/KitaitiMakoto/gtcrn
|
|
182
|
+
[TorchAudio]: https://github.com/ankane/torchaudio-ruby
|
|
183
|
+
[Itak]: https://gitlab.com/KitaitiMakoto/itak
|
data/Rakefile
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
require "rake/clean"
|
|
2
2
|
require "rake/testtask"
|
|
3
3
|
require "rubygems/tasks"
|
|
4
|
+
require "yard"
|
|
4
5
|
|
|
5
6
|
DL_NAME = "ndav".ext(RbConfig::CONFIG["DLEXT"])
|
|
6
7
|
DL_BUILD_PATH = File.join("ext", DL_NAME)
|
|
7
8
|
DL_PATH = File.join("lib", DL_NAME)
|
|
8
9
|
|
|
9
10
|
SRC = FileList["ext/**.{h,c,rb}"]
|
|
11
|
+
CLEAN.include SRC.select {|src| src.end_with?(".o", ".so", ".bundle", ".dll") }
|
|
10
12
|
|
|
11
13
|
task default: :test
|
|
12
14
|
|
|
13
15
|
Rake::TestTask.new test: DL_PATH
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
Gem::Tasks.new
|
|
17
|
+
YARD::Rake::YardocTask.new
|
|
16
18
|
|
|
17
19
|
file DL_PATH => DL_BUILD_PATH do |t|
|
|
18
20
|
copy t.source, t.name
|
data/ext/ndav.c
CHANGED
|
@@ -1,22 +1,34 @@
|
|
|
1
|
-
#include
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <ruby/memory_view.h>
|
|
3
|
+
|
|
4
|
+
static ID id_ndav_validated_descriptor;
|
|
5
|
+
static VALUE sym_data;
|
|
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;
|
|
20
32
|
}
|
|
21
33
|
|
|
22
34
|
static bool
|
|
@@ -25,24 +37,38 @@ ndav_get_memory_view(const VALUE ndav, rb_memory_view_t *view, int flags)
|
|
|
25
37
|
bool row_major_requested = (flags & RUBY_MEMORY_VIEW_ROW_MAJOR) == RUBY_MEMORY_VIEW_ROW_MAJOR;
|
|
26
38
|
bool column_major_requested = (flags & RUBY_MEMORY_VIEW_COLUMN_MAJOR) == RUBY_MEMORY_VIEW_COLUMN_MAJOR;
|
|
27
39
|
bool indirect_requested = (flags & RUBY_MEMORY_VIEW_INDIRECT) == RUBY_MEMORY_VIEW_INDIRECT;
|
|
28
|
-
|
|
40
|
+
VALUE desc = rb_funcall(ndav, id_ndav_validated_descriptor, 1, INT2NUM(flags));
|
|
41
|
+
if (!RB_TYPE_P(desc, T_HASH)) {
|
|
42
|
+
rb_warn("descriptor not Hash");
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
bool row_major_contiguous = rb_hash_aref(desc, sym_row_major_contiguous_p);
|
|
46
|
+
bool column_major_contiguous = rb_hash_aref(desc, sym_column_major_contiguous_p);
|
|
47
|
+
if (row_major_requested && column_major_requested) { // row-major OR column-major requested
|
|
48
|
+
if (!row_major_contiguous && !column_major_contiguous) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Currently, non-contiguous array not supported
|
|
53
|
+
else if (row_major_requested && !row_major_contiguous) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
// Currently, non-contiguous array not supported
|
|
57
|
+
else if (column_major_requested && !column_major_contiguous) {
|
|
29
58
|
return false;
|
|
30
59
|
}
|
|
31
60
|
bool writable_requested = (flags & RUBY_MEMORY_VIEW_WRITABLE) == RUBY_MEMORY_VIEW_WRITABLE;
|
|
32
61
|
// TODO: Handle flags
|
|
33
62
|
|
|
34
|
-
VALUE
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
view->readonly = true;
|
|
41
|
-
} else {
|
|
42
|
-
view->readonly = RTEST(readonly);
|
|
63
|
+
VALUE readonly_v = rb_hash_aref(desc, sym_readonly_p);
|
|
64
|
+
bool readonly = NIL_P(readonly_v) || RTEST(readonly_v);
|
|
65
|
+
if (writable_requested && readonly) {
|
|
66
|
+
rb_warn("not writable");
|
|
67
|
+
return false;
|
|
43
68
|
}
|
|
69
|
+
view->readonly = readonly;
|
|
44
70
|
view->obj = ndav;
|
|
45
|
-
VALUE val =
|
|
71
|
+
VALUE val = rb_hash_aref(desc, sym_data);
|
|
46
72
|
char *data = StringValuePtr(val);
|
|
47
73
|
view->data = data;
|
|
48
74
|
// TODO: Commonalize
|
|
@@ -50,59 +76,91 @@ ndav_get_memory_view(const VALUE ndav, rb_memory_view_t *view, int flags)
|
|
|
50
76
|
// * Use TypedData?
|
|
51
77
|
// * If so, calling attr readers at Ruby layer has performance disadvantage.
|
|
52
78
|
// * Freezing instance vars in #initialize and then embed them to TypedData may be a solution
|
|
53
|
-
VALUE item_size =
|
|
79
|
+
VALUE item_size = rb_hash_aref(desc, sym_item_size);
|
|
54
80
|
view->item_size = NUM2SSIZET(item_size);
|
|
55
|
-
VALUE byte_size =
|
|
81
|
+
VALUE byte_size = rb_hash_aref(desc, sym_byte_size);
|
|
56
82
|
view->byte_size = NUM2SSIZET(byte_size);
|
|
57
|
-
VALUE ndim =
|
|
83
|
+
VALUE ndim = rb_hash_aref(desc, sym_ndim);
|
|
58
84
|
view->ndim = NUM2SSIZET(ndim);
|
|
59
|
-
VALUE format =
|
|
85
|
+
VALUE format = rb_hash_aref(desc, sym_format);
|
|
60
86
|
view->format = StringValueCStr(format);
|
|
61
87
|
|
|
62
|
-
int state;
|
|
63
|
-
|
|
64
88
|
// TODO: Commonalize
|
|
65
|
-
VALUE shape =
|
|
89
|
+
VALUE shape = rb_hash_aref(desc, sym_shape);
|
|
66
90
|
if (!RB_TYPE_P(shape, T_ARRAY)) {
|
|
67
91
|
rb_warn("@shape is not an array");
|
|
68
92
|
return false;
|
|
69
93
|
}
|
|
70
|
-
ssize_t *view_shape =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
rb_protect(fill_size_array, (VALUE)&shape_args, &state);
|
|
77
|
-
if (state) {
|
|
78
|
-
xfree(view_shape);
|
|
79
|
-
rb_jump_tag(state);
|
|
94
|
+
ssize_t *view_shape = (ssize_t *)malloc(sizeof(ssize_t) * view->ndim);
|
|
95
|
+
if (!view_shape) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (!fill_size_array(shape, view_shape, view->ndim)) {
|
|
99
|
+
free((void *)view_shape);
|
|
80
100
|
return false;
|
|
81
101
|
}
|
|
82
102
|
view->shape = view_shape;
|
|
83
103
|
|
|
84
|
-
VALUE strides =
|
|
104
|
+
VALUE strides = rb_hash_aref(desc, sym_strides);
|
|
85
105
|
if (!RB_TYPE_P(strides, T_ARRAY)) {
|
|
86
106
|
rb_warn("@strides is not an array");
|
|
87
|
-
|
|
107
|
+
free((void *)view->shape);
|
|
88
108
|
return false;
|
|
89
109
|
}
|
|
90
|
-
ssize_t *view_strides =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
xfree((void *)view_shape);
|
|
99
|
-
xfree((void *)view_strides);
|
|
100
|
-
rb_jump_tag(state);
|
|
110
|
+
ssize_t *view_strides = (ssize_t *)malloc(sizeof(ssize_t) * view->ndim);
|
|
111
|
+
if (!view_strides) {
|
|
112
|
+
free((void *)view->shape);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
if (!fill_size_array(strides, view_strides, view->ndim)) {
|
|
116
|
+
free((void *)view->shape);
|
|
117
|
+
free((void *)view_strides);
|
|
101
118
|
return false;
|
|
102
119
|
}
|
|
103
120
|
view->strides = view_strides;
|
|
104
121
|
|
|
105
|
-
|
|
122
|
+
VALUE sub_offsets = rb_hash_aref(desc, sym_sub_offsets);
|
|
123
|
+
if (NIL_P(sub_offsets)) {
|
|
124
|
+
if (indirect_requested) {
|
|
125
|
+
rb_warn("indirect requested but sub_offsets is NULL");
|
|
126
|
+
free((void *)view->shape);
|
|
127
|
+
free((void *)view->strides);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
view->sub_offsets = NULL;
|
|
131
|
+
} else if (!RB_TYPE_P(sub_offsets, T_ARRAY)) {
|
|
132
|
+
rb_warn("sub_offsets is not an array");
|
|
133
|
+
free((void *)view->shape);
|
|
134
|
+
free((void *)view->strides);
|
|
135
|
+
return false;
|
|
136
|
+
} else {
|
|
137
|
+
ssize_t *view_sub_offsets = (ssize_t *)malloc(sizeof(ssize_t) * view->ndim);
|
|
138
|
+
if (!view_sub_offsets) {
|
|
139
|
+
free((void *)view->shape);
|
|
140
|
+
free((void *)view->strides);
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
if (!fill_size_array(sub_offsets, view_sub_offsets, view->ndim)) {
|
|
144
|
+
free((void *)view->shape);
|
|
145
|
+
free((void *)view->strides);
|
|
146
|
+
free((void *)view_sub_offsets);
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
view->sub_offsets = view_sub_offsets;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private_data_t *private_data = malloc(sizeof(private_data_t));
|
|
153
|
+
if (!private_data) {
|
|
154
|
+
free((void *)view->shape);
|
|
155
|
+
free((void *)view->strides);
|
|
156
|
+
free((void *)view->sub_offsets);
|
|
157
|
+
rb_warn("failed to alloc private_data");
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
private_data->descriptor = Qnil;
|
|
161
|
+
rb_gc_register_address(&private_data->descriptor);
|
|
162
|
+
private_data->descriptor = desc;
|
|
163
|
+
view->private_data = private_data;
|
|
106
164
|
|
|
107
165
|
return true;
|
|
108
166
|
}
|
|
@@ -111,13 +169,23 @@ static bool
|
|
|
111
169
|
ndav_release_memory_view(const VALUE ndav, rb_memory_view_t *view)
|
|
112
170
|
{
|
|
113
171
|
if (view->shape) {
|
|
114
|
-
|
|
172
|
+
free((void *)view->shape);
|
|
115
173
|
view->shape = NULL;
|
|
116
174
|
}
|
|
117
175
|
if (view->strides) {
|
|
118
|
-
|
|
176
|
+
free((void *)view->strides);
|
|
119
177
|
view->strides = NULL;
|
|
120
178
|
}
|
|
179
|
+
if (view->sub_offsets) {
|
|
180
|
+
free((void *)view->sub_offsets);
|
|
181
|
+
view->sub_offsets = NULL;
|
|
182
|
+
}
|
|
183
|
+
if (view->private_data) {
|
|
184
|
+
private_data_t *private_data = (private_data_t *)view->private_data;
|
|
185
|
+
rb_gc_unregister_address(&private_data->descriptor);
|
|
186
|
+
free(private_data);
|
|
187
|
+
view->private_data = NULL;
|
|
188
|
+
}
|
|
121
189
|
|
|
122
190
|
return true;
|
|
123
191
|
}
|
|
@@ -125,8 +193,8 @@ ndav_release_memory_view(const VALUE ndav, rb_memory_view_t *view)
|
|
|
125
193
|
static bool
|
|
126
194
|
ndav_memory_view_available_p(const VALUE obj)
|
|
127
195
|
{
|
|
128
|
-
VALUE
|
|
129
|
-
return
|
|
196
|
+
VALUE descriptor = rb_funcall(obj, id_ndav_validated_descriptor, 0);
|
|
197
|
+
return RTEST(descriptor);
|
|
130
198
|
}
|
|
131
199
|
|
|
132
200
|
const struct rb_memory_view_entry ndav_view_entry = {
|
|
@@ -135,11 +203,34 @@ const struct rb_memory_view_entry ndav_view_entry = {
|
|
|
135
203
|
ndav_memory_view_available_p
|
|
136
204
|
};
|
|
137
205
|
|
|
206
|
+
static VALUE
|
|
207
|
+
ndav_memory_viewable_s_register(VALUE mod, VALUE klass)
|
|
208
|
+
{
|
|
209
|
+
if (!rb_memory_view_register(klass, &ndav_view_entry)) {
|
|
210
|
+
// error message output in rb_memory_view_register()
|
|
211
|
+
rb_raise(rb_eArgError, "");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return Qtrue;
|
|
215
|
+
}
|
|
216
|
+
|
|
138
217
|
void
|
|
139
218
|
Init_ndav(void)
|
|
140
219
|
{
|
|
141
|
-
|
|
220
|
+
id_ndav_validated_descriptor = rb_intern("ndav_validated_descriptor");
|
|
221
|
+
sym_data = ID2SYM(rb_intern("data"));
|
|
222
|
+
sym_shape = ID2SYM(rb_intern("shape"));
|
|
223
|
+
sym_strides = ID2SYM(rb_intern("strides"));
|
|
224
|
+
sym_format = ID2SYM(rb_intern("format"));
|
|
225
|
+
sym_byte_size = ID2SYM(rb_intern("byte_size"));
|
|
226
|
+
sym_readonly_p = ID2SYM(rb_intern("readonly?"));
|
|
227
|
+
sym_sub_offsets = ID2SYM(rb_intern("sub_offsets"));
|
|
228
|
+
sym_ndim = ID2SYM(rb_intern("ndim"));
|
|
229
|
+
sym_item_size = ID2SYM(rb_intern("item_size"));
|
|
230
|
+
sym_row_major_contiguous_p = ID2SYM(rb_intern("row_major_contiguous?"));
|
|
231
|
+
sym_column_major_contiguous_p = ID2SYM(rb_intern("column_major_contiguous?"));
|
|
142
232
|
|
|
143
233
|
VALUE cNDAV = rb_define_class("NDAV", rb_cObject);
|
|
144
|
-
|
|
234
|
+
VALUE mMemoryViewable = rb_define_module_under(cNDAV, "MemoryViewable");
|
|
235
|
+
rb_define_singleton_method(mMemoryViewable, "register", ndav_memory_viewable_s_register, 1);
|
|
145
236
|
}
|
data/lib/ndav/converter.rb
CHANGED
|
@@ -4,6 +4,10 @@ class NDAV
|
|
|
4
4
|
alias from_fiddle_memory_view new
|
|
5
5
|
alias from_fiddle_pointer new
|
|
6
6
|
|
|
7
|
+
def from_string(str, *, **)
|
|
8
|
+
new(Fiddle::Pointer[str], *, **)
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
def register(cls, mdl, name:)
|
|
8
12
|
if mdl.const_defined?(:FromNDAV)
|
|
9
13
|
cls.extend mdl::FromNDAV
|
|
@@ -13,6 +17,19 @@ class NDAV
|
|
|
13
17
|
end
|
|
14
18
|
end
|
|
15
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
|
+
|
|
16
33
|
if mdl.const_defined?(:ToNDAV)
|
|
17
34
|
cls.include mdl::ToNDAV
|
|
18
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,47 @@
|
|
|
1
|
+
class NDAV
|
|
2
|
+
module MemoryViewable
|
|
3
|
+
class << self
|
|
4
|
+
def included(base)
|
|
5
|
+
::NDAV::MemoryViewable.register base
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# Overwrite this method or define each ndav_xxx method
|
|
12
|
+
def ndav_descriptor(simple: true, writable: false, format: false, multi_dimensional: false, strides: false, row_major: false, column_major: false, any_contiguous: false, indirect: false)
|
|
13
|
+
{
|
|
14
|
+
data: ndav_data,
|
|
15
|
+
shape: ndav_shape,
|
|
16
|
+
strides: ndav_strides,
|
|
17
|
+
format: ndav_format,
|
|
18
|
+
byte_size: ndav_byte_size,
|
|
19
|
+
readonly?: ndav_readonly?,
|
|
20
|
+
sub_offsets: ndav_sub_offsets
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ndav_validated_descriptor(flags = ::NDAV::Flags::FLAGS[:simple])
|
|
25
|
+
desc = ndav_descriptor(**NDAV::Flags.decode(flags)).to_h
|
|
26
|
+
unless desc.kind_of? Hash
|
|
27
|
+
warn "descriptor not Hash"
|
|
28
|
+
return false
|
|
29
|
+
end
|
|
30
|
+
[:data, :shape, :strides, :format, :byte_size, :readonly?].each do |key|
|
|
31
|
+
unless desc.key? key
|
|
32
|
+
warn ":#{key} not in descriptor"
|
|
33
|
+
return false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
desc.merge(
|
|
37
|
+
ndim: desc[:shape].length,
|
|
38
|
+
item_size: NDAV::ITEM_SIZES[desc[:format]],
|
|
39
|
+
row_major_contiguous?: ::NDAV.row_major_contiguous?(**desc),
|
|
40
|
+
column_major_contiguous?: ::NDAV.column_major_contiguous?(**desc)
|
|
41
|
+
)
|
|
42
|
+
rescue => err
|
|
43
|
+
warn err
|
|
44
|
+
return false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
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,10 +1,11 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = "ndav"
|
|
3
|
-
s.version = "0.0.
|
|
3
|
+
s.version = "0.0.5"
|
|
4
4
|
s.summary = "N-Dimensional Array View"
|
|
5
5
|
s.authors = ["Kitaiti Makoto"]
|
|
6
6
|
s.licenses = ["BSD-2-Clause"]
|
|
7
|
-
s.homepage = "https://gitlab.
|
|
7
|
+
s.homepage = "https://kitaitimakoto.gitlab.io/ndav"
|
|
8
|
+
s.metadata["source_code_uri"] = "https://gitlab.com/KitaitiMakoto/ndav"
|
|
8
9
|
|
|
9
10
|
s.files = Dir.chdir(__dir__) {`git ls-files -z`.split("\x0")}
|
|
10
11
|
s.extensions = ["ext/extconf.rb"]
|
|
@@ -20,4 +21,6 @@ Gem::Specification.new do |s|
|
|
|
20
21
|
s.add_development_dependency "rubygems-requirements-system"
|
|
21
22
|
s.add_development_dependency "red-arrow"
|
|
22
23
|
s.add_development_dependency "ffi"
|
|
24
|
+
s.add_development_dependency "yard"
|
|
25
|
+
s.add_development_dependency "commonmarker"
|
|
23
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
|
@@ -28,4 +28,19 @@ class TestNDAV < TestBase
|
|
|
28
28
|
assert_equal ptr, ndav.to_fiddle_pointer
|
|
29
29
|
assert_int_array ndav
|
|
30
30
|
end
|
|
31
|
+
|
|
32
|
+
def test_string
|
|
33
|
+
str = [1, 2, 3].pack("s*")
|
|
34
|
+
ndav = NDAV.from_string(str, format: "s")
|
|
35
|
+
|
|
36
|
+
assert_int_array ndav
|
|
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
|
|
31
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.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kitaiti Makoto
|
|
@@ -149,6 +149,34 @@ dependencies:
|
|
|
149
149
|
- - ">="
|
|
150
150
|
- !ruby/object:Gem::Version
|
|
151
151
|
version: '0'
|
|
152
|
+
- !ruby/object:Gem::Dependency
|
|
153
|
+
name: yard
|
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
|
155
|
+
requirements:
|
|
156
|
+
- - ">="
|
|
157
|
+
- !ruby/object:Gem::Version
|
|
158
|
+
version: '0'
|
|
159
|
+
type: :development
|
|
160
|
+
prerelease: false
|
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
162
|
+
requirements:
|
|
163
|
+
- - ">="
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
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'
|
|
152
180
|
executables: []
|
|
153
181
|
extensions:
|
|
154
182
|
- ext/extconf.rb
|
|
@@ -156,16 +184,18 @@ extra_rdoc_files: []
|
|
|
156
184
|
files:
|
|
157
185
|
- ".gitignore"
|
|
158
186
|
- ".gitlab-ci.yml"
|
|
187
|
+
- ".yardopts"
|
|
159
188
|
- Gemfile
|
|
160
189
|
- LICENSE.txt
|
|
161
190
|
- README.md
|
|
162
191
|
- Rakefile
|
|
163
192
|
- ext/extconf.rb
|
|
164
193
|
- ext/ndav.c
|
|
165
|
-
- ext/ndav.h
|
|
166
194
|
- lib/ndav.rb
|
|
167
195
|
- lib/ndav/converter.rb
|
|
168
196
|
- lib/ndav/ffi.rb
|
|
197
|
+
- lib/ndav/flags.rb
|
|
198
|
+
- lib/ndav/memory_viewable.rb
|
|
169
199
|
- ndav.gemspec
|
|
170
200
|
- test/helper.rb
|
|
171
201
|
- test/test_converter.rb
|
|
@@ -173,10 +203,12 @@ files:
|
|
|
173
203
|
- test/test_ffi.rb
|
|
174
204
|
- test/test_ndav.rb
|
|
175
205
|
- test/test_package.rb
|
|
176
|
-
|
|
206
|
+
- test/test_ractor.rb
|
|
207
|
+
homepage: https://kitaitimakoto.gitlab.io/ndav
|
|
177
208
|
licenses:
|
|
178
209
|
- BSD-2-Clause
|
|
179
|
-
metadata:
|
|
210
|
+
metadata:
|
|
211
|
+
source_code_uri: https://gitlab.com/KitaitiMakoto/ndav
|
|
180
212
|
rdoc_options: []
|
|
181
213
|
require_paths:
|
|
182
214
|
- lib
|
|
@@ -191,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
191
223
|
- !ruby/object:Gem::Version
|
|
192
224
|
version: '0'
|
|
193
225
|
requirements: []
|
|
194
|
-
rubygems_version: 4.0.
|
|
226
|
+
rubygems_version: 4.0.16
|
|
195
227
|
specification_version: 4
|
|
196
228
|
summary: N-Dimensional Array View
|
|
197
229
|
test_files: []
|
data/ext/ndav.h
DELETED