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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0194ddf5f19c3e973cb1ef947c125b598295db76eeca5998776612a592c40067'
4
- data.tar.gz: 59f077dfdf2cdb48c675cde2e9addd7094a01417210fe28f8d786b93213ff8f8
3
+ metadata.gz: ddc0e563bff86b51222b18b00f243b94b0f2e5a69dad3e7694c80a349775bf30
4
+ data.tar.gz: 0afbc73643e49372d2b5104358acdc7460b26d7e6cc8892d45a67e4530cbe3d9
5
5
  SHA512:
6
- metadata.gz: e0ff36126d7192ace3dba90a706ec0f45032c590dd5085c0ca1e4166c6a6708b518ad7077ef9af54a6def14911e3a62c3bbda0e0d904cea0dd51ab46a9fb25dd
7
- data.tar.gz: 97026defa9b86987290f46a5f04ae0133ca280a87bea04989954fb49055ccc230a5f33d5ac90a8d68c046a05dcfaa97c46c7d6f93fda90f4234f7bf074ae1293
6
+ metadata.gz: f3a264a0204009e7f6b7505cae42667abe159e7ce7cbc5a27ffa88578c5691138b1b938795e14083b6f4cc9605dbc53b70a3990df495443ec9f5a8ebd664d5b5
7
+ data.tar.gz: bafa151218a7e1acb22acd915fe08acb5147228980a2f441cd928ba66ae4425fb4fcd7bf9509e8ac28f5113e555ee9927b9a71b3d13ca2064134f144a524958c
data/.yardopts CHANGED
@@ -1,4 +1,4 @@
1
- --title="NDAV - N-Dimensional Array View" -M commonmarker
1
+ --title="NDAV - N-Dimensional Array View"
2
2
  -
3
3
  LICENSE.txt
4
4
  lib/ndav/ffi.rb
data/README.md CHANGED
@@ -9,6 +9,10 @@ It provides an interoperability layer for multi-dimensional arrays which can be
9
9
 
10
10
  ![NDAV converts library data each other](https://i.gyazo.com/905f541f15c0d57db5fc7ea3f6edaf07.png)
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 VALUE sym_row_major_contiguous_p;
15
- static VALUE sym_column_major_contiguous_p;
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
- sym_row_major_contiguous_p = ID2SYM(rb_intern("row_major_contiguous?"));
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
- if name == :simple
19
- state[name] = flags == 0
20
- else
21
- state[name] = flags & value == value
22
- end
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
@@ -0,0 +1,9 @@
1
+ class NDAV
2
+ module Import
3
+ require "fiddle/import"
4
+
5
+ extend Fiddle::Importer
6
+ dlload Fiddle::Handle::DEFAULT
7
+ extern "ssize_t rb_memory_view_item_size_from_format(const char *format, const char **err)"
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ class NDAV
2
+ ItemComponent = Data.define(
3
+ :format,
4
+ :native_size_p,
5
+ :little_endian_p,
6
+ :offset,
7
+ :size,
8
+ :repeat
9
+ ) {
10
+ def native_size? = native_size_p
11
+ def little_endian? = little_endian_p
12
+ }
13
+ end
@@ -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: 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
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
- ::NDAV::MemoryViewable.register base
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
- desc = ndav_descriptor(**NDAV::Flags.decode(flags)).to_h
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
- unless desc.key? key
59
- warn ":#{key} not in descriptor"
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
- item_size = NDAV::ITEM_SIZES[desc[:format]]
64
- unless desc[:shape].reduce(item_size, :*) == desc[:byte_size]
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(desc[:data]).to_i,
70
- ndim: desc[:shape].length,
108
+ addr: ::Fiddle::Pointer.to_ptr(data).to_i,
109
+ ndim: shape.length,
71
110
  item_size:,
72
- row_major_contiguous?: ::NDAV.row_major_contiguous?(**desc),
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
- # TODO: Want to use return value of rb_memory_view_parse_item_format
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|
13
- [format, [0].pack(format).bytesize]
14
- }.to_h
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
- # TODO: More options to trick for the case of MemoryView export is wrong but developer don't fix it
63
- def initialize(obj, shape: nil, strides: nil, format: nil, lifetime: nil)
64
- fmv = obj.kind_of?(::Fiddle::MemoryView) ? obj : ::Fiddle::MemoryView.new(obj)
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
- @format = format || fmv.format
71
- raise ArgumentError, %Q|unsupported format: #{@format.to_s.dump}, currently supported: #{ITEM_SIZES.keys}| unless ITEM_SIZES.key?(@format)
72
-
73
- item_size_by_format = ITEM_SIZES[@format]
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
- @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
78
- case [@shape, @byte_size]
134
+ case [shape, @byte_size]
79
135
  in [nil, nil]
80
- raise ArgumentError, "either shape or byte_size must be present in obj or arguments"
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
- n, r = @byte_size.divmod(@item_size)
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
- # noop
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 fmv
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(fmv)
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 = @shape.reduce(@item_size, :*)
138
- unless byte_size == @byte_size
139
- raise ArgumentError, "strides not match byte_size"
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.6"
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
@@ -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.6
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