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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +568 -0
- data/README.md +6 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +220 -0
- data/ext/ca_axis_group.c +201 -123
- data/ext/ca_binop_dispatch.c +46 -52
- data/ext/ca_binop_dispatch.h +1 -1
- data/ext/ca_categorical_iterator.c +249 -183
- data/ext/ca_compare.c +7 -12
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +38 -23
- data/ext/ca_kernel_iterator.c +1765 -2076
- data/ext/ca_kernel_iterator.h +584 -566
- data/ext/ca_monop_dispatch.c +86 -63
- data/ext/ca_monop_dispatch.h +5 -0
- data/ext/ca_obj_array.c +223 -56
- data/ext/ca_obj_bincmp.c +172 -138
- data/ext/ca_obj_binop.c +116 -230
- data/ext/ca_obj_bitarray.c +40 -26
- data/ext/ca_obj_bitfield.c +100 -68
- data/ext/ca_obj_block.c +19 -9
- data/ext/ca_obj_byte_swap.c +45 -46
- data/ext/ca_obj_const_string.c +269 -44
- data/ext/ca_obj_face.c +612 -89
- data/ext/ca_obj_face.h +111 -47
- data/ext/ca_obj_fake.c +36 -79
- data/ext/ca_obj_farray.c +7 -3
- data/ext/ca_obj_field.c +33 -8
- data/ext/ca_obj_fixlen_string.c +23 -24
- data/ext/ca_obj_grid.c +541 -210
- data/ext/ca_obj_meld.c +331 -111
- data/ext/ca_obj_moncmp.c +80 -65
- data/ext/ca_obj_monop.c +177 -149
- data/ext/ca_obj_object.c +664 -248
- data/ext/ca_obj_record.c +63 -23
- data/ext/ca_obj_reduce.c +18 -16
- data/ext/ca_obj_refer.c +140 -58
- data/ext/ca_obj_remap.c +85 -24
- data/ext/ca_obj_repeat.c +24 -9
- data/ext/ca_obj_roll.c +121 -16
- data/ext/ca_obj_select.c +61 -58
- data/ext/ca_obj_select_axis.c +73 -107
- data/ext/ca_obj_shift.c +32 -102
- data/ext/ca_obj_stack.c +372 -122
- data/ext/ca_obj_stride.c +348 -140
- data/ext/ca_obj_string.c +88 -22
- data/ext/ca_obj_tile.c +38 -21
- data/ext/ca_obj_time.c +45 -31
- data/ext/ca_obj_timedelta.c +43 -29
- data/ext/ca_obj_transpose.c +17 -6
- data/ext/ca_obj_triop.c +125 -141
- data/ext/ca_obj_window.c +330 -44
- data/ext/ca_op_ipower.c +24 -11
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +104 -81
- data/ext/ca_sweep_engine.h +7 -20
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +542 -0
- data/ext/carray.h +241 -92
- data/ext/carray_access.c +608 -276
- data/ext/carray_address_basis.c +494 -97
- data/ext/carray_attribute.c +129 -4
- data/ext/carray_bincount.c +39 -7
- data/ext/carray_broadcast.c +56 -19
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_call_cfunc.c +108 -28
- data/ext/carray_cast.c +1576 -223
- data/ext/carray_cast_func.rb +77 -24
- data/ext/carray_class.c +4 -3
- data/ext/carray_conversion.c +208 -75
- data/ext/carray_copy.c +82 -24
- data/ext/carray_core.c +646 -162
- data/ext/carray_count.c +171 -88
- data/ext/carray_element.c +94 -60
- data/ext/carray_factorize.c +670 -324
- data/ext/carray_generate.c +260 -83
- data/ext/carray_histogram.c +209 -106
- data/ext/carray_hold.c +14 -16
- data/ext/carray_index_classifier.c +422 -243
- data/ext/carray_internal.h +692 -3
- data/ext/carray_kernels_bincmp.c +7 -0
- data/ext/carray_kernels_binop.c +487 -120
- data/ext/carray_kernels_init.c +653 -50
- data/ext/carray_kernels_moncmp.c +15 -8
- data/ext/carray_kernels_monop.c +99 -43
- data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
- data/ext/carray_kernels_reduce_boolean.c +112 -36
- data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
- data/ext/carray_kernels_reduce_extreme.c +3032 -2892
- data/ext/carray_kernels_reduce_variance.c +439 -500
- data/ext/carray_kernels_scan.c +1097 -488
- data/ext/carray_kernels_search.c +3787 -2176
- data/ext/carray_kernels_sort.c +1200 -920
- data/ext/carray_kernels_triop.c +479 -7
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +967 -153
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +910 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +299 -100
- data/ext/carray_memory_view.c +872 -758
- data/ext/carray_operator.c +1171 -364
- data/ext/carray_order.c +505 -133
- data/ext/carray_partition.c +112 -65
- data/ext/carray_random.c +327 -239
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +346 -78
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +249 -86
- data/ext/carray_sort.c +224 -227
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +118 -39
- data/ext/carray_test.c +193 -69
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +579 -113
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +19 -4
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1202 -1014
- data/ext/ruby_carray.c +17 -5
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +160 -17
- data/lib/carray/autoload_carray.rb +23 -27
- data/lib/carray/autoload_method_extension.rb +20 -5
- data/lib/carray/axis_group.rb +360 -63
- data/lib/carray/basics.rb +162 -28
- data/lib/carray/bincount_nd.rb +18 -2
- data/lib/carray/block_iterator.rb +231 -58
- data/lib/carray/boolean_reduce.rb +34 -42
- data/lib/carray/categorical.rb +242 -52
- data/lib/carray/categorical_iterator.rb +437 -827
- data/lib/carray/complex.rb +41 -34
- data/lib/carray/conditional.rb +150 -14
- data/lib/carray/const_string.rb +59 -4
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +82 -7
- data/lib/carray/frame/csv_parser.rb +688 -87
- data/lib/carray/frame/describe.rb +80 -0
- data/lib/carray/frame/frame.rb +369 -45
- data/lib/carray/frame/group.rb +631 -90
- data/lib/carray/frame/io.rb +506 -79
- data/lib/carray/frame/join.rb +411 -62
- data/lib/carray/frame/records.rb +116 -19
- data/lib/carray/frame/reshape.rb +455 -0
- data/lib/carray/frame/sort.rb +30 -18
- data/lib/carray/frame/verbs.rb +868 -61
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +4 -0
- data/lib/carray/fuse_source.rb +118 -2
- data/lib/carray/fusion.rb +257 -42
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +54 -13
- data/lib/carray/iterator.rb +99 -9
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +216 -193
- data/lib/carray/mask_gap_fill.rb +112 -92
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +52 -21
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +18 -2
- data/lib/carray/methods/bit_string.rb +15 -4
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +18 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +25 -5
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +140 -10
- data/lib/carray/methods/join.rb +2 -7
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +182 -45
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/repeat.rb +1 -20
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +11 -2
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +65 -17
- data/lib/carray/segment_iterator.rb +1221 -0
- data/lib/carray/serialize.rb +214 -30
- data/lib/carray/slab_iterator.rb +101 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +12 -4
- data/lib/carray/struct.rb +137 -58
- data/lib/carray/struct_builder.rb +124 -17
- data/lib/carray/time.rb +1222 -186
- data/lib/carray/window_iterator.rb +242 -81
- data/lib/carray.rb +9 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +5 -1
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +5 -4
- data/yard-stubs/ca_obj_roll.rb +2 -3
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +9 -5
- data/yard-stubs/carray_access.rb +11 -5
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +88 -43
- data/yard-stubs/carray_conversion.rb +15 -2
- data/yard-stubs/carray_copy.rb +12 -5
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +60 -12
- data/yard-stubs/carray_math.rb +52 -14
- data/yard-stubs/carray_median_percentile.rb +15 -8
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +61 -29
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +21 -9
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +10 -2
- data/yard-stubs/carray_sort.rb +49 -8
- data/yard-stubs/carray_test.rb +15 -3
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -5
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_kernels_map.c +0 -3467
- data/lib/carray/arrow_tensor.rb +0 -401
- 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
|
+
}
|