1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
/* Copyright (c) 2008-2022 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Covered Software is provided under this License on an "as is"
* basis, without warranty of any kind, either expressed, implied, or
* statutory, including, without limitation, warranties that the
* Covered Software is free of defects, merchantable, fit for a
* particular purpose or non-infringing.
* See the Mozilla Public License v. 2.0 for more details.
*
* For more details, see http://www.mrtrix.org/.
*/
#ifndef __raw_h__
#define __raw_h__
/** \defgroup Binary Binary access functions
* \brief functions to provide easy access to binary data. */
#include <atomic>
#include "mrtrix.h"
#define BITMASK 0x01U << 7
#ifdef BYTE_ORDER_IS_BIG_ENDIAN
#define MRTRIX_IS_BIG_ENDIAN true
#define TO_LE(v) swap(v)
#define TO_BE(v) v
#else
#define MRTRIX_IS_BIG_ENDIAN false
#define TO_LE(v) v
#define TO_BE(v) swap(v)
#endif
namespace MR
{
/** \addtogroup Binary
* @{ */
namespace ByteOrder
{
template <typename ValueType>
inline typename std::enable_if<std::is_fundamental<ValueType>::value && sizeof(ValueType) == 1, ValueType>::type swap (ValueType v) {
return v;
}
template <typename ValueType>
inline typename std::enable_if<std::is_fundamental<ValueType>::value && sizeof(ValueType) == 2, ValueType>::type swap (ValueType v) {
union {
ValueType v;
uint8_t i[2];
} val = { v };
std::swap (val.i[0], val.i[1]);
return val.v;
}
template <typename ValueType>
inline typename std::enable_if<std::is_fundamental<ValueType>::value && sizeof(ValueType) == 4, ValueType>::type swap (ValueType v) {
union {
ValueType v;
uint8_t i[4];
} val = { v };
std::swap (val.i[0], val.i[3]);
std::swap (val.i[1], val.i[2]);
return val.v;
}
template <typename ValueType>
inline typename std::enable_if<std::is_fundamental<ValueType>::value && sizeof(ValueType) == 8, ValueType>::type swap (ValueType v) {
union {
ValueType v;
uint8_t i[8];
} val = { v };
std::swap (val.i[0], val.i[7]);
std::swap (val.i[1], val.i[6]);
std::swap (val.i[2], val.i[5]);
std::swap (val.i[3], val.i[4]);
return val.v;
}
template <typename ValueType>
inline typename std::enable_if<is_complex<ValueType>::value, ValueType>::type swap (ValueType v) { return { swap (v.real()), swap (v.imag()) }; }
template <typename ValueType>
inline ValueType LE (ValueType v) { return TO_LE (v); }
template <typename ValueType>
inline ValueType BE (ValueType v) { return TO_BE (v); }
template <typename ValueType>
inline ValueType swap (const ValueType value, bool is_big_endian) { return is_big_endian ? BE (value) : LE (value); }
}
namespace Raw {
namespace {
template <typename ValueType> ValueType* as (void* p) { return reinterpret_cast<ValueType*> (p); }
template <typename ValueType> const ValueType* as (const void* p) { return reinterpret_cast<const ValueType*> (p); }
}
// GET from pointer:
template <typename ValueType>
inline ValueType fetch_LE (const void* address) { return ByteOrder::LE (*as<ValueType> (address)); }
template <typename ValueType>
inline ValueType fetch_BE (const void* address) { return ByteOrder::BE (*as<ValueType> (address)); }
template <typename ValueType>
inline ValueType fetch_ (const void* address, bool is_big_endian = MRTRIX_IS_BIG_ENDIAN) { return ByteOrder::swap (*as<ValueType>(address), is_big_endian); }
template <typename ValueType>
inline ValueType fetch__native (const void* address) { return *as<ValueType>(address); }
// PUT at pointer:
template <typename ValueType>
inline void store_LE (const ValueType value, void* address) { *as<ValueType>(address) = ByteOrder::LE (value); }
template <typename ValueType>
inline void store_BE (const ValueType value, void* address) { *as<ValueType>(address) = ByteOrder::BE (value); }
template <typename ValueType>
inline void store (const ValueType value, void* address, bool is_big_endian = MRTRIX_IS_BIG_ENDIAN) { *as<ValueType>(address) = ByteOrder::swap (value, is_big_endian); }
template <typename ValueType>
inline void store_native (const ValueType value, void* address) { *as<ValueType>(address) = value; }
//! fetch \a value in little-endian format from offset \a i from \a data
template <typename ValueType>
inline ValueType fetch_LE (const void* data, size_t i) { return ByteOrder::LE (as<ValueType>(data)[i]); }
//! fetch \a value in big-endian format from offset \a i from \a data
template <typename ValueType>
inline ValueType fetch_BE (const void* data, size_t i) { return ByteOrder::BE (as<ValueType>(data)[i]); }
//! fetch \a value in format \a is_big_endian from offset \a i from \a data
template <typename ValueType>
inline ValueType fetch (const void* data, size_t i, bool is_big_endian = MRTRIX_IS_BIG_ENDIAN) { return ByteOrder::swap (as<ValueType>(data)[i], is_big_endian); }
//! fetch \a value in native format from offset \a i from \a data
template <typename ValueType>
inline ValueType fetch_native (const void* data, size_t i) { return as<ValueType>(data)[i]; }
//! store \a value in little-endian format at offset \a i from \a data
template <typename ValueType>
inline void store_LE (const ValueType value, void* data, size_t i) { as<ValueType>(data)[i] = ByteOrder::LE (value); }
//! store \a value in big-endian format at offset \a i from \a data
template <typename ValueType>
inline void store_BE (const ValueType value, void* data, size_t i) { as<ValueType>(data)[i] = ByteOrder::BE (value); }
//! store \a value in format \a is_big_endian at offset \a i from \a data
template <typename ValueType>
inline void store (const ValueType value, void* data, size_t i, bool is_big_endian = MRTRIX_IS_BIG_ENDIAN) { as<ValueType>(data)[i] = ByteOrder::swap (value, is_big_endian); }
//! store \a value in native format at offset \a i from \a data
template <typename ValueType>
inline void store_native (const ValueType value, void* data, size_t i) { as<ValueType>(data)[i] = value; }
//! \cond skip
template <>
inline bool fetch_native<bool> (const void* data, size_t i) { return ( as<uint8_t>(data)[i/8]) & (BITMASK >> i%8 ); }
template <>
inline void store_native<bool> (const bool value, void* data, size_t i) {
// bit of a hack - assume lock-free atomic operations on bytes, and that
// atomic<uint8_t> genuinely is one byte. Both now checked in thread
// initialisation (in Thread::__Backend() constructor)
std::atomic<uint8_t>* at = reinterpret_cast<std::atomic<uint8_t>*> (as<uint8_t>(data) + (i/8));
uint8_t prev = *at, new_value;
do {
if (value) new_value = prev | (BITMASK >> i%8);
else new_value = prev & ~(BITMASK >> i%8);
} while (!at->compare_exchange_weak (prev, new_value));
}
template <>
inline bool fetch<bool> (const void* data, size_t i, bool) { return fetch_native<bool> (data, i); }
template <>
inline void store<bool> (const bool value, void* data, size_t i, bool) { store_native<bool> (value, data, i); }
//! \endcond
}
/** @} */
}
#endif
|