1 #include "vctrs.h"
2 #include "type-data-frame.h"
3 #include "owned.h"
4 #include "utils.h"
5
6 // Initialised at load time
7 static SEXP syms_vec_restore_dispatch = NULL;
8 static SEXP fns_vec_restore_dispatch = NULL;
9
10 // [[ register() ]]
vctrs_restore_default(SEXP x,SEXP to)11 SEXP vctrs_restore_default(SEXP x, SEXP to) {
12 return vec_restore_default(x, to, vec_owned(x));
13 }
14
15 // Copy attributes except names and dim. This duplicates `x` if needed.
16 // [[ include("vctrs.h") ]]
vec_restore_default(SEXP x,SEXP to,const enum vctrs_owned owned)17 SEXP vec_restore_default(SEXP x, SEXP to, const enum vctrs_owned owned) {
18 SEXP attrib = ATTRIB(to);
19
20 const bool is_s4 = IS_S4_OBJECT(to);
21
22 if (attrib == R_NilValue && !is_s4) {
23 return x;
24 }
25
26 int n_protect = 0;
27
28 attrib = PROTECT(Rf_shallow_duplicate(attrib));
29 ++n_protect;
30
31 x = PROTECT(vec_clone_referenced(x, owned));
32 ++n_protect;
33
34 // Remove vectorised attributes which might be incongruent after reshaping.
35 // Shouldn't matter for GNU R but other R implementations might have checks.
36 // Also record class to set it later with `Rf_setAttrib()`. This restores
37 // the OBJECT bit and is likely more compatible with other implementations.
38 SEXP class = R_NilValue;
39
40 {
41 SEXP node = attrib;
42 SEXP prev = R_NilValue;
43
44 while (node != R_NilValue) {
45 SEXP tag = TAG(node);
46
47 // Skip special attributes
48 if (tag == R_NamesSymbol || tag == R_DimSymbol ||
49 tag == R_DimNamesSymbol || tag == R_ClassSymbol ||
50 tag == R_RowNamesSymbol) {
51 if (tag == R_ClassSymbol) {
52 class = CAR(node);
53 }
54
55 if (prev == R_NilValue) {
56 attrib = CDR(attrib);
57 } else {
58 SETCDR(prev, CDR(node));
59 }
60
61 node = CDR(node);
62 continue;
63 }
64
65 prev = node;
66 node = CDR(node);
67 }
68 }
69
70 // Copy attributes but keep names and dims. Don't restore names for
71 // shaped objects since those are generated from dimnames.
72 SEXP dim = PROTECT(Rf_getAttrib(x, R_DimSymbol));
73 ++n_protect;
74
75 if (dim == R_NilValue) {
76 SEXP nms = PROTECT(Rf_getAttrib(x, R_NamesSymbol));
77
78 // Check if `to` is a data frame early. If `x` and `to` point
79 // to the same reference, then `SET_ATTRIB()` would alter `to`.
80 SEXP rownms = PROTECT(df_rownames(x));
81 const bool restore_rownms = rownms != R_NilValue && is_data_frame(to);
82
83 SET_ATTRIB(x, attrib);
84
85 Rf_setAttrib(x, R_NamesSymbol, nms);
86
87 // Don't restore row names if `to` isn't a data frame
88 if (restore_rownms) {
89 Rf_setAttrib(x, R_RowNamesSymbol, rownms);
90 }
91 UNPROTECT(2);
92 } else {
93 SEXP dimnames = PROTECT(Rf_getAttrib(x, R_DimNamesSymbol));
94
95 SET_ATTRIB(x, attrib);
96
97 Rf_setAttrib(x, R_DimSymbol, dim);
98 Rf_setAttrib(x, R_DimNamesSymbol, dimnames);
99 UNPROTECT(1);
100 }
101
102 if (class != R_NilValue) {
103 Rf_setAttrib(x, R_ClassSymbol, class);
104 }
105
106 if (is_s4) {
107 r_mark_s4(x);
108 }
109
110 UNPROTECT(n_protect);
111 return x;
112 }
113
vec_restore_dispatch(SEXP x,SEXP to,SEXP n)114 static SEXP vec_restore_dispatch(SEXP x, SEXP to, SEXP n) {
115 return vctrs_dispatch3(syms_vec_restore_dispatch, fns_vec_restore_dispatch,
116 syms_x, x,
117 syms_to, to,
118 syms_n, n);
119 }
120
vec_bare_df_restore_impl(SEXP x,SEXP to,R_len_t size,const enum vctrs_owned owned)121 static SEXP vec_bare_df_restore_impl(SEXP x, SEXP to, R_len_t size,
122 const enum vctrs_owned owned) {
123 x = PROTECT(vec_restore_default(x, to, owned));
124
125 if (Rf_getAttrib(x, R_NamesSymbol) == R_NilValue) {
126 SEXP names = PROTECT(Rf_allocVector(STRSXP, Rf_length(x)));
127 Rf_setAttrib(x, R_NamesSymbol, names);
128 UNPROTECT(1);
129 }
130
131 SEXP rownames = PROTECT(df_rownames(x));
132 if (rownames == R_NilValue) {
133 init_compact_rownames(x, size);
134 } else if (rownames_type(rownames) == ROWNAMES_IDENTIFIERS) {
135 rownames = PROTECT(vec_as_names(rownames, p_unique_repair_silent_opts));
136 x = vec_proxy_set_names(x, rownames, owned);
137 UNPROTECT(1);
138 }
139
140 UNPROTECT(2);
141 return x;
142 }
143
144 // [[ register() ]]
vctrs_bare_df_restore(SEXP x,SEXP to,SEXP n)145 SEXP vctrs_bare_df_restore(SEXP x, SEXP to, SEXP n) {
146 return vec_bare_df_restore(x, to, n, vec_owned(x));
147 }
148
149 // [[ include("vctrs.h") ]]
vec_bare_df_restore(SEXP x,SEXP to,SEXP n,const enum vctrs_owned owned)150 SEXP vec_bare_df_restore(SEXP x, SEXP to, SEXP n, const enum vctrs_owned owned) {
151 if (TYPEOF(x) != VECSXP) {
152 stop_internal("vec_bare_df_restore",
153 "Attempt to restore data frame from a %s.",
154 Rf_type2char(TYPEOF(x)));
155 }
156
157 R_len_t size = (n == R_NilValue) ? df_raw_size(x) : r_int_get(n, 0);
158 return vec_bare_df_restore_impl(x, to, size, owned);
159 }
160
161 // Restore methods are passed the original atomic type back, so we
162 // first restore data frames as such before calling the restore
163 // method, if any
164 // [[ include("vctrs.h") ]]
vec_df_restore(SEXP x,SEXP to,SEXP n,const enum vctrs_owned owned)165 SEXP vec_df_restore(SEXP x, SEXP to, SEXP n, const enum vctrs_owned owned) {
166 SEXP out = PROTECT(vec_bare_df_restore(x, to, n, owned));
167 out = vec_restore_dispatch(out, to, n);
168 UNPROTECT(1);
169 return out;
170 }
171
172 // [[ register() ]]
vctrs_restore(SEXP x,SEXP to,SEXP n)173 SEXP vctrs_restore(SEXP x, SEXP to, SEXP n) {
174 return vec_restore(x, to, n, vec_owned(x));
175 }
176
177 // FIXME: Having `owned` as an argument to `vec_restore()` may be
178 // unnecessary once we have recursive proxy / restore mechanisms.
179 // It currently helps resolve performance issues in `vec_rbind()`'s usage of
180 // `df_assign()`, which repeatedly proxies and restores each column,
181 // causing duplication to occur. Passing `owned` through here allows us to
182 // call `vec_clone_referenced()`, which won't attempt to clone if we know we
183 // own the object. See #1151.
184 // [[ include("vctrs.h") ]]
vec_restore(SEXP x,SEXP to,SEXP n,const enum vctrs_owned owned)185 SEXP vec_restore(SEXP x, SEXP to, SEXP n, const enum vctrs_owned owned) {
186 switch (class_type(to)) {
187 default: return vec_restore_dispatch(x, to, n);
188 case vctrs_class_bare_factor:
189 case vctrs_class_bare_ordered:
190 case vctrs_class_none: return vec_restore_default(x, to, owned);
191 case vctrs_class_bare_date: return vec_date_restore(x, to, owned);
192 case vctrs_class_bare_posixct: return vec_posixct_restore(x, to, owned);
193 case vctrs_class_bare_posixlt: return vec_posixlt_restore(x, to, owned);
194 case vctrs_class_bare_data_frame:
195 case vctrs_class_bare_tibble: return vec_bare_df_restore(x, to, n, owned);
196 case vctrs_class_data_frame: return vec_df_restore(x, to, n, owned);
197 }
198 }
199
200
vctrs_init_proxy_restore(SEXP ns)201 void vctrs_init_proxy_restore(SEXP ns) {
202 syms_vec_restore_dispatch = Rf_install("vec_restore_dispatch");
203 fns_vec_restore_dispatch = Rf_findVar(syms_vec_restore_dispatch, ns);
204 }
205