carray 3.0.1 → 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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  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 +90 -41
  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 +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  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 +194 -42
  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 +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -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 +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  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 +551 -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 +88 -75
  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 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  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 +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
@@ -0,0 +1,1182 @@
1
+ #include "carray.h"
2
+ #include "carray_internal.h"
3
+
4
+ /* ------------------------------------------------------------------------
5
+ Reading year-first dates and times from text
6
+
7
+ A cell is read when it is written year first:
8
+
9
+ date YYYY sep M sep D sep is '-' or '/', the same twice;
10
+ month and day take one or two digits
11
+ time ( 'T' | ' '+ ) h ':' mm ( ':' ss ( ( '.' | ',' ) fraction )? )?
12
+ | 'T' hh hour takes one or two digits,
13
+ fraction one to nine
14
+ zone ' '? ( 'Z' | ( '+' | '-' ) hh ( ':'? mm )? ) after a time only
15
+
16
+ Surrounding ASCII whitespace is ignored. A time without a zone is UTC,
17
+ and a zone is folded into UTC. Nothing written in another order is read:
18
+ whether "01/02/2024" is January or February cannot be told from the text.
19
+
20
+ Read wide (when the caller has said the column is time), the year may
21
+ also carry a sign and more than four digits, and a date may stop after
22
+ the month ("2024-01") or the year ("2024", four digits unless signed) --
23
+ what CATime writes for a unit of months or years, or for a year past
24
+ 9999. Text is not read
25
+ wide when it is being tried as time, since "2024" is as likely a number.
26
+
27
+ The unit of the result is the finest the text shows, unless one is
28
+ given: :D when no cell has a time of day, :s with one, and :ms / :us /
29
+ :ns when a fraction of a second has up to 3 / 6 / 9 digits; read wide,
30
+ :M when no cell has a day and :Y when none has a month either.
31
+ ------------------------------------------------------------------------ */
32
+
33
+ enum {
34
+ CA_TIME_TEXT_OK = 0,
35
+ CA_TIME_TEXT_BLANK,
36
+ CA_TIME_TEXT_UNREADABLE
37
+ };
38
+
39
+ typedef struct {
40
+ int64_t days; /* days since 1970-01-01 */
41
+ int64_t nsod; /* nanoseconds into that day, 0 <= nsod < 86400e9 */
42
+ int clock; /* a time of day is written */
43
+ int fraction; /* digits written after the seconds */
44
+ int coarse; /* 0 a day is written, 1 only a month, 2 only a year */
45
+ int64_t months; /* months since 1970-01 */
46
+ } ca_time_text_t;
47
+
48
+ static const int64_t ca_ns_per_day = INT64_C(86400000000000);
49
+
50
+ static int
51
+ ca_time_text_is_space (char c)
52
+ {
53
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r'
54
+ || c == '\v' || c == '\f';
55
+ }
56
+
57
+ /* Read min..max digits at s[*i] into *out. */
58
+ static int
59
+ ca_time_text_digits (const char *s, long n, long *i, int min, int max,
60
+ int64_t *out)
61
+ {
62
+ int64_t v = 0;
63
+ int count = 0;
64
+ while ( *i < n && count < max && s[*i] >= '0' && s[*i] <= '9' ) {
65
+ v = v * 10 + ( s[*i] - '0' );
66
+ (*i)++;
67
+ count++;
68
+ }
69
+ if ( count < min ) {
70
+ return 0;
71
+ }
72
+ if ( *i < n && s[*i] >= '0' && s[*i] <= '9' ) {
73
+ return 0; /* more digits than the field takes */
74
+ }
75
+ *out = v;
76
+ return count;
77
+ }
78
+
79
+ static int
80
+ ca_time_text_leap (int64_t y)
81
+ {
82
+ return ( y % 4 == 0 && y % 100 != 0 ) || y % 400 == 0;
83
+ }
84
+
85
+ static int
86
+ ca_time_text_month_days (int64_t y, int64_t m)
87
+ {
88
+ static const int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
89
+ return ( m == 2 && ca_time_text_leap(y) ) ? 29 : days[m - 1];
90
+ }
91
+
92
+ /* Days since 1970-01-01 of a proleptic Gregorian date. */
93
+ static int64_t
94
+ ca_time_text_days_from_civil (int64_t y, int64_t m, int64_t d)
95
+ {
96
+ int64_t era, yoe, doy, doe;
97
+ y -= ( m <= 2 );
98
+ era = ( y >= 0 ? y : y - 399 ) / 400;
99
+ yoe = y - era * 400;
100
+ doy = ( 153 * ( m > 2 ? m - 3 : m + 9 ) + 2 ) / 5 + d - 1;
101
+ doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
102
+ return era * 146097 + doe - 719468;
103
+ }
104
+
105
+ static int
106
+ ca_time_text_read (VALUE str, ca_time_text_t *t, int wide)
107
+ {
108
+ const char *s = RSTRING_PTR(str);
109
+ long n = RSTRING_LEN(str);
110
+ long i = 0;
111
+ int64_t year, mon, day, hour = 0, min = 0, sec = 0, frac_ns = 0;
112
+ int64_t offset = 0;
113
+ int year_digits;
114
+ char sep;
115
+
116
+ while ( n > 0 && ca_time_text_is_space(s[0]) ) {
117
+ s++;
118
+ n--;
119
+ }
120
+ while ( n > 0 && ca_time_text_is_space(s[n-1]) ) {
121
+ n--;
122
+ }
123
+ if ( n == 0 ) {
124
+ return CA_TIME_TEXT_BLANK;
125
+ }
126
+
127
+ t->clock = 0;
128
+ t->fraction = 0;
129
+ t->coarse = 0;
130
+
131
+ if ( wide ) {
132
+ int sign = 0;
133
+ if ( s[i] == '+' || s[i] == '-' ) {
134
+ sign = ( s[i] == '-' ) ? -1 : 1;
135
+ i++;
136
+ }
137
+ year_digits = ca_time_text_digits(s, n, &i, 4, 12, &year);
138
+ if ( year_digits == 0 ) {
139
+ return CA_TIME_TEXT_UNREADABLE;
140
+ }
141
+ if ( sign < 0 ) {
142
+ year = -year;
143
+ }
144
+ /* A year alone is four digits unless signed: "20240101" is a date
145
+ written without separators, which is not read. */
146
+ if ( i == n && year_digits > 4 && sign == 0 ) {
147
+ return CA_TIME_TEXT_UNREADABLE;
148
+ }
149
+ }
150
+ else if ( ca_time_text_digits(s, n, &i, 4, 4, &year) == 0 ) {
151
+ return CA_TIME_TEXT_UNREADABLE;
152
+ }
153
+ if ( wide && i == n ) {
154
+ t->coarse = 2;
155
+ t->months = ( year - 1970 ) * 12;
156
+ t->days = ca_time_text_days_from_civil(year, 1, 1);
157
+ t->nsod = 0;
158
+ return CA_TIME_TEXT_OK;
159
+ }
160
+ if ( i >= n || ( s[i] != '-' && s[i] != '/' ) ) {
161
+ return CA_TIME_TEXT_UNREADABLE;
162
+ }
163
+ sep = s[i++];
164
+ if ( ca_time_text_digits(s, n, &i, 1, 2, &mon) == 0 ) {
165
+ return CA_TIME_TEXT_UNREADABLE;
166
+ }
167
+ if ( mon < 1 || mon > 12 ) {
168
+ return CA_TIME_TEXT_UNREADABLE;
169
+ }
170
+ t->months = ( year - 1970 ) * 12 + ( mon - 1 );
171
+ if ( wide && i == n ) {
172
+ t->coarse = 1;
173
+ t->days = ca_time_text_days_from_civil(year, mon, 1);
174
+ t->nsod = 0;
175
+ return CA_TIME_TEXT_OK;
176
+ }
177
+ if ( i >= n || s[i] != sep ) {
178
+ return CA_TIME_TEXT_UNREADABLE;
179
+ }
180
+ i++;
181
+ if ( ca_time_text_digits(s, n, &i, 1, 2, &day) == 0 ) {
182
+ return CA_TIME_TEXT_UNREADABLE;
183
+ }
184
+ if ( day < 1 || day > ca_time_text_month_days(year, mon) ) {
185
+ return CA_TIME_TEXT_UNREADABLE;
186
+ }
187
+
188
+ if ( i < n ) {
189
+ int by_t = ( s[i] == 'T' ), hour_digits;
190
+ if ( by_t ) {
191
+ i++;
192
+ }
193
+ else if ( s[i] == ' ' ) {
194
+ while ( i < n && s[i] == ' ' ) {
195
+ i++;
196
+ }
197
+ }
198
+ else {
199
+ return CA_TIME_TEXT_UNREADABLE;
200
+ }
201
+ hour_digits = ca_time_text_digits(s, n, &i, 1, 2, &hour);
202
+ if ( hour_digits == 0 ) {
203
+ return CA_TIME_TEXT_UNREADABLE;
204
+ }
205
+ if ( i >= n || s[i] != ':' ) {
206
+ /* 'T' hh alone is an hour (ISO 8601 reduced to the hour). */
207
+ if ( ! ( by_t && hour_digits == 2 ) ) {
208
+ return CA_TIME_TEXT_UNREADABLE;
209
+ }
210
+ }
211
+ else {
212
+ i++;
213
+ if ( ca_time_text_digits(s, n, &i, 2, 2, &min) == 0 ) {
214
+ return CA_TIME_TEXT_UNREADABLE;
215
+ }
216
+ if ( i < n && s[i] == ':' ) {
217
+ i++;
218
+ if ( ca_time_text_digits(s, n, &i, 2, 2, &sec) == 0 ) {
219
+ return CA_TIME_TEXT_UNREADABLE;
220
+ }
221
+ if ( i < n && ( s[i] == '.' || s[i] == ',' ) ) {
222
+ int64_t f;
223
+ int digits;
224
+ i++;
225
+ digits = ca_time_text_digits(s, n, &i, 1, 9, &f);
226
+ if ( digits == 0 ) {
227
+ return CA_TIME_TEXT_UNREADABLE;
228
+ }
229
+ t->fraction = digits;
230
+ frac_ns = f;
231
+ while ( digits++ < 9 ) {
232
+ frac_ns *= 10;
233
+ }
234
+ }
235
+ }
236
+ }
237
+ if ( hour > 24 || min > 59 || sec > 59 ) {
238
+ return CA_TIME_TEXT_UNREADABLE;
239
+ }
240
+ /* Hour 24 is the end of the day, 24:00:00, and nothing past it, as in
241
+ the strptime reader below. */
242
+ if ( hour == 24 && ( min || sec || frac_ns ) ) {
243
+ return CA_TIME_TEXT_UNREADABLE;
244
+ }
245
+ t->clock = 1;
246
+
247
+ if ( i < n && s[i] == ' ' ) {
248
+ i++; /* one space before a zone */
249
+ if ( i >= n ) {
250
+ return CA_TIME_TEXT_UNREADABLE;
251
+ }
252
+ }
253
+ if ( i < n ) {
254
+ if ( s[i] == 'Z' ) {
255
+ i++;
256
+ }
257
+ else if ( s[i] == '+' || s[i] == '-' ) {
258
+ int negative = ( s[i] == '-' );
259
+ int64_t oh, om = 0, hhmm;
260
+ int digits;
261
+ i++;
262
+ digits = ca_time_text_digits(s, n, &i, 2, 4, &hhmm);
263
+ if ( digits == 4 ) { /* +hhmm */
264
+ oh = hhmm / 100;
265
+ om = hhmm % 100;
266
+ }
267
+ else if ( digits == 2 ) { /* +hh or +hh:mm */
268
+ oh = hhmm;
269
+ if ( i < n && s[i] == ':' ) {
270
+ i++;
271
+ if ( ca_time_text_digits(s, n, &i, 2, 2, &om) == 0 ) {
272
+ return CA_TIME_TEXT_UNREADABLE;
273
+ }
274
+ }
275
+ }
276
+ else {
277
+ return CA_TIME_TEXT_UNREADABLE;
278
+ }
279
+ if ( oh > 23 || om > 59 ) {
280
+ return CA_TIME_TEXT_UNREADABLE;
281
+ }
282
+ offset = ( oh * 3600 + om * 60 ) * ( negative ? -1 : 1 );
283
+ }
284
+ else {
285
+ return CA_TIME_TEXT_UNREADABLE;
286
+ }
287
+ }
288
+ }
289
+ if ( i != n ) {
290
+ return CA_TIME_TEXT_UNREADABLE;
291
+ }
292
+
293
+ {
294
+ int64_t nsod = ( hour * 3600 + min * 60 + sec - offset ) * INT64_C(1000000000)
295
+ + frac_ns;
296
+ int64_t days = ca_time_text_days_from_civil(year, mon, day);
297
+ if ( nsod < 0 ) {
298
+ nsod += ca_ns_per_day;
299
+ days -= 1;
300
+ }
301
+ else if ( nsod >= ca_ns_per_day ) {
302
+ nsod -= ca_ns_per_day;
303
+ days += 1;
304
+ }
305
+ t->days = days;
306
+ t->nsod = nsod;
307
+ }
308
+ return CA_TIME_TEXT_OK;
309
+ }
310
+
311
+ /* Ticks per day and nanoseconds per tick of the units this reader writes. */
312
+ static int
313
+ ca_time_text_unit (ID unit, int64_t *per_day, int64_t *ns_per_tick)
314
+ {
315
+ if ( unit == rb_intern("D") ) {
316
+ *per_day = 1;
317
+ *ns_per_tick = ca_ns_per_day;
318
+ }
319
+ else if ( unit == rb_intern("s") ) {
320
+ *per_day = 86400;
321
+ *ns_per_tick = INT64_C(1000000000);
322
+ }
323
+ else if ( unit == rb_intern("ms") ) {
324
+ *per_day = INT64_C(86400000);
325
+ *ns_per_tick = INT64_C(1000000);
326
+ }
327
+ else if ( unit == rb_intern("us") ) {
328
+ *per_day = INT64_C(86400000000);
329
+ *ns_per_tick = INT64_C(1000);
330
+ }
331
+ else if ( unit == rb_intern("ns") ) {
332
+ *per_day = ca_ns_per_day;
333
+ *ns_per_tick = 1;
334
+ }
335
+ else {
336
+ return 0;
337
+ }
338
+ return 1;
339
+ }
340
+
341
+ /* CArray#__parse_time_text__(unit, report, wide = false)
342
+ -> [int64 ticks, unit] or nil
343
+
344
+ Reads an object array of year-first text into tick counts since the
345
+ epoch, wide as the grammar above says when wide is true. With unit nil
346
+ the unit is the finest the text shows; a unit this reader does not write
347
+ answers nil, and the caller parses another way.
348
+ With an Array as report, the addresses of cells that hold something but
349
+ do not read are pushed onto it; blank, nil and masked cells are missing,
350
+ not unreadable. A cell that reads but does not fit int64 ticks of the
351
+ unit raises RangeError. */
352
+ static VALUE
353
+ rb_ca_parse_time_text (int argc, VALUE *argv, VALUE self)
354
+ {
355
+ volatile VALUE src = self, out, vdays, vnsod, vmonths, vmask;
356
+ VALUE runit, report, rwide;
357
+ CArray *ca, *co, *cdays, *cnsod, *cmonths, *cmask;
358
+ VALUE *cells;
359
+ boolean8_t *m_in, *m_out;
360
+ int64_t *days, *nsod, *months, *ticks;
361
+ int64_t per_day = 1, ns_per_tick = 1;
362
+ ID unit;
363
+ int clock = 0, fraction = 0, coarse = 2, any = 0, wide;
364
+ int calendar = 0; /* 1 ticks of months, 2 of years */
365
+ ca_size_t i;
366
+
367
+ rb_scan_args(argc, argv, "21", &runit, &report, &rwide);
368
+ wide = RTEST(rwide);
369
+
370
+ if ( ! NIL_P(report) ) {
371
+ Check_Type(report, T_ARRAY);
372
+ }
373
+ if ( ! NIL_P(runit) ) {
374
+ unit = SYM2ID(rb_to_symbol(runit));
375
+ if ( ! ca_time_text_unit(unit, &per_day, &ns_per_tick) ) {
376
+ return Qnil;
377
+ }
378
+ }
379
+
380
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
381
+ if ( ca->data_type != CA_OBJECT ) {
382
+ rb_raise(rb_eArgError, "time text reading needs an object array");
383
+ }
384
+ if ( ! ca_is_entity(ca) ) {
385
+ src = rb_ca_copy(src);
386
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
387
+ }
388
+
389
+ out = rb_carray_new(CA_INT64, ca->ndim, ca->dim, 0, NULL);
390
+ TypedData_Get_Struct(out, CArray, &carray_data_type, co);
391
+ vmask = rb_carray_new(CA_BOOLEAN, ca->ndim, ca->dim, 0, NULL);
392
+ vdays = rb_carray_new(CA_INT64, ca->ndim, ca->dim, 0, NULL);
393
+ TypedData_Get_Struct(vdays, CArray, &carray_data_type, cdays);
394
+ vnsod = rb_carray_new(CA_INT64, ca->ndim, ca->dim, 0, NULL);
395
+ TypedData_Get_Struct(vnsod, CArray, &carray_data_type, cnsod);
396
+ vmonths = rb_carray_new(CA_INT64, ca->ndim, ca->dim, 0, NULL);
397
+ TypedData_Get_Struct(vmonths, CArray, &carray_data_type, cmonths);
398
+
399
+ cells = (VALUE *) ca->ptr;
400
+ m_in = ca_has_mask(ca) ? (boolean8_t *) ca->mask->ptr : NULL;
401
+ TypedData_Get_Struct(vmask, CArray, &carray_data_type, cmask);
402
+ m_out = (boolean8_t *) cmask->ptr;
403
+ days = (int64_t *) cdays->ptr;
404
+ nsod = (int64_t *) cnsod->ptr;
405
+ months = (int64_t *) cmonths->ptr;
406
+ ticks = (int64_t *) co->ptr;
407
+
408
+ for (i = 0; i < ca->elements; i++) {
409
+ VALUE v = cells[i];
410
+ int rc;
411
+ ca_time_text_t t;
412
+ if ( ( m_in && m_in[i] ) || NIL_P(v) ) {
413
+ rc = CA_TIME_TEXT_BLANK;
414
+ }
415
+ else if ( RB_TYPE_P(v, T_STRING) ) {
416
+ rc = ca_time_text_read(v, &t, wide);
417
+ }
418
+ else {
419
+ rc = CA_TIME_TEXT_UNREADABLE;
420
+ }
421
+ if ( rc == CA_TIME_TEXT_OK ) {
422
+ m_out[i] = 0;
423
+ days[i] = t.days;
424
+ nsod[i] = t.nsod;
425
+ months[i] = t.months;
426
+ clock |= t.clock;
427
+ if ( t.coarse < coarse ) {
428
+ coarse = t.coarse;
429
+ }
430
+ any = 1;
431
+ if ( t.fraction > fraction ) {
432
+ fraction = t.fraction;
433
+ }
434
+ }
435
+ else {
436
+ m_out[i] = 1;
437
+ if ( rc == CA_TIME_TEXT_UNREADABLE && ! NIL_P(report) ) {
438
+ rb_ary_push(report, SIZET2NUM(i));
439
+ }
440
+ }
441
+ }
442
+
443
+ if ( NIL_P(runit) ) {
444
+ const char *name = fraction > 6 ? "ns" : fraction > 3 ? "us"
445
+ : fraction > 0 ? "ms" : clock ? "s" : "D";
446
+ if ( any && ! clock && coarse > 0 ) {
447
+ name = ( coarse == 1 ) ? "M" : "Y";
448
+ calendar = coarse;
449
+ }
450
+ unit = rb_intern(name);
451
+ if ( ! calendar ) {
452
+ ca_time_text_unit(unit, &per_day, &ns_per_tick);
453
+ }
454
+ }
455
+
456
+ for (i = 0; i < ca->elements; i++) {
457
+ int64_t whole;
458
+ if ( m_out[i] ) {
459
+ ticks[i] = 0;
460
+ continue;
461
+ }
462
+ if ( calendar ) {
463
+ ticks[i] = ( calendar == 1 ) ? months[i]
464
+ : ( months[i] >= 0 ? months[i] / 12 : -( ( 11 - months[i] ) / 12 ) );
465
+ continue;
466
+ }
467
+ /* A time that reads but does not fit int64 ticks of the unit is out of
468
+ range, not unreadable, so it raises under every policy rather than
469
+ becoming a masked cell: with the unit chosen from the finest text in
470
+ the column, one cell with nanoseconds would otherwise mask every date
471
+ outside 1677..2262 without a word. */
472
+ if ( __builtin_mul_overflow(days[i], per_day, &whole)
473
+ || __builtin_add_overflow(whole, nsod[i] / ns_per_tick, &ticks[i]) ) {
474
+ rb_raise(rb_eRangeError, "time %" PRIsVALUE " does not fit int64 ticks of %" PRIsVALUE,
475
+ rb_inspect(cells[i]), rb_id2str(unit));
476
+ }
477
+ }
478
+
479
+ ca_mask_from_bytes(co, m_out);
480
+ RB_GC_GUARD(src);
481
+ RB_GC_GUARD(vdays);
482
+ RB_GC_GUARD(vnsod);
483
+ RB_GC_GUARD(vmonths);
484
+ RB_GC_GUARD(vmask);
485
+ return rb_assoc_new(out, ID2SYM(unit));
486
+ }
487
+
488
+ /* Floor division of an int128 by a positive int64. */
489
+ static __int128
490
+ ca_time_floor_div (__int128 a, int64_t b)
491
+ {
492
+ __int128 q = a / b;
493
+ if ( ( a % b ) != 0 && a < 0 ) {
494
+ q -= 1;
495
+ }
496
+ return q;
497
+ }
498
+
499
+ /* CArray.__time_ticks_from_fields__(year, mon, day, sec_of_day, frac_ns,
500
+ offset, present, kind, per)
501
+ -> int64 ticks
502
+
503
+ Ticks since the epoch from date and time fields already parsed, one
504
+ int64 array per field (offset in seconds east of UTC). Cells whose
505
+ present byte is 0 come back masked, and so do cells whose tick does not
506
+ fit int64. kind :fixed counts ticks of `per` nanoseconds; kind
507
+ :calendar counts ticks of `per` months and reads the year and month
508
+ only. */
509
+ static VALUE
510
+ rb_ca_s_time_ticks_from_fields (VALUE klass,
511
+ VALUE vyear, VALUE vmon, VALUE vday,
512
+ VALUE vsod, VALUE vfrac, VALUE voff,
513
+ VALUE vpresent, VALUE rkind, VALUE rper)
514
+ {
515
+ volatile VALUE out, vmask;
516
+ CArray *cy, *cm, *cd, *cs, *cf, *co_, *cp, *co, *cmask;
517
+ int64_t *y, *mo, *d, *sod, *frac, *off, *ticks;
518
+ uint8_t *present;
519
+ boolean8_t *m_out;
520
+ int64_t per = NUM2LL(rper);
521
+ int calendar = ( SYM2ID(rb_to_symbol(rkind)) == rb_intern("calendar") );
522
+ ca_size_t i, n;
523
+
524
+ if ( per <= 0 ) {
525
+ rb_raise(rb_eArgError, "tick length must be positive");
526
+ }
527
+ #define CA_TIME_FIELD(v, c, field, T) \
528
+ TypedData_Get_Struct(v, CArray, &carray_data_type, c); \
529
+ if ( ! ca_is_entity(c) ) { \
530
+ rb_raise(rb_eArgError, "time fields must be entities"); \
531
+ } \
532
+ field = (T *) c->ptr;
533
+ CA_TIME_FIELD(vyear, cy, y, int64_t);
534
+ CA_TIME_FIELD(vmon, cm, mo, int64_t);
535
+ CA_TIME_FIELD(vday, cd, d, int64_t);
536
+ CA_TIME_FIELD(vsod, cs, sod, int64_t);
537
+ CA_TIME_FIELD(vfrac, cf, frac, int64_t);
538
+ CA_TIME_FIELD(voff, co_, off, int64_t);
539
+ CA_TIME_FIELD(vpresent, cp, present, uint8_t);
540
+ #undef CA_TIME_FIELD
541
+ n = cy->elements;
542
+ if ( cy->data_type != CA_INT64 || cm->data_type != CA_INT64
543
+ || cd->data_type != CA_INT64 || cs->data_type != CA_INT64
544
+ || cf->data_type != CA_INT64 || co_->data_type != CA_INT64
545
+ || cp->data_type != CA_UINT8
546
+ || cm->elements != n || cd->elements != n || cs->elements != n
547
+ || cf->elements != n || co_->elements != n || cp->elements != n ) {
548
+ rb_raise(rb_eArgError, "time fields must be int64 arrays of one size "
549
+ "(present uint8)");
550
+ }
551
+
552
+ out = rb_carray_new(CA_INT64, 1, &n, 0, NULL);
553
+ TypedData_Get_Struct(out, CArray, &carray_data_type, co);
554
+ vmask = rb_carray_new(CA_BOOLEAN, 1, &n, 0, NULL);
555
+ ticks = (int64_t *) co->ptr;
556
+ TypedData_Get_Struct(vmask, CArray, &carray_data_type, cmask);
557
+ m_out = (boolean8_t *) cmask->ptr;
558
+
559
+ for (i = 0; i < n; i++) {
560
+ __int128 t;
561
+ if ( ! present[i] ) {
562
+ m_out[i] = 1;
563
+ ticks[i] = 0;
564
+ continue;
565
+ }
566
+ if ( calendar ) {
567
+ t = ca_time_floor_div((__int128) ( y[i] - 1970 ) * 12 + ( mo[i] - 1 ), per);
568
+ }
569
+ else {
570
+ __int128 ns = (__int128) ca_time_text_days_from_civil(y[i], mo[i], d[i])
571
+ * ca_ns_per_day
572
+ + (__int128) ( sod[i] - off[i] ) * 1000000000
573
+ + frac[i];
574
+ t = ca_time_floor_div(ns, per);
575
+ }
576
+ if ( t > INT64_MAX || t < INT64_MIN ) {
577
+ m_out[i] = 1;
578
+ ticks[i] = 0;
579
+ }
580
+ else {
581
+ m_out[i] = 0;
582
+ ticks[i] = (int64_t) t;
583
+ }
584
+ }
585
+ ca_mask_from_bytes(co, m_out);
586
+ RB_GC_GUARD(vmask);
587
+ return out;
588
+ }
589
+
590
+ /* ------------------------------------------------------------------------
591
+ Reading text in a strptime format
592
+
593
+ This reads the directives Date._strptime reads for a date and a time of
594
+ day, the same way, so that a format answers the same in either: %Y %y %m
595
+ %d %e %H %k %I %l %M %S %N %L %p %P %b %B %h %a %A %z %Z %n %t %%, and
596
+ %D %F %R %T %X %x, which stand for longer formats. A format with any
597
+ other directive is not read here (the caller uses Date._strptime).
598
+ Text left over after the format makes a cell unreadable, unless it is
599
+ whitespace. A zone this reader does not know the offset of (a name
600
+ other than Z, UTC and GMT, an offset written another way) is deferred to
601
+ Date._strptime for that cell.
602
+
603
+ Strict reading, used to find the format a column is written in, holds
604
+ the text to the shape of the format: %Y is four digits and %y two, %N
605
+ one to nine digits, the other numbers one or two digits (two when a
606
+ number follows directly), no sign and no leading space before a number,
607
+ %p takes AM or PM only, an unknown zone is unreadable rather than
608
+ deferred, and blank text is missing. Whitespace in the format matches
609
+ any run of whitespace, none included, in both readings.
610
+ ------------------------------------------------------------------------ */
611
+
612
+ enum {
613
+ CA_STRP_OK = 0,
614
+ CA_STRP_FAIL,
615
+ CA_STRP_DEFER
616
+ };
617
+
618
+ typedef struct {
619
+ int64_t year, mon, mday, hour, min, sec, frac_ns, offset;
620
+ int has_year, has_hour, merid, frac_digits;
621
+ } ca_strp_t;
622
+
623
+ static const char *ca_strp_months[] = {
624
+ "January", "February", "March", "April", "May", "June", "July",
625
+ "August", "September", "October", "November", "December"
626
+ };
627
+ static const char *ca_strp_days[] = {
628
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
629
+ "Saturday"
630
+ };
631
+
632
+ /* Whether a number follows directly: a digit, or a numeric directive. */
633
+ static int
634
+ ca_strp_number_next (const char *f, long flen, long fi)
635
+ {
636
+ char c;
637
+ if ( fi >= flen ) {
638
+ return 0;
639
+ }
640
+ c = f[fi];
641
+ if ( c >= '0' && c <= '9' ) {
642
+ return 1;
643
+ }
644
+ if ( c == '%' && fi + 1 < flen ) {
645
+ c = f[fi + 1];
646
+ if ( c == 'E' || c == 'O' ) {
647
+ c = ( fi + 2 < flen ) ? f[fi + 2] : '\0';
648
+ }
649
+ return c != '\0' && ( strchr("CDdeFGgHIjkLlMmNQRrSsTUuVvWwXxYy", c) != NULL
650
+ || ( c >= '0' && c <= '9' ) );
651
+ }
652
+ return 0;
653
+ }
654
+
655
+ /* Up to `width` digits (width 0: as many as there are). Returns the count
656
+ read, with the value of the first 18 in *v. */
657
+ static long
658
+ ca_strp_digits (const char *s, long n, long *si, long width, int64_t *v)
659
+ {
660
+ long count = 0;
661
+ *v = 0;
662
+ while ( *si < n && s[*si] >= '0' && s[*si] <= '9'
663
+ && ( width == 0 || count < width ) ) {
664
+ if ( count < 18 ) {
665
+ *v = *v * 10 + ( s[*si] - '0' );
666
+ }
667
+ (*si)++;
668
+ count++;
669
+ }
670
+ return count;
671
+ }
672
+
673
+ /* A one-or-two-digit field: %d %e %H %k %I %l %M %m %S. */
674
+ static int
675
+ ca_strp_small (const char *s, long n, long *si, int space_form, int strict,
676
+ int number_next, int64_t lo, int64_t hi, int64_t *v)
677
+ {
678
+ long count;
679
+ if ( space_form && ! strict && s[*si] == ' ' ) {
680
+ (*si)++;
681
+ count = ca_strp_digits(s, n, si, 1, v);
682
+ }
683
+ else {
684
+ count = ca_strp_digits(s, n, si, 2, v);
685
+ if ( strict && number_next && count != 2 ) {
686
+ return CA_STRP_FAIL;
687
+ }
688
+ }
689
+ if ( count == 0 || *v < lo || *v > hi ) {
690
+ return CA_STRP_FAIL;
691
+ }
692
+ return CA_STRP_OK;
693
+ }
694
+
695
+ static int
696
+ ca_strp_name (const char *s, long n, long *si, const char **names, int count)
697
+ {
698
+ int i;
699
+ for (i = 0; i < count; i++) {
700
+ long len = (long) strlen(names[i]);
701
+ if ( n - *si >= len && strncasecmp(names[i], s + *si, len) == 0 ) {
702
+ *si += len;
703
+ return i;
704
+ }
705
+ if ( n - *si >= 3 && strncasecmp(names[i], s + *si, 3) == 0 ) {
706
+ *si += 3;
707
+ return i;
708
+ }
709
+ }
710
+ return -1;
711
+ }
712
+
713
+ static int
714
+ ca_strp_is_alpha (char c)
715
+ {
716
+ return ( c >= 'A' && c <= 'Z' ) || ( c >= 'a' && c <= 'z' );
717
+ }
718
+
719
+ /* %z: [+-]hh, [+-]hhmm, [+-]hh:mm, Z, UTC, GMT. */
720
+ static int
721
+ ca_strp_zone (const char *s, long n, long *si, int strict, int64_t *offset)
722
+ {
723
+ int unknown = strict ? CA_STRP_FAIL : CA_STRP_DEFER;
724
+ long i = *si;
725
+ if ( s[i] == '+' || s[i] == '-' ) {
726
+ int negative = ( s[i] == '-' );
727
+ int64_t hh, mm = 0;
728
+ long count;
729
+ i++;
730
+ count = ca_strp_digits(s, n, &i, 0, &hh);
731
+ if ( count == 4 ) {
732
+ mm = hh % 100;
733
+ hh = hh / 100;
734
+ }
735
+ else if ( count == 2 ) {
736
+ if ( i < n && s[i] == ':' ) {
737
+ i++;
738
+ if ( ca_strp_digits(s, n, &i, 0, &mm) != 2 ) {
739
+ return unknown;
740
+ }
741
+ }
742
+ }
743
+ else {
744
+ return unknown;
745
+ }
746
+ if ( i < n && ( s[i] == ':' || s[i] == '.' || s[i] == ','
747
+ || ( s[i] >= '0' && s[i] <= '9' ) ) ) {
748
+ return unknown;
749
+ }
750
+ if ( hh > 23 || mm > 59 ) {
751
+ return unknown;
752
+ }
753
+ *offset = ( hh * 3600 + mm * 60 ) * ( negative ? -1 : 1 );
754
+ *si = i;
755
+ return CA_STRP_OK;
756
+ }
757
+ if ( ca_strp_is_alpha(s[i]) ) {
758
+ long len = 0;
759
+ while ( i + len < n && ca_strp_is_alpha(s[i + len]) ) {
760
+ len++;
761
+ }
762
+ if ( ( len == 1 && s[i] == 'Z' )
763
+ || ( len == 3 && ( strncmp(s + i, "UTC", 3) == 0
764
+ || strncmp(s + i, "GMT", 3) == 0 ) ) ) {
765
+ long j = i + len;
766
+ /* "UTC+09:00", "GMT standard time", "UTC dst" mean something else */
767
+ if ( j < n && ( s[j] == '+' || s[j] == '-' || s[j] == '.' ) ) {
768
+ return unknown;
769
+ }
770
+ if ( j < n && s[j] == ' ' ) {
771
+ long k = j;
772
+ while ( k < n && s[k] == ' ' ) {
773
+ k++;
774
+ }
775
+ if ( k < n && ca_strp_is_alpha(s[k]) ) {
776
+ return unknown;
777
+ }
778
+ }
779
+ *offset = 0;
780
+ *si = j;
781
+ return CA_STRP_OK;
782
+ }
783
+ return unknown;
784
+ }
785
+ return CA_STRP_FAIL;
786
+ }
787
+
788
+ static int ca_strp_run (const char *s, long n, long *si,
789
+ const char *f, long flen, int strict, ca_strp_t *t);
790
+
791
+ static int
792
+ ca_strp_sub (const char *s, long n, long *si, const char *sub, int strict,
793
+ ca_strp_t *t)
794
+ {
795
+ return ca_strp_run(s, n, si, sub, (long) strlen(sub), strict, t);
796
+ }
797
+
798
+ static int
799
+ ca_strp_run (const char *s, long n, long *si, const char *f, long flen,
800
+ int strict, ca_strp_t *t)
801
+ {
802
+ long fi = 0;
803
+ while ( fi < flen ) {
804
+ char c = f[fi];
805
+ if ( ca_time_text_is_space(c) ) {
806
+ while ( *si < n && ca_time_text_is_space(s[*si]) ) {
807
+ (*si)++;
808
+ }
809
+ while ( fi < flen && ca_time_text_is_space(f[fi]) ) {
810
+ fi++;
811
+ }
812
+ continue;
813
+ }
814
+ if ( *si >= n ) {
815
+ return CA_STRP_FAIL;
816
+ }
817
+ if ( c != '%' ) {
818
+ if ( s[*si] != c ) {
819
+ return CA_STRP_FAIL;
820
+ }
821
+ (*si)++;
822
+ fi++;
823
+ continue;
824
+ }
825
+ {
826
+ int rc = CA_STRP_OK;
827
+ int number_next;
828
+ int64_t v;
829
+ char d = ( fi + 1 < flen ) ? f[fi + 1] : '\0';
830
+ fi += 2;
831
+ number_next = ca_strp_number_next(f, flen, fi);
832
+ switch ( d ) {
833
+ case 'a': case 'A':
834
+ if ( ca_strp_name(s, n, si, ca_strp_days, 7) < 0 ) {
835
+ return CA_STRP_FAIL;
836
+ }
837
+ break;
838
+ case 'b': case 'B': case 'h': {
839
+ int m = ca_strp_name(s, n, si, ca_strp_months, 12);
840
+ if ( m < 0 ) {
841
+ return CA_STRP_FAIL;
842
+ }
843
+ t->mon = m + 1;
844
+ break;
845
+ }
846
+ case 'd': case 'e':
847
+ rc = ca_strp_small(s, n, si, 1, strict, number_next, 1, 31, &t->mday);
848
+ break;
849
+ case 'H': case 'k':
850
+ rc = ca_strp_small(s, n, si, 1, strict, number_next, 0, 24, &t->hour);
851
+ t->has_hour = 1;
852
+ break;
853
+ case 'I': case 'l':
854
+ rc = ca_strp_small(s, n, si, 1, strict, number_next, 1, 12, &t->hour);
855
+ t->has_hour = 1;
856
+ break;
857
+ case 'M':
858
+ rc = ca_strp_small(s, n, si, 0, strict, number_next, 0, 59, &t->min);
859
+ break;
860
+ case 'm':
861
+ rc = ca_strp_small(s, n, si, 0, strict, number_next, 1, 12, &t->mon);
862
+ break;
863
+ case 'S':
864
+ rc = ca_strp_small(s, n, si, 0, strict, number_next, 0, 60, &t->sec);
865
+ break;
866
+ case 'L': case 'N': {
867
+ int negative = 0;
868
+ long count, start, k;
869
+ int64_t ns = 0;
870
+ int rest = 0;
871
+ if ( ! strict && ( s[*si] == '+' || s[*si] == '-' ) ) {
872
+ negative = ( s[*si] == '-' );
873
+ (*si)++;
874
+ }
875
+ start = *si;
876
+ count = ca_strp_digits(s, n, si,
877
+ strict ? 9 : ( number_next ? ( d == 'L' ? 3 : 9 ) : 0 ),
878
+ &v);
879
+ if ( count == 0 ) {
880
+ return CA_STRP_FAIL;
881
+ }
882
+ for (k = 0; k < 9; k++) {
883
+ ns = ns * 10 + ( k < count ? s[start + k] - '0' : 0 );
884
+ }
885
+ for (k = 9; k < count; k++) {
886
+ rest |= ( s[start + k] != '0' );
887
+ }
888
+ if ( negative ) {
889
+ ns = - ns - ( rest ? 1 : 0 ); /* floor, as (fraction * 1e9).floor */
890
+ }
891
+ t->frac_ns = ns;
892
+ t->frac_digits = (int) count;
893
+ break;
894
+ }
895
+ case 'p': case 'P': {
896
+ char a, m;
897
+ int hour;
898
+ if ( n - *si < 2 ) {
899
+ return CA_STRP_FAIL;
900
+ }
901
+ a = s[*si];
902
+ hour = ( a == 'P' || a == 'p' ) ? 12 : 0;
903
+ if ( ! hour && ! ( a == 'A' || a == 'a' ) ) {
904
+ return CA_STRP_FAIL;
905
+ }
906
+ m = s[*si + 1];
907
+ if ( m == '.' && ! strict ) {
908
+ if ( n - *si < 4 || s[*si + 3] != '.' ) {
909
+ return CA_STRP_FAIL;
910
+ }
911
+ *si += 2;
912
+ m = s[*si];
913
+ }
914
+ if ( ! ( m == 'M' || m == 'm' ) ) {
915
+ return CA_STRP_FAIL;
916
+ }
917
+ *si += 2;
918
+ t->merid = hour;
919
+ break;
920
+ }
921
+ case 'Y': {
922
+ int negative = 0;
923
+ long count;
924
+ if ( ! strict && ( s[*si] == '+' || s[*si] == '-' ) ) {
925
+ negative = ( s[*si] == '-' );
926
+ (*si)++;
927
+ }
928
+ count = ca_strp_digits(s, n, si, ( strict || number_next ) ? 4 : 0, &v);
929
+ if ( count == 0 || ( strict && count != 4 ) ) {
930
+ return CA_STRP_FAIL;
931
+ }
932
+ if ( count > 9 ) {
933
+ return CA_STRP_DEFER; /* a year past int64 days */
934
+ }
935
+ t->year = negative ? -v : v;
936
+ t->has_year = 1;
937
+ break;
938
+ }
939
+ case 'y': {
940
+ long count = ca_strp_digits(s, n, si, 2, &v);
941
+ if ( count == 0 || ( strict && count != 2 ) ) {
942
+ return CA_STRP_FAIL;
943
+ }
944
+ t->year = v + ( v >= 69 ? 1900 : 2000 );
945
+ t->has_year = 1;
946
+ break;
947
+ }
948
+ case 'z': case 'Z':
949
+ rc = ca_strp_zone(s, n, si, strict, &t->offset);
950
+ break;
951
+ case 'n': case 't':
952
+ rc = ca_strp_sub(s, n, si, " ", strict, t);
953
+ break;
954
+ case 'D': case 'x':
955
+ rc = ca_strp_sub(s, n, si, "%m/%d/%y", strict, t);
956
+ break;
957
+ case 'F':
958
+ rc = ca_strp_sub(s, n, si, "%Y-%m-%d", strict, t);
959
+ break;
960
+ case 'R':
961
+ rc = ca_strp_sub(s, n, si, "%H:%M", strict, t);
962
+ break;
963
+ case 'T': case 'X':
964
+ rc = ca_strp_sub(s, n, si, "%H:%M:%S", strict, t);
965
+ break;
966
+ case '%':
967
+ if ( s[*si] != '%' ) {
968
+ return CA_STRP_FAIL;
969
+ }
970
+ (*si)++;
971
+ break;
972
+ default:
973
+ return CA_STRP_FAIL; /* not reached: checked up front */
974
+ }
975
+ if ( rc != CA_STRP_OK ) {
976
+ return rc;
977
+ }
978
+ }
979
+ }
980
+ return CA_STRP_OK;
981
+ }
982
+
983
+ /* Whether every directive of the format is one ca_strp_run reads. */
984
+ static int
985
+ ca_strp_supported (const char *f, long flen)
986
+ {
987
+ long i;
988
+ for (i = 0; i < flen; i++) {
989
+ if ( f[i] == '%' ) {
990
+ char d = ( i + 1 < flen ) ? f[i + 1] : '\0';
991
+ if ( d == '\0' || strchr("YymdeHkIlMSNLpPbBhaAzZntDFRTXx%", d) == NULL ) {
992
+ return 0;
993
+ }
994
+ i++;
995
+ }
996
+ }
997
+ return 1;
998
+ }
999
+
1000
+ /* One text in a format: CA_STRP_OK with the fields in *t, CA_STRP_FAIL,
1001
+ or CA_STRP_DEFER. */
1002
+ static int
1003
+ ca_strp_text (VALUE str, const char *f, long flen, int strict, ca_strp_t *t)
1004
+ {
1005
+ const char *s = RSTRING_PTR(str);
1006
+ long n = RSTRING_LEN(str);
1007
+ long si = 0;
1008
+ int rc;
1009
+ memset(t, 0, sizeof(*t));
1010
+ t->mon = 1;
1011
+ t->mday = 1;
1012
+ t->merid = -1;
1013
+ rc = ca_strp_run(s, n, &si, f, flen, strict, t);
1014
+ if ( rc != CA_STRP_OK ) {
1015
+ return rc;
1016
+ }
1017
+ while ( si < n && ca_time_text_is_space(s[si]) ) {
1018
+ si++;
1019
+ }
1020
+ if ( si < n || ! t->has_year ) {
1021
+ return CA_STRP_FAIL;
1022
+ }
1023
+ if ( t->merid >= 0 && t->has_hour ) {
1024
+ t->hour = t->hour % 12 + t->merid;
1025
+ }
1026
+ /* Hour 24 is the end of the day, 24:00:00, and nothing past it: 24:30
1027
+ would otherwise roll into the next day. */
1028
+ if ( t->hour == 24 && ( t->min || t->sec || t->frac_ns ) ) {
1029
+ return CA_STRP_FAIL;
1030
+ }
1031
+ if ( t->mday > ca_time_text_month_days(t->year, t->mon) ) {
1032
+ return CA_STRP_FAIL;
1033
+ }
1034
+ return CA_STRP_OK;
1035
+ }
1036
+
1037
+ /* CArray#__strptime_fields__(format, strict)
1038
+ -> [year, mon, day, sec_of_day, frac_ns, offset, present,
1039
+ unreadable, deferred, fraction_digits] or nil
1040
+
1041
+ Reads every cell of an object array in a strptime format into the int64
1042
+ field arrays CArray.__time_ticks_from_fields__ takes (present is uint8).
1043
+ nil when the format has a directive this reader does not read. A cell
1044
+ that holds text and does not read is listed by its address in
1045
+ `unreadable`; a cell that is not a String, or holds a zone or a year
1046
+ this reader leaves to Date._strptime, in `deferred`; nil and masked
1047
+ cells in neither, and blank text too when strict (a blank cell does not
1048
+ read in a format, as Date._strptime has it). fraction_digits is the widest fraction of a
1049
+ second read. */
1050
+ static VALUE
1051
+ rb_ca_strptime_fields (VALUE self, VALUE rformat, VALUE rstrict)
1052
+ {
1053
+ volatile VALUE src = self, rf = rformat;
1054
+ VALUE fields[7];
1055
+ volatile VALUE unreadable, deferred;
1056
+ CArray *ca, *cf[7];
1057
+ int64_t *y, *mo, *d, *sod, *frac, *off;
1058
+ uint8_t *present;
1059
+ VALUE *cells;
1060
+ boolean8_t *m_in;
1061
+ const char *f;
1062
+ long flen;
1063
+ int strict = RTEST(rstrict);
1064
+ int fraction = 0;
1065
+ ca_size_t i;
1066
+ int k;
1067
+
1068
+ StringValue(rf);
1069
+ f = RSTRING_PTR(rf);
1070
+ flen = RSTRING_LEN(rf);
1071
+ if ( ! ca_strp_supported(f, flen) ) {
1072
+ return Qnil;
1073
+ }
1074
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
1075
+ if ( ca->data_type != CA_OBJECT ) {
1076
+ rb_raise(rb_eArgError, "strptime reading needs an object array");
1077
+ }
1078
+ if ( ! ca_is_entity(ca) ) {
1079
+ src = rb_ca_copy(src);
1080
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
1081
+ }
1082
+ for (k = 0; k < 7; k++) {
1083
+ fields[k] = rb_carray_new(k == 6 ? CA_UINT8 : CA_INT64, 1, &ca->elements, 0, NULL);
1084
+ TypedData_Get_Struct(fields[k], CArray, &carray_data_type, cf[k]);
1085
+ }
1086
+ y = (int64_t *) cf[0]->ptr;
1087
+ mo = (int64_t *) cf[1]->ptr;
1088
+ d = (int64_t *) cf[2]->ptr;
1089
+ sod = (int64_t *) cf[3]->ptr;
1090
+ frac = (int64_t *) cf[4]->ptr;
1091
+ off = (int64_t *) cf[5]->ptr;
1092
+ present = (uint8_t *) cf[6]->ptr;
1093
+ unreadable = rb_ary_new();
1094
+ deferred = rb_ary_new();
1095
+
1096
+ cells = (VALUE *) ca->ptr;
1097
+ m_in = ca_has_mask(ca) ? (boolean8_t *) ca->mask->ptr : NULL;
1098
+ for (i = 0; i < ca->elements; i++) {
1099
+ VALUE v = cells[i];
1100
+ ca_strp_t t;
1101
+ int rc;
1102
+ y[i] = 1970; mo[i] = 1; d[i] = 1; sod[i] = 0; frac[i] = 0; off[i] = 0;
1103
+ present[i] = 0;
1104
+ if ( ( m_in && m_in[i] ) || NIL_P(v) ) {
1105
+ continue;
1106
+ }
1107
+ if ( ! RB_TYPE_P(v, T_STRING) ) {
1108
+ rb_ary_push(deferred, SIZET2NUM(i));
1109
+ continue;
1110
+ }
1111
+ if ( strict ) { /* finding a format: blank is missing */
1112
+ const char *s = RSTRING_PTR(v);
1113
+ long n = RSTRING_LEN(v), j = 0;
1114
+ while ( j < n && ca_time_text_is_space(s[j]) ) {
1115
+ j++;
1116
+ }
1117
+ if ( j == n ) {
1118
+ continue;
1119
+ }
1120
+ }
1121
+ rc = ca_strp_text(v, f, flen, strict, &t);
1122
+ if ( rc == CA_STRP_OK ) {
1123
+ y[i] = t.year;
1124
+ mo[i] = t.mon;
1125
+ d[i] = t.mday;
1126
+ sod[i] = t.hour * 3600 + t.min * 60 + t.sec;
1127
+ frac[i] = t.frac_ns;
1128
+ off[i] = t.offset;
1129
+ present[i] = 1;
1130
+ if ( t.frac_digits > fraction ) {
1131
+ fraction = t.frac_digits;
1132
+ }
1133
+ }
1134
+ else if ( rc == CA_STRP_DEFER ) {
1135
+ rb_ary_push(deferred, SIZET2NUM(i));
1136
+ }
1137
+ else {
1138
+ rb_ary_push(unreadable, SIZET2NUM(i));
1139
+ }
1140
+ }
1141
+ RB_GC_GUARD(src);
1142
+ RB_GC_GUARD(rf);
1143
+ return rb_ary_new_from_args(10, fields[0], fields[1], fields[2], fields[3],
1144
+ fields[4], fields[5], fields[6],
1145
+ unreadable, deferred, INT2NUM(fraction));
1146
+ }
1147
+
1148
+ /* CArray.__strptime_fits__(text, formats) -> indices
1149
+
1150
+ The indices of the formats `text` reads in, strictly. */
1151
+ static VALUE
1152
+ rb_ca_s_strptime_fits (VALUE klass, VALUE text, VALUE formats)
1153
+ {
1154
+ volatile VALUE out = rb_ary_new();
1155
+ long i;
1156
+ StringValue(text);
1157
+ Check_Type(formats, T_ARRAY);
1158
+ for (i = 0; i < RARRAY_LEN(formats); i++) {
1159
+ VALUE f = rb_ary_entry(formats, i);
1160
+ ca_strp_t t;
1161
+ StringValue(f);
1162
+ if ( ca_strp_supported(RSTRING_PTR(f), RSTRING_LEN(f))
1163
+ && ca_strp_text(text, RSTRING_PTR(f), RSTRING_LEN(f), 1, &t) == CA_STRP_OK ) {
1164
+ rb_ary_push(out, LONG2NUM(i));
1165
+ }
1166
+ }
1167
+ return out;
1168
+ }
1169
+
1170
+ void
1171
+ Init_carray_time_text (void)
1172
+ {
1173
+ /* Internal: CAFrame reads text columns as time with this reader. */
1174
+ rb_define_method(rb_cCArray, "__parse_time_text__", rb_ca_parse_time_text, -1);
1175
+ /* Internal: CArray.time turns parsed fields into ticks with this. */
1176
+ rb_define_singleton_method(rb_cCArray, "__time_ticks_from_fields__",
1177
+ rb_ca_s_time_ticks_from_fields, 9);
1178
+ /* Internal: CArray.time and CAFrame read a strptime format with these. */
1179
+ rb_define_method(rb_cCArray, "__strptime_fields__", rb_ca_strptime_fields, 2);
1180
+ rb_define_singleton_method(rb_cCArray, "__strptime_fits__",
1181
+ rb_ca_s_strptime_fits, 2);
1182
+ }