PVData C++  8.0.6
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
byteBuffer.h
1 /* byteBuffer.h */
2 /*
3  * Copyright information and license terms for this software can be
4  * found in the file LICENSE that is included with the distribution
5  */
9 #ifndef BYTEBUFFER_H
10 #define BYTEBUFFER_H
11 
12 #include <string>
13 #include <cstring>
14 #include <cstdlib>
15 
16 #include <epicsEndian.h>
17 #include <shareLib.h>
18 #include <epicsAssert.h>
19 #include <compilerDependencies.h>
20 
21 #include <pv/templateMeta.h>
22 #include <pv/pvType.h>
23 #include <pv/epicsException.h>
24 
25 
26 #ifndef EPICS_ALWAYS_INLINE
27 # define EPICS_ALWAYS_INLINE inline
28 #endif
29 
30 /* various compilers provide builtins for byte order swaps.
31  * conditions based on boost endian library
32  */
33 #if defined(__clang__)
34 
35 #if __has_builtin(__builtin_bswap16)
36 #define _PVA_swap16(X) __builtin_bswap16(X)
37 #endif
38 #if __has_builtin(__builtin_bswap32)
39 #define _PVA_swap32(X) __builtin_bswap32(X)
40 #endif
41 #if __has_builtin(__builtin_bswap64)
42 #define _PVA_swap64(X) __builtin_bswap64(X)
43 #endif
44 
45 #elif defined(__GNUC__) && ((__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=3))
46 
47 #if (__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=8)
48 #define _PVA_swap16(X) __builtin_bswap16(X)
49 #endif
50 
51 #define _PVA_swap32(X) __builtin_bswap32(X)
52 #define _PVA_swap64(X) __builtin_bswap64(X)
53 
54 #elif defined(_MSC_VER)
55 
56 #define _PVA_swap16(X) _byteswap_ushort(X)
57 #define _PVA_swap32(X) _byteswap_ulong(X)
58 #define _PVA_swap64(X) _byteswap_uint64(X)
59 
60 #endif
61 
62 namespace epics {namespace pvData {
63 
64 namespace detail {
65 template<typename T>
66 struct asInt {
67  static EPICS_ALWAYS_INLINE T from(T v) { return v; }
68  static EPICS_ALWAYS_INLINE T to(T v) { return v; }
69 };
70 template<>
71 struct asInt<float> {
72  union pun {float f; uint32 i;};
73  static EPICS_ALWAYS_INLINE float from(uint32 v) {
74  pun P;
75  P.i = v;
76  return P.f;
77  }
78  static EPICS_ALWAYS_INLINE uint32 to(float v) {
79  pun P;
80  P.f = v;
81  return P.i;
82  }
83 };
84 template<>
85 struct asInt<double> {
86  union pun {double f; uint64 i;};
87  static EPICS_ALWAYS_INLINE double from(uint64 v) {
88  pun P;
89  P.i = v;
90  return P.f;
91  }
92  static EPICS_ALWAYS_INLINE uint64 to(double v) {
93  pun P;
94  P.f = v;
95  return P.i;
96  }
97 };
98 
99 template<int N>
100 struct swap; // no default
101 template<>
102 struct swap<1> {
103  static EPICS_ALWAYS_INLINE uint8 op(uint8 v) { return v; }
104 };
105 template<>
106 struct swap<2> {
107  static EPICS_ALWAYS_INLINE uint16 op(uint16 v) {
108 #ifdef _PVA_swap16
109  return _PVA_swap16(v);
110 #else
111  return (((v) >> 8) | ((v) << 8));
112 #endif
113  }
114 };
115 template<>
116 struct swap<4> {
117  static EPICS_ALWAYS_INLINE uint32 op(uint32 v) {
118 #ifdef _PVA_swap32
119  return _PVA_swap32(v);
120 #else
121  return ((((v) & 0xff000000) >> 24) |
122  (((v) & 0x00ff0000) >> 8) |
123  (((v) & 0x0000ff00) << 8) |
124  (((v) & 0x000000ff) << 24));
125 #endif
126  }
127 };
128 template<>
129 struct swap<8> {
130 #ifdef _PVA_swap64
131  static EPICS_ALWAYS_INLINE uint64 op(uint64 v) {
132  return _PVA_swap64(v);
133  }
134 #else
135  static inline uint64 op(uint64 v) {
136  return (((v) >> 56) | \
137  (((v) >> 40) & 0x0000ff00) | \
138  (((v) >> 24) & 0x00ff0000) | \
139  (((v) >> 8) & 0xff000000) | \
140  (((v) << 8) & ((uint64_t)0xff << 32)) | \
141  (((v) << 24) & ((uint64_t)0xff << 40)) | \
142  (((v) << 40) & ((uint64_t)0xff << 48)) | \
143  (((v) << 56)));
144  }
145 #endif
146 };
147 
148 #undef _PVA_swap16
149 #undef _PVA_swap32
150 #undef _PVA_swap64
151 
152 /* PVD serialization doesn't pay attention to alignement,
153  * which some targets (ARM and powerpc) really care about and treat unaligned
154  * access as a fault, or with a heavy penalty (~= to a syscall).
155  *
156  * For those targets,, we will have to live with the increase
157  * in execution time and/or object code size of byte-wise copy.
158  *
159  * Treat x86 32/64 as an outlier, and assume all other targets
160  * need, or greatly benefit, from aligned access.
161  */
162 
163 #if !(defined(__x86_64__) || defined(_M_AMD64) || defined(__i386__) || defined(_M_IX86))
164 
165 template<typename T>
166 union alignu {
167  T val;
168  char bytes[sizeof(T)];
169 };
170 
171 template<typename T>
172 EPICS_ALWAYS_INLINE void store_unaligned(char *buf, T val)
173 {
174  alignu<T> A;
175  A.val = val;
176  for(unsigned i=0, N=sizeof(T); i<N; i++) {
177  buf[i] = A.bytes[i];
178  }
179 }
180 
181 template<typename T>
182 EPICS_ALWAYS_INLINE T load_unaligned(const char *buf)
183 {
184  alignu<T> A;
185  for(unsigned i=0, N=sizeof(T); i<N; i++) {
186  A.bytes[i] = buf[i];
187  }
188  return A.val;
189 }
190 
191 #else /* alignement */
192 
193 template<typename T>
194 EPICS_ALWAYS_INLINE void store_unaligned(char *buf, T val)
195 {
196  *reinterpret_cast<T*>(buf) = val;
197 }
198 
199 template<typename T>
200 EPICS_ALWAYS_INLINE T load_unaligned(const char *buf)
201 {
202  return *reinterpret_cast<const T*>(buf);
203 }
204 
205 #endif /* alignement */
206 
207 } // namespace detail
208 
211 template<typename T>
212 EPICS_ALWAYS_INLINE T swap(T val)
213 {
214  return detail::asInt<T>::from(
215  detail::swap<sizeof(T)>::op(
216  detail::asInt<T>::to(val)));
217 }
218 
219 #define is_aligned(POINTER, BYTE_COUNT) \
220  (((std::size_t)(POINTER)) % (BYTE_COUNT) == 0)
221 
222 #if defined (__GNUC__) && (__GNUC__ < 3)
223 #define GET(T) get((T*)0)
224 #else
225 #define GET(T) get<T>()
226 #endif
227 
237 {
238 public:
246  ByteBuffer(std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
247  _buffer((char*)std::malloc(size)), _size(size),
248  _reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
249  _reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
250  _wrapped(false)
251  {
252  if(!_buffer)
253  throw std::bad_alloc();
254  clear();
255  }
256 
265  ByteBuffer(char* buffer, std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
266  _buffer(buffer), _size(size),
267  _reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
268  _reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
269  _wrapped(true)
270  {
271  if(!_buffer)
272  throw std::invalid_argument("ByteBuffer can't be constructed with NULL");
273  clear();
274  }
279  {
280  if (_buffer && !_wrapped) std::free(_buffer);
281  }
288  inline void setEndianess(int byteOrder)
289  {
290  _reverseEndianess = (byteOrder != EPICS_BYTE_ORDER);
291  _reverseFloatEndianess = (byteOrder != EPICS_FLOAT_WORD_ORDER);
292  }
297  inline const char* getBuffer() const
298  {
299  return _buffer;
300  }
305  inline void clear()
306  {
307  _position = _buffer;
308  _limit = _buffer + _size;
309  }
324  inline void flip() {
325  _limit = _position;
326  _position = _buffer;
327  }
342  inline void rewind() {
343  _position = _buffer;
344  }
349  inline std::size_t getPosition() const
350  {
351  return _position - _buffer;
352  }
360  inline void setPosition(std::size_t pos)
361  {
362  assert(pos<=_size);
363  _position = _buffer + pos;
364  assert(_position<=_limit);
365  }
371  inline std::size_t getLimit() const
372  {
373  return _limit - _buffer;
374  }
383  inline void setLimit(std::size_t limit)
384  {
385  assert(limit<=_size);
386  _limit = _buffer + limit;
387  assert(_position<=_limit);
388  }
394  inline std::size_t getRemaining() const
395  {
396  return _limit - _position;
397  }
403  EPICS_ALWAYS_INLINE std::size_t getSize() const
404  {
405  return _size;
406  }
412  template<typename T>
413  inline void put(T value);
420  template<typename T>
421  inline void put(std::size_t index, T value) const;
428 #if defined (__GNUC__) && (__GNUC__ < 3)
429  template<typename T>
430  inline T get(const T*);
431 #else
432  template<typename T>
433  inline T get();
434 #endif
435 
443  template<typename T>
444  inline T get(std::size_t index) const;
454  inline void put(const char* src, std::size_t src_offset, std::size_t count) {
455  assert(count<=getRemaining());
456  memcpy(_position, src + src_offset, count);
457  _position += count;
458  }
468  inline void get(char* dest, std::size_t dest_offset, std::size_t count) {
469  assert(count<=getRemaining());
470  memcpy(dest + dest_offset, _position, count);
471  _position += count;
472  }
480  template<typename T>
481  inline void putArray(const T* values, std::size_t count);
489  template<typename T>
490  inline void getArray(T* values, std::size_t count);
495  template<typename T>
496  EPICS_ALWAYS_INLINE bool reverse() const
497  {
498  return sizeof(T)>1 && _reverseEndianess;
499  }
505  EPICS_ALWAYS_INLINE void putBoolean( bool value) { put< int8>(value ? 1 : 0); }
511  EPICS_ALWAYS_INLINE void putByte ( int8 value) { put< int8>(value); }
517  EPICS_ALWAYS_INLINE void putShort ( int16 value) { put< int16>(value); }
523  EPICS_ALWAYS_INLINE void putInt ( int32 value) { put< int32>(value); }
529  EPICS_ALWAYS_INLINE void putLong ( int64 value) { put< int64>(value); }
535  EPICS_ALWAYS_INLINE void putFloat ( float value) { put< float>(value); }
541  EPICS_ALWAYS_INLINE void putDouble (double value) { put<double>(value); }
542 
549  EPICS_ALWAYS_INLINE void putBoolean(std::size_t index, bool value) { put< int8>(index, value); }
556  EPICS_ALWAYS_INLINE void putByte (std::size_t index, int8 value) { put< int8>(index, value); }
563  EPICS_ALWAYS_INLINE void putShort (std::size_t index, int16 value) { put< int16>(index, value); }
570  EPICS_ALWAYS_INLINE void putInt (std::size_t index, int32 value) { put< int32>(index, value); }
577  EPICS_ALWAYS_INLINE void putLong (std::size_t index, int64 value) { put< int64>(index, value); }
584  EPICS_ALWAYS_INLINE void putFloat (std::size_t index, float value) { put< float>(index, value); }
591  EPICS_ALWAYS_INLINE void putDouble (std::size_t index, double value) { put<double>(index, value); }
597  EPICS_ALWAYS_INLINE bool getBoolean() { return GET( int8) != 0; }
603  EPICS_ALWAYS_INLINE int8 getByte () { return GET( int8); }
609  EPICS_ALWAYS_INLINE int16 getShort () { return GET( int16); }
615  EPICS_ALWAYS_INLINE int32 getInt () { return GET( int32); }
621  EPICS_ALWAYS_INLINE int64 getLong () { return GET( int64); }
627  EPICS_ALWAYS_INLINE float getFloat () { return GET( float); }
633  EPICS_ALWAYS_INLINE double getDouble () { return GET(double); }
640  EPICS_ALWAYS_INLINE bool getBoolean(std::size_t index) { return get< int8>(index) != 0; }
647  EPICS_ALWAYS_INLINE int8 getByte (std::size_t index) { return get< int8>(index); }
654  EPICS_ALWAYS_INLINE int16 getShort (std::size_t index) { return get< int16>(index); }
661  EPICS_ALWAYS_INLINE int32 getInt (std::size_t index) { return get< int32>(index); }
668  EPICS_ALWAYS_INLINE int64 getLong (std::size_t index) { return get< int64>(index); }
675  EPICS_ALWAYS_INLINE float getFloat (std::size_t index) { return get< float>(index); }
682  EPICS_ALWAYS_INLINE double getDouble (std::size_t index) { return get<double>(index); }
683 
684  // TODO remove
685  EPICS_ALWAYS_INLINE const char* getArray() const EPICS_DEPRECATED
686  {
687  return _buffer;
688  }
689 
690 
691 private:
692  char* const _buffer;
693  char* _position;
694  char* _limit;
695  const std::size_t _size;
696  bool _reverseEndianess;
697  bool _reverseFloatEndianess;
698  const bool _wrapped;
699 };
700 
701  template<>
702  EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<bool>() const
703  {
704  return false;
705  }
706 
707  template<>
708  EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<int8>() const
709  {
710  return false;
711  }
712 
713  template<>
714  EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<uint8>() const
715  {
716  return false;
717  }
718 
719  template<>
720  EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<float>() const
721  {
722  return _reverseFloatEndianess;
723  }
724 
725  template<>
726  EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<double>() const
727  {
728  return _reverseFloatEndianess;
729  }
730 
731  // the following methods must come after the specialized reverse<>() methods to make pre-gcc3 happy
732 
733  template<typename T>
734  inline void ByteBuffer::put(T value)
735  {
736  assert(sizeof(T)<=getRemaining());
737 
738  if(reverse<T>())
739  value = swap<T>(value);
740 
741  detail::store_unaligned(_position, value);
742  _position += sizeof(T);
743  }
744 
745  template<typename T>
746  inline void ByteBuffer::put(std::size_t index, T value) const
747  {
748  assert(_buffer+index<=_limit);
749 
750  if(reverse<T>())
751  value = swap<T>(value);
752 
753  detail::store_unaligned(_buffer+index, value);
754  }
755 
756 #if defined (__GNUC__) && (__GNUC__ < 3)
757  template<typename T>
758  inline T ByteBuffer::get(const T*)
759 #else
760  template<typename T>
761  inline T ByteBuffer::get()
762 #endif
763  {
764  assert(sizeof(T)<=getRemaining());
765 
766  T value = detail::load_unaligned<T>(_position);
767  _position += sizeof(T);
768 
769  if(reverse<T>())
770  value = swap<T>(value);
771  return value;
772  }
773 
774  template<typename T>
775  inline T ByteBuffer::get(std::size_t index) const
776  {
777  assert(_buffer+index<=_limit);
778 
779  T value = detail::load_unaligned<T>(_buffer + index);
780 
781  if(reverse<T>())
782  value = swap<T>(value);
783  return value;
784  }
785 
786  template<typename T>
787  inline void ByteBuffer::putArray(const T* values, std::size_t count)
788  {
789  size_t n = sizeof(T)*count; // bytes
790  assert(n<=getRemaining());
791 
792  if (reverse<T>()) {
793  for(std::size_t i=0; i<count; i++) {
794  detail::store_unaligned(_position+i*sizeof(T), swap<T>(values[i]));
795  }
796  } else {
797  memcpy(_position, values, n);
798  }
799  _position += n;
800  }
801 
802  template<typename T>
803  inline void ByteBuffer::getArray(T* values, std::size_t count)
804  {
805  size_t n = sizeof(T)*count; // bytes
806  assert(n<=getRemaining());
807 
808  if (reverse<T>()) {
809  for(std::size_t i=0; i<count; i++) {
810  values[i] = swap<T>(detail::load_unaligned<T>(_position+i*sizeof(T)));
811  }
812  } else {
813  memcpy(values, _position, n);
814  }
815  _position += n;
816  }
817 
818 }}
819 #endif /* BYTEBUFFER_H */
void putShort(std::size_t index, int16 value)
Definition: byteBuffer.h:563
void putDouble(double value)
Definition: byteBuffer.h:541
void putByte(std::size_t index, int8 value)
Definition: byteBuffer.h:556
int8 getByte(std::size_t index)
Definition: byteBuffer.h:647
void putInt(int32 value)
Definition: byteBuffer.h:523
void putFloat(std::size_t index, float value)
Definition: byteBuffer.h:584
void putInt(std::size_t index, int32 value)
Definition: byteBuffer.h:570
valarray< size_t > size() const
ByteBuffer(char *buffer, std::size_t size, int byteOrder=EPICS_BYTE_ORDER)
Definition: byteBuffer.h:265
float getFloat(std::size_t index)
Definition: byteBuffer.h:675
std::size_t getSize() const
Definition: byteBuffer.h:403
double getDouble(std::size_t index)
Definition: byteBuffer.h:682
void putArray(const T *values, std::size_t count)
Definition: byteBuffer.h:787
uint32_t uint32
Definition: pvType.h:99
void setLimit(std::size_t limit)
Definition: byteBuffer.h:383
uint8_t uint8
Definition: pvType.h:91
int16_t int16
Definition: pvType.h:79
void setEndianess(int byteOrder)
Definition: byteBuffer.h:288
T swap(T val)
Definition: byteBuffer.h:212
void putDouble(std::size_t index, double value)
Definition: byteBuffer.h:591
bool getBoolean(std::size_t index)
Definition: byteBuffer.h:640
void putLong(std::size_t index, int64 value)
Definition: byteBuffer.h:577
This class implements a Bytebuffer that is like the java.nio.ByteBuffer.
Definition: byteBuffer.h:236
std::size_t getLimit() const
Definition: byteBuffer.h:371
void putByte(int8 value)
Definition: byteBuffer.h:511
std::size_t getPosition() const
Definition: byteBuffer.h:349
constexpr iterator_traits< _InputIterator >::difference_type count(_InputIterator __first, _InputIterator __last, const _Tp &__value)
const char * getBuffer() const
Definition: byteBuffer.h:297
int64_t int64
Definition: pvType.h:87
void putShort(int16 value)
Definition: byteBuffer.h:517
void setPosition(std::size_t pos)
Definition: byteBuffer.h:360
int32_t int32
Definition: pvType.h:83
void putBoolean(bool value)
Definition: byteBuffer.h:505
int32 getInt(std::size_t index)
Definition: byteBuffer.h:661
ByteBuffer(std::size_t size, int byteOrder=EPICS_BYTE_ORDER)
Definition: byteBuffer.h:246
uint16_t uint16
Definition: pvType.h:95
int8_t int8
Definition: pvType.h:75
void putLong(int64 value)
Definition: byteBuffer.h:529
int16 getShort(std::size_t index)
Definition: byteBuffer.h:654
void putFloat(float value)
Definition: byteBuffer.h:535
std::size_t getRemaining() const
Definition: byteBuffer.h:394
uint64_t uint64
Definition: pvType.h:103
void putBoolean(std::size_t index, bool value)
Definition: byteBuffer.h:549
int64 getLong(std::size_t index)
Definition: byteBuffer.h:668
void put(const char *src, std::size_t src_offset, std::size_t count)
Definition: byteBuffer.h:454