@@ -109,6 +109,10 @@ int nanoarrow_ptype_is_data_frame(SEXP ptype) {
109109 (Rf_xlength (ptype ) > 0 && has_attrib_safe (ptype , R_NamesSymbol )));
110110}
111111
112+ int nanoarrow_ptype_is_nanoarrow_vctr (SEXP ptype ) {
113+ return Rf_inherits (ptype , "nanoarrow_vctr" );
114+ }
115+
112116SEXP nanoarrow_materialize_realloc (SEXP ptype , R_xlen_t len ) {
113117 SEXP result ;
114118
@@ -123,7 +127,27 @@ SEXP nanoarrow_materialize_realloc(SEXP ptype, R_xlen_t len) {
123127 }
124128 }
125129
126- if (nanoarrow_ptype_is_data_frame (ptype )) {
130+ if (nanoarrow_ptype_is_nanoarrow_vctr (ptype )) {
131+ // The object we return here is one that will accumulate chunks and
132+ // be finalized with a value (rather than being strictly copied into
133+ // after every new chunk is seen).
134+ result = PROTECT (Rf_allocVector (INTSXP , len ));
135+ Rf_copyMostAttrib (ptype , result );
136+
137+ // For the purposes of building the list of chunks, chunks is a pairlist
138+ // (it will be converted to a regular list when this converter is finalized)
139+ // Technically the first value here won't be used (this simplifies the
140+ // appending).
141+ SEXP chunks_list = PROTECT (Rf_list1 (R_NilValue ));
142+
143+ // To start, the chunks list and the end of the chunks list are the same node
144+ SEXP chunks_tail_sym = PROTECT (Rf_install ("chunks_tail" ));
145+ SEXP chunks_sym = PROTECT (Rf_install ("chunks" ));
146+ Rf_setAttrib (result , chunks_sym , chunks_list );
147+ Rf_setAttrib (result , chunks_tail_sym , chunks_list );
148+
149+ UNPROTECT (4 );
150+ } else if (nanoarrow_ptype_is_data_frame (ptype )) {
127151 R_xlen_t num_cols = Rf_xlength (ptype );
128152 result = PROTECT (Rf_allocVector (VECSXP , num_cols ));
129153 for (R_xlen_t i = 0 ; i < num_cols ; i ++ ) {
@@ -208,6 +232,10 @@ static void fill_vec_with_nulls(SEXP x, R_xlen_t offset, R_xlen_t len) {
208232}
209233
210234static void copy_vec_into (SEXP x , SEXP dst , R_xlen_t offset , R_xlen_t len ) {
235+ if (nanoarrow_ptype_is_nanoarrow_vctr (dst )) {
236+ Rf_error ("Can't copy_vec_into() to nanoarrow_vctr" );
237+ }
238+
211239 if (nanoarrow_ptype_is_data_frame (dst )) {
212240 if (!nanoarrow_ptype_is_data_frame (x )) {
213241 Rf_error ("Expected record-style vctr result but got non-record-style result" );
@@ -276,22 +304,26 @@ static int nanoarrow_materialize_nanoarrow_vctr(struct RConverter* converter,
276304 // This is a case where the callee needs ownership, which we can do via a
277305 // shallow copy.
278306 SEXP converter_shelter = R_ExternalPtrProtected (converter_xptr );
279-
280- // TODO: Check that this SEXP has a lifecycle that is going to work with this
281307 SEXP array_xptr = VECTOR_ELT (converter_shelter , 2 );
282308
283309 SEXP array_out_xptr = PROTECT (nanoarrow_array_owning_xptr ());
284310 struct ArrowArray * out_array = nanoarrow_output_array_from_xptr (array_xptr );
285311 array_export (array_xptr , out_array );
286312
287- // Append the chunk to the pairlist
313+ // Get the cached copy of the pairlist node at the end of the current
314+ // chunks list.
288315 SEXP chunks_tail_sym = PROTECT (Rf_install ("chunks_tail" ));
289316 SEXP chunks_tail = PROTECT (Rf_getAttrib (converter -> dst .vec_sexp , chunks_tail_sym ));
290317
318+ // Create a length-1 pairlist node containing the chunk
291319 SEXP next_sexp = PROTECT (Rf_cons (array_out_xptr , R_NilValue ));
320+
321+ // Append it to the end of the current pairlist
292322 SETCDR (chunks_tail , next_sexp );
293323 UNPROTECT (1 );
294324
325+ // Update the cached copy of the pairlist node at the end of the current
326+ // chunks list.
295327 Rf_setAttrib (converter -> dst .vec_sexp , chunks_tail_sym , next_sexp );
296328 UNPROTECT (3 );
297329
@@ -308,20 +340,19 @@ static int nanoarrow_materialize_other(struct RConverter* converter,
308340 UNPROTECT (1 );
309341 }
310342
311- // A unique situation where we don't want owning external pointers because we know
312- // these are protected for the duration of our call into R and because we don't want
313- // the underlying array to be released and invalidate the converter. The R code in
314- // convert_fallback_other() takes care of ensuring an independent copy with the correct
315- // offset/length.
316- SEXP schema_xptr = PROTECT (R_MakeExternalPtr (
317- (struct ArrowSchema * )converter -> schema_view .schema , R_NilValue , R_NilValue ));
318- Rf_setAttrib (schema_xptr , R_ClassSymbol , nanoarrow_cls_schema );
319- // We do need to set the protected member of the array external pointer to signal that
320- // it is not an independent array (i.e., force a shallow copy).
321- SEXP array_xptr = PROTECT (R_MakeExternalPtr (
322- (struct ArrowArray * )converter -> array_view .array , schema_xptr , converter_xptr ));
323- Rf_setAttrib (array_xptr , R_ClassSymbol , nanoarrow_cls_array );
343+ // Special-case the nanoarrow_vctr conversion
344+ if (Rf_inherits (converter -> dst .vec_sexp , "nanoarrow_vctr" )) {
345+ return nanoarrow_materialize_nanoarrow_vctr (converter , converter_xptr );
346+ }
347+
348+ // We've ensured proper ownership of array_xptr and ensured that its
349+ // schema is set, so we can pass these safely to the R-level
350+ // convert_fallback_other.
351+ SEXP converter_shelter = R_ExternalPtrProtected (converter_xptr );
352+ SEXP array_xptr = VECTOR_ELT (converter_shelter , 2 );
324353
354+ // The R code in convert_fallback_other() takes care of ensuring an independent copy
355+ // with the correct offset/length if it is necessary to update them.
325356 SEXP offset_sexp = PROTECT (
326357 Rf_ScalarReal ((double )(converter -> src .array_view -> offset + converter -> src .offset )));
327358 SEXP length_sexp = PROTECT (Rf_ScalarReal ((double )converter -> src .length ));
@@ -335,7 +366,7 @@ static int nanoarrow_materialize_other(struct RConverter* converter,
335366 copy_vec_into (result_src , converter -> dst .vec_sexp , converter -> dst .offset ,
336367 converter -> dst .length );
337368
338- UNPROTECT (7 );
369+ UNPROTECT (5 );
339370 return NANOARROW_OK ;
340371}
341372
0 commit comments