Skip to content

Commit 15d4a58

Browse files
committed
DPL: refactor DataHeader to not include boost
This separates Stack in its own header so that boost is not exposed when DataHeader is included. Apart from the discussion of whether we should include boost in our ROOT_INCLUDE_PATH, this is in any case a good refactoring which separates the data header description from their actual manipulation.
1 parent 561e37e commit 15d4a58

File tree

18 files changed

+219
-159
lines changed

18 files changed

+219
-159
lines changed

Algorithm/test/headerstack.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <cstring> // memcmp
2323
#include "Headers/DataHeader.h" // hexdump
2424
#include "Headers/NameHeader.h"
25+
#include "Headers/Stack.h"
2526
#include "../include/Algorithm/HeaderStack.h"
2627

2728
using DataHeader = o2::header::DataHeader;

DataFormats/Headers/include/Headers/DataHeader.h

Lines changed: 2 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
#include <algorithm> // std::min
3838
#include <stdexcept>
3939
#include <string>
40-
#include "MemoryResources/MemoryResources.h"
40+
// FIXME: for o2::byte. Use std::byte as soon as we move to C++17..
41+
#include "MemoryResources/Types.h"
4142

4243
namespace o2 {
4344
namespace header {
@@ -468,149 +469,6 @@ auto get(const void* buffer, size_t len = 0)
468469
return get<HeaderType>(reinterpret_cast<const byte *>(buffer), len);
469470
}
470471

471-
//__________________________________________________________________________________________________
472-
/// @struct Stack
473-
/// @brief a move-only header stack with serialized headers
474-
/// This is the flat buffer where all the headers in a multi-header go.
475-
/// This guy knows how to move the serialized content to FairMQ
476-
/// and inform it how to release when all is sent.
477-
/// methods to construct a multi-header
478-
/// intended use:
479-
/// - as a variadic intializer list (as an argument to a function)
480-
///
481-
/// One can also use the ctor directly:
482-
// Stack::Stack(const T& header1, const T& header2, ...)
483-
// - arguments can be headers, or stacks, all will be concatenated in a new Stack
484-
/// - returns a Stack ready to be shipped.
485-
struct Stack {
486-
487-
private:
488-
static void freefn(void* data, void* hint)
489-
{
490-
boost::container::pmr::memory_resource* resource = static_cast<boost::container::pmr::memory_resource*>(hint);
491-
resource->deallocate(data, 0, 0);
492-
}
493-
494-
struct freeobj {
495-
freeobj() {}
496-
freeobj(boost::container::pmr::memory_resource* mr) : resource(mr) {}
497-
498-
boost::container::pmr::memory_resource* resource{ nullptr };
499-
void operator()(o2::byte* ptr) { Stack::freefn(ptr, resource); }
500-
};
501-
502-
public:
503-
using allocator_type = boost::container::pmr::polymorphic_allocator<o2::byte>;
504-
using value_type = o2::byte;
505-
using BufferType = std::unique_ptr<value_type[], freeobj>;
506-
507-
Stack() = default;
508-
Stack(Stack&&) = default;
509-
Stack(Stack&) = delete;
510-
Stack& operator=(Stack&) = delete;
511-
Stack& operator=(Stack&&) = default;
512-
513-
value_type* data() const { return buffer.get(); }
514-
size_t size() const { return bufferSize; }
515-
allocator_type get_allocator() const { return allocator; }
516-
517-
//
518-
boost::container::pmr::memory_resource* getFreefnHint() const noexcept { return allocator.resource(); }
519-
static auto getFreefn() noexcept { return &freefn; }
520-
521-
/// The magic constructors: take arbitrary number of headers and serialize them
522-
/// into the buffer buffer allocated by the specified polymorphic allocator. By default
523-
/// allocation is done using new_delete_resource.
524-
/// In the final stack the first header must be DataHeader.
525-
/// all headers must derive from BaseHeader, in addition also other stacks can be passed to ctor.
526-
template <typename FirstArgType, typename... Headers,
527-
typename std::enable_if_t<
528-
!std::is_convertible<FirstArgType, boost::container::pmr::polymorphic_allocator<o2::byte>>::value, int> = 0>
529-
Stack(FirstArgType&& firstHeader, Headers&&... headers)
530-
: Stack(boost::container::pmr::new_delete_resource(), std::forward<FirstArgType>(firstHeader),
531-
std::forward<Headers>(headers)...)
532-
{
533-
}
534-
535-
template <typename... Headers>
536-
Stack(const allocator_type allocatorArg, Headers&&... headers)
537-
: allocator{ allocatorArg },
538-
bufferSize{ calculateSize(std::forward<Headers>(headers)...) },
539-
buffer{ static_cast<o2::byte*>(allocator.resource()->allocate(bufferSize, alignof(std::max_align_t))),
540-
freeobj(getFreefnHint()) }
541-
{
542-
inject(buffer.get(), std::forward<Headers>(headers)...);
543-
}
544-
545-
private:
546-
allocator_type allocator{ boost::container::pmr::new_delete_resource() };
547-
size_t bufferSize{ 0 };
548-
BufferType buffer{ nullptr, freeobj{ getFreefnHint() } };
549-
550-
template <typename T, typename... Args>
551-
static size_t calculateSize(T&& h, Args&&... args) noexcept
552-
{
553-
return calculateSize(std::forward<T>(h)) + calculateSize(std::forward<Args>(args)...);
554-
}
555-
556-
template <typename T>
557-
static size_t calculateSize(T&& h) noexcept
558-
{
559-
return h.size();
560-
}
561-
562-
//recursion terminator
563-
constexpr static size_t calculateSize() { return 0; }
564-
565-
template <typename T>
566-
static o2::byte* inject(o2::byte* here, T&& h) noexcept
567-
{
568-
using headerType = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
569-
static_assert(
570-
std::is_base_of<BaseHeader, headerType>::value == true || std::is_same<Stack, headerType>::value == true,
571-
"header stack parameters are restricted to stacks and headers derived from BaseHeader");
572-
std::copy(h.data(), h.data() + h.size(), here);
573-
return here + h.size();
574-
// somehow could not trigger copy elision for placed construction, TODO: check out if this is possible here
575-
// headerType* placed = new (here) headerType(std::forward<T>(h));
576-
// return here + placed->size();
577-
}
578-
579-
template <typename T, typename... Args>
580-
static o2::byte* inject(o2::byte* here, T&& h, Args&&... args) noexcept
581-
{
582-
auto alsohere = inject(here, h);
583-
// the type might be a stack itself, loop through headers and set the flag in the last one
584-
if (h.size() > 0) {
585-
BaseHeader* next = BaseHeader::get(here);
586-
while (next->flagsNextHeader) {
587-
next = next->next();
588-
}
589-
next->flagsNextHeader = hasNonEmptyArg(args...);
590-
}
591-
return inject(alsohere, args...);
592-
}
593-
594-
// helper function to check if there is at least one non-empty header/stack in the argument pack
595-
template <typename T, typename... Args>
596-
static bool hasNonEmptyArg(const T& h, const Args&... args) noexcept
597-
{
598-
if (h.size() > 0) {
599-
return true;
600-
}
601-
return hasNonEmptyArg(args...);
602-
}
603-
604-
template <typename T>
605-
static bool hasNonEmptyArg(const T& h) noexcept
606-
{
607-
if (h.size() > 0) {
608-
return true;
609-
}
610-
return false;
611-
}
612-
};
613-
614472
//__________________________________________________________________________________________________
615473
/// this 128 bit type for a header field describing the payload data type
616474
struct printDataDescription {
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
#ifndef O2_HEADERS_STACK_H
11+
#define O2_HEADERS_STACK_H
12+
13+
#include "MemoryResources/MemoryResources.h"
14+
#include "Headers/DataHeader.h"
15+
16+
namespace o2
17+
{
18+
namespace header
19+
{
20+
//__________________________________________________________________________________________________
21+
/// @struct Stack
22+
/// @brief a move-only header stack with serialized headers
23+
/// This is the flat buffer where all the headers in a multi-header go.
24+
/// This guy knows how to move the serialized content to FairMQ
25+
/// and inform it how to release when all is sent.
26+
/// methods to construct a multi-header
27+
/// intended use:
28+
/// - as a variadic intializer list (as an argument to a function)
29+
///
30+
/// One can also use the ctor directly:
31+
// Stack::Stack(const T& header1, const T& header2, ...)
32+
// - arguments can be headers, or stacks, all will be concatenated in a new Stack
33+
/// - returns a Stack ready to be shipped.
34+
struct Stack {
35+
36+
private:
37+
static void freefn(void* data, void* hint)
38+
{
39+
boost::container::pmr::memory_resource* resource = static_cast<boost::container::pmr::memory_resource*>(hint);
40+
resource->deallocate(data, 0, 0);
41+
}
42+
43+
struct freeobj {
44+
freeobj() {}
45+
freeobj(boost::container::pmr::memory_resource* mr) : resource(mr) {}
46+
47+
boost::container::pmr::memory_resource* resource{ nullptr };
48+
void operator()(o2::byte* ptr) { Stack::freefn(ptr, resource); }
49+
};
50+
51+
public:
52+
using allocator_type = boost::container::pmr::polymorphic_allocator<o2::byte>;
53+
using value_type = o2::byte;
54+
using BufferType = std::unique_ptr<value_type[], freeobj>;
55+
56+
Stack() = default;
57+
Stack(Stack&&) = default;
58+
Stack(Stack&) = delete;
59+
Stack& operator=(Stack&) = delete;
60+
Stack& operator=(Stack&&) = default;
61+
62+
value_type* data() const { return buffer.get(); }
63+
size_t size() const { return bufferSize; }
64+
allocator_type get_allocator() const { return allocator; }
65+
66+
//
67+
boost::container::pmr::memory_resource* getFreefnHint() const noexcept { return allocator.resource(); }
68+
static auto getFreefn() noexcept { return &freefn; }
69+
70+
/// The magic constructors: take arbitrary number of headers and serialize them
71+
/// into the buffer buffer allocated by the specified polymorphic allocator. By default
72+
/// allocation is done using new_delete_resource.
73+
/// In the final stack the first header must be DataHeader.
74+
/// all headers must derive from BaseHeader, in addition also other stacks can be passed to ctor.
75+
template <typename FirstArgType, typename... Headers,
76+
typename std::enable_if_t<
77+
!std::is_convertible<FirstArgType, boost::container::pmr::polymorphic_allocator<o2::byte>>::value, int> = 0>
78+
Stack(FirstArgType&& firstHeader, Headers&&... headers)
79+
: Stack(boost::container::pmr::new_delete_resource(), std::forward<FirstArgType>(firstHeader),
80+
std::forward<Headers>(headers)...)
81+
{
82+
}
83+
84+
template <typename... Headers>
85+
Stack(const allocator_type allocatorArg, Headers&&... headers)
86+
: allocator{ allocatorArg },
87+
bufferSize{ calculateSize(std::forward<Headers>(headers)...) },
88+
buffer{ static_cast<o2::byte*>(allocator.resource()->allocate(bufferSize, alignof(std::max_align_t))),
89+
freeobj(getFreefnHint()) }
90+
{
91+
inject(buffer.get(), std::forward<Headers>(headers)...);
92+
}
93+
94+
private:
95+
allocator_type allocator{ boost::container::pmr::new_delete_resource() };
96+
size_t bufferSize{ 0 };
97+
BufferType buffer{ nullptr, freeobj{ getFreefnHint() } };
98+
99+
template <typename T, typename... Args>
100+
static size_t calculateSize(T&& h, Args&&... args) noexcept
101+
{
102+
return calculateSize(std::forward<T>(h)) + calculateSize(std::forward<Args>(args)...);
103+
}
104+
105+
template <typename T>
106+
static size_t calculateSize(T&& h) noexcept
107+
{
108+
return h.size();
109+
}
110+
111+
//recursion terminator
112+
constexpr static size_t calculateSize() { return 0; }
113+
114+
template <typename T>
115+
static o2::byte* inject(o2::byte* here, T&& h) noexcept
116+
{
117+
using headerType = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
118+
static_assert(
119+
std::is_base_of<BaseHeader, headerType>::value == true || std::is_same<Stack, headerType>::value == true,
120+
"header stack parameters are restricted to stacks and headers derived from BaseHeader");
121+
std::copy(h.data(), h.data() + h.size(), here);
122+
return here + h.size();
123+
// somehow could not trigger copy elision for placed construction, TODO: check out if this is possible here
124+
// headerType* placed = new (here) headerType(std::forward<T>(h));
125+
// return here + placed->size();
126+
}
127+
128+
template <typename T, typename... Args>
129+
static o2::byte* inject(o2::byte* here, T&& h, Args&&... args) noexcept
130+
{
131+
auto alsohere = inject(here, h);
132+
// the type might be a stack itself, loop through headers and set the flag in the last one
133+
if (h.size() > 0) {
134+
BaseHeader* next = BaseHeader::get(here);
135+
while (next->flagsNextHeader) {
136+
next = next->next();
137+
}
138+
next->flagsNextHeader = hasNonEmptyArg(args...);
139+
}
140+
return inject(alsohere, args...);
141+
}
142+
143+
// helper function to check if there is at least one non-empty header/stack in the argument pack
144+
template <typename T, typename... Args>
145+
static bool hasNonEmptyArg(const T& h, const Args&... args) noexcept
146+
{
147+
if (h.size() > 0) {
148+
return true;
149+
}
150+
return hasNonEmptyArg(args...);
151+
}
152+
153+
template <typename T>
154+
static bool hasNonEmptyArg(const T& h) noexcept
155+
{
156+
if (h.size() > 0) {
157+
return true;
158+
}
159+
return false;
160+
}
161+
};
162+
163+
} // namespace header
164+
} // namespace o2
165+
166+
#endif // HEADERS_STACK_H

DataFormats/Headers/test/testDataHeader.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <iomanip>
1717
#include "Headers/DataHeader.h"
1818
#include "Headers/NameHeader.h"
19+
#include "Headers/Stack.h"
1920

2021
#include <chrono>
2122

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
#ifndef O2_MEMORY_RESOURCES_TYPES_
12+
#define O2_MEMORY_RESOURCES_TYPES_
13+
14+
namespace o2
15+
{
16+
using byte = unsigned char;
17+
} // namespace o2
18+
19+
#endif

Framework/Core/include/Framework/DataAllocator.h

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -412,21 +412,7 @@ class DataAllocator
412412
o2::header::SerializationMethod serializationMethod, //
413413
size_t payloadSize); //
414414

415-
Output getOutputByBind(OutputRef&& ref)
416-
{
417-
if (ref.label.empty()) {
418-
throw std::runtime_error("Invalid (empty) OutputRef provided.");
419-
}
420-
for (size_t ri = 0, re = mAllowedOutputRoutes.size(); ri != re; ++ri) {
421-
if (mAllowedOutputRoutes[ri].matcher.binding.value == ref.label) {
422-
auto spec = mAllowedOutputRoutes[ri].matcher;
423-
return Output{ spec.origin, spec.description, ref.subSpec, spec.lifetime, std::move(ref.headerStack) };
424-
}
425-
}
426-
throw std::runtime_error("Unable to find OutputSpec with label " + ref.label);
427-
assert(false);
428-
}
429-
415+
Output getOutputByBind(OutputRef&& ref);
430416
void addPartToContext(FairMQMessagePtr&& payload,
431417
const Output &spec,
432418
o2::header::SerializationMethod serializationMethod);

Framework/Core/include/Framework/Output.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "Headers/DataHeader.h"
1414
#include "Framework/Lifetime.h"
15+
#include "Headers/Stack.h"
1516

1617
namespace o2
1718
{

0 commit comments

Comments
 (0)