carray 3.0.4 → 3.0.5

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +205 -0
  3. data/carray.gemspec +1 -1
  4. data/ext/ca_array_pool.c +25 -1
  5. data/ext/ca_axis_dispatch.c +13 -20
  6. data/ext/ca_categorical_iterator.c +27 -20
  7. data/ext/ca_compare.c +4 -4
  8. data/ext/ca_composite_dispatch.c +9 -9
  9. data/ext/ca_kernel_iterator.c +6 -6
  10. data/ext/ca_kernel_iterator.h +112 -83
  11. data/ext/ca_obj_array.c +69 -19
  12. data/ext/ca_obj_bincmp.c +1 -0
  13. data/ext/ca_obj_binop.c +1 -0
  14. data/ext/ca_obj_bitarray.c +5 -1
  15. data/ext/ca_obj_bitfield.c +58 -43
  16. data/ext/ca_obj_byte_swap.c +2 -1
  17. data/ext/ca_obj_const_string.c +15 -3
  18. data/ext/ca_obj_face.c +42 -1
  19. data/ext/ca_obj_field.c +13 -3
  20. data/ext/ca_obj_grid.c +52 -5
  21. data/ext/ca_obj_monop.c +46 -10
  22. data/ext/ca_obj_object.c +64 -12
  23. data/ext/ca_obj_record.c +3 -0
  24. data/ext/ca_obj_reduce.c +4 -0
  25. data/ext/ca_obj_refer.c +18 -9
  26. data/ext/ca_obj_roll.c +26 -6
  27. data/ext/ca_obj_select.c +34 -5
  28. data/ext/ca_obj_shift.c +31 -7
  29. data/ext/ca_obj_stack.c +1 -0
  30. data/ext/ca_obj_stride.c +28 -19
  31. data/ext/ca_obj_string.c +11 -2
  32. data/ext/ca_obj_tile.c +27 -7
  33. data/ext/ca_obj_transpose.c +8 -4
  34. data/ext/ca_obj_triop.c +3 -0
  35. data/ext/ca_obj_window.c +79 -50
  36. data/ext/ca_op_ipower.c +4 -4
  37. data/ext/ca_sort_kernels.h +6 -3
  38. data/ext/carray.h +33 -0
  39. data/ext/carray_access.c +16 -3
  40. data/ext/carray_attribute.c +24 -0
  41. data/ext/carray_cast.c +243 -82
  42. data/ext/carray_cast_func.rb +56 -26
  43. data/ext/carray_conversion.c +2 -2
  44. data/ext/carray_core.c +3 -0
  45. data/ext/carray_count.c +6 -0
  46. data/ext/carray_element.c +9 -4
  47. data/ext/carray_factorize.c +20 -20
  48. data/ext/carray_generate.c +8 -2
  49. data/ext/carray_histogram.c +1 -1
  50. data/ext/carray_index_classifier.c +4 -4
  51. data/ext/carray_internal.h +12 -0
  52. data/ext/carray_kernels_bincmp.c +168 -168
  53. data/ext/carray_kernels_binop.c +900 -900
  54. data/ext/carray_kernels_init.c +4 -4
  55. data/ext/carray_kernels_moncmp.c +96 -96
  56. data/ext/carray_kernels_monop.c +1984 -1984
  57. data/ext/carray_kernels_reduce_aggregate.c +1733 -1733
  58. data/ext/carray_kernels_reduce_cumulative.c +642 -642
  59. data/ext/carray_kernels_reduce_extreme.c +1150 -1150
  60. data/ext/carray_kernels_reduce_variance.c +258 -258
  61. data/ext/carray_kernels_scan.c +128 -128
  62. data/ext/carray_kernels_search.c +340 -340
  63. data/ext/carray_kernels_sort.c +295 -304
  64. data/ext/carray_kernels_triop.c +114 -114
  65. data/ext/carray_key_windows.c +4 -0
  66. data/ext/carray_mask.c +45 -22
  67. data/ext/carray_memory_view.c +2 -2
  68. data/ext/carray_operator.c +5 -5
  69. data/ext/carray_order.c +227 -8
  70. data/ext/carray_random.c +18 -18
  71. data/ext/carray_scatter.c +24 -7
  72. data/ext/carray_slab.c +32 -9
  73. data/ext/carray_sort.c +224 -16
  74. data/ext/carray_sort_kernel.c +5 -7
  75. data/ext/carray_struct.c +35 -43
  76. data/ext/carray_time_text.c +4 -2
  77. data/ext/carray_utils.c +63 -21
  78. data/ext/mkkernel.rb +210 -115
  79. data/ext/version.h +4 -4
  80. data/lib/carray/attribute.rb +17 -4
  81. data/lib/carray/basics.rb +27 -7
  82. data/lib/carray/categorical.rb +30 -16
  83. data/lib/carray/categorical_iterator.rb +6 -6
  84. data/lib/carray/const_string.rb +10 -0
  85. data/lib/carray/data_type_extension.rb +8 -1
  86. data/lib/carray/fixlen_string.rb +41 -2
  87. data/lib/carray/frame/column_verbs.rb +4 -3
  88. data/lib/carray/frame/concat.rb +17 -3
  89. data/lib/carray/frame/frame.rb +6 -7
  90. data/lib/carray/frame/group.rb +100 -13
  91. data/lib/carray/frame/io.rb +3 -1
  92. data/lib/carray/fuse_source.rb +31 -2
  93. data/lib/carray/lazy.rb +35 -0
  94. data/lib/carray/methods/is_in.rb +8 -1
  95. data/lib/carray/segment_iterator.rb +12 -8
  96. data/lib/carray/serialize.rb +19 -2
  97. data/lib/carray/stack.rb +15 -12
  98. data/lib/carray/string.rb +8 -0
  99. data/lib/carray/string_operation_extension.rb +13 -5
  100. data/lib/carray/struct_builder.rb +5 -3
  101. data/lib/carray/time.rb +107 -68
  102. data/yard-stubs/ca_obj_transpose.rb +5 -4
  103. data/yard-stubs/carray_attribute.rb +3 -1
  104. data/yard-stubs/carray_cast.rb +2 -0
  105. data/yard-stubs/carray_conversion.rb +5 -1
  106. data/yard-stubs/carray_element.rb +7 -6
  107. data/yard-stubs/carray_mask.rb +8 -3
  108. data/yard-stubs/carray_math.rb +14 -4
  109. data/yard-stubs/carray_order.rb +100 -4
  110. data/yard-stubs/carray_scatter.rb +7 -5
  111. metadata +1 -1
data/ext/carray_sort.c CHANGED
@@ -103,16 +103,207 @@ sort_addr_merge (struct sort_addr_key *a, struct sort_addr_key *aux,
103
103
  ca_size_t n, int (*cmp)(const void *, const void *))
104
104
  CA_SORT_MERGE_BY_CMP(struct sort_addr_key)
105
105
 
106
+ /* -------------------------------------------------------------------
107
+
108
+ Multi-key sort by refinement (MSD), for keys of the numeric data types.
109
+
110
+ The first key is sorted whole by the typed pair kernel
111
+ (ca_sort_quick_pair_*); then only the runs of rows that tie on it are
112
+ sorted by the second key, and so on, until no run is left or the keys
113
+ run out. The answer is the one sort_addr_cmp gives:
114
+
115
+ - a pair sorts by (value, row), so rows that tie on every key stay in
116
+ row order (the stable tie-break of sort_addr_cmp);
117
+ - each run is split into numbers, NaN and masked cells, in the order
118
+ sort_addr_cmp puts them (numbers, NaN, masked for masked_last;
119
+ masked, numbers, NaN otherwise). The NaN and the masked cells of a
120
+ run tie among themselves (CA_FLOAT_ORDER_CMP / sort_addr_cmp), so
121
+ each is a run for the next key. The split is a stable one done in
122
+ the load, not ca_partition_nan_pair_*, which allocates on every call
123
+ and is called here once per run;
124
+ - -0.0 and 0.0 tie, by == as by CA_FLOAT_ORDER_CMP.
125
+
126
+ A run is (lo, n): the rows perm[lo..lo+n). Within a run the rows are
127
+ in row order, since they tie on every key before it, so the stable
128
+ split keeps them so.
129
+
130
+ ------------------------------------------------------------------- */
131
+
132
+ typedef struct {
133
+ ca_size_t lo;
134
+ ca_size_t n;
135
+ } msd_run_t;
136
+
137
+ typedef struct {
138
+ msd_run_t *runs;
139
+ ca_size_t count;
140
+ } msd_runs_t;
141
+
142
+ static void
143
+ msd_add_run (msd_runs_t *out, ca_size_t lo, ca_size_t n)
144
+ {
145
+ if ( out && n > 1 ) {
146
+ out->runs[out->count].lo = lo;
147
+ out->runs[out->count].n = n;
148
+ out->count++;
149
+ }
150
+ }
151
+
152
+ /* Sort each run of +in+ by +key+, writing the new order into perm, and
153
+ add the runs that still tie to +out+ (NULL for the last key). */
154
+ #define DEFINE_MSD_REFINE(TYPE, SUFFIX, IS_FLOAT) \
155
+ static void \
156
+ msd_refine_##SUFFIX (CArray *key, ca_size_t *perm, void *vbuf, \
157
+ msd_runs_t *in, msd_runs_t *out, int masked_last) \
158
+ { \
159
+ const TYPE *ptr = (const TYPE *) key->ptr; \
160
+ const boolean8_t *m = ( key->mask ) ? (boolean8_t *) key->mask->ptr : NULL; \
161
+ ca_pair_##SUFFIX *buf = (ca_pair_##SUFFIX *) vbuf; \
162
+ ca_size_t r; \
163
+ \
164
+ for (r=0; r<in->count; r++) { \
165
+ ca_size_t lo = in->runs[r].lo; \
166
+ ca_size_t n = in->runs[r].n; \
167
+ ca_size_t *p = perm + lo; \
168
+ ca_size_t n_num = 0, n_nan = 0, n_mask = 0; \
169
+ ca_size_t o_num, o_nan, o_mask; \
170
+ ca_size_t k, s; \
171
+ \
172
+ for (k=0; k<n; k++) { \
173
+ ca_size_t row = p[k]; \
174
+ if ( m && m[row] ) n_mask++; \
175
+ else if ( IS_FLOAT && isnan((double) ptr[row]) ) n_nan++; \
176
+ else n_num++; \
177
+ } \
178
+ o_mask = masked_last ? n_num + n_nan : 0; \
179
+ o_num = masked_last ? 0 : n_mask; \
180
+ o_nan = o_num + n_num; \
181
+ { \
182
+ ca_size_t i_num = o_num, i_nan = o_nan, i_mask = o_mask; \
183
+ for (k=0; k<n; k++) { \
184
+ ca_size_t row = p[k]; \
185
+ TYPE v = ptr[row]; \
186
+ ca_size_t at; \
187
+ if ( m && m[row] ) at = i_mask++; \
188
+ else if ( IS_FLOAT && isnan((double) v) ) at = i_nan++; \
189
+ else at = i_num++; \
190
+ buf[at].v = v; \
191
+ buf[at].i = row; \
192
+ } \
193
+ } \
194
+ ca_sort_quick_pair_##SUFFIX(buf + o_num, n_num); \
195
+ for (k=0; k<n; k++) { \
196
+ p[k] = buf[k].i; \
197
+ } \
198
+ if ( ! out ) continue; \
199
+ for (s=0, k=1; k<=n_num; k++) { \
200
+ if ( k == n_num || buf[o_num + k].v != buf[o_num + s].v ) { \
201
+ msd_add_run(out, lo + o_num + s, k - s); \
202
+ s = k; \
203
+ } \
204
+ } \
205
+ msd_add_run(out, lo + o_nan, n_nan); \
206
+ msd_add_run(out, lo + o_mask, n_mask); \
207
+ } \
208
+ }
209
+
210
+ DEFINE_MSD_REFINE(int8_t, i8, 0)
211
+ DEFINE_MSD_REFINE(uint8_t, u8, 0)
212
+ DEFINE_MSD_REFINE(int16_t, i16, 0)
213
+ DEFINE_MSD_REFINE(uint16_t, u16, 0)
214
+ DEFINE_MSD_REFINE(int32_t, i32, 0)
215
+ DEFINE_MSD_REFINE(uint32_t, u32, 0)
216
+ DEFINE_MSD_REFINE(int64_t, i64, 0)
217
+ DEFINE_MSD_REFINE(uint64_t, u64, 0)
218
+ DEFINE_MSD_REFINE(float32_t, f32, 1)
219
+ DEFINE_MSD_REFINE(double, f64, 1)
220
+
221
+ /* Whether every key has a typed pair kernel, so that MSD can sort them. */
222
+ static int
223
+ msd_is_sortable (struct cmp_base *base)
224
+ {
225
+ int j;
226
+ for (j=0; j<base->n; j++) {
227
+ switch ( base->ca[j]->data_type ) {
228
+ case CA_BOOLEAN: case CA_INT8: case CA_UINT8:
229
+ case CA_INT16: case CA_UINT16:
230
+ case CA_INT32: case CA_UINT32:
231
+ case CA_INT64: case CA_UINT64:
232
+ case CA_FLOAT32: case CA_FLOAT64:
233
+ break;
234
+ default:
235
+ return 0;
236
+ }
237
+ }
238
+ return 1;
239
+ }
240
+
241
+ typedef struct {
242
+ struct cmp_base *base;
243
+ ca_size_t *perm; /* elements rows, the answer */
244
+ void *buf; /* elements pairs of 16 bytes */
245
+ msd_run_t *runs[2]; /* elements / 2 + 1 runs each */
246
+ ca_size_t elements;
247
+ } msd_sort_t;
248
+
249
+ static void
250
+ msd_sort (msd_sort_t *s)
251
+ {
252
+ struct cmp_base *base = s->base;
253
+ msd_runs_t a, b, *in = &a, *out = &b;
254
+ ca_size_t i;
255
+ int j;
256
+
257
+ for (i=0; i<s->elements; i++) {
258
+ s->perm[i] = i;
259
+ }
260
+ a.runs = s->runs[0]; a.count = 0;
261
+ b.runs = s->runs[1]; b.count = 0;
262
+ msd_add_run(in, 0, s->elements);
263
+
264
+ for (j=0; j<base->n && in->count > 0; j++) {
265
+ CArray *key = base->ca[j];
266
+ msd_runs_t *next = ( j < base->n - 1 ) ? out : NULL;
267
+ msd_runs_t *t;
268
+ if ( next ) next->count = 0;
269
+ switch ( key->data_type ) {
270
+ case CA_BOOLEAN:
271
+ case CA_INT8: msd_refine_i8 (key, s->perm, s->buf, in, next, base->masked_last); break;
272
+ case CA_UINT8: msd_refine_u8 (key, s->perm, s->buf, in, next, base->masked_last); break;
273
+ case CA_INT16: msd_refine_i16(key, s->perm, s->buf, in, next, base->masked_last); break;
274
+ case CA_UINT16: msd_refine_u16(key, s->perm, s->buf, in, next, base->masked_last); break;
275
+ case CA_INT32: msd_refine_i32(key, s->perm, s->buf, in, next, base->masked_last); break;
276
+ case CA_UINT32: msd_refine_u32(key, s->perm, s->buf, in, next, base->masked_last); break;
277
+ case CA_INT64: msd_refine_i64(key, s->perm, s->buf, in, next, base->masked_last); break;
278
+ case CA_UINT64: msd_refine_u64(key, s->perm, s->buf, in, next, base->masked_last); break;
279
+ case CA_FLOAT32: msd_refine_f32(key, s->perm, s->buf, in, next, base->masked_last); break;
280
+ case CA_FLOAT64: msd_refine_f64(key, s->perm, s->buf, in, next, base->masked_last); break;
281
+ default:
282
+ rb_raise(rb_eRuntimeError, "%s: [BUG] data type %d has no pair kernel",
283
+ ca_calling_method_name(), (int) key->data_type);
284
+ }
285
+ if ( ! next ) break;
286
+ t = in; in = out; out = t;
287
+ }
288
+ }
289
+
290
+ /* ----------------------------------------------------------------- */
291
+
106
292
  typedef struct {
107
293
  struct sort_addr_key *data;
108
294
  struct sort_addr_key *aux; /* NULL: no object key, libc sorts */
109
295
  ca_size_t elements;
296
+ msd_sort_t *msd; /* non-NULL: every key is numeric, sort by MSD */
110
297
  } sort_addr_run_t;
111
298
 
112
299
  static VALUE
113
300
  sort_addr_run (VALUE arg)
114
301
  {
115
302
  sort_addr_run_t *r = (sort_addr_run_t *) arg;
303
+ if ( r->msd ) {
304
+ msd_sort(r->msd);
305
+ return Qnil;
306
+ }
116
307
  if ( r->aux ) {
117
308
  sort_addr_merge(r->data, r->aux, r->elements,
118
309
  (int (*)(const void*,const void*)) sort_addr_cmp);
@@ -145,8 +336,9 @@ rb_ca_s_sort_addr_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
145
336
  CArray *co;
146
337
  struct cmp_base base_s, *base = &base_s;
147
338
  struct sort_addr_key *data;
148
- volatile VALUE hlist = 0, hdata = 0, haux = 0, hmodes = 0;
339
+ volatile VALUE hlist = 0, hdata = 0, haux = 0, hmodes = 0, hruns0 = 0, hruns1 = 0;
149
340
  sort_addr_run_t run;
341
+ msd_sort_t msd;
150
342
  char *modes;
151
343
  int has_object = 0;
152
344
  ca_size_t elements;
@@ -203,26 +395,42 @@ rb_ca_s_sort_addr_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
203
395
  }
204
396
  modes[argc] = '\0';
205
397
 
206
- data = ALLOCV_N(struct sort_addr_key, hdata, elements);
207
- for (i=0; i<elements; i++) {
208
- data[i].i = i;
209
- data[i].base = base;
210
- }
211
-
212
- run.data = data;
213
- run.aux = has_object ? ALLOCV_N(struct sort_addr_key, haux, elements) : NULL;
214
- run.elements = elements;
215
- ca_attach_window(argc, base->ca, modes, sort_addr_run, (VALUE) &run);
216
-
217
398
  out = rb_ca_template_with_type(argv[0], INT2NUM(CA_SIZE), INT2NUM(0));
218
399
  TypedData_Get_Struct(out, CArray, &carray_data_type, co);
219
400
  q = (ca_size_t *) co->ptr;
220
-
221
- for (i=0; i<elements; i++) {
222
- *q = data[i].i;
223
- q++;
401
+
402
+ run.data = NULL;
403
+ run.aux = NULL;
404
+ run.elements = elements;
405
+ run.msd = NULL;
406
+
407
+ if ( msd_is_sortable(base) ) {
408
+ ca_size_t nruns = elements / 2 + 1;
409
+ msd.base = base;
410
+ msd.perm = q;
411
+ msd.buf = ALLOCV(hdata, (size_t) elements * sizeof(ca_pair_f64));
412
+ msd.runs[0] = ALLOCV_N(msd_run_t, hruns0, nruns);
413
+ msd.runs[1] = ( argc > 1 ) ? ALLOCV_N(msd_run_t, hruns1, nruns) : NULL;
414
+ msd.elements = elements;
415
+ run.msd = &msd;
416
+ ca_attach_window(argc, base->ca, modes, sort_addr_run, (VALUE) &run);
417
+ }
418
+ else {
419
+ data = ALLOCV_N(struct sort_addr_key, hdata, elements);
420
+ for (i=0; i<elements; i++) {
421
+ data[i].i = i;
422
+ data[i].base = base;
423
+ }
424
+ run.data = data;
425
+ run.aux = has_object ? ALLOCV_N(struct sort_addr_key, haux, elements) : NULL;
426
+ ca_attach_window(argc, base->ca, modes, sort_addr_run, (VALUE) &run);
427
+ for (i=0; i<elements; i++) {
428
+ q[i] = data[i].i;
429
+ }
224
430
  }
225
431
 
432
+ ALLOCV_END(hruns1);
433
+ ALLOCV_END(hruns0);
226
434
  ALLOCV_END(haux);
227
435
  ALLOCV_END(hdata);
228
436
  ALLOCV_END(hmodes);
@@ -453,16 +453,15 @@ DEFINE_PARTITION_NAN(double, f64);
453
453
  preserved), so argsort on NaN-containing input keeps NaN indices
454
454
  in ascending order.
455
455
 
456
- Implementation: 2-pass stable filter via xmalloc scratch (single
457
- xmalloc + xfree per fiber; negligible overhead vs the sort
458
- itself). */
456
+ Implementation: 2-pass stable filter through scratch, n pairs that
457
+ the caller provides. The caller takes it before it opens its walks:
458
+ an allocation here, inside the walk, would raise past their finish. */
459
459
  #define DEFINE_PARTITION_NAN_PAIR(TYPE, SUFFIX) \
460
460
  ca_size_t \
461
- ca_partition_nan_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n) \
461
+ ca_partition_nan_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n, \
462
+ ca_pair_##SUFFIX *scratch) \
462
463
  { \
463
464
  if ( n == 0 ) return 0; \
464
- ca_pair_##SUFFIX *scratch = \
465
- (ca_pair_##SUFFIX *) xmalloc((size_t) n * sizeof(ca_pair_##SUFFIX)); \
466
465
  ca_size_t fin = 0, nan_pos = 0; \
467
466
  for ( ca_size_t k = 0; k < n; k++ ) { \
468
467
  if ( !isnan(a[k].v) ) scratch[fin++] = a[k]; \
@@ -472,7 +471,6 @@ ca_partition_nan_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n) \
472
471
  if ( isnan(a[k].v) ) scratch[finite_count + nan_pos++] = a[k]; \
473
472
  } \
474
473
  memcpy(a, scratch, (size_t) n * sizeof(ca_pair_##SUFFIX)); \
475
- xfree(scratch); \
476
474
  return finite_count; \
477
475
  } \
478
476
  struct ca_partition_nan_pair_##SUFFIX##_eat_semicolon
data/ext/carray_struct.c CHANGED
@@ -194,20 +194,40 @@ ca_struct_write_primitive_fast (char *p, int type_code, VALUE val)
194
194
  to keep neighbouring bits untouched.
195
195
  --------------------------------------------------------------------- */
196
196
 
197
+ /* The word's bytes are read and written least significant first on
198
+ every host: the layout numbers the bits byte by byte from the record's
199
+ start, so a field read through a wider word than its neighbour still
200
+ sees the same bits. */
201
+ static uint64_t
202
+ ca_struct_load_le (const char *p, int n)
203
+ {
204
+ uint64_t word = 0;
205
+ int i;
206
+ for (i = 0; i < n; i++) {
207
+ word |= (uint64_t) (uint8_t) p[i] << (8 * i);
208
+ }
209
+ return word;
210
+ }
211
+
212
+ static void
213
+ ca_struct_store_le (char *p, int n, uint64_t word)
214
+ {
215
+ int i;
216
+ for (i = 0; i < n; i++) {
217
+ p[i] = (char) (word >> (8 * i));
218
+ }
219
+ }
220
+
197
221
  static VALUE
198
222
  ca_struct_read_bitfield_fast (const char *p, int view_bytes,
199
223
  int bit_in_word, int bits)
200
224
  {
201
- uint64_t word = 0;
202
- switch (view_bytes) {
203
- case 1: { uint8_t v; memcpy(&v, p, 1); word = v; break; }
204
- case 2: { uint16_t v; memcpy(&v, p, 2); word = v; break; }
205
- case 4: { uint32_t v; memcpy(&v, p, 4); word = v; break; }
206
- case 8: { uint64_t v; memcpy(&v, p, 8); word = v; break; }
207
- default:
225
+ uint64_t word;
226
+ if ( view_bytes != 1 && view_bytes != 2 && view_bytes != 4 && view_bytes != 8 ) {
208
227
  rb_raise(rb_eRuntimeError,
209
228
  "[BUG] bitfield view_bytes %d not in {1,2,4,8}", view_bytes);
210
229
  }
230
+ word = ca_struct_load_le(p, view_bytes);
211
231
  uint64_t mask = (bits >= 64) ? ~(uint64_t)0
212
232
  : ((uint64_t)1 << bits) - 1;
213
233
  uint64_t val = (word >> bit_in_word) & mask;
@@ -224,7 +244,11 @@ static void
224
244
  ca_struct_write_bitfield_fast (char *p, int view_bytes,
225
245
  int bit_in_word, int bits, VALUE val)
226
246
  {
227
- uint64_t v;
247
+ uint64_t v, word;
248
+ if ( view_bytes != 1 && view_bytes != 2 && view_bytes != 4 && view_bytes != 8 ) {
249
+ rb_raise(rb_eRuntimeError,
250
+ "[BUG] bitfield view_bytes %d not in {1,2,4,8}", view_bytes);
251
+ }
228
252
  if ( bits == 1 ) {
229
253
  if ( val == Qtrue || val == INT2FIX(1) ) v = 1;
230
254
  else if ( val == Qfalse || val == INT2FIX(0) ) v = 0;
@@ -240,41 +264,9 @@ ca_struct_write_bitfield_fast (char *p, int view_bytes,
240
264
  uint64_t mask = (bits >= 64) ? ~(uint64_t)0
241
265
  : ((uint64_t)1 << bits) - 1;
242
266
  v &= mask;
243
- uint64_t shifted_mask = mask << bit_in_word;
244
- uint64_t shifted_val = v << bit_in_word;
245
- switch (view_bytes) {
246
- case 1: {
247
- uint8_t word;
248
- memcpy(&word, p, 1);
249
- word = (uint8_t)((word & ~(uint8_t)shifted_mask) | (uint8_t)shifted_val);
250
- memcpy(p, &word, 1);
251
- return;
252
- }
253
- case 2: {
254
- uint16_t word;
255
- memcpy(&word, p, 2);
256
- word = (uint16_t)((word & ~(uint16_t)shifted_mask) | (uint16_t)shifted_val);
257
- memcpy(p, &word, 2);
258
- return;
259
- }
260
- case 4: {
261
- uint32_t word;
262
- memcpy(&word, p, 4);
263
- word = (uint32_t)((word & ~(uint32_t)shifted_mask) | (uint32_t)shifted_val);
264
- memcpy(p, &word, 4);
265
- return;
266
- }
267
- case 8: {
268
- uint64_t word;
269
- memcpy(&word, p, 8);
270
- word = (word & ~shifted_mask) | shifted_val;
271
- memcpy(p, &word, 8);
272
- return;
273
- }
274
- default:
275
- rb_raise(rb_eRuntimeError,
276
- "[BUG] bitfield view_bytes %d not in {1,2,4,8}", view_bytes);
277
- }
267
+ word = ca_struct_load_le(p, view_bytes);
268
+ word = (word & ~(mask << bit_in_word)) | (v << bit_in_word);
269
+ ca_struct_store_le(p, view_bytes, word);
278
270
  }
279
271
 
280
272
  /* ---------------------------------------------------------------------
@@ -234,7 +234,9 @@ ca_time_text_read (VALUE str, ca_time_text_t *t, int wide)
234
234
  }
235
235
  }
236
236
  }
237
- if ( hour > 24 || min > 59 || sec > 59 ) {
237
+ /* Second 60 is the leap second; it carries into the next minute below,
238
+ as CArray.time reads it. */
239
+ if ( hour > 24 || min > 59 || sec > 60 ) {
238
240
  return CA_TIME_TEXT_UNREADABLE;
239
241
  }
240
242
  /* Hour 24 is the end of the day, 24:00:00, and nothing past it, as in
@@ -359,7 +361,7 @@ rb_ca_parse_time_text (int argc, VALUE *argv, VALUE self)
359
361
  boolean8_t *m_in, *m_out;
360
362
  int64_t *days, *nsod, *months, *ticks;
361
363
  int64_t per_day = 1, ns_per_tick = 1;
362
- ID unit;
364
+ ID unit = 0;
363
365
  int clock = 0, fraction = 0, coarse = 2, any = 0, wide;
364
366
  int calendar = 0; /* 1 ticks of months, 2 of years */
365
367
  ca_size_t i;
data/ext/carray_utils.c CHANGED
@@ -371,6 +371,10 @@ ca_parse_range_without_check (VALUE arg, ca_size_t size,
371
371
  last = NUM2SIZE(x.end);
372
372
  excl = RTEST(x.exclude_end);
373
373
  step = NUM2SIZE(x.step);
374
+ if ( step == 0 ) {
375
+ rb_raise(rb_eArgError, "%s: step should not be 0",
376
+ ca_calling_method_name());
377
+ }
374
378
  if ( excl ) {
375
379
  last += ( (last>=start) ? -1 : 1 );
376
380
  }
@@ -391,6 +395,10 @@ ca_parse_range_without_check (VALUE arg, ca_size_t size,
391
395
  if ( NIL_P(arg0) ) { /* [nil,k] */
392
396
  start = 0;
393
397
  step = NUM2SIZE(arg1);
398
+ if ( step == 0 ) {
399
+ rb_raise(rb_eArgError, "%s: step should not be 0",
400
+ ca_calling_method_name());
401
+ }
394
402
  count = (size-1)/llabs(step) + 1;
395
403
  *poffset = start;
396
404
  *pcount = count;
@@ -401,6 +409,10 @@ ca_parse_range_without_check (VALUE arg, ca_size_t size,
401
409
  last = NUM2SIZE(RANGE_END(arg0));
402
410
  excl = RTEST(RANGE_EXCL(arg0));
403
411
  step = NUM2SIZE(arg1);
412
+ if ( step == 0 ) {
413
+ rb_raise(rb_eArgError, "%s: step should not be 0",
414
+ ca_calling_method_name());
415
+ }
404
416
  if ( excl ) {
405
417
  last += ( (last>=start) ? -1 : 1 );
406
418
  }
@@ -418,6 +430,10 @@ ca_parse_range_without_check (VALUE arg, ca_size_t size,
418
430
  last = NUM2SIZE(x.end);
419
431
  excl = RTEST(x.exclude_end);
420
432
  step = NUM2SIZE(x.step);
433
+ if ( step == 0 ) {
434
+ rb_raise(rb_eArgError, "%s: step should not be 0",
435
+ ca_calling_method_name());
436
+ }
421
437
  if ( excl ) {
422
438
  last += ( (last>=start) ? -1 : 1 );
423
439
  }
@@ -922,6 +938,15 @@ ca_reduce_fill (VALUE result, VALUE fill, int whole)
922
938
  if ( ca_has_mask(cr) ) {
923
939
  result = rb_ca_mask_fill_copy(result, fill);
924
940
  }
941
+ else if ( cr->elements > 0 ) {
942
+ /* No cell needed the fill, but a fill the result cannot hold is
943
+ refused all the same, not only when some cell happens to be
944
+ undefined: it goes into a one-cell array of the result's kind. */
945
+ volatile VALUE one = rb_funcall(rb_funcall(result, rb_intern("flatten"), 0),
946
+ rb_intern("[]"), 1,
947
+ rb_range_new(INT2FIX(0), INT2FIX(0), 0));
948
+ rb_ca_store_addr(rb_ca_template(one), 0, fill);
949
+ }
925
950
  return whole ? rb_ca_fetch_addr(result, 0) : result;
926
951
  }
927
952
  return ( result == CA_UNDEF ) ? fill : result;
@@ -1253,11 +1278,36 @@ rb_hash_has_key(VALUE hash, VALUE key)
1253
1278
  return Qfalse;
1254
1279
  }
1255
1280
 
1281
+ /* The next comma-separated key of a spec, its length in *len, or NULL
1282
+ past the last key. The spec is read in place: a copy to cut up would
1283
+ be a C allocation, and its failure has nowhere to go but to drop every
1284
+ keyword. */
1285
+ static const char *
1286
+ option_key (const char **sp, long *len)
1287
+ {
1288
+ const char *p = *sp, *end;
1289
+
1290
+ if ( p == NULL ) {
1291
+ return NULL;
1292
+ }
1293
+ end = strchr(p, ',');
1294
+ if ( end ) {
1295
+ *len = end - p;
1296
+ *sp = end + 1;
1297
+ }
1298
+ else {
1299
+ *len = (long) strlen(p);
1300
+ *sp = NULL;
1301
+ }
1302
+ return p;
1303
+ }
1304
+
1256
1305
  void
1257
1306
  rb_scan_options (VALUE ropt, const char *spec_in, ...)
1258
1307
  {
1259
1308
  VALUE *vp;
1260
- char *sp, *tok, *spec;
1309
+ const char *sp, *tok;
1310
+ long len;
1261
1311
  int has_option = 0;
1262
1312
  va_list vals;
1263
1313
 
@@ -1272,22 +1322,19 @@ rb_scan_options (VALUE ropt, const char *spec_in, ...)
1272
1322
  }
1273
1323
 
1274
1324
  va_start(vals, spec_in);
1275
- sp = spec = strdup(spec_in);
1276
- tok = strsep1(&sp, ',');
1277
- while ( tok != NULL ) {
1325
+ sp = spec_in;
1326
+ while ( ( tok = option_key(&sp, &len) ) != NULL ) {
1278
1327
  vp = va_arg(vals, VALUE*);
1279
1328
  if ( has_option ) {
1280
- VALUE key = ID2SYM(rb_intern(tok));
1329
+ VALUE key = ID2SYM(rb_intern2(tok, len));
1281
1330
  if ( rb_hash_has_key(ropt, key) ) {
1282
1331
  *vp = rb_hash_aref(ropt, key);
1283
1332
  }
1284
1333
  /* key absent: leave *vp unchanged (caller's pre-initialized default) */
1285
1334
  }
1286
1335
  /* opts == Qnil: leave all *vp unchanged */
1287
- tok = strsep1(&sp, ',');
1288
1336
  }
1289
1337
  va_end(vals);
1290
- free(spec);
1291
1338
 
1292
1339
  /* Strict: reject any key not named in spec, matching Ruby's native
1293
1340
  keyword-argument behaviour ("unknown keyword: :foo"). Every call
@@ -1300,15 +1347,12 @@ rb_scan_options (VALUE ropt, const char *spec_in, ...)
1300
1347
  VALUE key = rb_ary_entry(keys, i);
1301
1348
  VALUE kname = SYMBOL_P(key) ? rb_sym2str(key) : rb_obj_as_string(key);
1302
1349
  const char *kn = StringValueCStr(kname);
1303
- char *sp2, *tok2, *spec2;
1350
+ long kn_len = RSTRING_LEN(kname);
1304
1351
  int found = 0;
1305
- sp2 = spec2 = strdup(spec_in);
1306
- tok2 = strsep1(&sp2, ',');
1307
- while ( tok2 != NULL ) {
1308
- if ( strcmp(tok2, kn) == 0 ) { found = 1; break; }
1309
- tok2 = strsep1(&sp2, ',');
1352
+ sp = spec_in;
1353
+ while ( ( tok = option_key(&sp, &len) ) != NULL ) {
1354
+ if ( len == kn_len && memcmp(tok, kn, len) == 0 ) { found = 1; break; }
1310
1355
  }
1311
- free(spec2);
1312
1356
  if ( ! found ) {
1313
1357
  rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE, rb_inspect(key));
1314
1358
  }
@@ -1341,7 +1385,8 @@ void
1341
1385
  rb_set_options (VALUE ropt, const char *spec_in, ...)
1342
1386
  {
1343
1387
  VALUE rval;
1344
- char *sp, *tok, *spec;
1388
+ const char *sp, *tok;
1389
+ long len;
1345
1390
  int has_option = 0;
1346
1391
  va_list vals;
1347
1392
 
@@ -1356,17 +1401,14 @@ rb_set_options (VALUE ropt, const char *spec_in, ...)
1356
1401
  }
1357
1402
 
1358
1403
  va_start(vals, spec_in);
1359
- sp = spec = strdup(spec_in);
1360
- tok = strsep1(&sp, ',');
1361
- while ( tok != NULL ) {
1404
+ sp = spec_in;
1405
+ while ( ( tok = option_key(&sp, &len) ) != NULL ) {
1362
1406
  rval = va_arg(vals, VALUE);
1363
1407
  if ( has_option ) {
1364
- rb_hash_aset(ropt, ID2SYM(rb_intern(tok)), rval);
1408
+ rb_hash_aset(ropt, ID2SYM(rb_intern2(tok, len)), rval);
1365
1409
  }
1366
- tok = strsep1(&sp, ',');
1367
1410
  }
1368
1411
  va_end(vals);
1369
- free(spec);
1370
1412
 
1371
1413
  return;
1372
1414
  }