carray 3.0.2 → 3.0.4

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
@@ -0,0 +1,542 @@
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 coderange of UTF-8 text, as rb_enc_str_coderange answers it: 7BIT,
43
+ VALID, or BROKEN for an invalid sequence (a stray continuation byte, an
44
+ overlong form, a surrogate, past U+10FFFF, or a sequence cut short).
45
+ Runs of ASCII are passed over 8 bytes at a time, which Ruby's scan does
46
+ not do once it has met a multibyte character. */
47
+ static int
48
+ ca_csv_utf8_coderange (const unsigned char *s, long n)
49
+ {
50
+ long i = 0;
51
+ int ascii = 1;
52
+ while ( i < n ) {
53
+ unsigned c, c1;
54
+ while ( i + 8 <= n ) {
55
+ uint64_t w;
56
+ memcpy(&w, s + i, 8);
57
+ if ( w & 0x8080808080808080ULL ) {
58
+ break;
59
+ }
60
+ i += 8;
61
+ }
62
+ if ( i >= n ) {
63
+ break;
64
+ }
65
+ c = s[i];
66
+ if ( c < 0x80 ) {
67
+ i += 1;
68
+ continue;
69
+ }
70
+ ascii = 0;
71
+ if ( c < 0xC2 || c > 0xF4 ) {
72
+ return ENC_CODERANGE_BROKEN;
73
+ }
74
+ if ( c < 0xE0 ) {
75
+ if ( i + 1 >= n || ( s[i + 1] & 0xC0 ) != 0x80 ) {
76
+ return ENC_CODERANGE_BROKEN;
77
+ }
78
+ i += 2;
79
+ continue;
80
+ }
81
+ if ( i + 1 >= n ) {
82
+ return ENC_CODERANGE_BROKEN;
83
+ }
84
+ c1 = s[i + 1];
85
+ if ( ( c1 & 0xC0 ) != 0x80
86
+ || ( c == 0xE0 && c1 < 0xA0 ) || ( c == 0xED && c1 > 0x9F )
87
+ || ( c == 0xF0 && c1 < 0x90 ) || ( c == 0xF4 && c1 > 0x8F ) ) {
88
+ return ENC_CODERANGE_BROKEN;
89
+ }
90
+ if ( c < 0xF0 ) {
91
+ if ( i + 2 >= n || ( s[i + 2] & 0xC0 ) != 0x80 ) {
92
+ return ENC_CODERANGE_BROKEN;
93
+ }
94
+ i += 3;
95
+ continue;
96
+ }
97
+ if ( i + 3 >= n || ( s[i + 2] & 0xC0 ) != 0x80 || ( s[i + 3] & 0xC0 ) != 0x80 ) {
98
+ return ENC_CODERANGE_BROKEN;
99
+ }
100
+ i += 4;
101
+ }
102
+ return ascii ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID;
103
+ }
104
+
105
+ /* The coderange of the text, computed here for UTF-8 and cached on the
106
+ String as Ruby would cache it, so the Ruby reader's later String#count on
107
+ the same chunk does not scan it again. */
108
+ static int
109
+ ca_csv_text_coderange (VALUE text, rb_encoding *enc)
110
+ {
111
+ int cr = ENC_CODERANGE(text);
112
+ if ( cr != ENC_CODERANGE_UNKNOWN || enc != rb_utf8_encoding() ) {
113
+ return rb_enc_str_coderange(text);
114
+ }
115
+ cr = ca_csv_utf8_coderange((const unsigned char *) RSTRING_PTR(text),
116
+ RSTRING_LEN(text));
117
+ ENC_CODERANGE_SET(text, cr);
118
+ return cr;
119
+ }
120
+
121
+ /* The end of the line content that starts at p: the position of its "\n",
122
+ or n; *next is where the following line starts. A "\r" before the "\n",
123
+ or at the end of the text, is not content. */
124
+ static long
125
+ ca_csv_line_end (const char *s, long n, long p, long *next)
126
+ {
127
+ const char *nl = memchr(s + p, '\n', n - p);
128
+ long e = nl ? nl - s : n;
129
+ *next = nl ? e + 1 : n;
130
+ if ( e > p && s[e - 1] == '\r' ) {
131
+ e -= 1;
132
+ }
133
+ return e;
134
+ }
135
+
136
+ /* A growing list of (start, end) pairs. A pair at or past the length of
137
+ the text indexes the unescaped tail; a missing cell is (-1, -1). */
138
+ typedef struct {
139
+ int64_t *v;
140
+ long len, cap; /* in pairs */
141
+ } ca_csv_cell_ranges;
142
+
143
+ static void
144
+ ca_csv_push_cell_range (ca_csv_cell_ranges *a, int64_t start, int64_t end)
145
+ {
146
+ if ( a->len == a->cap ) {
147
+ a->cap = a->cap ? 2 * a->cap : 4096;
148
+ REALLOC_N(a->v, int64_t, 2 * a->cap);
149
+ }
150
+ a->v[2 * a->len] = start;
151
+ a->v[2 * a->len + 1] = end;
152
+ a->len++;
153
+ }
154
+
155
+ typedef struct {
156
+ ca_csv_cell_ranges cells; /* row after row */
157
+ ca_csv_cell_ranges pending; /* blank lines before the first record */
158
+ VALUE tail; /* unescaped quoted fields, or Qnil */
159
+ VALUE keep; /* the indexes of the columns to read, or Qnil */
160
+ char *kept; /* keep as a byte per column, 1 for one read;
161
+ NULL for every column, or before ncol is known */
162
+ long ncol;
163
+ long width; /* the cells of a row: those of the columns read */
164
+ long records;
165
+ } ca_csv_walk_t;
166
+
167
+ /* Set w->kept and w->width from w->keep for w->ncol columns. Returns 0 when
168
+ keep names no column or one past the last. */
169
+ static int
170
+ ca_csv_set_kept_columns (ca_csv_walk_t *w)
171
+ {
172
+ long i;
173
+ w->kept = ALLOC_N(char, w->ncol);
174
+ memset(w->kept, 0, w->ncol);
175
+ for (i = 0; i < RARRAY_LEN(w->keep); i++) {
176
+ long j = NUM2LONG(rb_ary_entry(w->keep, i));
177
+ if ( j < 0 || j >= w->ncol ) {
178
+ return 0;
179
+ }
180
+ if ( ! w->kept[j] ) {
181
+ w->kept[j] = 1;
182
+ w->width++;
183
+ }
184
+ }
185
+ return w->width > 0;
186
+ }
187
+
188
+ /* Read every record of s into w. Returns 0 when the text is one this
189
+ reader declines (see the header comment). The cell ranges it grows are
190
+ the caller's to free, whether it returns or raises. */
191
+ static int
192
+ ca_csv_walk_records (const char *s, long n, const char *sp, long slen,
193
+ const char *qp, long qlen, ca_csv_walk_t *w)
194
+ {
195
+ long ncol = w->ncol, p = 0;
196
+
197
+ if ( ! NIL_P(w->keep) && ncol > 0 && ! ca_csv_set_kept_columns(w) ) {
198
+ return 0;
199
+ }
200
+ while ( p < n ) {
201
+ long next, e = ca_csv_line_end(s, n, p, &next), k, fields = 0;
202
+ int blank = 1;
203
+
204
+ w->records += 1;
205
+
206
+ /* A blank line: empty, or only spaces and tabs with no separator. */
207
+ for (k = p; k < e; k++) {
208
+ if ( s[k] != ' ' && s[k] != '\t' ) {
209
+ blank = 0;
210
+ break;
211
+ }
212
+ }
213
+ if ( blank ) {
214
+ for (k = p; k + slen <= e; k++) {
215
+ if ( memcmp(s + k, sp, slen) == 0 ) {
216
+ blank = 0;
217
+ break;
218
+ }
219
+ }
220
+ }
221
+ if ( blank ) {
222
+ int64_t a = e > p ? p : -1, b = e > p ? e : -1;
223
+ if ( ncol == 1 ) {
224
+ if ( w->kept == NULL || w->kept[0] ) {
225
+ ca_csv_push_cell_range(&w->cells, a, b);
226
+ }
227
+ }
228
+ else if ( ncol == 0 ) {
229
+ /* Before the first record the column count is not known: keep the
230
+ line, a row if that record turns out to have one column. */
231
+ ca_csv_push_cell_range(&w->pending, a, b);
232
+ }
233
+ p = next;
234
+ continue;
235
+ }
236
+
237
+ for (;;) {
238
+ int64_t a = -1, b = -1;
239
+ /* The field of a column not read is passed over, its text not
240
+ unescaped. */
241
+ int read = w->kept == NULL || ( fields < ncol && w->kept[fields] );
242
+ if ( ca_csv_at(s, n, p, qp, qlen) ) {
243
+ /* A quoted field, which may run onto later lines. */
244
+ long q = p + qlen, start = q, tail0 = -1;
245
+ for (;;) {
246
+ const char *hit = NULL;
247
+ long r = q;
248
+ while ( r + qlen <= n ) {
249
+ const char *c = memchr(s + r, qp[0], n - r);
250
+ if ( c == NULL ) {
251
+ break;
252
+ }
253
+ r = c - s;
254
+ if ( ca_csv_at(s, n, r, qp, qlen) ) {
255
+ hit = c;
256
+ break;
257
+ }
258
+ r++;
259
+ }
260
+ if ( hit == NULL ) {
261
+ return 0; /* unterminated */
262
+ }
263
+ if ( ca_csv_at(s, n, r + qlen, qp, qlen) ) {
264
+ /* a doubled quote is a literal one */
265
+ if ( ! read ) {
266
+ q = r + 2 * qlen;
267
+ continue;
268
+ }
269
+ if ( NIL_P(w->tail) ) {
270
+ w->tail = rb_str_buf_new(256);
271
+ }
272
+ if ( tail0 < 0 ) {
273
+ tail0 = RSTRING_LEN(w->tail);
274
+ rb_str_cat(w->tail, s + start, r + qlen - start);
275
+ }
276
+ else {
277
+ rb_str_cat(w->tail, s + q, r + qlen - q);
278
+ }
279
+ q = r + 2 * qlen;
280
+ continue;
281
+ }
282
+ if ( tail0 < 0 ) {
283
+ a = start;
284
+ b = r;
285
+ }
286
+ else {
287
+ rb_str_cat(w->tail, s + q, r - q);
288
+ a = n + tail0;
289
+ b = n + RSTRING_LEN(w->tail);
290
+ }
291
+ p = r + qlen;
292
+ break;
293
+ }
294
+ e = ca_csv_line_end(s, n, p, &next);
295
+ if ( p != e && ! ca_csv_at(s, n, p, sp, slen) ) {
296
+ return 0; /* text after the closing quote */
297
+ }
298
+ }
299
+ else {
300
+ long r = p;
301
+ if ( slen == 1 && qlen == 1 ) {
302
+ const char sc = sp[0], qc = qp[0];
303
+ while ( r < e && s[r] != sc ) {
304
+ if ( s[r] == qc ) {
305
+ return 0; /* a quote in an unquoted field */
306
+ }
307
+ r++;
308
+ }
309
+ }
310
+ else {
311
+ while ( r < e && ! ca_csv_at(s, n, r, sp, slen) ) {
312
+ if ( ca_csv_at(s, n, r, qp, qlen) ) {
313
+ return 0; /* a quote in an unquoted field */
314
+ }
315
+ r++;
316
+ }
317
+ }
318
+ a = r > p ? p : -1;
319
+ b = r > p ? r : -1;
320
+ p = r;
321
+ }
322
+ if ( ++fields > ncol && ncol > 0 ) {
323
+ return 0; /* a row too long */
324
+ }
325
+ if ( read ) {
326
+ ca_csv_push_cell_range(&w->cells, a, b);
327
+ }
328
+ if ( p < e && ca_csv_at(s, n, p, sp, slen) ) {
329
+ p += slen;
330
+ continue;
331
+ }
332
+ break;
333
+ }
334
+ if ( ncol == 0 ) {
335
+ /* The first record sets the column count; the blank lines before it
336
+ are rows of a one-column file and nothing otherwise. */
337
+ ncol = fields;
338
+ if ( ! NIL_P(w->keep) ) {
339
+ /* Its fields were all read, the columns being unknown until now:
340
+ keep those of the columns read. */
341
+ long j, k = 0;
342
+ w->ncol = ncol;
343
+ if ( ! ca_csv_set_kept_columns(w) ) {
344
+ return 0;
345
+ }
346
+ for (j = 0; j < ncol; j++) {
347
+ if ( w->kept[j] ) {
348
+ w->cells.v[2 * k] = w->cells.v[2 * j];
349
+ w->cells.v[2 * k + 1] = w->cells.v[2 * j + 1];
350
+ k++;
351
+ }
352
+ }
353
+ w->cells.len = k;
354
+ }
355
+ if ( ncol == 1 && w->pending.len > 0 ) {
356
+ long i;
357
+ for (i = 0; i < w->cells.len; i++) {
358
+ ca_csv_push_cell_range(&w->pending, w->cells.v[2 * i], w->cells.v[2 * i + 1]);
359
+ }
360
+ { ca_csv_cell_ranges t = w->cells; w->cells = w->pending; w->pending = t; }
361
+ w->pending.len = 0;
362
+ }
363
+ }
364
+ for (; fields < ncol; fields++) {
365
+ if ( w->kept == NULL || w->kept[fields] ) {
366
+ ca_csv_push_cell_range(&w->cells, -1, -1);
367
+ }
368
+ }
369
+ p = next;
370
+ }
371
+ w->ncol = ncol;
372
+ if ( NIL_P(w->keep) ) {
373
+ w->width = ncol;
374
+ }
375
+ return ncol > 0; /* 0: no record */
376
+ }
377
+
378
+ /* The arguments both readers take, checked; 0 for text they decline. */
379
+ static int
380
+ ca_csv_check_reader_args (VALUE text, VALUE vsep, VALUE vquote, VALUE vncol,
381
+ VALUE keep, long *ncol)
382
+ {
383
+ rb_encoding *enc;
384
+ Check_Type(text, T_STRING);
385
+ Check_Type(vsep, T_STRING);
386
+ Check_Type(vquote, T_STRING);
387
+ if ( ! NIL_P(keep) ) {
388
+ Check_Type(keep, T_ARRAY);
389
+ }
390
+ *ncol = NUM2LONG(vncol);
391
+ enc = rb_enc_get(text);
392
+ if ( ( enc != rb_utf8_encoding() && enc != rb_usascii_encoding() )
393
+ || ca_csv_text_coderange(text, enc) == ENC_CODERANGE_BROKEN || *ncol < 0 ) {
394
+ return 0;
395
+ }
396
+ return RSTRING_LEN(vsep) >= 1 && RSTRING_LEN(vquote) >= 1;
397
+ }
398
+
399
+ /* What a reader makes of the walked records: the Ruby value it answers. */
400
+ typedef VALUE (*ca_csv_build_result) (VALUE text, ca_csv_walk_t *w);
401
+
402
+ typedef struct {
403
+ VALUE text, vsep, vquote, vncol;
404
+ ca_csv_walk_t w;
405
+ ca_csv_build_result build;
406
+ } ca_csv_read_body_context;
407
+
408
+ static VALUE
409
+ ca_csv_read_body_walk_and_build (VALUE varg)
410
+ {
411
+ ca_csv_read_body_context *c = (ca_csv_read_body_context *) varg;
412
+ if ( ! ca_csv_check_reader_args(c->text, c->vsep, c->vquote, c->vncol, c->w.keep,
413
+ &c->w.ncol) ) {
414
+ return Qnil;
415
+ }
416
+ if ( ! ca_csv_walk_records(RSTRING_PTR(c->text), RSTRING_LEN(c->text),
417
+ RSTRING_PTR(c->vsep), RSTRING_LEN(c->vsep),
418
+ RSTRING_PTR(c->vquote), RSTRING_LEN(c->vquote), &c->w) ) {
419
+ return Qnil;
420
+ }
421
+ return c->build(c->text, &c->w);
422
+ }
423
+
424
+ static VALUE
425
+ ca_csv_read_body_free_cell_ranges (VALUE varg)
426
+ {
427
+ ca_csv_read_body_context *c = (ca_csv_read_body_context *) varg;
428
+ xfree(c->w.cells.v);
429
+ xfree(c->w.pending.v);
430
+ xfree(c->w.kept);
431
+ return Qnil;
432
+ }
433
+
434
+ /* Walk the records of text and build the answer from them, with the cell
435
+ ranges freed however that ends: the walk and the build both allocate,
436
+ and so may raise. */
437
+ static VALUE
438
+ ca_csv_read_body (VALUE text, VALUE vsep, VALUE vquote, VALUE vncol, VALUE keep,
439
+ ca_csv_build_result build)
440
+ {
441
+ ca_csv_read_body_context c = {
442
+ text, vsep, vquote, vncol,
443
+ { { NULL, 0, 0 }, { NULL, 0, 0 }, Qnil, keep, NULL, 0, 0, 0 }, build
444
+ };
445
+ VALUE out = rb_ensure(ca_csv_read_body_walk_and_build, (VALUE) &c,
446
+ ca_csv_read_body_free_cell_ranges, (VALUE) &c);
447
+ RB_GC_GUARD(c.w.tail);
448
+ return out;
449
+ }
450
+
451
+ static VALUE
452
+ ca_csv_build_string_cells (VALUE text, ca_csv_walk_t *w)
453
+ {
454
+ volatile VALUE out;
455
+ rb_encoding *enc = rb_enc_get(text);
456
+ const char *s = RSTRING_PTR(text);
457
+ long n = RSTRING_LEN(text), i;
458
+
459
+ out = rb_ary_new_capa(w->cells.len);
460
+ for (i = 0; i < w->cells.len; i++) {
461
+ int64_t a = w->cells.v[2 * i], b = w->cells.v[2 * i + 1];
462
+ VALUE cell;
463
+ if ( a < 0 ) {
464
+ cell = CA_UNDEF;
465
+ }
466
+ else if ( a < n ) {
467
+ cell = rb_enc_str_new(s + a, b - a, enc);
468
+ }
469
+ else {
470
+ cell = rb_enc_str_new(RSTRING_PTR(w->tail) + (a - n), b - a, enc);
471
+ }
472
+ rb_ary_push(out, cell);
473
+ }
474
+ return rb_ary_new3(3, LONG2NUM(w->ncol), out, LONG2NUM(w->records));
475
+ }
476
+
477
+ static VALUE
478
+ ca_csv_build_const_string_columns (VALUE text, ca_csv_walk_t *w)
479
+ {
480
+ volatile VALUE buffer, cols;
481
+ ca_size_t dim[1];
482
+ long nrow, i, j;
483
+
484
+ if ( NIL_P(w->tail) ) {
485
+ buffer = text;
486
+ }
487
+ else {
488
+ buffer = rb_str_buf_new(RSTRING_LEN(text) + RSTRING_LEN(w->tail));
489
+ rb_str_cat(buffer, RSTRING_PTR(text), RSTRING_LEN(text));
490
+ rb_str_cat(buffer, RSTRING_PTR(w->tail), RSTRING_LEN(w->tail));
491
+ rb_enc_copy(buffer, text);
492
+ }
493
+
494
+ nrow = w->cells.len / w->width;
495
+ dim[0] = (ca_size_t) nrow;
496
+ cols = rb_ary_new_capa(w->width);
497
+ for (j = 0; j < w->width; j++) {
498
+ CArray *pe;
499
+ int64_t *range;
500
+ boolean8_t *m = NULL;
501
+ VALUE col = rb_carray_new(CA_FIXLEN, 1, dim, 2 * sizeof(int64_t), NULL);
502
+ rb_ary_push(cols, col);
503
+ TypedData_Get_Struct(col, CArray, &carray_data_type, pe);
504
+ range = (int64_t *) pe->ptr;
505
+ for (i = 0; i < nrow; i++) {
506
+ int64_t a = w->cells.v[2 * (i * w->width + j)];
507
+ int64_t b = w->cells.v[2 * (i * w->width + j) + 1];
508
+ if ( a < 0 ) {
509
+ if ( m == NULL ) {
510
+ ca_create_mask(pe);
511
+ m = (boolean8_t *) pe->mask->ptr;
512
+ }
513
+ m[i] = 1;
514
+ a = b = 0;
515
+ }
516
+ range[2 * i] = a;
517
+ range[2 * i + 1] = b;
518
+ }
519
+ }
520
+ return rb_ary_new3(4, LONG2NUM(w->ncol), buffer, cols, LONG2NUM(w->records));
521
+ }
522
+
523
+ static VALUE
524
+ rb_ca_s_csv_read_body_as_string_cells (VALUE klass, VALUE text, VALUE vsep, VALUE vquote,
525
+ VALUE vncol, VALUE keep)
526
+ {
527
+ return ca_csv_read_body(text, vsep, vquote, vncol, keep, ca_csv_build_string_cells);
528
+ }
529
+
530
+ static VALUE
531
+ rb_ca_s_csv_read_body_as_const_string_columns (VALUE klass, VALUE text, VALUE vsep, VALUE vquote,
532
+ VALUE vncol, VALUE keep)
533
+ {
534
+ return ca_csv_read_body(text, vsep, vquote, vncol, keep, ca_csv_build_const_string_columns);
535
+ }
536
+
537
+ void
538
+ Init_caframe_csv_reader (void)
539
+ {
540
+ rb_define_singleton_method(rb_cCArray, "__csv_read_body_as_string_cells__", rb_ca_s_csv_read_body_as_string_cells, 5);
541
+ rb_define_singleton_method(rb_cCArray, "__csv_read_body_as_const_string_columns__", rb_ca_s_csv_read_body_as_const_string_columns, 5);
542
+ }