From a9111d78d35b66cea7a68967e1471199646f756e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 30 Dec 2015 16:22:08 -0500 Subject: [PATCH] overhaul byteBuffer implementation align(n) now fills skipped bytes with '\0'. align(n,f) to choose a different fill value. No other changes intended Use intrinsic byte order swapping builtins for gcc, clang, and msvc. use assert() to check pre/post conditions. Remove unused condition macros and unreachable code. Add tests of byte swapping primitives and test the correctness of unaligned operations. add illustrations of flip() and rewind() --- src/misc/pv/byteBuffer.h | 580 ++++++++++++-------------------- testApp/misc/testByteBuffer.cpp | 101 ++++-- 2 files changed, 278 insertions(+), 403 deletions(-) diff --git a/src/misc/pv/byteBuffer.h b/src/misc/pv/byteBuffer.h index dbce897..c87e0d9 100644 --- a/src/misc/pv/byteBuffer.h +++ b/src/misc/pv/byteBuffer.h @@ -15,179 +15,153 @@ #include #include +#include +#include #include #include -namespace epics { - namespace pvData { - -/* -TODO can be used: +#ifndef EPICS_ALWAYS_INLINE +# define EPICS_ALWAYS_INLINE inline +#endif -MS Visual C++: +/* various compilers provide builtins for byte order swaps. + * conditions based on boost endian library + */ +#if defined(__GNUC__) && ((__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=3)) -You include intrin.h and call the following functions: +#if (__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=8) +#define _PVA_swap16(X) __builtin_bswap16(X) +#endif -For 16 bit numbers: -unsigned short _byteswap_ushort(unsigned short value); +#define _PVA_swap32(X) __builtin_bswap32(X) +#define _PVA_swap64(X) __builtin_bswap64(X) -For 32 bit numbers: -unsigned long _byteswap_ulong(unsigned long value); +#elif defined(__clang__) -For 64 bit numbers: -unsigned __int64 _byteswap_uint64(unsigned __int64 value); -*/ +#if __has_builtin(__builtin_bswap16) +#define _PVA_swap16(X) __builtin_bswap16(X) +#endif +#if __has_builtin(__builtin_bswap32) +#define _PVA_swap32(X) __builtin_bswap32(X) +#endif +#if __has_builtin(__builtin_bswap64) +#define _PVA_swap64(X) __builtin_bswap64(X) +#endif -/* -For floats and doubles it's more difficult as with plain integers as these may or not may be in the host machines byte-order. -You can get little-endian floats on big-endian machines and vice versa. -*/ +#elif defined(_MSC_VER) - - -#define GCC_VERSION_SINCE(major, minor, patchlevel) \ - (defined(__GNUC__) && !defined(__INTEL_COMPILER) && \ - ((__GNUC__ > (major)) || \ - (__GNUC__ == (major) && __GNUC_MINOR__ > (minor)) || \ - (__GNUC__ == (major) && __GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ >= (patchlevel)))) - - -#if GCC_VERSION_SINCE(4,3,0) - -#define swap32(x) __builtin_bswap32(x) -#define swap64(x) __builtin_bswap64(x) - -#define __byte_swap16(x) \ - (((x) >> 8) | \ - ((x) << 8)) - -static inline uint16_t -swap16(uint16_t _x) -{ - return (__byte_swap16(_x)); -} - -#else - - -#define __byte_swap16(x) \ - (((x) >> 8) | \ - ((x) << 8)) - -#define __byte_swap32(x) \ - ((((x) & 0xff000000) >> 24) | \ - (((x) & 0x00ff0000) >> 8) | \ - (((x) & 0x0000ff00) << 8) | \ - (((x) & 0x000000ff) << 24)) - -#define __byte_swap64(x) \ - (((x) >> 56) | \ - (((x) >> 40) & 0xff00) | \ - (((x) >> 24) & 0xff0000) | \ - (((x) >> 8) & 0xff000000) | \ - (((x) << 8) & ((uint64_t)0xff << 32)) | \ - (((x) << 24) & ((uint64_t)0xff << 40)) | \ - (((x) << 40) & ((uint64_t)0xff << 48)) | \ - (((x) << 56))) - -static inline uint16_t -swap16(uint16_t _x) -{ - return (__byte_swap16(_x)); -} - -static inline uint32_t -swap32(uint32_t _x) -{ - return (__byte_swap32(_x)); -} - -static inline uint64_t -swap64(uint64_t _x) -{ - return (__byte_swap64(_x)); -} +#define _PVA_swap16(X) _byteswap_ushort(X) +#define _PVA_swap32(X) _byteswap_ulong(X) +#define _PVA_swap64(X) _byteswap_uint64(X) #endif +namespace epics {namespace pvData { +namespace detail { template -inline T swap(T val) { return val; } // not valid - +struct asInt { + static EPICS_ALWAYS_INLINE T from(T v) { return v; } + static EPICS_ALWAYS_INLINE T to(T v) { return v; } +}; template<> -inline int16 swap(int16 val) -{ - return swap16(val); -} - +struct asInt { + union pun {float f; uint32 i;}; + static EPICS_ALWAYS_INLINE float from(uint32 v) { + pun P; + P.i = v; + return P.f; + } + static EPICS_ALWAYS_INLINE uint32 to(float v) { + pun P; + P.f = v; + return P.i; + } +}; template<> -inline int32 swap(int32 val) -{ - return swap32(val); -} +struct asInt { + union pun {double f; uint64 i;}; + static EPICS_ALWAYS_INLINE double from(uint64 v) { + pun P; + P.i = v; + return P.f; + } + static EPICS_ALWAYS_INLINE uint64 to(double v) { + pun P; + P.f = v; + return P.i; + } +}; +template +struct swap; // no default template<> -inline int64 swap(int64 val) -{ - return swap64(val); -} +struct swap<1> { + static EPICS_ALWAYS_INLINE uint8 op(uint8 v) { return v; } +}; +template<> +struct swap<2> { + static EPICS_ALWAYS_INLINE uint16 op(uint16 v) { +#ifdef _PVA_swap16 + return _PVA_swap16(v); +#else + return (((v) >> 8) | ((v) << 8)); +#endif + } +}; +template<> +struct swap<4> { + static EPICS_ALWAYS_INLINE uint32 op(uint32 v) { +#ifdef _PVA_swap32 + return _PVA_swap32(v); +#else + return ((((v) & 0xff000000) >> 24) | + (((v) & 0x00ff0000) >> 8) | + (((v) & 0x0000ff00) << 8) | + (((v) & 0x000000ff) << 24)); +#endif + } +}; +template<> +struct swap<8> { +#ifdef _PVA_swap64 + static EPICS_ALWAYS_INLINE uint64 op(uint64 v) { + return _PVA_swap64(v); + } +#else + static inline uint64 op(uint64 v) { + return (((v) >> 56) | \ + (((v) >> 40) & 0x0000ff00) | \ + (((v) >> 24) & 0x00ff0000) | \ + (((v) >> 8) & 0xff000000) | \ + (((v) << 8) & ((uint64_t)0xff << 32)) | \ + (((v) << 24) & ((uint64_t)0xff << 40)) | \ + (((v) << 40) & ((uint64_t)0xff << 48)) | \ + (((v) << 56))); + } +#endif +}; -template<> -inline uint16 swap(uint16 val) -{ - return swap16(val); -} +#undef _PVA_swap16 +#undef _PVA_swap32 +#undef _PVA_swap64 -template<> -inline uint32 swap(uint32 val) -{ - return swap32(val); -} +} // namespace detail -template<> -inline uint64 swap(uint64 val) +//! Unconditional byte order swap. +//! defined for integer and floating point types +template +EPICS_ALWAYS_INLINE T swap(T val) { - return swap64(val); -} - -template<> -inline float swap(float val) -{ - union { - int32 i; - float f; - } conv; - conv.f = val; - conv.i = swap32(conv.i); - return conv.f; -} - -template<> -inline double swap(double val) -{ - union { - int64 i; - double d; - } conv; - conv.d = val; - conv.i = swap64(conv.i); - return conv.d; + return detail::asInt::from( + detail::swap::op( + detail::asInt::to(val))); } #define is_aligned(POINTER, BYTE_COUNT) \ - (((std::ptrdiff_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0) - -/*template */ - -#define ENDIANESS_SUPPORT true -#define UNALIGNED_ACCESS true -#define ADAPTIVE_ACCESS true -#define USE_INLINE_MEMCPY true + (((std::size_t)(POINTER)) % (BYTE_COUNT) == 0) #if defined (__GNUC__) && (__GNUC__ < 3) #define GET(T) get((T*)0) @@ -278,8 +252,18 @@ public: _limit = _buffer + _size; } /** - * Makes a buffer ready for a new sequence of channel-write or relative get operations: - * It sets the limit to the current position and then sets the position to zero. + * Makes a buffer ready to read out previously written values. + * + * Typically _limit==_buffer+_size is the initial state, but this is not + * required. + * + * V _buffer V _position V _limit V _buffer+_size + * |_______written_______|____uninitialized___|____allocated___| + * + * becomes + * + * V _buffer/_position V _limit V _buffer+size + * |_______written_______|________________allocated____________| */ inline void flip() { _limit = _position; @@ -288,6 +272,16 @@ public: /** * Makes a buffer ready for re-reading the data that it already contains: * It leaves the limit unchanged and sets the position to zero. + * + * Note that this may allow reading of uninitialized values. flip() should be considered + * + * V _buffer V _position V _limit V _buffer+_size + * |_______written_______|____uninitialized___|____allocated___| + * + * becomes + * + * V _buffer/_position V _limit V _buffer+size + * |_______written_______|____uninitialized___|____allocated___| */ inline void rewind() { _position = _buffer; @@ -298,7 +292,7 @@ public: */ inline std::size_t getPosition() const { - return (std::size_t)(((std::ptrdiff_t)(const void *)_position) - ((std::ptrdiff_t)(const void *)_buffer)); + return _position - _buffer; } /** * Sets the buffer position. @@ -309,7 +303,9 @@ public: */ inline void setPosition(std::size_t pos) { + assert(pos<=_size); _position = _buffer + pos; + assert(_position<=_limit); } /** * Returns this buffer's limit. @@ -318,7 +314,7 @@ public: */ inline std::size_t getLimit() const { - return (std::size_t)(((std::ptrdiff_t)(const void *)_limit) - ((std::ptrdiff_t)(const void *)_buffer)); + return _limit - _buffer; } /** * Sets this buffer's limit. @@ -330,7 +326,9 @@ public: */ inline void setLimit(std::size_t limit) { + assert(limit<=_size); _limit = _buffer + limit; + assert(_position<=_limit); } /** * Returns the number of elements between the current position and the limit. @@ -339,7 +337,7 @@ public: */ inline std::size_t getRemaining() const { - return (std::size_t)(((std::ptrdiff_t)(const void *)_limit) - ((std::ptrdiff_t)(const void *)_position)); + return _limit - _position; } /** * Returns The size, i.e. capacity of the raw data buffer in bytes. @@ -364,7 +362,7 @@ public: * @param value The value to be put into the byte buffer. */ template - inline void put(std::size_t index, T value); + inline void put(std::size_t index, T value) const; /** * Get the new object from the byte buffer. The item MUST have type @c T. * The position is adjusted based on the type. @@ -387,17 +385,18 @@ public: * @return The object. */ template - inline T get(std::size_t index); + inline T get(std::size_t index) const; /** * Put a sub-array of bytes into the byte buffer. * The position is increased by the count. * * @param src The source array. * @param src_offset The starting position within src. - * @param count The number of bytes to put into the byte buffer, + * @param count The number of bytes to put into the byte buffer. + * Must be less than getRemaining() */ inline void put(const char* src, std::size_t src_offset, std::size_t count) { - //if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow"); + assert(count<=getRemaining()); memcpy(_position, src + src_offset, count); _position += count; } @@ -408,9 +407,10 @@ public: * @param dest The destination array. * @param dest_offset The starting position within src. * @param count The number of bytes to put into the byte buffer. + * Must be less than getRemaining() */ inline void get(char* dest, std::size_t dest_offset, std::size_t count) { - //if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow"); + assert(count<=getRemaining()); memcpy(dest + dest_offset, _position, count); _position += count; } @@ -437,19 +437,26 @@ public: * @return (false,true) if (is, is not) the EPICS_BYTE_ORDER */ template - inline bool reverse() const + EPICS_ALWAYS_INLINE bool reverse() const { - return _reverseEndianess; + return sizeof(T)>1 && _reverseEndianess; } /** - * Adjust position so that it is aligned to the specified size. - * Size MUST be a power of 2. - * @param size The alignment requirement. + * Adjust position to the next multiple of 'size. + * @param size The alignment requirement, must be a power of 2. (unchecked) + * @param fill value to use for padding bytes (default '\0'). + * + * @note This alignment is absolute, not necessarily with respect to _buffer. */ - inline void align(std::size_t size) + inline void align(std::size_t size, char fill='\0') { - const std::size_t k = size - 1; - _position = (char*)((((std::ptrdiff_t)(const void *)_position) + k) & ~(k)); + const std::size_t k = size - 1, bufidx = (std::size_t)_position; + if(bufidx&k) { + std::size_t npad = size-(bufidx&k); + assert(npad<=getRemaining()); + std::fill(_position, _position+npad, fill); + _position += npad; + } } /** * Put a boolean value into the byte buffer. @@ -687,96 +694,26 @@ private: template inline void ByteBuffer::put(T value) { - // this avoids int8 specialization, compiler will take care if optimization, -O2 or more - if (sizeof(T) == 1) - { - *(_position++) = (int8)value; - return; - } + assert(sizeof(T)<=getRemaining()); - if (ENDIANESS_SUPPORT && reverse()) - { + if(reverse()) value = swap(value); - } - - if (UNALIGNED_ACCESS) - { - // NOTE: some CPU handle unaligned access pretty good (e.g. x86) - *((T*)_position) = value; - _position += sizeof(T); - } - else - { - // NOTE: this check and branching does not always pay off - if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T))) - { - *((T*)_position) = value; - _position += sizeof(T); - } - else - { - if (USE_INLINE_MEMCPY) - { - // NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc - memcpy(_position, &value, sizeof(T)); - _position += sizeof(T); - } - else - { - // NOTE: compiler should optimize this and unroll the loop - for (size_t i = 0; i < sizeof(T); i++) - _position[i] = ((char*)&value)[i]; - _position += sizeof(T); - } - } - } + //assert(is_aligned(_position, sizeof(T))); + *((T*)_position) = value; + _position += sizeof(T); } template - inline void ByteBuffer::put(std::size_t index, T value) + inline void ByteBuffer::put(std::size_t index, T value) const { - // this avoids int8 specialization, compiler will take care if optimization, -O2 or more - if (sizeof(T) == 1) - { - *(_buffer + index) = (int8)value; - return; - } + assert(_buffer+index<=_limit); - if (ENDIANESS_SUPPORT && reverse()) - { + if(reverse()) value = swap(value); - } - - if (UNALIGNED_ACCESS) - { - // NOTE: some CPU handle unaligned access pretty good (e.g. x86) - *((T*)(_buffer + index)) = value; - } - else - { - // NOTE: this check and branching does not always pay off - if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T))) - { - *((T*)(_buffer + index)) = value; - } - else - { - if (USE_INLINE_MEMCPY) - { - // NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc - memcpy(_buffer + index, &value, sizeof(T)); - } - else - { - // NOTE: compiler should optimize this and unroll the loop - char *p = _buffer + index; - for (size_t i = 0; i < sizeof(T); i++) - p[i] = ((char*)&value)[i]; - } - } - } + //assert(is_aligned(_buffer+index, sizeof(T))); //TODO: special case for targets which support unaligned access + *((T*)(_buffer+index)) = value; } #if defined (__GNUC__) && (__GNUC__ < 3) @@ -787,160 +724,65 @@ private: inline T ByteBuffer::get() #endif { - // this avoids int8 specialization, compiler will take care if optimization, -O2 or more - if (sizeof(T) == 1) - { - return (int8)(*(_position++)); - } + assert(sizeof(T)<=getRemaining()); + //assert(is_aligned(_position, sizeof(T))); + T value = *((T*)_position); + _position += sizeof(T); - T value; - - if (UNALIGNED_ACCESS) - { - // NOTE: some CPU handle unaligned access pretty good (e.g. x86) - value = *((T*)_position); - _position += sizeof(T); - } - else - { - // NOTE: this check and branching does not always pay off - if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T))) - { - value = *((T*)_position); - _position += sizeof(T); - } - else - { - if (USE_INLINE_MEMCPY) - { - // NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc - memcpy(&value, _position, sizeof(T)); - _position += sizeof(T); - } - else - { - // NOTE: compiler should optimize this and unroll the loop - for (size_t i = 0; i < sizeof(T); i++) - ((char*)&value)[i] = _position[i]; - _position += sizeof(T); - } - } - } - - if (ENDIANESS_SUPPORT && reverse()) - { + if(reverse()) value = swap(value); - } - return value; } template - inline T ByteBuffer::get(std::size_t index) + inline T ByteBuffer::get(std::size_t index) const { - // this avoids int8 specialization, compiler will take care if optimization, -O2 or more - if (sizeof(T) == 1) - { - return (int8)(*(_buffer + index)); - } + assert(_buffer+index<=_limit); + //assert(is_aligned(_position, sizeof(T))); + T value = *((T*)(_buffer + index)); - T value; - - if (UNALIGNED_ACCESS) - { - // NOTE: some CPU handle unaligned access pretty good (e.g. x86) - value = *((T*)(_buffer + index)); - } - else - { - // NOTE: this check and branching does not always pay off - if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T))) - { - value = *((T*)(_buffer + index)); - } - else - { - if (USE_INLINE_MEMCPY) - { - // NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc - memcpy(&value, _buffer + index, sizeof(T)); - } - else - { - // NOTE: compiler should optimize this and unroll the loop - char* p = _buffer + index; - for (size_t i = 0; i < sizeof(T); i++) - ((char*)&value)[i] = p[i]; - } - } - } - - if (ENDIANESS_SUPPORT && reverse()) - { + if(reverse()) value = swap(value); - } - return value; } template inline void ByteBuffer::putArray(const T* values, std::size_t count) { - // this avoids int8 specialization, compiler will take care if optimization, -O2 or more - if (sizeof(T) == 1) - { - put((const char*)values, 0, count); - return; - } - - T* start = (T*)_position; - - size_t n = sizeof(T)*count; // we require aligned arrays... - memcpy(_position, values, n); - _position += n; + //assert(is_aligned(_position, sizeof(T))); + T* start = (T*)_position; + size_t n = sizeof(T)*count; // bytes + assert(n<=getRemaining()); - // ... so that we can be fast changing endianness - if (ENDIANESS_SUPPORT && reverse()) - { - for (std::size_t i = 0; i < count; i++) - { - *start = swap(*start); - start++; - } + if (reverse()) { + for(std::size_t i=0; i(values[i]); + } else { + memcpy(start, values, n); } + _position += n; } template inline void ByteBuffer::getArray(T* values, std::size_t count) { - // this avoids int8 specialization, compiler will take care if optimization, -O2 or more - if (sizeof(T) == 1) - { - get((char*)values, 0, count); - return; - } - - T* start = (T*)values; - - size_t n = sizeof(T)*count; // we require aligned arrays... - memcpy(values, _position, n); - _position += n; + //assert(is_aligned(_position, sizeof(T))); + const T* start = (T*)_position; + size_t n = sizeof(T)*count; // bytes + assert(n<=getRemaining()); - // ... so that we can be fast changing endianness - if (ENDIANESS_SUPPORT && reverse()) - { - for (std::size_t i = 0; i < count; i++) - { - *start = swap(*start); - start++; - } + if (reverse()) { + for(std::size_t i=0; i(start[i]); + } else { + memcpy(values, start, n); } + _position += n; } - } -} +}} #endif /* BYTEBUFFER_H */ diff --git a/testApp/misc/testByteBuffer.cpp b/testApp/misc/testByteBuffer.cpp index 3333212..ecc5aa8 100644 --- a/testApp/misc/testByteBuffer.cpp +++ b/testApp/misc/testByteBuffer.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -23,11 +24,12 @@ using namespace epics::pvData; using std::string; using std::cout; +static void testBasicOperations() { + std::auto_ptr buff(new ByteBuffer(32)); - ByteBuffer* buff = new ByteBuffer(32); testOk1(buff->getSize()==32); -// testOk1(buff->getByteOrder()==EPICS_BYTE_ORDER); + testOk1(buff->getPosition()==0); testOk1(buff->getLimit()==32); testOk1(buff->getRemaining()==32); @@ -70,18 +72,6 @@ void testBasicOperations() { testOk1(buff->getLong(8)==2345678123LL); testOk1(buff->getFloat(16)==testFloat); testOk1(buff->getDouble(20)==testDouble); -/* - std::size_t sp = buff->getPosition(); - buff->setPosition(0); - testOk1(buff->getBoolean()==true); - testOk1(buff->getByte()==-12); - testOk1(buff->getShort()==10516); - testOk1(buff->getInt()==0x1937628B); - testOk1(buff->getLong()==2345678123LL); - testOk1(buff->getFloat()==testFloat); - testOk1(buff->getDouble()==testDouble); - buff->setPosition(sp); -*/ buff->flip(); testOk1(buff->getLimit()==28); @@ -134,19 +124,7 @@ void testBasicOperations() { buff->putLong(8, 2345678123LL); buff->putFloat(16, testFloat); buff->putDouble(20, testDouble); -/* - buff->clear(); - buff->setPosition(28); - sp = buff->getPosition(); - buff->putBoolean(true); - buff->putByte(-12); - buff->putShort(10516); - buff->putInt(0x1937628B); - buff->putLong(2345678123LL); - buff->putFloat(testFloat); - buff->putDouble(testDouble); - buff->setPosition(sp); -*/ + buff->flip(); testOk1(buff->getLimit()==28); testOk1(buff->getPosition()==0); @@ -197,19 +175,19 @@ void testBasicOperations() { testOk1(buff->getPosition()==6); testOk1(strncmp(&src[2],&dst[2],6)==0); - cout<<" First 10 characters of destination: >>"<>"< buf(new ByteBuffer(32,EPICS_ENDIAN_LITTLE)); char refBuffer[] = { (char)0x02, (char)0x01, (char)0x0D, (char)0x0C, (char)0x0B, (char)0x0A}; #else + std::auto_ptr buf(new ByteBuffer(32,EPICS_ENDIAN_BIG)); ByteBuffer* buff = new ByteBuffer(32, EPICS_ENDIAN_BIG); char refBuffer[] = { (char)0x01, (char)0x02, (char)0x0A, (char)0x0B, (char)0x0C, (char)0x0D }; @@ -224,15 +202,70 @@ void testInverseEndianness() { testOk1(buff->getShort()==0x0102); testOk1(buff->getInt()==0x0A0B0C0D); +} - delete buff; +static +void testSwap() +{ + testDiag("Check epics::pvData::swap(v)"); + testOk1(swap(0x80)==0x80); + testOk1(swap(0x7080)==0x8070); + testOk1(swap(0x10203040)==0x40302010); + uint64 a = 0x10203040, + b = 0x80706050; + a <<= 32; + b <<= 32; + a |= 0x50607080; + b |= 0x40302010; + testOk1(swap(a)==b); +} + +static +void testUnaligned() +{ + testDiag("test correctness of unaligned access"); + + ByteBuffer buf(32, EPICS_ENDIAN_BIG); + + // malloc() should give us a buffer aligned to at least native integer size + buf.align(sizeof(int)); + testOk1(buf.getPosition()==0); + + buf.clear(); + buf.put(0x42); + buf.put(0x1020); + buf.align(2, '\x41'); + testOk1(buf.getPosition()==4); + + testOk1(memcmp(buf.getBuffer(), "\x42\x10\x20\x41", 4)==0); + + buf.clear(); + buf.put(0x42); + buf.put(0x12345678); + buf.align(4, '\x41'); + testOk1(buf.getPosition()==8); + + testOk1(memcmp(buf.getBuffer(), "\x42\x12\x34\x56\x78\x41\x41\x41", 8)==0); + + buf.clear(); + buf.put(0x42); + uint64 val = 0x12345678; + val<<=32; + val |= 0x90abcdef; + buf.put(val); + buf.align(8, '\x41'); + testOk1(buf.getPosition()==16); + + testOk1(memcmp(buf.getBuffer(), "\x42\x12\x34\x56\x78\x90\xab\xcd\xef\x41\x41\x41", 8)==0); } MAIN(testByteBuffer) { - testPlan(82); + testPlan(93); testDiag("Tests byteBuffer"); testBasicOperations(); testInverseEndianness(); + testSwap(); + testUnaligned(); return testDone(); }