diff --git a/tunsafe_endian.h b/tunsafe_endian.h index 45cb316..d6a0acd 100644 --- a/tunsafe_endian.h +++ b/tunsafe_endian.h @@ -10,43 +10,14 @@ #endif #include -#define ByteSwap32Fallback(x) ( \ - (((uint32)(x) & (uint32)0x000000fful) << 24) | \ - (((uint32)(x) & (uint32)0x0000ff00ul) << 8) | \ - (((uint32)(x) & (uint32)0x00ff0000ul) >> 8) | \ - (((uint32)(x) & (uint32)0xff000000ul) >> 24)) - -#define ByteSwap16Fallback(x) ((uint16)( \ - (((uint16)(x) & (uint16)0x00ffu) << 8) | \ - (((uint16)(x) & (uint16)0xff00u) >> 8))) - -#define ByteSwap64Fallback(x) ((uint64)ByteSwap32Fallback(x)<<32 | ByteSwap32Fallback(x>>32)) - -#define ReadBE32AlignedFallback(pt) (((uint32)((pt)[0] & 0xFF) << 24) ^ \ - ((uint32)((pt)[1] & 0xFF) << 16) ^ \ - ((uint32)((pt)[2] & 0xFF) << 8) ^ \ - ((uint32)((pt)[3] & 0xFF))) -#define WriteBE32AlignedFallback(ct, st) { \ - (ct)[0] = (char)((st) >> 24); \ - (ct)[1] = (char)((st) >> 16); \ - (ct)[2] = (char)((st) >> 8); \ - (ct)[3] = (char)(st); } - - - - #if defined(OS_WIN) && defined(COMPILER_MSVC) #define ByteSwap16(x) _byteswap_ushort((uint16)x) #define ByteSwap32(x) _byteswap_ulong((uint32)x) #define ByteSwap64(x) _byteswap_uint64((uint64)x) -#elif defined(COMPILER_GCC) +#else #define ByteSwap16(x) __builtin_bswap16((uint16)x) #define ByteSwap32(x) __builtin_bswap32((uint32)x) #define ByteSwap64(x) __builtin_bswap64((uint64)x) -#else -#define ByteSwap16 ByteSwap16Fallback -#define ByteSwap32 ByteSwap32Fallback -#define ByteSwap64 ByteSwap64Fallback #endif #if defined(ARCH_CPU_LITTLE_ENDIAN) @@ -56,41 +27,78 @@ #define ToLE64(x) (x) #define ToLE32(x) (x) #define ToLE16(x) (x) -#else +#elif defined(ARCH_CPU_BIG_ENDIAN) #define ToBE64(x) (x) #define ToBE32(x) (x) #define ToBE16(x) (x) #define ToLE64(x) ByteSwap64(x) #define ToLE32(x) ByteSwap32(x) #define ToLE16(x) ByteSwap16(x) +#else +#error The CPU is neither big / little endian #endif -#define ReadBE16Aligned(pt) ToBE16(*(uint16*)(pt)) -#define WriteBE16Aligned(ct, st) (*(uint16*)(ct) = ToBE16(st)) -#define ReadBE32Aligned(pt) ToBE32(*(uint32*)(pt)) -#define WriteBE32Aligned(ct, st) (*(uint32*)(ct) = ToBE32(st)) +#if !(defined(COMPILER_GCC) || defined(COMPILER_CLANG) || defined(ARCH_CPU_ALLOW_UNALIGNED)) +#error The CPU does not support unaligned memory accesses +#endif // defined(ARCH_CPU_ALLOW_UNALIGNED) -// todo: these need to support unaligned pointers -#define ReadBE16(pt) ToBE16(*(uint16*)(pt)) -#define WriteBE16(ct, st) (*(uint16*)(ct) = ToBE16(st)) -#define ReadBE32(pt) ToBE32(*(uint32*)(pt)) -#define WriteBE32(ct, st) (*(uint32*)(ct) = ToBE32(st)) -#define ReadBE64(pt) ToBE64(*(uint64*)(pt)) -#define WriteBE64(ct, st) (*(uint64*)(ct) = ToBE64(st)) +#if defined(COMPILER_GCC) || defined(COMPILER_CLANG) +// The WriteBE/WriteLE functions below write a uint to a char pointer which +// is not valid per the C spec because of aliasing, so work around it. +typedef uint16 __attribute__((__may_alias__)) uint16_unaligned __attribute__((aligned(1))); +typedef uint32 __attribute__((__may_alias__)) uint32_unaligned __attribute__((aligned(1))); +typedef uint64 __attribute__((__may_alias__)) uint64_unaligned __attribute__((aligned(1))); +typedef uint16 __attribute__((__may_alias__)) uint16_aligned; +typedef uint32 __attribute__((__may_alias__)) uint32_aligned; +typedef uint64 __attribute__((__may_alias__)) uint64_aligned; +#else +typedef uint16 uint16_unaligned; +typedef uint32 uint32_unaligned; +typedef uint64 uint64_unaligned; +typedef uint16 uint16_aligned; +typedef uint32 uint32_aligned; +typedef uint64 uint64_aligned; +#endif -#define ReadLE16(pt) ToLE16(*(uint16*)(pt)) -#define WriteLE16(ct, st) (*(uint16*)(ct) = ToLE16(st)) -#define ReadLE32(pt) ToLE32(*(uint32*)(pt)) -#define WriteLE32(ct, st) (*(uint32*)(ct) = ToLE32(st)) -#define ReadLE64(pt) ToLE64(*(uint64*)(pt)) -#define WriteLE64(ct, st) (*(uint64*)(ct) = ToLE64(st)) -#define Read16(pt) (*(uint16*)(pt)) -#define Write16(ct, st) (*(uint16*)(ct) = (st)) -#define Read32(pt) (*(uint32*)(pt)) -#define Write32(ct, st) (*(uint32*)(ct) = (st)) -#define Read64(pt) (*(uint64*)(pt)) -#define Write64(ct, st) (*(uint64*)(ct) = (st)) +// Use the _Aligned variants when you are sure that the pointers are aligned +#define Read16Aligned(pt) *(uint16_aligned*)(pt) +#define Read32Aligned(pt) *(uint32_aligned*)(pt) +#define Read64Aligned(pt) *(uint64_aligned*)(pt) +#define Write16Aligned(ct, st) (*(uint16_aligned*)(ct) = (st)) +#define Write32Aligned(ct, st) (*(uint32_aligned*)(ct) = (st)) +#define Write64Aligned(ct, st) (*(uint64_aligned*)(ct) = (st)) +#define ReadBE16Aligned(pt) ToBE16(Read16Aligned(pt)) +#define ReadBE32Aligned(pt) ToBE32(Read32Aligned(pt)) +#define ReadBE64Aligned(pt) ToBE64(Read64Aligned(pt)) +#define WriteBE16Aligned(ct, st) Write16Aligned(ct, ToBE16(st)) +#define WriteBE32Aligned(ct, st) Write32Aligned(ct, ToBE32(st)) +#define WriteBE64Aligned(ct, st) Write64Aligned(ct, ToBE64(st)) +#define ReadLE16Aligned(pt) ToLE16(Read16Aligned(pt)) +#define ReadLE32Aligned(pt) ToLE32(Read32Aligned(pt)) +#define ReadLE64Aligned(pt) ToLE64(Read64Aligned(pt)) +#define WriteLE16Aligned(ct, st) Write16Aligned(ct, ToLE16(st)) +#define WriteLE32Aligned(ct, st) Write32Aligned(ct, ToLE32(st)) +#define WriteLE64Aligned(ct, st) Write64Aligned(ct, ToLE64(st)) +// Use the below this when pointers may be unaligned +#define Read16(pt) *(uint16_unaligned*)(pt) +#define Read32(pt) *(uint32_unaligned*)(pt) +#define Read64(pt) *(uint64_unaligned*)(pt) +#define Write16(ct, st) (*(uint16_unaligned*)(ct) = (st)) +#define Write32(ct, st) (*(uint32_unaligned*)(ct) = (st)) +#define Write64(ct, st) (*(uint64_unaligned*)(ct) = (st)) +#define ReadBE16(pt) ToBE16(Read16(pt)) +#define ReadBE32(pt) ToBE32(Read32(pt)) +#define ReadBE64(pt) ToBE64(Read64(pt)) +#define WriteBE16(ct, st) Write16(ct, ToBE16(st)) +#define WriteBE32(ct, st) Write32(ct, ToBE32(st)) +#define WriteBE64(ct, st) Write64(ct, ToBE64(st)) +#define ReadLE16(pt) ToLE16(Read16(pt)) +#define ReadLE32(pt) ToLE32(Read32(pt)) +#define ReadLE64(pt) ToLE64(Read64(pt)) +#define WriteLE16(ct, st) Write16(ct, ToLE16(st)) +#define WriteLE32(ct, st) Write32(ct, ToLE32(st)) +#define WriteLE64(ct, st) Write64(ct, ToLE64(st)) #endif // TINYVPN_ENDIAN_H_