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