carray 3.0.2 → 3.0.3

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.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +66 -71
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +132 -22
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +543 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +47 -49
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. data/ext/carray_kernels_triop.c +0 -1392
@@ -0,0 +1,463 @@
1
+ /* CSV body reader for CAFrame.from_csv.
2
+
3
+ CArray.__csv_read_body_as_string_cells__(text, sep, quote, ncol, keep)
4
+ -> [ncol, cells, records]
5
+ reads every record of text into one flat Array of nrow * ncol cells, row
6
+ after row: a String for a field, UNDEF for a missing one (an unquoted
7
+ empty field, or a cell a short row never reached). keep, when not nil,
8
+ is the ascending Array of the indexes of the columns to read: a row then
9
+ has a cell for each of them only, and the fields of the others are passed
10
+ over without being copied out of the text. records counts the
11
+ records read, blank lines included, as the Ruby tokenizer numbers them.
12
+ With ncol 0 the column count is the first record's, as for a file with no
13
+ header; a later record that is longer is then one this reader declines.
14
+
15
+ CArray.__csv_read_body_as_const_string_columns__(text, sep, quote, ncol, keep)
16
+ -> [ncol, buffer, [pairs, ...], records]
17
+ reads the same records into one fixlen-16 (start, end) pair entity per
18
+ column read, the layout CAConstString.wrap takes, with missing cells masked.
19
+ The pairs index buffer, which is text itself when no field held a doubled
20
+ quote, and text followed by those fields' unescaped bytes otherwise, so
21
+ the cells are not copied out of the text.
22
+
23
+ Both follow the rules of the Ruby tokenizer in
24
+ lib/carray/frame/csv_parser.rb, which stays the reference: a quoted field
25
+ may hold the separator, doubled quotes and line breaks; a line that is
26
+ empty, or only spaces and tabs with no separator, is not a row unless the
27
+ file has one column. They answer nil, for the Ruby tokenizer to read the
28
+ text and say what is wrong, when the text is not UTF-8 or US-ASCII or has
29
+ an invalid byte, when a record is malformed or longer than ncol, and when
30
+ keep names no column or one past the last. */
31
+
32
+ #include "carray.h"
33
+ #include "carray_internal.h"
34
+ #include "ruby/encoding.h"
35
+
36
+ static int
37
+ ca_csv_at (const char *s, long n, long p, const char *t, long tlen)
38
+ {
39
+ return p + tlen <= n && memcmp(s + p, t, tlen) == 0;
40
+ }
41
+
42
+ /* The end of the line content that starts at p: the position of its "\n",
43
+ or n; *next is where the following line starts. A "\r" before the "\n",
44
+ or at the end of the text, is not content. */
45
+ static long
46
+ ca_csv_line_end (const char *s, long n, long p, long *next)
47
+ {
48
+ const char *nl = memchr(s + p, '\n', n - p);
49
+ long e = nl ? nl - s : n;
50
+ *next = nl ? e + 1 : n;
51
+ if ( e > p && s[e - 1] == '\r' ) {
52
+ e -= 1;
53
+ }
54
+ return e;
55
+ }
56
+
57
+ /* A growing list of (start, end) pairs. A pair at or past the length of
58
+ the text indexes the unescaped tail; a missing cell is (-1, -1). */
59
+ typedef struct {
60
+ int64_t *v;
61
+ long len, cap; /* in pairs */
62
+ } ca_csv_cell_ranges;
63
+
64
+ static void
65
+ ca_csv_push_cell_range (ca_csv_cell_ranges *a, int64_t start, int64_t end)
66
+ {
67
+ if ( a->len == a->cap ) {
68
+ a->cap = a->cap ? 2 * a->cap : 4096;
69
+ REALLOC_N(a->v, int64_t, 2 * a->cap);
70
+ }
71
+ a->v[2 * a->len] = start;
72
+ a->v[2 * a->len + 1] = end;
73
+ a->len++;
74
+ }
75
+
76
+ typedef struct {
77
+ ca_csv_cell_ranges cells; /* row after row */
78
+ ca_csv_cell_ranges pending; /* blank lines before the first record */
79
+ VALUE tail; /* unescaped quoted fields, or Qnil */
80
+ VALUE keep; /* the indexes of the columns to read, or Qnil */
81
+ char *kept; /* keep as a byte per column, 1 for one read;
82
+ NULL for every column, or before ncol is known */
83
+ long ncol;
84
+ long width; /* the cells of a row: those of the columns read */
85
+ long records;
86
+ } ca_csv_walk_t;
87
+
88
+ /* Set w->kept and w->width from w->keep for w->ncol columns. Returns 0 when
89
+ keep names no column or one past the last. */
90
+ static int
91
+ ca_csv_set_kept_columns (ca_csv_walk_t *w)
92
+ {
93
+ long i;
94
+ w->kept = ALLOC_N(char, w->ncol);
95
+ memset(w->kept, 0, w->ncol);
96
+ for (i = 0; i < RARRAY_LEN(w->keep); i++) {
97
+ long j = NUM2LONG(rb_ary_entry(w->keep, i));
98
+ if ( j < 0 || j >= w->ncol ) {
99
+ return 0;
100
+ }
101
+ if ( ! w->kept[j] ) {
102
+ w->kept[j] = 1;
103
+ w->width++;
104
+ }
105
+ }
106
+ return w->width > 0;
107
+ }
108
+
109
+ /* Read every record of s into w. Returns 0 when the text is one this
110
+ reader declines (see the header comment). The cell ranges it grows are
111
+ the caller's to free, whether it returns or raises. */
112
+ static int
113
+ ca_csv_walk_records (const char *s, long n, const char *sp, long slen,
114
+ const char *qp, long qlen, ca_csv_walk_t *w)
115
+ {
116
+ long ncol = w->ncol, p = 0;
117
+
118
+ if ( ! NIL_P(w->keep) && ncol > 0 && ! ca_csv_set_kept_columns(w) ) {
119
+ return 0;
120
+ }
121
+ while ( p < n ) {
122
+ long next, e = ca_csv_line_end(s, n, p, &next), k, fields = 0;
123
+ int blank = 1;
124
+
125
+ w->records += 1;
126
+
127
+ /* A blank line: empty, or only spaces and tabs with no separator. */
128
+ for (k = p; k < e; k++) {
129
+ if ( s[k] != ' ' && s[k] != '\t' ) {
130
+ blank = 0;
131
+ break;
132
+ }
133
+ }
134
+ if ( blank ) {
135
+ for (k = p; k + slen <= e; k++) {
136
+ if ( memcmp(s + k, sp, slen) == 0 ) {
137
+ blank = 0;
138
+ break;
139
+ }
140
+ }
141
+ }
142
+ if ( blank ) {
143
+ int64_t a = e > p ? p : -1, b = e > p ? e : -1;
144
+ if ( ncol == 1 ) {
145
+ if ( w->kept == NULL || w->kept[0] ) {
146
+ ca_csv_push_cell_range(&w->cells, a, b);
147
+ }
148
+ }
149
+ else if ( ncol == 0 ) {
150
+ /* Before the first record the column count is not known: keep the
151
+ line, a row if that record turns out to have one column. */
152
+ ca_csv_push_cell_range(&w->pending, a, b);
153
+ }
154
+ p = next;
155
+ continue;
156
+ }
157
+
158
+ for (;;) {
159
+ int64_t a = -1, b = -1;
160
+ /* The field of a column not read is passed over, its text not
161
+ unescaped. */
162
+ int read = w->kept == NULL || ( fields < ncol && w->kept[fields] );
163
+ if ( ca_csv_at(s, n, p, qp, qlen) ) {
164
+ /* A quoted field, which may run onto later lines. */
165
+ long q = p + qlen, start = q, tail0 = -1;
166
+ for (;;) {
167
+ const char *hit = NULL;
168
+ long r = q;
169
+ while ( r + qlen <= n ) {
170
+ const char *c = memchr(s + r, qp[0], n - r);
171
+ if ( c == NULL ) {
172
+ break;
173
+ }
174
+ r = c - s;
175
+ if ( ca_csv_at(s, n, r, qp, qlen) ) {
176
+ hit = c;
177
+ break;
178
+ }
179
+ r++;
180
+ }
181
+ if ( hit == NULL ) {
182
+ return 0; /* unterminated */
183
+ }
184
+ if ( ca_csv_at(s, n, r + qlen, qp, qlen) ) {
185
+ /* a doubled quote is a literal one */
186
+ if ( ! read ) {
187
+ q = r + 2 * qlen;
188
+ continue;
189
+ }
190
+ if ( NIL_P(w->tail) ) {
191
+ w->tail = rb_str_buf_new(256);
192
+ }
193
+ if ( tail0 < 0 ) {
194
+ tail0 = RSTRING_LEN(w->tail);
195
+ rb_str_cat(w->tail, s + start, r + qlen - start);
196
+ }
197
+ else {
198
+ rb_str_cat(w->tail, s + q, r + qlen - q);
199
+ }
200
+ q = r + 2 * qlen;
201
+ continue;
202
+ }
203
+ if ( tail0 < 0 ) {
204
+ a = start;
205
+ b = r;
206
+ }
207
+ else {
208
+ rb_str_cat(w->tail, s + q, r - q);
209
+ a = n + tail0;
210
+ b = n + RSTRING_LEN(w->tail);
211
+ }
212
+ p = r + qlen;
213
+ break;
214
+ }
215
+ e = ca_csv_line_end(s, n, p, &next);
216
+ if ( p != e && ! ca_csv_at(s, n, p, sp, slen) ) {
217
+ return 0; /* text after the closing quote */
218
+ }
219
+ }
220
+ else {
221
+ long r = p;
222
+ if ( slen == 1 && qlen == 1 ) {
223
+ const char sc = sp[0], qc = qp[0];
224
+ while ( r < e && s[r] != sc ) {
225
+ if ( s[r] == qc ) {
226
+ return 0; /* a quote in an unquoted field */
227
+ }
228
+ r++;
229
+ }
230
+ }
231
+ else {
232
+ while ( r < e && ! ca_csv_at(s, n, r, sp, slen) ) {
233
+ if ( ca_csv_at(s, n, r, qp, qlen) ) {
234
+ return 0; /* a quote in an unquoted field */
235
+ }
236
+ r++;
237
+ }
238
+ }
239
+ a = r > p ? p : -1;
240
+ b = r > p ? r : -1;
241
+ p = r;
242
+ }
243
+ if ( ++fields > ncol && ncol > 0 ) {
244
+ return 0; /* a row too long */
245
+ }
246
+ if ( read ) {
247
+ ca_csv_push_cell_range(&w->cells, a, b);
248
+ }
249
+ if ( p < e && ca_csv_at(s, n, p, sp, slen) ) {
250
+ p += slen;
251
+ continue;
252
+ }
253
+ break;
254
+ }
255
+ if ( ncol == 0 ) {
256
+ /* The first record sets the column count; the blank lines before it
257
+ are rows of a one-column file and nothing otherwise. */
258
+ ncol = fields;
259
+ if ( ! NIL_P(w->keep) ) {
260
+ /* Its fields were all read, the columns being unknown until now:
261
+ keep those of the columns read. */
262
+ long j, k = 0;
263
+ w->ncol = ncol;
264
+ if ( ! ca_csv_set_kept_columns(w) ) {
265
+ return 0;
266
+ }
267
+ for (j = 0; j < ncol; j++) {
268
+ if ( w->kept[j] ) {
269
+ w->cells.v[2 * k] = w->cells.v[2 * j];
270
+ w->cells.v[2 * k + 1] = w->cells.v[2 * j + 1];
271
+ k++;
272
+ }
273
+ }
274
+ w->cells.len = k;
275
+ }
276
+ if ( ncol == 1 && w->pending.len > 0 ) {
277
+ long i;
278
+ for (i = 0; i < w->cells.len; i++) {
279
+ ca_csv_push_cell_range(&w->pending, w->cells.v[2 * i], w->cells.v[2 * i + 1]);
280
+ }
281
+ { ca_csv_cell_ranges t = w->cells; w->cells = w->pending; w->pending = t; }
282
+ w->pending.len = 0;
283
+ }
284
+ }
285
+ for (; fields < ncol; fields++) {
286
+ if ( w->kept == NULL || w->kept[fields] ) {
287
+ ca_csv_push_cell_range(&w->cells, -1, -1);
288
+ }
289
+ }
290
+ p = next;
291
+ }
292
+ w->ncol = ncol;
293
+ if ( NIL_P(w->keep) ) {
294
+ w->width = ncol;
295
+ }
296
+ return ncol > 0; /* 0: no record */
297
+ }
298
+
299
+ /* The arguments both readers take, checked; 0 for text they decline. */
300
+ static int
301
+ ca_csv_check_reader_args (VALUE text, VALUE vsep, VALUE vquote, VALUE vncol,
302
+ VALUE keep, long *ncol)
303
+ {
304
+ rb_encoding *enc;
305
+ Check_Type(text, T_STRING);
306
+ Check_Type(vsep, T_STRING);
307
+ Check_Type(vquote, T_STRING);
308
+ if ( ! NIL_P(keep) ) {
309
+ Check_Type(keep, T_ARRAY);
310
+ }
311
+ *ncol = NUM2LONG(vncol);
312
+ enc = rb_enc_get(text);
313
+ if ( ( enc != rb_utf8_encoding() && enc != rb_usascii_encoding() )
314
+ || rb_enc_str_coderange(text) == ENC_CODERANGE_BROKEN || *ncol < 0 ) {
315
+ return 0;
316
+ }
317
+ return RSTRING_LEN(vsep) >= 1 && RSTRING_LEN(vquote) >= 1;
318
+ }
319
+
320
+ /* What a reader makes of the walked records: the Ruby value it answers. */
321
+ typedef VALUE (*ca_csv_build_result) (VALUE text, ca_csv_walk_t *w);
322
+
323
+ typedef struct {
324
+ VALUE text, vsep, vquote, vncol;
325
+ ca_csv_walk_t w;
326
+ ca_csv_build_result build;
327
+ } ca_csv_read_body_context;
328
+
329
+ static VALUE
330
+ ca_csv_read_body_walk_and_build (VALUE varg)
331
+ {
332
+ ca_csv_read_body_context *c = (ca_csv_read_body_context *) varg;
333
+ if ( ! ca_csv_check_reader_args(c->text, c->vsep, c->vquote, c->vncol, c->w.keep,
334
+ &c->w.ncol) ) {
335
+ return Qnil;
336
+ }
337
+ if ( ! ca_csv_walk_records(RSTRING_PTR(c->text), RSTRING_LEN(c->text),
338
+ RSTRING_PTR(c->vsep), RSTRING_LEN(c->vsep),
339
+ RSTRING_PTR(c->vquote), RSTRING_LEN(c->vquote), &c->w) ) {
340
+ return Qnil;
341
+ }
342
+ return c->build(c->text, &c->w);
343
+ }
344
+
345
+ static VALUE
346
+ ca_csv_read_body_free_cell_ranges (VALUE varg)
347
+ {
348
+ ca_csv_read_body_context *c = (ca_csv_read_body_context *) varg;
349
+ xfree(c->w.cells.v);
350
+ xfree(c->w.pending.v);
351
+ xfree(c->w.kept);
352
+ return Qnil;
353
+ }
354
+
355
+ /* Walk the records of text and build the answer from them, with the cell
356
+ ranges freed however that ends: the walk and the build both allocate,
357
+ and so may raise. */
358
+ static VALUE
359
+ ca_csv_read_body (VALUE text, VALUE vsep, VALUE vquote, VALUE vncol, VALUE keep,
360
+ ca_csv_build_result build)
361
+ {
362
+ ca_csv_read_body_context c = {
363
+ text, vsep, vquote, vncol,
364
+ { { NULL, 0, 0 }, { NULL, 0, 0 }, Qnil, keep, NULL, 0, 0, 0 }, build
365
+ };
366
+ VALUE out = rb_ensure(ca_csv_read_body_walk_and_build, (VALUE) &c,
367
+ ca_csv_read_body_free_cell_ranges, (VALUE) &c);
368
+ RB_GC_GUARD(c.w.tail);
369
+ return out;
370
+ }
371
+
372
+ static VALUE
373
+ ca_csv_build_string_cells (VALUE text, ca_csv_walk_t *w)
374
+ {
375
+ volatile VALUE out;
376
+ rb_encoding *enc = rb_enc_get(text);
377
+ const char *s = RSTRING_PTR(text);
378
+ long n = RSTRING_LEN(text), i;
379
+
380
+ out = rb_ary_new_capa(w->cells.len);
381
+ for (i = 0; i < w->cells.len; i++) {
382
+ int64_t a = w->cells.v[2 * i], b = w->cells.v[2 * i + 1];
383
+ VALUE cell;
384
+ if ( a < 0 ) {
385
+ cell = CA_UNDEF;
386
+ }
387
+ else if ( a < n ) {
388
+ cell = rb_enc_str_new(s + a, b - a, enc);
389
+ }
390
+ else {
391
+ cell = rb_enc_str_new(RSTRING_PTR(w->tail) + (a - n), b - a, enc);
392
+ }
393
+ rb_ary_push(out, cell);
394
+ }
395
+ return rb_ary_new3(3, LONG2NUM(w->ncol), out, LONG2NUM(w->records));
396
+ }
397
+
398
+ static VALUE
399
+ ca_csv_build_const_string_columns (VALUE text, ca_csv_walk_t *w)
400
+ {
401
+ volatile VALUE buffer, cols;
402
+ ca_size_t dim[1];
403
+ long nrow, i, j;
404
+
405
+ if ( NIL_P(w->tail) ) {
406
+ buffer = text;
407
+ }
408
+ else {
409
+ buffer = rb_str_buf_new(RSTRING_LEN(text) + RSTRING_LEN(w->tail));
410
+ rb_str_cat(buffer, RSTRING_PTR(text), RSTRING_LEN(text));
411
+ rb_str_cat(buffer, RSTRING_PTR(w->tail), RSTRING_LEN(w->tail));
412
+ rb_enc_copy(buffer, text);
413
+ }
414
+
415
+ nrow = w->cells.len / w->width;
416
+ dim[0] = (ca_size_t) nrow;
417
+ cols = rb_ary_new_capa(w->width);
418
+ for (j = 0; j < w->width; j++) {
419
+ CArray *pe;
420
+ int64_t *range;
421
+ boolean8_t *m = NULL;
422
+ VALUE col = rb_carray_new(CA_FIXLEN, 1, dim, 2 * sizeof(int64_t), NULL);
423
+ rb_ary_push(cols, col);
424
+ TypedData_Get_Struct(col, CArray, &carray_data_type, pe);
425
+ range = (int64_t *) pe->ptr;
426
+ for (i = 0; i < nrow; i++) {
427
+ int64_t a = w->cells.v[2 * (i * w->width + j)];
428
+ int64_t b = w->cells.v[2 * (i * w->width + j) + 1];
429
+ if ( a < 0 ) {
430
+ if ( m == NULL ) {
431
+ ca_create_mask(pe);
432
+ m = (boolean8_t *) pe->mask->ptr;
433
+ }
434
+ m[i] = 1;
435
+ a = b = 0;
436
+ }
437
+ range[2 * i] = a;
438
+ range[2 * i + 1] = b;
439
+ }
440
+ }
441
+ return rb_ary_new3(4, LONG2NUM(w->ncol), buffer, cols, LONG2NUM(w->records));
442
+ }
443
+
444
+ static VALUE
445
+ rb_ca_s_csv_read_body_as_string_cells (VALUE klass, VALUE text, VALUE vsep, VALUE vquote,
446
+ VALUE vncol, VALUE keep)
447
+ {
448
+ return ca_csv_read_body(text, vsep, vquote, vncol, keep, ca_csv_build_string_cells);
449
+ }
450
+
451
+ static VALUE
452
+ rb_ca_s_csv_read_body_as_const_string_columns (VALUE klass, VALUE text, VALUE vsep, VALUE vquote,
453
+ VALUE vncol, VALUE keep)
454
+ {
455
+ return ca_csv_read_body(text, vsep, vquote, vncol, keep, ca_csv_build_const_string_columns);
456
+ }
457
+
458
+ void
459
+ Init_caframe_csv_reader (void)
460
+ {
461
+ rb_define_singleton_method(rb_cCArray, "__csv_read_body_as_string_cells__", rb_ca_s_csv_read_body_as_string_cells, 5);
462
+ rb_define_singleton_method(rb_cCArray, "__csv_read_body_as_const_string_columns__", rb_ca_s_csv_read_body_as_const_string_columns, 5);
463
+ }
data/ext/carray.h CHANGED
@@ -716,6 +716,12 @@ typedef struct {
716
716
  0 = outer-axis stack (legacy default).
717
717
  parent_ndim = K innermost (= old merge at=-1).
718
718
  Set by ca_stack_setup_with_axis / preserved on clone. */
719
+ CArray **mask_standins; /* NULL, or ALLOC_N(CArray*, n_parents) once the mask is
720
+ built: the all-false masks standing in for parents that
721
+ have none (owned, freed with the view) */
722
+ CArray *mask_owner; /* on the mask of a CAStack: that CAStack, whose stand-ins
723
+ this mask reads until they give way to the parents'
724
+ own masks (ca_multi_parent_mask_refresh); else NULL */
719
725
  } CAStack;
720
726
 
721
727
  /* CAMeld (ragged concatenate view along an existing axis). Unlike CAStack
@@ -749,6 +755,8 @@ typedef struct {
749
755
  ca_size_t *seg_offset; /* ALLOC_N(ca_size_t, K+1); prefix sum along meld_axis.
750
756
  seg_offset[0]=0, seg_offset[k+1]=seg_offset[k]+parents[k]->dim[meld_axis],
751
757
  seg_offset[K]=dim[meld_axis]. */
758
+ CArray **mask_standins; /* as CAStack's: stand-in masks this view owns, or NULL */
759
+ CArray *mask_owner; /* as CAStack's */
752
760
  } CAMeld;
753
761
 
754
762
  /* Multi-parent view layout convention (CA_FLAG_MULTI_PARENTS). A view that
@@ -786,9 +794,14 @@ typedef struct {
786
794
  VALUE self;
787
795
  } CAObject; /* 48 + 4*(ndim) (bytes) */
788
796
 
789
- /*
790
- CAObjectMask is an internal class
791
- used only as mask array of CAObject.
797
+ /*
798
+ CAObjectMask is an internal class used only as the mask array of a
799
+ CAObject. Its bits live in the CAObject's mask hooks (mask_copy_data,
800
+ mask_fetch_addr, ...) when it has them, and in `cache` -- the mask
801
+ buffer of the CAObject's internal data array -- always. Like the mask
802
+ of any view it is cold at rest: ptr is NULL, and every read and write
803
+ goes through its slots to the hooks. An attach publishes the cache in
804
+ ptr, and `holds` counts the attaches not yet detached.
792
805
  */
793
806
 
794
807
  typedef struct {
@@ -807,6 +820,8 @@ typedef struct {
807
820
  ext/ca_array_pool.c. */
808
821
  /* -------------*/
809
822
  VALUE array;
823
+ char *cache;
824
+ long holds;
810
825
  } CAObjectMask;
811
826
 
812
827
  /* CARepeat (Phase R migration): structurally identical to CAStride.
@@ -862,7 +877,7 @@ int8_t ca_promote_type (int8_t a, int8_t b);
862
877
  /* Infer a data_type code from a Ruby *value* (literal Numeric, true/false,
863
878
  Complex, etc.) — distinct from rb_ca_guess_type which interprets the
864
879
  object as a data_type *representation* (e.g. T_FIXNUM as a data_type code).
865
- Used by then_else and by ca_lazy_wrap_scalar's bool-self corner case.
880
+ Used by rb_ca_cast_self_or_other.
866
881
  CArray is not accepted (callers should use ca->data_type directly). */
867
882
  int8_t ca_value_to_data_type (VALUE obj);
868
883
 
@@ -985,21 +1000,21 @@ extern VALUE rb_cCArrayObject;
985
1000
  #define CA_CHECK_DATA_TYPE(data_type) \
986
1001
  do { \
987
1002
  if ( data_type <= CA_NONE || data_type >= CA_NTYPE ) { \
988
- rb_raise(rb_eRuntimeError, "invalid data_type id %i", data_type); \
1003
+ rb_raise(rb_eArgError, "invalid data_type id %i", data_type); \
989
1004
  } \
990
1005
  if ( ! ca_valid[data_type] ) { \
991
- rb_raise(rb_eRuntimeError, "data_type %s is disabled", ca_type_name[data_type]); \
1006
+ rb_raise(rb_eCADataTypeError, "data_type %s is disabled", ca_type_name[data_type]); \
992
1007
  } \
993
1008
  } while (0)
994
1009
 
995
1010
  #define CA_CHECK_DATA_TYPE_NUMERIC(data_type) \
996
1011
  if ( data_type <= CA_NONE || data_type >= CA_NTYPE || !ca_valid[data_type] || data_type == CA_FIXLEN || data_type == CA_OBJECT ) { \
997
- rb_raise(rb_eRuntimeError, "invalid numeric data type"); \
1012
+ rb_raise(rb_eCADataTypeError, "invalid numeric data type"); \
998
1013
  }
999
1014
 
1000
1015
  #define CA_CHECK_RANK(ndim) \
1001
1016
  if ( ndim <= 0 || ndim > CA_RANK_MAX ) { \
1002
- rb_raise(rb_eRuntimeError, "invalid ndim"); \
1017
+ rb_raise(rb_eArgError, "invalid ndim"); \
1003
1018
  }
1004
1019
 
1005
1020
  #define CA_CHECK_DIM(ndim, dim) \
@@ -1007,44 +1022,46 @@ extern VALUE rb_cCArrayObject;
1007
1022
  int8_t i_; \
1008
1023
  for (i_=0; i_<ndim; i_++) { \
1009
1024
  if ( dim[i_] < 0 ) { \
1010
- rb_raise(rb_eRuntimeError, "negative size dimension at %i-dim", i_); \
1025
+ rb_raise(rb_eArgError, "negative size dimension at %i-dim", i_); \
1011
1026
  } \
1012
1027
  } \
1013
1028
  }
1014
1029
 
1030
+ /* A fixlen cell is at least one byte wide. A zero-width cell has one
1031
+ possible value, and a strided view of it cannot say which cell is which:
1032
+ its byte strides are all zero. */
1015
1033
  #define CA_CHECK_BYTES(data_type, bytes) \
1016
1034
  if ( data_type == CA_FIXLEN ) { \
1017
- if ( bytes < 0 ) { \
1018
- rb_raise(rb_eRuntimeError, "invalid bytes"); \
1035
+ if ( bytes <= 0 ) { \
1036
+ rb_raise(rb_eArgError, \
1037
+ "invalid bytes (a fixlen needs bytes: of 1 or more)"); \
1019
1038
  } \
1020
1039
  } \
1021
1040
  else { \
1022
1041
  bytes = ca_sizeof[data_type]; \
1023
1042
  if ( bytes <= 0 ) { \
1024
- rb_raise(rb_eRuntimeError, "invalid bytes"); \
1043
+ rb_raise(rb_eArgError, "invalid bytes"); \
1025
1044
  } \
1026
1045
  }
1027
1046
 
1047
+ /* An index into `n` places, counted from the end when negative, outside of
1048
+ which IndexError is raised naming the method the user called, `what` the
1049
+ index is ("index", "address") and the value given. */
1050
+ NORETURN(void ca_index_out_of_range (ca_size_t idx, ca_size_t n, const char *what));
1051
+
1052
+ /* Normalise `index` in place against `dim` (a negative one counts from the
1053
+ end) and raise through ca_index_out_of_range when it falls outside. */
1028
1054
  #define CA_CHECK_INDEX(index, dim) \
1029
1055
  do { \
1030
- if ( index < 0 ) { \
1031
- index += (dim); \
1056
+ ca_size_t ca_check_index_given_ = (index); \
1057
+ if ( (index) < 0 ) { \
1058
+ (index) += (dim); \
1032
1059
  } \
1033
- if ( index < 0 || index >= (dim) ) { \
1034
- rb_raise(rb_eIndexError, "index out of range ( %" PRId64 " <=> 0..%" PRId64 " )", (int64_t) index, (int64_t) dim-1); \
1060
+ if ( (index) < 0 || (index) >= (dim) ) { \
1061
+ ca_index_out_of_range(ca_check_index_given_, (dim), "index"); \
1035
1062
  } \
1036
1063
  } while (0)
1037
1064
 
1038
- #define CA_CHECK_BOUND(ca, idx) \
1039
- { \
1040
- int8_t i; \
1041
- for (i=0; i<ca->ndim; i++) { \
1042
- if ( idx[i] < 0 || idx[i] >= ca->dim[i] ) { \
1043
- rb_raise(rb_eRuntimeError, "index out of range at %i-dim ( %i <=> 0..%i )", i, idx[i], ca->dim[i]-1); \
1044
- } \
1045
- } \
1046
- }
1047
-
1048
1065
  /* -------------------------------------------------------------------- */
1049
1066
 
1050
1067
  /* complex <-> Ruby Complex conversion
@@ -1070,6 +1087,10 @@ extern VALUE rb_cCArrayObject;
1070
1087
  # endif
1071
1088
  #endif
1072
1089
 
1090
+ /* A string as a complex cell: a real number read as Float() reads it
1091
+ (nan / inf included), else as Complex() does; anything else raises. */
1092
+ double complex ca_str_to_cmplx (VALUE str);
1093
+
1073
1094
  static inline double complex
1074
1095
  rb_carray_num2cmplx (VALUE num)
1075
1096
  {
@@ -1079,6 +1100,12 @@ rb_carray_num2cmplx (VALUE num)
1079
1100
  if ( RB_FLOAT_TYPE_P(num) || RB_INTEGER_TYPE_P(num) ) {
1080
1101
  return (double complex) NUM2DBL(num);
1081
1102
  }
1103
+ if ( RB_TYPE_P(num, T_STRING) ) {
1104
+ return ca_str_to_cmplx(num);
1105
+ }
1106
+ if ( NIL_P(num) ) {
1107
+ rb_raise(rb_eTypeError, "can't convert nil into Complex");
1108
+ }
1082
1109
  if ( rb_respond_to(num, rb_intern("to_c")) ) {
1083
1110
  VALUE c = rb_funcall(num, rb_intern("to_c"), 0);
1084
1111
  return CMPLX(NUM2DBL(rb_complex_real(c)), NUM2DBL(rb_complex_imag(c)));
@@ -1125,6 +1152,7 @@ double OBJ2DBL (VALUE v);
1125
1152
  object->int/float cast loop so an unparseable cell becomes UNDEF instead
1126
1153
  of a silent 0.0 (float) or a raise (int). See ext/carray_cast.c. */
1127
1154
  int ca_obj2dbl_ok (VALUE v, double *out);
1155
+ int ca_obj2cmplx_ok (VALUE v, double complex *out);
1128
1156
  int rb_obj2long_ok (VALUE v, long *out);
1129
1157
  int rb_obj2ulong_ok (VALUE v, unsigned long *out);
1130
1158
  int rb_obj2ll_ok (VALUE v, long long *out);
@@ -1293,6 +1321,10 @@ VALUE rb_ca_farray (VALUE self);
1293
1321
 
1294
1322
  /* --- ca_obj_stride.c --- */
1295
1323
 
1324
+ /* ca_stride_setup with ca == NULL runs its checks and returns 0 without
1325
+ writing. ca_stride_new calls it that way before it allocates, so a
1326
+ rejected argument leaves no struct behind; a subclass constructor can
1327
+ do the same through its own setup function. */
1296
1328
  int ca_stride_setup (CAStride *ca, int8_t obj_type, CArray *parent,
1297
1329
  int8_t data_type, ca_size_t bytes,
1298
1330
  int8_t ndim, ca_size_t *dim,
@@ -1459,6 +1491,7 @@ int ca_is_object_type (void *ap);
1459
1491
 
1460
1492
  void ca_check_type (void *ap, int8_t data_type);
1461
1493
  #define ca_check_data_type(ap, data_type) ca_check_type(ap, data_type)
1494
+ void ca_check_uninitialized (void *ap); /* initialize / initialize_copy entry */
1462
1495
  void ca_check_ndim (void *ap, int ndim);
1463
1496
  void ca_check_shape (void *ap, int ndim, ca_size_t *dim);
1464
1497
  void ca_check_same_data_type (void *ap1, void *ap2);
@@ -1474,7 +1507,7 @@ int ca_is_valid_index (void *ap, ca_size_t *idx);
1474
1507
 
1475
1508
  /* API : allocate, attach, update, sync, detach */
1476
1509
 
1477
- /* Attach lifecycle contract (R1-R5). Ownership-based, and NOT transitive.
1510
+ /* Attach lifecycle contract (R1-R6). Ownership-based, and NOT transitive.
1478
1511
  This block is the canonical statement of the contract; the other documents
1479
1512
  copy it. guides/devel/04_attach_lifecycle.md discusses it at length.
1480
1513
 
@@ -1493,6 +1526,14 @@ int ca_is_valid_index (void *ap, ca_size_t *idx);
1493
1526
  R5 (traffic) Attach hands you a buffer, not a live array. Use x->ptr;
1494
1527
  a view derived from x composes past the buffer to the
1495
1528
  root.
1529
+ R6 (raising) ca_allocate and ca_attach that raise have taken
1530
+ nothing; ca_allocate_n and ca_attach_n take all of
1531
+ their arrays or none. Close a window with
1532
+ ca_sync_detach, which detaches even when the sync
1533
+ raises -- ca_sync then ca_detach leaves the window open
1534
+ on a raise. Anything between that can raise (Ruby
1535
+ called from inside the window) needs an ensure of its
1536
+ own.
1496
1537
  */
1497
1538
 
1498
1539
  void ca_allocate (void *ap);
@@ -1500,6 +1541,7 @@ void ca_attach (void *ca);
1500
1541
  void ca_update (void *ca);
1501
1542
  void ca_sync (void *ca);
1502
1543
  void ca_detach (void *ca);
1544
+ void ca_sync_detach (void *ca);
1503
1545
 
1504
1546
  void ca_allocate_n (int n, ...);
1505
1547
  void ca_attach_n (int n, ...);
@@ -1672,6 +1714,7 @@ void ca_parse_range_without_check (VALUE arg, ca_size_t size,
1672
1714
  ca_size_t *offset, ca_size_t *count, ca_size_t *step);
1673
1715
 
1674
1716
  int ca_equal (void *ap, void *bp);
1717
+ int ca_eql_storage (void *ap, void *bp);
1675
1718
  void ca_zerodiv(void) __attribute__((noreturn));
1676
1719
  /* ca_rand removed in 3.0; use rb_random_real/rb_random_ulong_limited instead */
1677
1720
  ca_size_t ca_bounds_normalize_index (int8_t bounds, ca_size_t size0, ca_size_t k);