1 #include "vctrs.h"
2 #include "c.h"
3 #include "ptype-common.h"
4 #include "slice.h"
5 #include "slice-assign.h"
6 #include "owned.h"
7 #include "utils.h"
8 
9 // Defined in slice-chop.c
10 SEXP vec_as_indices(SEXP indices, R_len_t n, SEXP names);
11 
12 
13 static SEXP vec_unchop(SEXP x,
14                        SEXP indices,
15                        SEXP ptype,
16                        SEXP name_spec,
17                        const struct name_repair_opts* name_repair);
18 
19 // [[ register() ]]
vctrs_unchop(SEXP x,SEXP indices,SEXP ptype,SEXP name_spec,SEXP name_repair)20 SEXP vctrs_unchop(SEXP x, SEXP indices, SEXP ptype, SEXP name_spec, SEXP name_repair) {
21   struct name_repair_opts name_repair_opts = new_name_repair_opts(name_repair, args_empty, false);
22   PROTECT_NAME_REPAIR_OPTS(&name_repair_opts);
23 
24   SEXP out = vec_unchop(x, indices, ptype, name_spec, &name_repair_opts);
25 
26   UNPROTECT(1);
27   return out;
28 }
29 
30 enum fallback_homogeneous {
31   FALLBACK_HOMOGENEOUS_false = 0,
32   FALLBACK_HOMOGENEOUS_true
33 };
34 static SEXP vec_unchop_fallback(SEXP ptype,
35                                 SEXP x,
36                                 SEXP indices,
37                                 SEXP name_spec,
38                                 const struct name_repair_opts* name_repair,
39                                 enum fallback_homogeneous homogenous);
40 
vec_unchop(SEXP xs,SEXP indices,SEXP ptype,SEXP name_spec,const struct name_repair_opts * name_repair)41 static SEXP vec_unchop(SEXP xs,
42                        SEXP indices,
43                        SEXP ptype,
44                        SEXP name_spec,
45                        const struct name_repair_opts* name_repair) {
46   if (!vec_is_list(xs)) {
47     Rf_errorcall(R_NilValue, "`x` must be a list");
48   }
49 
50   if (indices == R_NilValue) {
51     return vec_c(xs, ptype, name_spec, name_repair);
52   }
53 
54   R_len_t xs_size = vec_size(xs);
55 
56   // Apply size/type checking to `indices` before possibly exiting early from
57   // having a `NULL` common type
58   if (xs_size != vec_size(indices)) {
59     Rf_errorcall(R_NilValue, "`x` and `indices` must be lists of the same size");
60   }
61 
62   if (!vec_is_list(indices)) {
63     Rf_errorcall(R_NilValue, "`indices` must be a list of integers, or `NULL`");
64   }
65 
66   ptype = PROTECT(vec_ptype_common_params(xs, ptype, DF_FALLBACK_DEFAULT, S3_FALLBACK_true));
67 
68   if (needs_vec_c_fallback(ptype)) {
69     SEXP out = vec_unchop_fallback(ptype, xs, indices, name_spec, name_repair, FALLBACK_HOMOGENEOUS_false);
70     UNPROTECT(1);
71     return out;
72   }
73   // FIXME: Needed for dplyr::summarise() which passes a non-fallback ptype
74   if (needs_vec_c_homogeneous_fallback(xs, ptype)) {
75     SEXP out = vec_unchop_fallback(ptype, xs, indices, name_spec, name_repair, FALLBACK_HOMOGENEOUS_true);
76     UNPROTECT(1);
77     return out;
78   }
79 
80   if (ptype == R_NilValue) {
81     UNPROTECT(1);
82     return R_NilValue;
83   }
84 
85   xs = PROTECT(vec_cast_common(xs, ptype));
86 
87   bool assign_names = !Rf_inherits(name_spec, "rlang_zap");
88   SEXP xs_names = PROTECT(r_names(xs));
89   bool xs_is_named = xs_names != R_NilValue && !is_data_frame(ptype);
90 
91   R_len_t out_size = 0;
92 
93   // `out_size` is computed from `indices`
94   for (R_len_t i = 0; i < xs_size; ++i) {
95     SEXP x = VECTOR_ELT(xs, i);
96 
97     if (x == R_NilValue) {
98       continue;
99     }
100 
101     R_len_t index_size = Rf_length(VECTOR_ELT(indices, i));
102     out_size += index_size;
103 
104     // Each element of `xs` is recycled to its corresponding index's size
105     x = vec_recycle(x, index_size, args_empty);
106     SET_VECTOR_ELT(xs, i, x);
107   }
108 
109   SEXP locs = PROTECT(vec_as_indices(indices, out_size, R_NilValue));
110 
111   SEXP proxy = vec_proxy(ptype);
112   PROTECT_INDEX proxy_pi;
113   PROTECT_WITH_INDEX(proxy, &proxy_pi);
114 
115   proxy = vec_init(proxy, out_size);
116   REPROTECT(proxy, proxy_pi);
117 
118   SEXP out_names = R_NilValue;
119   PROTECT_INDEX out_names_pi;
120   PROTECT_WITH_INDEX(out_names, &out_names_pi);
121 
122   const struct vec_assign_opts unchop_assign_opts = {
123     .assign_names = assign_names,
124     .ignore_outer_names = true
125   };
126 
127   for (R_len_t i = 0; i < xs_size; ++i) {
128     SEXP x = VECTOR_ELT(xs, i);
129 
130     if (x == R_NilValue) {
131       continue;
132     }
133 
134     SEXP loc = VECTOR_ELT(locs, i);
135 
136     if (assign_names) {
137       R_len_t size = Rf_length(loc);
138       SEXP outer = xs_is_named ? STRING_ELT(xs_names, i) : R_NilValue;
139       SEXP inner = PROTECT(vec_names(x));
140       SEXP x_nms = PROTECT(apply_name_spec(name_spec, outer, inner, size));
141 
142       if (x_nms != R_NilValue) {
143         R_LAZY_ALLOC(out_names, out_names_pi, STRSXP, out_size);
144 
145         // If there is no name to assign, skip the assignment since
146         // `out_names` already contains empty strings
147         if (x_nms != chrs_empty) {
148           out_names = chr_assign(out_names, loc, x_nms, VCTRS_OWNED_true);
149           REPROTECT(out_names, out_names_pi);
150         }
151       }
152 
153       UNPROTECT(2);
154     }
155 
156     // Total ownership of `proxy` because it was freshly created with `vec_init()`
157     proxy = vec_proxy_assign_opts(proxy, loc, x, VCTRS_OWNED_true, &unchop_assign_opts);
158     REPROTECT(proxy, proxy_pi);
159   }
160 
161   SEXP out_size_sexp = PROTECT(r_int(out_size));
162 
163   SEXP out = PROTECT(vec_restore(proxy, ptype, out_size_sexp, VCTRS_OWNED_true));
164 
165   if (out_names != R_NilValue) {
166     out_names = PROTECT(vec_as_names(out_names, name_repair));
167     out = vec_set_names(out, out_names);
168     UNPROTECT(1);
169   } else if (!assign_names) {
170     // FIXME: `vec_ptype2()` doesn't consistently zaps names, so `out`
171     // might have been initialised with names. This branch can be
172     // removed once #1020 is resolved.
173     out = vec_set_names(out, R_NilValue);
174   }
175 
176   UNPROTECT(8);
177   return out;
178 }
179 
180 // This is essentially:
181 // vec_slice_fallback(vec_c_fallback_invoke(!!!x), order(vec_c(!!!indices)))
182 // with recycling of each element of `x` to the corresponding index size
vec_unchop_fallback(SEXP ptype,SEXP x,SEXP indices,SEXP name_spec,const struct name_repair_opts * name_repair,enum fallback_homogeneous homogeneous)183 static SEXP vec_unchop_fallback(SEXP ptype,
184                                 SEXP x,
185                                 SEXP indices,
186                                 SEXP name_spec,
187                                 const struct name_repair_opts* name_repair,
188                                 enum fallback_homogeneous homogeneous) {
189   R_len_t x_size = vec_size(x);
190   x = PROTECT(r_clone_referenced(x));
191 
192   R_len_t out_size = 0;
193 
194   // Recycle `x` elements to the size of their corresponding index
195   for (R_len_t i = 0; i < x_size; ++i) {
196     SEXP elt = VECTOR_ELT(x, i);
197 
198     R_len_t index_size = vec_size(VECTOR_ELT(indices, i));
199     out_size += index_size;
200 
201     SET_VECTOR_ELT(x, i, vec_recycle_fallback(elt, index_size, args_empty));
202   }
203 
204   indices = PROTECT(vec_as_indices(indices, out_size, R_NilValue));
205 
206   SEXP out = R_NilValue;
207   if (homogeneous) {
208     out = PROTECT(vec_c_fallback_invoke(x, name_spec));
209   } else {
210     out = PROTECT(vec_c_fallback(ptype, x, name_spec, name_repair));
211   }
212 
213   const struct name_repair_opts name_repair_opts = {
214     .type = name_repair_none,
215     .fn = R_NilValue
216   };
217 
218   indices = PROTECT(vec_c(
219     indices,
220     vctrs_shared_empty_int,
221     R_NilValue,
222     &name_repair_opts
223   ));
224 
225   const int* p_indices = INTEGER(indices);
226 
227   SEXP locations = PROTECT(Rf_allocVector(INTSXP, out_size));
228   int* p_locations = INTEGER(locations);
229 
230   // Initialize with missing to handle locations that are never selected
231   for (R_len_t i = 0; i < out_size; ++i) {
232     p_locations[i] = NA_INTEGER;
233   }
234 
235   for (R_len_t i = 0; i < out_size; ++i) {
236     const int index = p_indices[i];
237 
238     if (index == NA_INTEGER) {
239       continue;
240     }
241 
242     p_locations[index - 1] = i + 1;
243   }
244 
245   out = PROTECT(vec_slice_fallback(out, locations));
246 
247   UNPROTECT(6);
248   return out;
249 }
250