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