1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing,
12 // software distributed under the License is distributed on an
13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, either express or implied. See the License for the
15 // specific language governing permissions and limitations
18 #include "./arrow_types.h"
20 #if defined(ARROW_R_WITH_ARROW)
22 #include <arrow/array.h>
23 #include <arrow/chunked_array.h>
24 #include <arrow/compute/api.h>
25 #include <arrow/util/bitmap_reader.h>
27 #include <cpp11/altrep.hpp>
28 #include <cpp11/declarations.hpp>
29 #if defined(HAS_ALTREP)
31 #if R_VERSION < R_Version(3, 6, 0)
33 // workaround because R's <R_ext/Altrep.h> not so conveniently uses `class`
34 // as a variable name, and C++ is not happy about that
36 // SEXP R_new_altrep(R_altrep_class_t class, SEXP data1, SEXP data2);
40 // Because functions declared in <R_ext/Altrep.h> have C linkage
42 #include <R_ext/Altrep.h>
45 // undo the workaround
49 #include <R_ext/Altrep.h>
52 #include "./r_task_group.h"
59 template <typename c_type
>
60 R_xlen_t
Standard_Get_region(SEXP data2
, R_xlen_t i
, R_xlen_t n
, c_type
* buf
);
63 R_xlen_t Standard_Get_region
<double>(SEXP data2
, R_xlen_t i
, R_xlen_t n
, double* buf
) {
64 return REAL_GET_REGION(data2
, i
, n
, buf
);
68 R_xlen_t Standard_Get_region
<int>(SEXP data2
, R_xlen_t i
, R_xlen_t n
, int* buf
) {
69 return INTEGER_GET_REGION(data2
, i
, n
, buf
);
72 void DeleteArray(std::shared_ptr
<Array
>* ptr
) { delete ptr
; }
73 using Pointer
= cpp11::external_pointer
<std::shared_ptr
<Array
>, DeleteArray
>;
75 // the Array that is being wrapped by the altrep object
76 static const std::shared_ptr
<Array
>& GetArray(SEXP alt
) {
77 return *Pointer(R_altrep_data1(alt
));
80 // base class for all altrep vectors
82 // data1: the Array as an external pointer.
83 // data2: starts as NULL, and becomes a standard R vector with the same
84 // data if necessary: if materialization is needed, e.g. if we need
85 // to access its data pointer, with DATAPTR().
86 template <typename Impl
>
87 struct AltrepVectorBase
{
88 // store the Array as an external pointer in data1, mark as immutable
89 static SEXP
Make(const std::shared_ptr
<Array
>& array
) {
90 SEXP alt
= R_new_altrep(Impl::class_t
, Pointer(new std::shared_ptr
<Array
>(array
)),
92 MARK_NOT_MUTABLE(alt
);
97 // Is the vector materialized, i.e. does the data2 slot contain a
98 // standard R vector with the same data as the array.
99 static bool IsMaterialized(SEXP alt
) { return !Rf_isNull(R_altrep_data2(alt
)); }
101 static R_xlen_t
Length(SEXP alt
) { return GetArray(alt
)->length(); }
103 static int No_NA(SEXP alt
) { return GetArray(alt
)->null_count() == 0; }
105 static int Is_sorted(SEXP alt
) { return UNKNOWN_SORTEDNESS
; }
107 // What gets printed on .Internal(inspect(<the altrep object>))
108 static Rboolean
Inspect(SEXP alt
, int pre
, int deep
, int pvec
,
109 void (*inspect_subtree
)(SEXP
, int, int, int)) {
110 const auto& array
= GetArray(alt
);
111 Rprintf("arrow::Array<%s, %d nulls> len=%d, Array=<%p>\n",
112 array
->type()->ToString().c_str(), array
->null_count(), array
->length(),
117 // Duplication is done by first materializing the vector and
118 // then make a lazy duplicate of data2
119 static SEXP
Duplicate(SEXP alt
, Rboolean
/* deep */) {
120 return Rf_lazy_duplicate(Impl::Materialize(alt
));
123 static SEXP
Coerce(SEXP alt
, int type
) {
124 return Rf_coerceVector(Impl::Materialize(alt
), type
);
127 static SEXP
Serialized_state(SEXP alt
) { return Impl::Materialize(alt
); }
129 static SEXP
Unserialize(SEXP
/* class_ */, SEXP state
) { return state
; }
132 // altrep R vector shadowing an primitive (int or double) Array.
134 // This tries as much as possible to directly use the data
135 // from the Array and minimize data copies.
136 template <int sexp_type
>
137 struct AltrepVectorPrimitive
: public AltrepVectorBase
<AltrepVectorPrimitive
<sexp_type
>> {
138 using Base
= AltrepVectorBase
<AltrepVectorPrimitive
<sexp_type
>>;
140 // singleton altrep class description
141 static R_altrep_class_t class_t
;
143 using c_type
= typename
std::conditional
<sexp_type
== REALSXP
, double, int>::type
;
145 // Force materialization. After calling this, the data2 slot of the altrep
146 // object contains a standard R vector with the same data, with
147 // R sentinels where the Array has nulls.
149 // The Array remains available so that it can be used by Length(), Min(), etc ...
150 static SEXP
Materialize(SEXP alt
) {
151 if (!Base::IsMaterialized(alt
)) {
152 auto size
= Base::Length(alt
);
154 // create a standard R vector
155 SEXP copy
= PROTECT(Rf_allocVector(sexp_type
, size
));
157 // copy the data from the array, through Get_region
158 Get_region(alt
, 0, size
, reinterpret_cast<c_type
*>(DATAPTR(copy
)));
160 // store as data2, this is now considered materialized
161 R_set_altrep_data2(alt
, copy
);
162 MARK_NOT_MUTABLE(copy
);
166 return R_altrep_data2(alt
);
169 // R calls this to get a pointer to the start of the vector data
170 // but only if this is possible without allocating (in the R sense).
171 static const void* Dataptr_or_null(SEXP alt
) {
172 // data2 has been created, and so the R sentinels are in place where the array has
174 if (Base::IsMaterialized(alt
)) {
175 return DATAPTR_RO(R_altrep_data2(alt
));
178 // the Array has no nulls, we can directly return the start of its data
179 const auto& array
= GetArray(alt
);
180 if (array
->null_count() == 0) {
181 return reinterpret_cast<const void*>(array
->data()->template GetValues
<c_type
>(1));
184 // Otherwise: if the array has nulls and data2 has not been generated: give up
188 // R calls this to get a pointer to the start of the data, R allocations are allowed.
189 static void* Dataptr(SEXP alt
, Rboolean writeable
) {
190 // If the object hasn't been materialized, and the array has no
191 // nulls we can directly point to the array data.
192 if (!Base::IsMaterialized(alt
)) {
193 const auto& array
= GetArray(alt
);
195 if (array
->null_count() == 0) {
196 return reinterpret_cast<void*>(
197 const_cast<c_type
*>(array
->data()->template GetValues
<c_type
>(1)));
201 // Otherwise we have to materialize and hand the pointer to data2
203 // NOTE: this returns the DATAPTR() of data2 even in the case writeable = TRUE
205 // which is risky because C(++) clients of this object might
206 // modify data2, and therefore make it diverge from the data of the Array,
207 // but the object was marked as immutable on creation, so doing this is
208 // disregarding the R api.
210 // Simply stop() when `writeable = TRUE` is too strong, e.g. this fails
211 // identical() which calls DATAPTR() even though DATAPTR_RO() would
213 return DATAPTR(Materialize(alt
));
216 // The value at position i
217 static c_type
Elt(SEXP alt
, R_xlen_t i
) {
218 const auto& array
= GetArray(alt
);
219 return array
->IsNull(i
) ? cpp11::na
<c_type
>()
220 : array
->data()->template GetValues
<c_type
>(1)[i
];
223 // R calls this when it wants data from position `i` to `i + n` copied into `buf`
224 // The returned value is the number of values that were really copied
225 // (this can be lower than n)
226 static R_xlen_t
Get_region(SEXP alt
, R_xlen_t i
, R_xlen_t n
, c_type
* buf
) {
227 // If we have data2, we can just copy the region into buf
228 // using the standard Get_region for this R type
229 if (Base::IsMaterialized(alt
)) {
230 return Standard_Get_region
<c_type
>(R_altrep_data2(alt
), i
, n
, buf
);
233 // The vector was not materialized, aka we don't have data2
235 // In that case, we copy the data from the Array, and then
236 // do a second pass to force the R sentinels for where the
239 // This only materialize the region, into buf. Not the entire vector.
240 auto slice
= GetArray(alt
)->Slice(i
, n
);
241 R_xlen_t ncopy
= slice
->length();
243 // first copy the data buffer
244 memcpy(buf
, slice
->data()->template GetValues
<c_type
>(1), ncopy
* sizeof(c_type
));
246 // then set the R NA sentinels if needed
247 if (slice
->null_count() > 0) {
248 internal::BitmapReader
bitmap_reader(slice
->null_bitmap()->data(), slice
->offset(),
251 for (R_xlen_t j
= 0; j
< ncopy
; j
++, bitmap_reader
.Next()) {
252 if (bitmap_reader
.IsNotSet()) {
253 buf
[j
] = cpp11::na
<c_type
>();
261 static std::shared_ptr
<arrow::compute::ScalarAggregateOptions
> NaRmOptions(
262 const std::shared_ptr
<Array
>& array
, bool na_rm
) {
263 auto options
= std::make_shared
<arrow::compute::ScalarAggregateOptions
>(
264 arrow::compute::ScalarAggregateOptions::Defaults());
265 options
->min_count
= 0;
266 options
->skip_nulls
= na_rm
;
271 static SEXP
MinMax(SEXP alt
, Rboolean narm
) {
272 using data_type
= typename
std::conditional
<sexp_type
== REALSXP
, double, int>::type
;
274 typename
std::conditional
<sexp_type
== INTSXP
, Int32Scalar
, DoubleScalar
>::type
;
276 const auto& array
= GetArray(alt
);
277 bool na_rm
= narm
== TRUE
;
278 auto n
= array
->length();
279 auto null_count
= array
->null_count();
280 if ((na_rm
|| n
== 0) && null_count
== n
) {
281 return Rf_ScalarReal(Min
? R_PosInf
: R_NegInf
);
283 if (!na_rm
&& null_count
> 0) {
284 return cpp11::as_sexp(cpp11::na
<data_type
>());
287 auto options
= NaRmOptions(array
, na_rm
);
290 ValueOrStop(arrow::compute::CallFunction("min_max", {array
}, options
.get()));
291 const auto& minmax_scalar
=
292 internal::checked_cast
<const StructScalar
&>(*minmax
.scalar());
294 const auto& result_scalar
= internal::checked_cast
<const scalar_type
&>(
295 *ValueOrStop(minmax_scalar
.field(Min
? "min" : "max")));
296 return cpp11::as_sexp(result_scalar
.value
);
299 static SEXP
Min(SEXP alt
, Rboolean narm
) { return MinMax
<true>(alt
, narm
); }
301 static SEXP
Max(SEXP alt
, Rboolean narm
) { return MinMax
<false>(alt
, narm
); }
303 static SEXP
Sum(SEXP alt
, Rboolean narm
) {
304 using data_type
= typename
std::conditional
<sexp_type
== REALSXP
, double, int>::type
;
306 const auto& array
= GetArray(alt
);
307 bool na_rm
= narm
== TRUE
;
308 auto null_count
= array
->null_count();
310 if (!na_rm
&& null_count
> 0) {
311 return cpp11::as_sexp(cpp11::na
<data_type
>());
313 auto options
= NaRmOptions(array
, na_rm
);
316 ValueOrStop(arrow::compute::CallFunction("sum", {array
}, options
.get()));
318 if (sexp_type
== INTSXP
) {
319 // When calling the "sum" function on an int32 array, we get an Int64 scalar
320 // in case of overflow, make it a double like R
321 int64_t value
= internal::checked_cast
<const Int64Scalar
&>(*sum
.scalar()).value
;
322 if (value
<= INT32_MIN
|| value
> INT32_MAX
) {
323 return Rf_ScalarReal(static_cast<double>(value
));
325 return Rf_ScalarInteger(static_cast<int>(value
));
328 return Rf_ScalarReal(
329 internal::checked_cast
<const DoubleScalar
&>(*sum
.scalar()).value
);
333 template <int sexp_type
>
334 R_altrep_class_t AltrepVectorPrimitive
<sexp_type
>::class_t
;
336 // Implementation for string arrays
337 template <typename Type
>
338 struct AltrepVectorString
: public AltrepVectorBase
<AltrepVectorString
<Type
>> {
339 using Base
= AltrepVectorBase
<AltrepVectorString
<Type
>>;
341 static R_altrep_class_t class_t
;
342 using StringArrayType
= typename TypeTraits
<Type
>::ArrayType
;
344 // Helper class to convert to R strings
345 struct RStringViewer
{
346 explicit RStringViewer(const std::shared_ptr
<Array
>& array
)
348 string_array_(internal::checked_cast
<const StringArrayType
*>(array
.get())),
349 strip_out_nuls_(GetBoolOption("arrow.skip_nul", false)),
350 nul_was_stripped_(false) {}
352 // convert the i'th string of the Array to an R string (CHARSXP)
353 SEXP
Convert(size_t i
) {
354 if (array_
->IsNull(i
)) {
358 view_
= string_array_
->GetView(i
);
359 bool no_nul
= std::find(view_
.begin(), view_
.end(), '\0') == view_
.end();
362 return Rf_mkCharLenCE(view_
.data(), view_
.size(), CE_UTF8
);
363 } else if (strip_out_nuls_
) {
364 return ConvertStripNul();
373 // strip the nuls and then convert to R string
374 SEXP
ConvertStripNul() {
375 const char* old_string
= view_
.data();
377 size_t stripped_len
= 0, nul_count
= 0;
379 for (size_t i
= 0; i
< view_
.size(); i
++) {
380 if (old_string
[i
] == '\0') {
383 if (nul_count
== 1) {
384 // first nul spotted: allocate stripped string storage
385 stripped_string_
.assign(view_
.begin(), view_
.end());
389 // don't copy old_string[i] (which is \0) into stripped_string
394 stripped_string_
[stripped_len
++] = old_string
[i
];
398 nul_was_stripped_
= true;
399 return Rf_mkCharLenCE(stripped_string_
.data(), stripped_len
, CE_UTF8
);
402 bool nul_was_stripped() const { return nul_was_stripped_
; }
404 // throw R error about embedded nul
406 stripped_string_
= "embedded nul in string: '";
407 for (char c
: view_
) {
409 stripped_string_
+= c
;
411 stripped_string_
+= "\\0";
416 "'; to strip nuls when converting from Arrow to R, set options(arrow.skip_nul "
419 Rf_error(stripped_string_
.c_str());
422 const std::shared_ptr
<Array
>& array_
;
423 const StringArrayType
* string_array_
;
424 std::string stripped_string_
;
425 const bool strip_out_nuls_
;
426 bool nul_was_stripped_
;
427 util::string_view view_
;
430 // Get a single string, as a CHARSXP SEXP,
431 // either from data2 or directly from the Array
432 static SEXP
Elt(SEXP alt
, R_xlen_t i
) {
433 if (Base::IsMaterialized(alt
)) {
434 return STRING_ELT(R_altrep_data2(alt
), i
);
439 const auto& array
= GetArray(alt
);
440 RStringViewer
r_string_viewer(array
);
442 // r_string_viewer.Convert(i) might jump so it's wrapped
443 // in cpp11::unwind_protect() so that string_viewer
444 // can be properly destructed before the unwinding continues
446 cpp11::unwind_protect([&]() {
447 s
= r_string_viewer
.Convert(i
);
448 if (r_string_viewer
.nul_was_stripped()) {
449 cpp11::warning("Stripping '\\0' (nul) from character vector");
457 static void* Dataptr(SEXP alt
, Rboolean writeable
) { return DATAPTR(Materialize(alt
)); }
459 static SEXP
Materialize(SEXP alt
) {
460 if (Base::IsMaterialized(alt
)) {
461 return R_altrep_data2(alt
);
466 const auto& array
= GetArray(alt
);
467 R_xlen_t n
= array
->length();
468 SEXP data2
= PROTECT(Rf_allocVector(STRSXP
, n
));
469 MARK_NOT_MUTABLE(data2
);
471 RStringViewer
r_string_viewer(array
);
473 // r_string_viewer.Convert(i) might jump so we have to
474 // wrap it in unwind_protect() to:
475 // - correctly destruct the C++ objects
476 // - resume the unwinding
477 cpp11::unwind_protect([&]() {
478 for (R_xlen_t i
= 0; i
< n
; i
++) {
479 SET_STRING_ELT(data2
, i
, r_string_viewer
.Convert(i
));
482 if (r_string_viewer
.nul_was_stripped()) {
483 cpp11::warning("Stripping '\\0' (nul) from character vector");
487 // only set to data2 if all the values have been converted
488 R_set_altrep_data2(alt
, data2
);
489 UNPROTECT(1); // data2
496 static const void* Dataptr_or_null(SEXP alt
) {
497 if (Base::IsMaterialized(alt
)) return DATAPTR(R_altrep_data2(alt
));
503 static void Set_elt(SEXP alt
, R_xlen_t i
, SEXP v
) {
504 Rf_error("ALTSTRING objects of type <arrow::array_string_vector> are immutable");
508 template <typename Type
>
509 R_altrep_class_t AltrepVectorString
<Type
>::class_t
;
511 // initialize altrep, altvec, altreal, and altinteger methods
512 template <typename AltrepClass
>
513 void InitAltrepMethods(R_altrep_class_t class_t
, DllInfo
* dll
) {
514 R_set_altrep_Length_method(class_t
, AltrepClass::Length
);
515 R_set_altrep_Inspect_method(class_t
, AltrepClass::Inspect
);
516 R_set_altrep_Duplicate_method(class_t
, AltrepClass::Duplicate
);
517 R_set_altrep_Serialized_state_method(class_t
, AltrepClass::Serialized_state
);
518 R_set_altrep_Unserialize_method(class_t
, AltrepClass::Unserialize
);
519 R_set_altrep_Coerce_method(class_t
, AltrepClass::Coerce
);
522 template <typename AltrepClass
>
523 void InitAltvecMethods(R_altrep_class_t class_t
, DllInfo
* dll
) {
524 R_set_altvec_Dataptr_method(class_t
, AltrepClass::Dataptr
);
525 R_set_altvec_Dataptr_or_null_method(class_t
, AltrepClass::Dataptr_or_null
);
528 template <typename AltrepClass
>
529 void InitAltRealMethods(R_altrep_class_t class_t
, DllInfo
* dll
) {
530 R_set_altreal_No_NA_method(class_t
, AltrepClass::No_NA
);
531 R_set_altreal_Is_sorted_method(class_t
, AltrepClass::Is_sorted
);
533 R_set_altreal_Sum_method(class_t
, AltrepClass::Sum
);
534 R_set_altreal_Min_method(class_t
, AltrepClass::Min
);
535 R_set_altreal_Max_method(class_t
, AltrepClass::Max
);
537 R_set_altreal_Elt_method(class_t
, AltrepClass::Elt
);
538 R_set_altreal_Get_region_method(class_t
, AltrepClass::Get_region
);
541 template <typename AltrepClass
>
542 void InitAltIntegerMethods(R_altrep_class_t class_t
, DllInfo
* dll
) {
543 R_set_altinteger_No_NA_method(class_t
, AltrepClass::No_NA
);
544 R_set_altinteger_Is_sorted_method(class_t
, AltrepClass::Is_sorted
);
546 R_set_altinteger_Sum_method(class_t
, AltrepClass::Sum
);
547 R_set_altinteger_Min_method(class_t
, AltrepClass::Min
);
548 R_set_altinteger_Max_method(class_t
, AltrepClass::Max
);
550 R_set_altinteger_Elt_method(class_t
, AltrepClass::Elt
);
551 R_set_altinteger_Get_region_method(class_t
, AltrepClass::Get_region
);
554 template <typename AltrepClass
>
555 void InitAltRealClass(DllInfo
* dll
, const char* name
) {
556 AltrepClass::class_t
= R_make_altreal_class(name
, "arrow", dll
);
557 InitAltrepMethods
<AltrepClass
>(AltrepClass::class_t
, dll
);
558 InitAltvecMethods
<AltrepClass
>(AltrepClass::class_t
, dll
);
559 InitAltRealMethods
<AltrepClass
>(AltrepClass::class_t
, dll
);
562 template <typename AltrepClass
>
563 void InitAltIntegerClass(DllInfo
* dll
, const char* name
) {
564 AltrepClass::class_t
= R_make_altinteger_class(name
, "arrow", dll
);
565 InitAltrepMethods
<AltrepClass
>(AltrepClass::class_t
, dll
);
566 InitAltvecMethods
<AltrepClass
>(AltrepClass::class_t
, dll
);
567 InitAltIntegerMethods
<AltrepClass
>(AltrepClass::class_t
, dll
);
570 template <typename AltrepClass
>
571 void InitAltStringClass(DllInfo
* dll
, const char* name
) {
572 AltrepClass::class_t
= R_make_altstring_class(name
, "arrow", dll
);
573 R_set_altrep_Length_method(AltrepClass::class_t
, AltrepClass::Length
);
574 R_set_altrep_Inspect_method(AltrepClass::class_t
, AltrepClass::Inspect
);
575 R_set_altrep_Duplicate_method(AltrepClass::class_t
, AltrepClass::Duplicate
);
576 R_set_altrep_Serialized_state_method(AltrepClass::class_t
,
577 AltrepClass::Serialized_state
);
578 R_set_altrep_Unserialize_method(AltrepClass::class_t
, AltrepClass::Unserialize
);
579 R_set_altrep_Coerce_method(AltrepClass::class_t
, AltrepClass::Coerce
);
581 R_set_altvec_Dataptr_method(AltrepClass::class_t
, AltrepClass::Dataptr
);
582 R_set_altvec_Dataptr_or_null_method(AltrepClass::class_t
, AltrepClass::Dataptr_or_null
);
584 R_set_altstring_Elt_method(AltrepClass::class_t
, AltrepClass::Elt
);
585 R_set_altstring_Set_elt_method(AltrepClass::class_t
, AltrepClass::Set_elt
);
586 R_set_altstring_No_NA_method(AltrepClass::class_t
, AltrepClass::No_NA
);
587 R_set_altstring_Is_sorted_method(AltrepClass::class_t
, AltrepClass::Is_sorted
);
592 // initialize the altrep classes
593 void Init_Altrep_classes(DllInfo
* dll
) {
594 InitAltRealClass
<AltrepVectorPrimitive
<REALSXP
>>(dll
, "arrow::array_dbl_vector");
595 InitAltIntegerClass
<AltrepVectorPrimitive
<INTSXP
>>(dll
, "arrow::array_int_vector");
597 InitAltStringClass
<AltrepVectorString
<StringType
>>(dll
, "arrow::array_string_vector");
598 InitAltStringClass
<AltrepVectorString
<LargeStringType
>>(
599 dll
, "arrow::array_large_string_vector");
602 // return an altrep R vector that shadows the array if possible
603 SEXP
MakeAltrepVector(const std::shared_ptr
<ChunkedArray
>& chunked_array
) {
604 // special case when there is only one array
605 if (chunked_array
->num_chunks() == 1) {
606 const auto& array
= chunked_array
->chunk(0);
608 // - the arrow.use_altrep is set to TRUE or unset (implicit TRUE)
609 // - the array has at least one element
610 if (arrow::r::GetBoolOption("arrow.use_altrep", true) && array
->length() > 0) {
611 switch (array
->type()->id()) {
612 case arrow::Type::DOUBLE
:
613 return altrep::AltrepVectorPrimitive
<REALSXP
>::Make(array
);
615 case arrow::Type::INT32
:
616 return altrep::AltrepVectorPrimitive
<INTSXP
>::Make(array
);
618 case arrow::Type::STRING
:
619 return altrep::AltrepVectorString
<StringType
>::Make(array
);
621 case arrow::Type::LARGE_STRING
:
622 return altrep::AltrepVectorString
<LargeStringType
>::Make(array
);
632 bool is_arrow_altrep(SEXP x
) {
634 SEXP info
= ALTREP_CLASS_SERIALIZED_CLASS(ALTREP_CLASS(x
));
635 SEXP pkg
= ALTREP_SERIALIZED_CLASS_PKGSYM(info
);
637 if (pkg
== symbols::arrow
) return true;
643 std::shared_ptr
<Array
> vec_to_arrow_altrep_bypass(SEXP x
) {
644 if (is_arrow_altrep(x
)) {
651 } // namespace altrep
661 // return an altrep R vector that shadows the array if possible
662 SEXP
MakeAltrepVector(const std::shared_ptr
<ChunkedArray
>& chunked_array
) {
666 bool is_arrow_altrep(SEXP
) { return false; }
668 std::shared_ptr
<Array
> vec_to_arrow_altrep_bypass(SEXP x
) { return nullptr; }
670 } // namespace altrep
677 void test_SET_STRING_ELT(SEXP s
) { SET_STRING_ELT(s
, 0, Rf_mkChar("forbidden")); }
680 bool test_same_Array(SEXP x
, SEXP y
) {
681 auto* p_x
= reinterpret_cast<std::shared_ptr
<arrow::Array
>*>(x
);
682 auto* p_y
= reinterpret_cast<std::shared_ptr
<arrow::Array
>*>(y
);
684 return p_x
->get() == p_y
->get();
688 bool is_arrow_altrep(SEXP x
) { return arrow::r::altrep::is_arrow_altrep(x
); }