Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions compile_commands.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I supposed to gitignore this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep these commands are specific to your setup

{
"command": "clang++ -arch arm64 -std=gnu++17 -I\"/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/include\" -DNDEBUG -I\"/Users/hadleywickham/Library/R/arm64/4.5/library/cpp11/include\" -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c address.cpp -o address.o",
"file": "address.cpp",
"directory": "/Users/hadleywickham/Documents/r-lib/lobstr/src"
},
{
"command": "clang++ -arch arm64 -std=gnu++17 -I\"/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/include\" -DNDEBUG -I\"/Users/hadleywickham/Library/R/arm64/4.5/library/cpp11/include\" -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c cpp11.cpp -o cpp11.o",
"file": "cpp11.cpp",
"directory": "/Users/hadleywickham/Documents/r-lib/lobstr/src"
},
{
"command": "clang++ -arch arm64 -std=gnu++17 -I\"/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/include\" -DNDEBUG -I\"/Users/hadleywickham/Library/R/arm64/4.5/library/cpp11/include\" -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c inspect.cpp -o inspect.o",
"file": "inspect.cpp",
"directory": "/Users/hadleywickham/Documents/r-lib/lobstr/src"
},
{
"command": "clang++ -arch arm64 -std=gnu++17 -I\"/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/include\" -DNDEBUG -I\"/Users/hadleywickham/Library/R/arm64/4.5/library/cpp11/include\" -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c size.cpp -o size.o",
"file": "size.cpp",
"directory": "/Users/hadleywickham/Documents/r-lib/lobstr/src"
}
]
36 changes: 33 additions & 3 deletions src/inspect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,39 @@ SEXP obj_children_(
}
}

// CHARSXPs have fake attriibutes
if (max_depth > 0 && TYPEOF(x) != CHARSXP && !Rf_isNull(ATTRIB(x))) {
recurse(&children, seen, "_attrib", ATTRIB(x), max_depth, expand);
// CHARSXPs have fake attributes
if (max_depth > 0 && TYPEOF(x) != CHARSXP && ANY_ATTRIB(x)) {
// Count attributes
R_xlen_t n_attribs = 0;
R_mapAttrib(x, [](SEXP tag, SEXP val, void* data) -> SEXP {
(*(R_xlen_t*)data)++;
return NULL;
}, &n_attribs);

// Build a list of attribute values
struct CollectData { SEXP vals; SEXP names; R_xlen_t i; bool any_named; };
SEXP attrib_vals = PROTECT(Rf_allocVector(VECSXP, n_attribs));
SEXP attrib_names = PROTECT(Rf_allocVector(STRSXP, n_attribs));
CollectData cd = {attrib_vals, attrib_names, 0, false};

R_mapAttrib(x, [](SEXP tag, SEXP val, void* data) -> SEXP {
CollectData* d = (CollectData*)data;
SET_VECTOR_ELT(d->vals, d->i, val);
if (TYPEOF(tag) == SYMSXP) {
SET_STRING_ELT(d->names, d->i, PRINTNAME(tag));
d->any_named = true;
}
d->i++;
return NULL;
}, &cd);

// Only set names when attributes have tags, to avoid adding a
// spurious names attribute to the container
if (cd.any_named) {
Rf_setAttrib(attrib_vals, R_NamesSymbol, attrib_names);
}
recurse(&children, seen, "_attrib", attrib_vals, max_depth, expand);
UNPROTECT(2);
}

SEXP out = PROTECT(children.vector());
Expand Down
21 changes: 19 additions & 2 deletions src/size.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,25 @@ double obj_size_tree(SEXP x,
#endif

// CHARSXPs have fake attributes
if (TYPEOF(x) != CHARSXP )
size += obj_size_tree(ATTRIB(x), base_env, sizeof_node, sizeof_vector, seen, depth + 1);
if (TYPEOF(x) != CHARSXP && ANY_ATTRIB(x)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have collect_attribs() should we just use it here? Probably a little bit less efficient but that's unlikely to matter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea!

struct attrib_size_data {
double* size;
SEXP base_env;
int sizeof_node;
int sizeof_vector;
std::set<SEXP>* seen;
int depth;
};
attrib_size_data cb_data = {&size, base_env, sizeof_node, sizeof_vector, &seen, depth};
R_mapAttrib(x, [](SEXP tag, SEXP val, void* data) -> SEXP {
attrib_size_data* d = (attrib_size_data*)data;
// Account for the pairlist cons cell
*(d->size) += d->sizeof_node;
*(d->size) += obj_size_tree(tag, d->base_env, d->sizeof_node, d->sizeof_vector, *(d->seen), d->depth + 1);
*(d->size) += obj_size_tree(val, d->base_env, d->sizeof_node, d->sizeof_vector, *(d->seen), d->depth + 1);
return NULL;
}, &cb_data);
}

switch (TYPEOF(x)) {
// Vectors -------------------------------------------------------------------
Expand Down
31 changes: 31 additions & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,35 @@ static inline
SEXP R_ParentEnv(SEXP x) {
return ENCLOS(x);
}

static inline
int ANY_ATTRIB(SEXP x) {
return ATTRIB(x) != R_NilValue;
}
#endif

#if R_VERSION < R_Version(4, 6, 0)
// Polyfill for R_mapAttrib(), available in R >= 4.6.0.
// Pulled exactly as-is from R:
// https://github.com/r-devel/r-svn/blob/a39f4a28848fd02a1310b455353a871f2bb1965b/src/main/attrib.c#L2014
// https://github.com/r-devel/r-svn/blob/a39f4a28848fd02a1310b455353a871f2bb1965b/doc/manual/R-exts.texi#L17920
static inline
SEXP R_mapAttrib(SEXP x, SEXP (*FUN)(SEXP, SEXP, void *), void *data) {
PROTECT_INDEX api;
SEXP a = ATTRIB(x);
SEXP val = NULL;

PROTECT_WITH_INDEX(a, &api);
while (a != R_NilValue) {
SEXP tag = PROTECT(TAG(a));
SEXP attr = PROTECT(CAR(a));
val = FUN(tag, attr, data);
UNPROTECT(2);
if (val != NULL)
break;
REPROTECT(a = CDR(a), api);
}
UNPROTECT(1);
return val;
}
#endif
2 changes: 1 addition & 1 deletion tests/testthat/_snaps/sxp.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
Output
[1] <INTSXP[10]> (altrep )
_class [2] <RAWSXP[144]> ()
_attrib [3] <LISTSXP> ()
_attrib [3] <VECSXP[3]> ()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically a regression, but I'm fine with it.

Copy link
Member

@lionel- lionel- Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm it'd be very confusing for such a low level inspection tool to misrepresent data structures involved.

But that was an easy fix (just collect in a pairlist), I just pushed it.

[4] <SYMSXP: compact_intseq> ()
[5] <SYMSXP: base> ()
[6] <INTSXP[1]> ()
Expand Down