/* * Load/Store Operators * (C) 1999-2007,2015 Jack Lloyd * 2007 Yves Jerschow * * Botan is released under the Simplified BSD License (see license.txt) */ #ifndef BOTAN_LOAD_STORE_H__ #define BOTAN_LOAD_STORE_H__ #include #include #include #include #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) #define BOTAN_ENDIAN_N2B(x) (x) #define BOTAN_ENDIAN_B2N(x) (x) #define BOTAN_ENDIAN_N2L(x) reverse_bytes(x) #define BOTAN_ENDIAN_L2N(x) reverse_bytes(x) #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) #define BOTAN_ENDIAN_N2L(x) (x) #define BOTAN_ENDIAN_L2N(x) (x) #define BOTAN_ENDIAN_N2B(x) reverse_bytes(x) #define BOTAN_ENDIAN_B2N(x) reverse_bytes(x) #endif #endif namespace Botan { /** * Byte extraction * @param byte_num which byte to extract, 0 == highest byte * @param input the value to extract from * @return byte byte_num of input */ template inline byte get_byte(size_t byte_num, T input) { return static_cast( input >> (((~byte_num)&(sizeof(T)-1)) << 3) ); } /** * Make a u16bit from two bytes * @param i0 the first byte * @param i1 the second byte * @return i0 || i1 */ inline u16bit make_u16bit(byte i0, byte i1) { return ((static_cast(i0) << 8) | i1); } /** * Make a u32bit from four bytes * @param i0 the first byte * @param i1 the second byte * @param i2 the third byte * @param i3 the fourth byte * @return i0 || i1 || i2 || i3 */ inline u32bit make_u32bit(byte i0, byte i1, byte i2, byte i3) { return ((static_cast(i0) << 24) | (static_cast(i1) << 16) | (static_cast(i2) << 8) | (static_cast(i3))); } /** * Make a u32bit from eight bytes * @param i0 the first byte * @param i1 the second byte * @param i2 the third byte * @param i3 the fourth byte * @param i4 the fifth byte * @param i5 the sixth byte * @param i6 the seventh byte * @param i7 the eighth byte * @return i0 || i1 || i2 || i3 || i4 || i5 || i6 || i7 */ inline u64bit make_u64bit(byte i0, byte i1, byte i2, byte i3, byte i4, byte i5, byte i6, byte i7) { return ((static_cast(i0) << 56) | (static_cast(i1) << 48) | (static_cast(i2) << 40) | (static_cast(i3) << 32) | (static_cast(i4) << 24) | (static_cast(i5) << 16) | (static_cast(i6) << 8) | (static_cast(i7))); } /** * Load a big-endian word * @param in a pointer to some bytes * @param off an offset into the array * @return off'th T of in, as a big-endian value */ template inline T load_be(const byte in[], size_t off) { in += off * sizeof(T); T out = 0; for(size_t i = 0; i != sizeof(T); ++i) out = (out << 8) | in[i]; return out; } /** * Load a little-endian word * @param in a pointer to some bytes * @param off an offset into the array * @return off'th T of in, as a litte-endian value */ template inline T load_le(const byte in[], size_t off) { in += off * sizeof(T); T out = 0; for(size_t i = 0; i != sizeof(T); ++i) out = (out << 8) | in[sizeof(T)-1-i]; return out; } /** * Load a big-endian u16bit * @param in a pointer to some bytes * @param off an offset into the array * @return off'th u16bit of in, as a big-endian value */ template<> inline u16bit load_be(const byte in[], size_t off) { in += off * sizeof(u16bit); #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u16bit x; std::memcpy(&x, in, sizeof(x)); return BOTAN_ENDIAN_N2B(x); #else return make_u16bit(in[0], in[1]); #endif } /** * Load a little-endian u16bit * @param in a pointer to some bytes * @param off an offset into the array * @return off'th u16bit of in, as a little-endian value */ template<> inline u16bit load_le(const byte in[], size_t off) { in += off * sizeof(u16bit); #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u16bit x; std::memcpy(&x, in, sizeof(x)); return BOTAN_ENDIAN_N2L(x); #else return make_u16bit(in[1], in[0]); #endif } /** * Load a big-endian u32bit * @param in a pointer to some bytes * @param off an offset into the array * @return off'th u32bit of in, as a big-endian value */ template<> inline u32bit load_be(const byte in[], size_t off) { in += off * sizeof(u32bit); #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u32bit x; std::memcpy(&x, in, sizeof(x)); return BOTAN_ENDIAN_N2B(x); #else return make_u32bit(in[0], in[1], in[2], in[3]); #endif } /** * Load a little-endian u32bit * @param in a pointer to some bytes * @param off an offset into the array * @return off'th u32bit of in, as a little-endian value */ template<> inline u32bit load_le(const byte in[], size_t off) { in += off * sizeof(u32bit); #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u32bit x; std::memcpy(&x, in, sizeof(x)); return BOTAN_ENDIAN_N2L(x); #else return make_u32bit(in[3], in[2], in[1], in[0]); #endif } /** * Load a big-endian u64bit * @param in a pointer to some bytes * @param off an offset into the array * @return off'th u64bit of in, as a big-endian value */ template<> inline u64bit load_be(const byte in[], size_t off) { in += off * sizeof(u64bit); #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u64bit x; std::memcpy(&x, in, sizeof(x)); return BOTAN_ENDIAN_N2B(x); #else return make_u64bit(in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]); #endif } /** * Load a little-endian u64bit * @param in a pointer to some bytes * @param off an offset into the array * @return off'th u64bit of in, as a little-endian value */ template<> inline u64bit load_le(const byte in[], size_t off) { in += off * sizeof(u64bit); #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u64bit x; std::memcpy(&x, in, sizeof(x)); return BOTAN_ENDIAN_N2L(x); #else return make_u64bit(in[7], in[6], in[5], in[4], in[3], in[2], in[1], in[0]); #endif } /** * Load two little-endian words * @param in a pointer to some bytes * @param x0 where the first word will be written * @param x1 where the second word will be written */ template inline void load_le(const byte in[], T& x0, T& x1) { x0 = load_le(in, 0); x1 = load_le(in, 1); } /** * Load four little-endian words * @param in a pointer to some bytes * @param x0 where the first word will be written * @param x1 where the second word will be written * @param x2 where the third word will be written * @param x3 where the fourth word will be written */ template inline void load_le(const byte in[], T& x0, T& x1, T& x2, T& x3) { x0 = load_le(in, 0); x1 = load_le(in, 1); x2 = load_le(in, 2); x3 = load_le(in, 3); } /** * Load eight little-endian words * @param in a pointer to some bytes * @param x0 where the first word will be written * @param x1 where the second word will be written * @param x2 where the third word will be written * @param x3 where the fourth word will be written * @param x4 where the fifth word will be written * @param x5 where the sixth word will be written * @param x6 where the seventh word will be written * @param x7 where the eighth word will be written */ template inline void load_le(const byte in[], T& x0, T& x1, T& x2, T& x3, T& x4, T& x5, T& x6, T& x7) { x0 = load_le(in, 0); x1 = load_le(in, 1); x2 = load_le(in, 2); x3 = load_le(in, 3); x4 = load_le(in, 4); x5 = load_le(in, 5); x6 = load_le(in, 6); x7 = load_le(in, 7); } /** * Load a variable number of little-endian words * @param out the output array of words * @param in the input array of bytes * @param count how many words are in in */ template inline void load_le(T out[], const byte in[], size_t count) { if(count > 0) { #if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) std::memcpy(out, in, sizeof(T)*count); #elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) std::memcpy(out, in, sizeof(T)*count); const size_t blocks = count - (count % 4); const size_t left = count - blocks; for(size_t i = 0; i != blocks; i += 4) bswap_4(out + i); for(size_t i = 0; i != left; ++i) out[blocks+i] = reverse_bytes(out[blocks+i]); #else for(size_t i = 0; i != count; ++i) out[i] = load_le(in, i); #endif } } /** * Load two big-endian words * @param in a pointer to some bytes * @param x0 where the first word will be written * @param x1 where the second word will be written */ template inline void load_be(const byte in[], T& x0, T& x1) { x0 = load_be(in, 0); x1 = load_be(in, 1); } /** * Load four big-endian words * @param in a pointer to some bytes * @param x0 where the first word will be written * @param x1 where the second word will be written * @param x2 where the third word will be written * @param x3 where the fourth word will be written */ template inline void load_be(const byte in[], T& x0, T& x1, T& x2, T& x3) { x0 = load_be(in, 0); x1 = load_be(in, 1); x2 = load_be(in, 2); x3 = load_be(in, 3); } /** * Load eight big-endian words * @param in a pointer to some bytes * @param x0 where the first word will be written * @param x1 where the second word will be written * @param x2 where the third word will be written * @param x3 where the fourth word will be written * @param x4 where the fifth word will be written * @param x5 where the sixth word will be written * @param x6 where the seventh word will be written * @param x7 where the eighth word will be written */ template inline void load_be(const byte in[], T& x0, T& x1, T& x2, T& x3, T& x4, T& x5, T& x6, T& x7) { x0 = load_be(in, 0); x1 = load_be(in, 1); x2 = load_be(in, 2); x3 = load_be(in, 3); x4 = load_be(in, 4); x5 = load_be(in, 5); x6 = load_be(in, 6); x7 = load_be(in, 7); } /** * Load a variable number of big-endian words * @param out the output array of words * @param in the input array of bytes * @param count how many words are in in */ template inline void load_be(T out[], const byte in[], size_t count) { if(count > 0) { #if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) std::memcpy(out, in, sizeof(T)*count); #elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) std::memcpy(out, in, sizeof(T)*count); const size_t blocks = count - (count % 4); const size_t left = count - blocks; for(size_t i = 0; i != blocks; i += 4) bswap_4(out + i); for(size_t i = 0; i != left; ++i) out[blocks+i] = reverse_bytes(out[blocks+i]); #else for(size_t i = 0; i != count; ++i) out[i] = load_be(in, i); #endif } } /** * Store a big-endian u16bit * @param in the input u16bit * @param out the byte array to write to */ inline void store_be(u16bit in, byte out[2]) { #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u16bit o = BOTAN_ENDIAN_N2B(in); std::memcpy(out, &o, sizeof(o)); #else out[0] = get_byte(0, in); out[1] = get_byte(1, in); #endif } /** * Store a little-endian u16bit * @param in the input u16bit * @param out the byte array to write to */ inline void store_le(u16bit in, byte out[2]) { #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u16bit o = BOTAN_ENDIAN_N2L(in); std::memcpy(out, &o, sizeof(o)); #else out[0] = get_byte(1, in); out[1] = get_byte(0, in); #endif } /** * Store a big-endian u32bit * @param in the input u32bit * @param out the byte array to write to */ inline void store_be(u32bit in, byte out[4]) { #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u32bit o = BOTAN_ENDIAN_B2N(in); std::memcpy(out, &o, sizeof(o)); #else out[0] = get_byte(0, in); out[1] = get_byte(1, in); out[2] = get_byte(2, in); out[3] = get_byte(3, in); #endif } /** * Store a little-endian u32bit * @param in the input u32bit * @param out the byte array to write to */ inline void store_le(u32bit in, byte out[4]) { #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u32bit o = BOTAN_ENDIAN_L2N(in); std::memcpy(out, &o, sizeof(o)); #else out[0] = get_byte(3, in); out[1] = get_byte(2, in); out[2] = get_byte(1, in); out[3] = get_byte(0, in); #endif } /** * Store a big-endian u64bit * @param in the input u64bit * @param out the byte array to write to */ inline void store_be(u64bit in, byte out[8]) { #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u64bit o = BOTAN_ENDIAN_B2N(in); std::memcpy(out, &o, sizeof(o)); #else out[0] = get_byte(0, in); out[1] = get_byte(1, in); out[2] = get_byte(2, in); out[3] = get_byte(3, in); out[4] = get_byte(4, in); out[5] = get_byte(5, in); out[6] = get_byte(6, in); out[7] = get_byte(7, in); #endif } /** * Store a little-endian u64bit * @param in the input u64bit * @param out the byte array to write to */ inline void store_le(u64bit in, byte out[8]) { #if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK u64bit o = BOTAN_ENDIAN_L2N(in); std::memcpy(out, &o, sizeof(o)); #else out[0] = get_byte(7, in); out[1] = get_byte(6, in); out[2] = get_byte(5, in); out[3] = get_byte(4, in); out[4] = get_byte(3, in); out[5] = get_byte(2, in); out[6] = get_byte(1, in); out[7] = get_byte(0, in); #endif } /** * Store two little-endian words * @param out the output byte array * @param x0 the first word * @param x1 the second word */ template inline void store_le(byte out[], T x0, T x1) { store_le(x0, out + (0 * sizeof(T))); store_le(x1, out + (1 * sizeof(T))); } /** * Store two big-endian words * @param out the output byte array * @param x0 the first word * @param x1 the second word */ template inline void store_be(byte out[], T x0, T x1) { store_be(x0, out + (0 * sizeof(T))); store_be(x1, out + (1 * sizeof(T))); } /** * Store four little-endian words * @param out the output byte array * @param x0 the first word * @param x1 the second word * @param x2 the third word * @param x3 the fourth word */ template inline void store_le(byte out[], T x0, T x1, T x2, T x3) { store_le(x0, out + (0 * sizeof(T))); store_le(x1, out + (1 * sizeof(T))); store_le(x2, out + (2 * sizeof(T))); store_le(x3, out + (3 * sizeof(T))); } /** * Store four big-endian words * @param out the output byte array * @param x0 the first word * @param x1 the second word * @param x2 the third word * @param x3 the fourth word */ template inline void store_be(byte out[], T x0, T x1, T x2, T x3) { store_be(x0, out + (0 * sizeof(T))); store_be(x1, out + (1 * sizeof(T))); store_be(x2, out + (2 * sizeof(T))); store_be(x3, out + (3 * sizeof(T))); } /** * Store eight little-endian words * @param out the output byte array * @param x0 the first word * @param x1 the second word * @param x2 the third word * @param x3 the fourth word * @param x4 the fifth word * @param x5 the sixth word * @param x6 the seventh word * @param x7 the eighth word */ template inline void store_le(byte out[], T x0, T x1, T x2, T x3, T x4, T x5, T x6, T x7) { store_le(x0, out + (0 * sizeof(T))); store_le(x1, out + (1 * sizeof(T))); store_le(x2, out + (2 * sizeof(T))); store_le(x3, out + (3 * sizeof(T))); store_le(x4, out + (4 * sizeof(T))); store_le(x5, out + (5 * sizeof(T))); store_le(x6, out + (6 * sizeof(T))); store_le(x7, out + (7 * sizeof(T))); } /** * Store eight big-endian words * @param out the output byte array * @param x0 the first word * @param x1 the second word * @param x2 the third word * @param x3 the fourth word * @param x4 the fifth word * @param x5 the sixth word * @param x6 the seventh word * @param x7 the eighth word */ template inline void store_be(byte out[], T x0, T x1, T x2, T x3, T x4, T x5, T x6, T x7) { store_be(x0, out + (0 * sizeof(T))); store_be(x1, out + (1 * sizeof(T))); store_be(x2, out + (2 * sizeof(T))); store_be(x3, out + (3 * sizeof(T))); store_be(x4, out + (4 * sizeof(T))); store_be(x5, out + (5 * sizeof(T))); store_be(x6, out + (6 * sizeof(T))); store_be(x7, out + (7 * sizeof(T))); } template void copy_out_be(byte out[], size_t out_bytes, const T in[]) { while(out_bytes >= sizeof(T)) { store_be(in[0], out); out += sizeof(T); out_bytes -= sizeof(T); in += 1; } for(size_t i = 0; i != out_bytes; ++i) out[i] = get_byte(i%8, in[0]); } template void copy_out_vec_be(byte out[], size_t out_bytes, const std::vector& in) { copy_out_be(out, out_bytes, in.data()); } template void copy_out_le(byte out[], size_t out_bytes, const T in[]) { while(out_bytes >= sizeof(T)) { store_le(in[0], out); out += sizeof(T); out_bytes -= sizeof(T); in += 1; } for(size_t i = 0; i != out_bytes; ++i) out[i] = get_byte(sizeof(T) - 1 - (i % 8), in[0]); } template void copy_out_vec_le(byte out[], size_t out_bytes, const std::vector& in) { copy_out_le(out, out_bytes, in.data()); } } #endif