Skip to content

Commit 9405fa9

Browse files
committed
SERVER-22800 Refactor DataTypeEndian
Centralize the code to be more brief and extensible for endian conversions
1 parent d8d237d commit 9405fa9

File tree

1 file changed

+56
-98
lines changed

1 file changed

+56
-98
lines changed

src/mongo/base/data_type_endian.h

Lines changed: 56 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -37,107 +37,69 @@
3737
namespace mongo {
3838

3939
template <typename T>
40-
struct BigEndian {
41-
BigEndian() {}
42-
BigEndian(T t) : value(t) {}
43-
T value;
44-
45-
operator T() const {
46-
return value;
47-
}
48-
};
49-
50-
template <typename T>
51-
BigEndian<T> tagBigEndian(T t) {
52-
return t;
53-
}
54-
55-
template <typename T>
56-
struct LittleEndian {
57-
LittleEndian() {}
58-
LittleEndian(T t) : value(t) {}
59-
T value;
60-
61-
operator T() const {
62-
return value;
63-
}
64-
};
65-
66-
template <typename T>
67-
LittleEndian<T> tagLittleEndian(T t) {
68-
return t;
69-
}
70-
71-
template <typename T>
72-
struct DataType::Handler<BigEndian<T>> {
73-
static void unsafeLoad(BigEndian<T>* t, const char* ptr, size_t* advanced) {
74-
if (t) {
75-
DataType::unsafeLoad(&t->value, ptr, advanced);
76-
77-
t->value = endian::bigToNative(t->value);
78-
} else {
79-
DataType::unsafeLoad(decltype(&t->value){nullptr}, ptr, advanced);
80-
}
81-
}
82-
83-
static Status load(BigEndian<T>* t,
84-
const char* ptr,
85-
size_t length,
86-
size_t* advanced,
87-
std::ptrdiff_t debug_offset) {
88-
if (t) {
89-
Status x = DataType::load(&t->value, ptr, length, advanced, debug_offset);
90-
91-
if (x.isOK()) {
92-
t->value = endian::bigToNative(t->value);
93-
}
94-
95-
return x;
96-
} else {
97-
return DataType::load(
98-
decltype(&t->value){nullptr}, ptr, length, advanced, debug_offset);
99-
}
100-
}
101-
102-
static void unsafeStore(const BigEndian<T>& t, char* ptr, size_t* advanced) {
103-
DataType::unsafeStore(endian::nativeToBig(t.value), ptr, advanced);
104-
}
105-
106-
static Status store(const BigEndian<T>& t,
107-
char* ptr,
108-
size_t length,
109-
size_t* advanced,
110-
std::ptrdiff_t debug_offset) {
111-
return DataType::store(endian::nativeToBig(t.value), ptr, length, advanced, debug_offset);
112-
}
113-
114-
static BigEndian<T> defaultConstruct() {
115-
return DataType::defaultConstruct<T>();
116-
}
117-
};
40+
struct IsEndian : std::false_type {};
41+
42+
#define MAKE_ENDIAN(name, loadFunc, storeFunc) \
43+
template <typename T> \
44+
struct name { \
45+
using value_type = T; \
46+
name() {} \
47+
name(T t) : value(t) {} \
48+
T value; \
49+
\
50+
operator T() const { \
51+
return value; \
52+
} \
53+
\
54+
static auto load(T t) -> decltype(loadFunc(t)) { \
55+
return loadFunc(t); \
56+
} \
57+
\
58+
static auto store(T t) -> decltype(storeFunc(t)) { \
59+
return storeFunc(t); \
60+
} \
61+
}; \
62+
\
63+
template <typename T> \
64+
name<T> tag##name(T t) { \
65+
return t; \
66+
} \
67+
\
68+
template <typename T> \
69+
struct IsEndian<name<T>> : std::true_type {};
70+
71+
/**
72+
* BigEndian and LittleEndian offer support for using natively encoded types
73+
* and reading/writing them as big endian or little endian through char ptrs.
74+
*
75+
* The Reverse variants assume the pointed to bytes are natively encoded and
76+
* return big or little endian encoded types. I.e. you probably shouldn't use
77+
* them with floats and should be wary of using them with signed types.
78+
*/
79+
MAKE_ENDIAN(BigEndian, endian::bigToNative, endian::nativeToBig)
80+
MAKE_ENDIAN(LittleEndian, endian::littleToNative, endian::nativeToLittle)
81+
MAKE_ENDIAN(ReverseBigEndian, endian::nativeToBig, endian::bigToNative)
82+
MAKE_ENDIAN(ReverseLittleEndian, endian::nativeToLittle, endian::littleToNative)
11883

11984
template <typename T>
120-
struct DataType::Handler<LittleEndian<T>> {
121-
static void unsafeLoad(LittleEndian<T>* t, const char* ptr, size_t* advanced) {
85+
struct DataType::Handler<T, typename std::enable_if<IsEndian<T>::value>::type> {
86+
static void unsafeLoad(T* t, const char* ptr, size_t* advanced) {
12287
if (t) {
12388
DataType::unsafeLoad(&t->value, ptr, advanced);
12489

125-
t->value = endian::littleToNative(t->value);
90+
t->value = T::load(t->value);
12691
} else {
12792
DataType::unsafeLoad(decltype(&t->value){nullptr}, ptr, advanced);
12893
}
12994
}
13095

131-
static Status load(LittleEndian<T>* t,
132-
const char* ptr,
133-
size_t length,
134-
size_t* advanced,
135-
std::ptrdiff_t debug_offset) {
96+
static Status load(
97+
T* t, const char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) {
13698
if (t) {
13799
Status x = DataType::load(&t->value, ptr, length, advanced, debug_offset);
138100

139101
if (x.isOK()) {
140-
t->value = endian::littleToNative(t->value);
102+
t->value = T::load(t->value);
141103
}
142104

143105
return x;
@@ -147,21 +109,17 @@ struct DataType::Handler<LittleEndian<T>> {
147109
}
148110
}
149111

150-
static void unsafeStore(const LittleEndian<T>& t, char* ptr, size_t* advanced) {
151-
DataType::unsafeStore(endian::nativeToLittle(t.value), ptr, advanced);
112+
static void unsafeStore(const T& t, char* ptr, size_t* advanced) {
113+
DataType::unsafeStore(T::store(t.value), ptr, advanced);
152114
}
153115

154-
static Status store(const LittleEndian<T>& t,
155-
char* ptr,
156-
size_t length,
157-
size_t* advanced,
158-
std::ptrdiff_t debug_offset) {
159-
return DataType::store(
160-
endian::nativeToLittle(t.value), ptr, length, advanced, debug_offset);
116+
static Status store(
117+
const T& t, char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) {
118+
return DataType::store(T::store(t.value), ptr, length, advanced, debug_offset);
161119
}
162120

163-
static LittleEndian<T> defaultConstruct() {
164-
return DataType::defaultConstruct<T>();
121+
static typename T::value_type defaultConstruct() {
122+
return DataType::defaultConstruct<typename T::value_type>();
165123
}
166124
};
167125

0 commit comments

Comments
 (0)