pulseaudio/src/pulsecore/endianmacros.h

161 lines
4.8 KiB
C
Raw Normal View History

#ifndef fooendianmacroshfoo
#define fooendianmacroshfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#ifndef PACKAGE
#error "Please include config.h before including this file!"
#endif
#ifdef HAVE_BYTESWAP_H
#include <byteswap.h>
#endif
#ifdef HAVE_BYTESWAP_H
#define PA_INT16_SWAP(x) ((int16_t) bswap_16((uint16_t) x))
#define PA_UINT16_SWAP(x) ((uint16_t) bswap_16((uint16_t) x))
#define PA_INT32_SWAP(x) ((int32_t) bswap_32((uint32_t) x))
#define PA_UINT32_SWAP(x) ((uint32_t) bswap_32((uint32_t) x))
#else
2009-01-08 23:30:51 +01:00
#define PA_INT16_SWAP(x) ( (int16_t) ( ((uint16_t) (x) >> 8) | ((uint16_t) (x) << 8) ) )
#define PA_UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) (x) >> 8) | ((uint16_t) (x) << 8) ) )
#define PA_INT32_SWAP(x) ( (int32_t) ( ((uint32_t) (x) >> 24) | ((uint32_t) (x) << 24) | (((uint32_t) (x) & 0xFF00) << 8) | ((((uint32_t) (x)) >> 8) & 0xFF00) ) )
#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) (x) >> 24) | ((uint32_t) (x) << 24) | (((uint32_t) (x) & 0xFF00) << 8) | ((((uint32_t) (x)) >> 8) & 0xFF00) ) )
#endif
static inline uint32_t PA_READ24BE(const uint8_t *p) {
2009-01-16 03:15:39 +01:00
return
((uint32_t) p[0] << 16) |
((uint32_t) p[1] << 8) |
((uint32_t) p[2]);
}
static inline uint32_t PA_READ24LE(const uint8_t *p) {
2009-01-16 03:15:39 +01:00
return
((uint32_t) p[2] << 16) |
((uint32_t) p[1] << 8) |
((uint32_t) p[0]);
}
static inline void PA_WRITE24BE(uint8_t *p, uint32_t u) {
2009-01-16 03:15:39 +01:00
p[0] = (uint8_t) (u >> 16);
p[1] = (uint8_t) (u >> 8);
p[2] = (uint8_t) u;
}
static inline void PA_WRITE24LE(uint8_t *p, uint32_t u) {
2009-01-16 03:15:39 +01:00
p[2] = (uint8_t) (u >> 16);
p[1] = (uint8_t) (u >> 8);
p[0] = (uint8_t) u;
}
endianmacros: Replace borked PA_FLOAT32_SWAP() with PA_READ_FLOAT32RE() / PA_WRITE_FLOAT32RE() building PA with -O0 leads to test failure in mix-test on i386 issue reported by Felipe, see http://lists.freedesktop.org/archives/pulseaudio-discuss/2014-August/021406.html the problem is the value 0xbeffbd7f: when byte-swapped it becomes 0x7fbdffbe and according to IEEE-754 represents a signalling NaN (starting with s111 1111 10, see http://en.wikipedia.org/wiki/NaN) when this value is assigned to a floating point register, it becomes 0x7ffdffbe, representing a quiet NaN (starting with s111 1111 11) -- a signalling NaN is turned into a quiet NaN! so PA_FLOAT32_SWAP(PA_FLOAT32_SWAP(x)) != x for certain values, uhuh! the following test code can be used; due to volatile, it will always demonstrate the issue; without volatile, it depends on the optimization level (i386, 32-bit, gcc 4.9): // snip static inline float PA_FLOAT32_SWAP(float x) { union { float f; uint32_t u; } t; t.f = x; t.u = bswap_32(t.u); return t.f; } int main() { unsigned x = 0xbeffbd7f; volatile float f = PA_FLOAT32_SWAP(*(float *)&x); printf("%08x %08x %08x %f\n", 0xbeffbd7f, *(unsigned *)&f, bswap_32(*(unsigned *)&f), f); } // snip the problem goes away with optimization when no temporary floating point registers are used the proposed solution is to avoid passing swapped floating point data in a float; this is done with new functions PA_READ_FLOAT32RE() and PA_WRITE_FLOAT32RE() which use uint32_t to dereference a pointer and byte-swap the data, hence no temporary float variable is used also delete PA_FLOAT32_TO_LE()/_BE(), not used Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net> Reported-by: Felipe Sateler <fsateler@debian.org>
2014-09-02 23:53:09 +02:00
static inline float PA_READ_FLOAT32RE(const void *p) {
2008-08-09 17:03:43 +02:00
union {
float f;
uint32_t u;
} t;
endianmacros: Replace borked PA_FLOAT32_SWAP() with PA_READ_FLOAT32RE() / PA_WRITE_FLOAT32RE() building PA with -O0 leads to test failure in mix-test on i386 issue reported by Felipe, see http://lists.freedesktop.org/archives/pulseaudio-discuss/2014-August/021406.html the problem is the value 0xbeffbd7f: when byte-swapped it becomes 0x7fbdffbe and according to IEEE-754 represents a signalling NaN (starting with s111 1111 10, see http://en.wikipedia.org/wiki/NaN) when this value is assigned to a floating point register, it becomes 0x7ffdffbe, representing a quiet NaN (starting with s111 1111 11) -- a signalling NaN is turned into a quiet NaN! so PA_FLOAT32_SWAP(PA_FLOAT32_SWAP(x)) != x for certain values, uhuh! the following test code can be used; due to volatile, it will always demonstrate the issue; without volatile, it depends on the optimization level (i386, 32-bit, gcc 4.9): // snip static inline float PA_FLOAT32_SWAP(float x) { union { float f; uint32_t u; } t; t.f = x; t.u = bswap_32(t.u); return t.f; } int main() { unsigned x = 0xbeffbd7f; volatile float f = PA_FLOAT32_SWAP(*(float *)&x); printf("%08x %08x %08x %f\n", 0xbeffbd7f, *(unsigned *)&f, bswap_32(*(unsigned *)&f), f); } // snip the problem goes away with optimization when no temporary floating point registers are used the proposed solution is to avoid passing swapped floating point data in a float; this is done with new functions PA_READ_FLOAT32RE() and PA_WRITE_FLOAT32RE() which use uint32_t to dereference a pointer and byte-swap the data, hence no temporary float variable is used also delete PA_FLOAT32_TO_LE()/_BE(), not used Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net> Reported-by: Felipe Sateler <fsateler@debian.org>
2014-09-02 23:53:09 +02:00
t.u = PA_UINT32_SWAP(*(uint32_t *) p);
2008-08-09 17:03:43 +02:00
return t.f;
}
endianmacros: Replace borked PA_FLOAT32_SWAP() with PA_READ_FLOAT32RE() / PA_WRITE_FLOAT32RE() building PA with -O0 leads to test failure in mix-test on i386 issue reported by Felipe, see http://lists.freedesktop.org/archives/pulseaudio-discuss/2014-August/021406.html the problem is the value 0xbeffbd7f: when byte-swapped it becomes 0x7fbdffbe and according to IEEE-754 represents a signalling NaN (starting with s111 1111 10, see http://en.wikipedia.org/wiki/NaN) when this value is assigned to a floating point register, it becomes 0x7ffdffbe, representing a quiet NaN (starting with s111 1111 11) -- a signalling NaN is turned into a quiet NaN! so PA_FLOAT32_SWAP(PA_FLOAT32_SWAP(x)) != x for certain values, uhuh! the following test code can be used; due to volatile, it will always demonstrate the issue; without volatile, it depends on the optimization level (i386, 32-bit, gcc 4.9): // snip static inline float PA_FLOAT32_SWAP(float x) { union { float f; uint32_t u; } t; t.f = x; t.u = bswap_32(t.u); return t.f; } int main() { unsigned x = 0xbeffbd7f; volatile float f = PA_FLOAT32_SWAP(*(float *)&x); printf("%08x %08x %08x %f\n", 0xbeffbd7f, *(unsigned *)&f, bswap_32(*(unsigned *)&f), f); } // snip the problem goes away with optimization when no temporary floating point registers are used the proposed solution is to avoid passing swapped floating point data in a float; this is done with new functions PA_READ_FLOAT32RE() and PA_WRITE_FLOAT32RE() which use uint32_t to dereference a pointer and byte-swap the data, hence no temporary float variable is used also delete PA_FLOAT32_TO_LE()/_BE(), not used Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net> Reported-by: Felipe Sateler <fsateler@debian.org>
2014-09-02 23:53:09 +02:00
static inline void PA_WRITE_FLOAT32RE(void *p, float x) {
union {
float f;
uint32_t u;
} t;
t.f = x;
*(uint32_t *) p = PA_UINT32_SWAP(t.u);
}
#define PA_MAYBE_INT16_SWAP(c,x) ((c) ? PA_INT16_SWAP(x) : (x))
#define PA_MAYBE_UINT16_SWAP(c,x) ((c) ? PA_UINT16_SWAP(x) : (x))
2009-01-08 23:30:51 +01:00
#define PA_MAYBE_INT32_SWAP(c,x) ((c) ? PA_INT32_SWAP(x) : (x))
#define PA_MAYBE_UINT32_SWAP(c,x) ((c) ? PA_UINT32_SWAP(x) : (x))
#ifdef WORDS_BIGENDIAN
#define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x)
#define PA_INT16_FROM_BE(x) ((int16_t)(x))
#define PA_INT16_TO_LE(x) PA_INT16_SWAP(x)
#define PA_INT16_TO_BE(x) ((int16_t)(x))
#define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x)
#define PA_UINT16_FROM_BE(x) ((uint16_t)(x))
#define PA_UINT16_TO_LE(x) PA_UINT16_SWAP(x)
#define PA_UINT16_TO_BE(x) ((uint16_t)(x))
#define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x)
#define PA_INT32_FROM_BE(x) ((int32_t)(x))
#define PA_INT32_TO_LE(x) PA_INT32_SWAP(x)
#define PA_INT32_TO_BE(x) ((int32_t)(x))
#define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x)
#define PA_UINT32_FROM_BE(x) ((uint32_t)(x))
#define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x)
#define PA_UINT32_TO_BE(x) ((uint32_t)(x))
2009-01-16 03:15:39 +01:00
#define PA_READ24NE(x) PA_READ24BE(x)
#define PA_WRITE24NE(x,y) PA_WRITE24BE((x),(y))
#define PA_READ24RE(x) PA_READ24LE(x)
#define PA_WRITE24RE(x,y) PA_WRITE24LE((x),(y))
#else
#define PA_INT16_FROM_LE(x) ((int16_t)(x))
#define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x)
#define PA_INT16_TO_LE(x) ((int16_t)(x))
#define PA_INT16_TO_BE(x) PA_INT16_SWAP(x)
#define PA_UINT16_FROM_LE(x) ((uint16_t)(x))
#define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x)
#define PA_UINT16_TO_LE(x) ((uint16_t)(x))
#define PA_UINT16_TO_BE(x) PA_UINT16_SWAP(x)
#define PA_INT32_FROM_LE(x) ((int32_t)(x))
#define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x)
#define PA_INT32_TO_LE(x) ((int32_t)(x))
#define PA_INT32_TO_BE(x) PA_INT32_SWAP(x)
#define PA_UINT32_FROM_LE(x) ((uint32_t)(x))
#define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x)
#define PA_UINT32_TO_LE(x) ((uint32_t)(x))
#define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x)
2009-01-16 03:15:39 +01:00
#define PA_READ24NE(x) PA_READ24LE(x)
#define PA_WRITE24NE(x,y) PA_WRITE24LE((x),(y))
#define PA_READ24RE(x) PA_READ24BE(x)
#define PA_WRITE24RE(x,y) PA_WRITE24BE((x),(y))
#endif
#endif