diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index bb9af397d527b..6ba38ea34ff50 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -51,6 +51,9 @@ #include #include #include +#if defined(_LIBCPP_VERSION) +#include "Framework/DataRefUtils.h" +#endif #include #include "rapidjson/document.h" #include "rapidjson/writer.h" @@ -986,6 +989,9 @@ void* CcdbApi::extractFromTFile(TFile& file, TClass const* cl, const char* what) if (!cl) { return nullptr; } +#if defined(_LIBCPP_VERSION) + o2::framework::DataRefUtils::registerLibstdcxxStdArray(file, cl); +#endif auto object = file.GetObjectChecked(what, cl); if (!object) { // it could be that object was stored with previous convention diff --git a/Framework/Core/include/Framework/DataRefUtils.h b/Framework/Core/include/Framework/DataRefUtils.h index 498b4eb5f7a87..fce3435f66299 100644 --- a/Framework/Core/include/Framework/DataRefUtils.h +++ b/Framework/Core/include/Framework/DataRefUtils.h @@ -31,6 +31,10 @@ namespace o2::conf { class ConfigurableParam; } +#if defined(_LIBCPP_VERSION) +class TClass; +class TFile; +#endif namespace o2::framework { @@ -185,6 +189,10 @@ struct DataRefUtils { // Decode a CCDB object using the CcdbApi. static void* decodeCCDB(DataRef const& ref, std::type_info const& info); static std::map extractCCDBHeaders(DataRef const& ref); +#if defined(_LIBCPP_VERSION) + /// Make a std::array written with libstdc++ readable from file by this libc++ build. + static void registerLibstdcxxStdArray(TFile& file, TClass const* cl); +#endif /// Return a span over the raw CCDB payload bytes, stripping the flattened HTTP /// headers footer that CCDBFetcherHelper appends. Use this when the CCDB entry is /// a binary blob rather than a ROOT-serialised object. diff --git a/Framework/Core/src/DataRefUtils.cxx b/Framework/Core/src/DataRefUtils.cxx index 894ad0b8ace63..0b5aee4287abb 100644 --- a/Framework/Core/src/DataRefUtils.cxx +++ b/Framework/Core/src/DataRefUtils.cxx @@ -20,6 +20,11 @@ #include #include #include +#if defined(_LIBCPP_VERSION) +#include +#include +#include +#endif namespace o2::framework { @@ -31,6 +36,9 @@ void* extractFromTFile(TFile& file, TClass const* cl, const char* what) if (!cl) { return nullptr; } +#if defined(_LIBCPP_VERSION) + DataRefUtils::registerLibstdcxxStdArray(file, cl); +#endif auto object = file.GetObjectChecked(what, cl); if (!object) { // it could be that object was stored with previous convention @@ -73,6 +81,37 @@ void* extractFromTFile(TFile& file, TClass const* cl, const char* what) return result; } } // namespace + +#if defined(_LIBCPP_VERSION) +// The only member of std::array is T _M_elems[N] in libstdc++, T __elems_[N] in libc++, so +// objects written on Linux carry a StreamerInfo with a checksum the libc++ class does not have. +// When opening the file, ROOT drops that StreamerInfo as an alias of the in-memory one +// (TStreamerInfo::BuildCheck), and the read fails with "Could not find the StreamerInfo with a +// checksum", leaving the array zero-filled. Register the on-file layout as a version of its own, +// under the in-memory member name, so that the payload, T[N] for both, is read as usual. +void DataRefUtils::registerLibstdcxxStdArray(TFile& file, TClass const* cl) +{ + auto* infos = std::strncmp(cl->GetName(), "array<", 6) == 0 ? file.GetStreamerInfoCache() : nullptr; + auto* onfile = infos ? dynamic_cast(infos->FindObject(cl->GetName())) : nullptr; + if (!onfile || onfile->GetNelement() != 1 || std::strcmp(onfile->GetElement(0)->GetName(), "_M_elems") != 0) { + return; + } + // Exists before BuildCheck, so that the on-file layout gets a version of its own + auto* current = static_cast(const_cast(cl)->GetStreamerInfo()); + auto* info = static_cast(cl->FindStreamerInfo(onfile->GetCheckSum())); + if (!info) { + info = static_cast(onfile->Clone()); + info->BuildCheck(&file); + } + // Also when ROOT registered it itself: it then reads nothing, without any error + if (std::strcmp(info->GetElement(0)->GetName(), current->GetElement(0)->GetName()) != 0) { + info->GetElement(0)->SetName(current->GetElement(0)->GetName()); + info->Clear("build"); + info->BuildOld(); + } +} +#endif + // Adapted from CcdbApi private method interpretAsTMemFileAndExtract // If the former is moved to public, throws on error and could be changed to // not require a mutex we could use it.