diff --git td/generate/tl-parser/crc32.c td/generate/tl-parser/crc32.c index b7e0218..4f7fe4a 100644 --- td/generate/tl-parser/crc32.c +++ td/generate/tl-parser/crc32.c @@ -298,43 +298,15 @@ unsigned int crc32_table0[256] = { }; static unsigned int crc32_partial (const void *data, int len, unsigned crc) { - const int *p = (const int *) data; - int x; -#define DO_ONE(v) crc ^= v; crc = crc32_table0[crc & 0xff] ^ crc32_table1[(crc & 0xff00) >> 8] ^ crc32_table2[(crc & 0xff0000) >> 16] ^ crc32_table[crc >> 24]; -#define DO_FOUR(p) DO_ONE((p)[0]); DO_ONE((p)[1]); DO_ONE((p)[2]); DO_ONE((p)[3]); - - for (x = (len >> 5); x > 0; x--) { - DO_FOUR (p); - DO_FOUR (p + 4); - p += 8; - } - if (len & 16) { - DO_FOUR (p); - p += 4; - } - if (len & 8) { - DO_ONE (p[0]); - DO_ONE (p[1]); - p += 2; - } - if (len & 4) { - DO_ONE (*p++); - } - /* - for (x = (len >> 2) & 7; x > 0; x--) { - DO_ONE (*p++); - } - */ -#undef DO_ONE -#undef DO_FOUR - const char *q = (const char *) p; - if (len & 2) { - crc = crc32_table[(crc ^ q[0]) & 0xff] ^ (crc >> 8); - crc = crc32_table[(crc ^ q[1]) & 0xff] ^ (crc >> 8); - q += 2; - } - if (len & 1) { - crc = crc32_table[(crc ^ *q++) & 0xff] ^ (crc >> 8); + // NB: the original word-at-a-time (slicing-by-4) loop reads 32-bit words from + // the buffer and is only correct on little-endian hosts; on big-endian PPC it + // effectively byte-swaps each word and computes wrong CRC32 values (e.g. the + // TL `Vector` constructor id, breaking generated *_api code). Use a portable + // byte-wise CRC32 that yields the canonical little-endian result everywhere. + const unsigned char *q = (const unsigned char *) data; + int i; + for (i = 0; i < len; i++) { + crc = crc32_table[(crc ^ q[i]) & 0xff] ^ (crc >> 8); } return crc; } diff --git td/mtproto/CryptoStorer.h td/mtproto/CryptoStorer.h index fde1644..a3a1a45 100644 --- td/mtproto/CryptoStorer.h +++ td/mtproto/CryptoStorer.h @@ -47,7 +47,7 @@ class ObjectImpl { if (empty()) { return; } - storer.store_binary(message_id_); + storer.store_binary(message_id_.get()); storer.store_binary(seq_no_); storer.store_binary(static_cast(object_storer_.size())); storer.store_storer(object_storer_); @@ -140,7 +140,7 @@ class QueryImpl { template void do_store(StorerT &storer) const { - storer.store_binary(query_.message_id); + storer.store_binary(query_.message_id.get()); storer.store_binary(query_.seq_no); InvokeAfter invoke_after(query_.invoke_after_message_ids); @@ -318,7 +318,7 @@ class CryptoImpl { return storer.store_storer(destroy_key_storer_); default: - storer.store_binary(message_id_); + storer.store_binary(message_id_.get()); storer.store_binary(seq_no_); storer.store_binary(static_cast(container_storer_.size())); storer.store_storer(container_storer_); diff --git td/mtproto/DhHandshake.cpp td/mtproto/DhHandshake.cpp index b2d224a..0341157 100644 --- td/mtproto/DhHandshake.cpp +++ td/mtproto/DhHandshake.cpp @@ -9,6 +9,7 @@ #include "td/mtproto/DhCallback.h" #include "td/utils/as.h" +#include "td/utils/endian_load.h" #include "td/utils/crypto.h" #include "td/utils/logging.h" #include "td/utils/Slice.h" @@ -225,7 +226,7 @@ std::pair DhHandshake::gen_key() { int64 DhHandshake::calc_key_id(Slice auth_key) { UInt<160> auth_key_sha1; sha1(auth_key, auth_key_sha1.raw); - return as(auth_key_sha1.raw + 12); + return le_load_int64(auth_key_sha1.raw + 12); } } // namespace mtproto diff --git td/mtproto/Handshake.cpp td/mtproto/Handshake.cpp index 1478e3c..aa89270 100644 --- td/mtproto/Handshake.cpp +++ td/mtproto/Handshake.cpp @@ -13,6 +13,7 @@ #include "td/mtproto/utils.h" #include "td/utils/as.h" +#include "td/utils/endian_load.h" #include "td/utils/common.h" #include "td/utils/crypto.h" #include "td/utils/format.h" @@ -24,6 +25,7 @@ #include "td/utils/tl_parsers.h" #include +#include namespace td { namespace mtproto { @@ -248,7 +250,7 @@ Status AuthKeyHandshake::on_server_dh_params(Slice message, Callback *connection } auth_key_.set_created_at(dh_inner_data.server_time_); - server_salt_ = as(new_nonce_.raw) ^ as(server_nonce_.raw); + server_salt_ = le_load_int64(new_nonce_.raw) ^ le_load_int64(server_nonce_.raw); state_ = State::DHGenResponse; return Status::OK(); diff --git td/mtproto/RSA.cpp td/mtproto/RSA.cpp index c5e54e2..7e3d32f 100644 --- td/mtproto/RSA.cpp +++ td/mtproto/RSA.cpp @@ -9,6 +9,7 @@ #include "td/mtproto/mtproto_api.h" #include "td/utils/as.h" +#include "td/utils/endian_load.h" #include "td/utils/common.h" #include "td/utils/crypto.h" #include "td/utils/misc.h" @@ -119,7 +120,7 @@ int64 RSA::get_fingerprint() const { CHECK(size == tmp.size()); unsigned char key_sha1[20]; sha1(Slice(tmp.data(), tmp.size()), key_sha1); - return as(key_sha1 + 12); + return le_load_int64(key_sha1 + 12); } size_t RSA::size() const { diff --git td/mtproto/SessionConnection.cpp td/mtproto/SessionConnection.cpp index 05b1556..cf6b917 100644 --- td/mtproto/SessionConnection.cpp +++ td/mtproto/SessionConnection.cpp @@ -17,6 +17,7 @@ #include "td/utils/algorithm.h" #include "td/utils/as.h" +#include "td/utils/endian_load.h" #include "td/utils/common.h" #include "td/utils/format.h" #include "td/utils/Gzip.h" @@ -506,7 +507,7 @@ Status SessionConnection::on_slice_packet(const MsgInfo &info, Slice packet) { return Status::Error(PSLICE() << "Receive packet of size " << packet.size()); } - int32 constructor_id = as(packet.begin()); + int32 constructor_id = le_load_int32(packet.begin()); if (constructor_id == mtproto_api::msg_container::ID) { return on_packet_container(info, packet.substr(4)); } diff --git td/mtproto/TcpTransport.cpp td/mtproto/TcpTransport.cpp index 2d1b651..7422e66 100644 --- td/mtproto/TcpTransport.cpp +++ td/mtproto/TcpTransport.cpp @@ -7,6 +7,7 @@ #include "td/mtproto/TcpTransport.h" #include "td/utils/as.h" +#include "td/utils/bits.h" #include "td/utils/common.h" #include "td/utils/Random.h" #include "td/utils/Slice.h" @@ -17,6 +18,27 @@ namespace td { namespace mtproto { namespace tcp { +// TDLib's TCP transport framing (length prefix, obfuscation header fields) is +// little-endian on the wire. On big-endian PPC these helpers byte-swap; on +// little-endian hosts they are identity. +namespace { +#if defined(__BIG_ENDIAN__) || defined(__ppc__) || defined(__powerpc__) || defined(__POWERPC__) +inline uint32 wire_le32(uint32 x) { + return bswap32(x); +} +inline uint16 wire_le16(uint16 x) { + return static_cast((x >> 8) | (x << 8)); +} +#else +inline uint32 wire_le32(uint32 x) { + return x; +} +inline uint16 wire_le16(uint16 x) { + return x; +} +#endif +} // namespace + size_t IntermediateTransport::read_from_stream(ChainBufferReader *stream, BufferSlice *message, uint32 *quick_ack) { CHECK(message); size_t stream_size = stream->size(); @@ -27,6 +49,7 @@ size_t IntermediateTransport::read_from_stream(ChainBufferReader *stream, Buffer uint32 data_size; auto it = stream->clone(); it.advance(header_size, MutableSlice(reinterpret_cast(&data_size), sizeof(data_size))); + data_size = wire_le32(data_size); if (data_size & (1u << 31)) { if (quick_ack) { *quick_ack = data_size; @@ -69,7 +92,7 @@ void IntermediateTransport::write_prepare_inplace(BufferWriter *message, bool qu message->confirm_append(append.size()); } - as(message->as_mutable_slice().begin()) = static_cast(size + append_size); + as(message->as_mutable_slice().begin()) = wire_le32(static_cast(size + append_size)); } void IntermediateTransport::init_output_stream(ChainBufferWriter *stream) { @@ -95,12 +118,12 @@ void ObfuscatedTransport::init(ChainBufferReader *input, ChainBufferWriter *outp if (as(header.data()) == 0xef) { continue; } - uint32 first_int = as(header.data()); + uint32 first_int = wire_le32(as(header.data())); if (first_int == 0x44414548 || first_int == 0x54534f50 || first_int == 0x20544547 || first_int == 0x4954504f || first_int == 0xdddddddd || first_int == 0xeeeeeeee || first_int == 0x02010316) { continue; } - uint32 second_int = as(header.data() + sizeof(uint32)); + uint32 second_int = wire_le32(as(header.data() + sizeof(uint32))); if (second_int == 0) { continue; } @@ -108,7 +131,7 @@ void ObfuscatedTransport::init(ChainBufferReader *input, ChainBufferWriter *outp } as(header_slice.begin() + 56) = impl_.with_padding() ? 0xdddddddd : 0xeeeeeeee; if (dc_id_ != 0) { - as(header_slice.begin() + 60) = dc_id_; + as(header_slice.begin() + 60) = static_cast(wire_le16(static_cast(dc_id_))); } string rheader = header; diff --git td/mtproto/Transport.cpp td/mtproto/Transport.cpp index 1a98182..64212f0 100644 --- td/mtproto/Transport.cpp +++ td/mtproto/Transport.cpp @@ -11,6 +11,7 @@ #include "td/mtproto/MessageId.h" #include "td/utils/as.h" +#include "td/utils/endian_load.h" #include "td/utils/crypto.h" #include "td/utils/format.h" #include "td/utils/logging.h" @@ -224,7 +225,7 @@ Status Transport::read_crypto_impl(int X, MutableSlice message, const AuthKey &a auto to_decrypt = MutableSlice(header->encrypt_begin(), message.uend()); to_decrypt.remove_suffix(to_decrypt.size() & 15); - if (header->auth_key_id != auth_key.id()) { + if (le_swap64(header->auth_key_id) != auth_key.id()) { return Status::Error(PSLICE() << "Invalid MTProto message: auth_key_id mismatch [found = " << format::as_hex(header->auth_key_id) << "] [expected = " << format::as_hex(auth_key.id()) << "]"); @@ -247,12 +248,12 @@ Status Transport::read_crypto_impl(int X, MutableSlice message, const AuthKey &a //FIXME: rewrite without reinterpret cast auto *prefix = reinterpret_cast(header->data); *prefix_ptr = prefix; - size_t data_size = prefix->message_data_length + sizeof(PrefixT); + size_t data_size = le_swap32(prefix->message_data_length) + sizeof(PrefixT); bool is_length_bad = false; UInt128 real_message_key; if (packet_info->version == 1) { - is_length_bad |= packet_info->check_mod4 && prefix->message_data_length % 4 != 0; + is_length_bad |= packet_info->check_mod4 && le_swap32(prefix->message_data_length) % 4 != 0; auto expected_size = calc_crypto_size(data_size); is_length_bad |= expected_size != message.size(); auto check_size = data_size * (1 - is_length_bad) + tail_size * is_length_bad; @@ -272,26 +273,26 @@ Status Transport::read_crypto_impl(int X, MutableSlice message, const AuthKey &a } if (packet_info->version == 2) { - if (packet_info->check_mod4 && prefix->message_data_length % 4 != 0) { + if (packet_info->check_mod4 && le_swap32(prefix->message_data_length) % 4 != 0) { return Status::Error(PSLICE() << "Invalid MTProto message: invalid length (not divisible by four)" << tag("total_size", message.size()) - << tag("message_data_length", prefix->message_data_length)); + << tag("message_data_length", le_swap32(prefix->message_data_length))); } - if (tail_size - sizeof(PrefixT) < prefix->message_data_length) { + if (tail_size - sizeof(PrefixT) < le_swap32(prefix->message_data_length)) { return Status::Error(PSLICE() << "Invalid MTProto message: invalid length (message_data_length is too big)" << tag("total_size", message.size()) - << tag("message_data_length", prefix->message_data_length)); + << tag("message_data_length", le_swap32(prefix->message_data_length))); } size_t pad_size = tail_size - data_size; if (pad_size < 12 || pad_size > 1024) { return Status::Error(PSLICE() << "Invalid MTProto message: invalid length (invalid padding length)" << tag("padding_size", pad_size) << tag("total_size", message.size()) - << tag("message_data_length", prefix->message_data_length)); + << tag("message_data_length", le_swap32(prefix->message_data_length))); } } else { if (is_length_bad) { return Status::Error(PSLICE() << "Invalid MTProto message: invalid length " << tag("total_size", message.size()) - << tag("message_data_length", prefix->message_data_length)); + << tag("message_data_length", le_swap32(prefix->message_data_length))); } } @@ -308,10 +309,10 @@ Status Transport::read_crypto(MutableSlice message, const AuthKey &auth_key, Pac CHECK(prefix != nullptr); CHECK(packet_info != nullptr); packet_info->type = PacketInfo::Common; - packet_info->salt = header->salt; - packet_info->session_id = header->session_id; - packet_info->message_id = MessageId(prefix->msg_id); - packet_info->seq_no = prefix->seq_no; + packet_info->salt = le_swap64(header->salt); + packet_info->session_id = le_swap64(header->session_id); + packet_info->message_id = MessageId(le_swap64(prefix->msg_id)); + packet_info->seq_no = le_swap32(prefix->seq_no); return Status::OK(); } Status Transport::read_e2e_crypto(MutableSlice message, const AuthKey &auth_key, PacketInfo *packet_info, @@ -378,9 +379,9 @@ BufferWriter Transport::write_crypto(const Storer &storer, const AuthKey &auth_k //FIXME: rewrite without reinterpret cast auto &header = *reinterpret_cast(packet.as_mutable_slice().begin()); - header.auth_key_id = auth_key.id(); - header.salt = packet_info->salt; - header.session_id = packet_info->session_id; + header.auth_key_id = le_swap64(auth_key.id()); + header.salt = le_swap64(packet_info->salt); + header.session_id = le_swap64(packet_info->session_id); write_crypto_impl(0, storer, auth_key, packet_info, &header, data_size, padded_size); @@ -400,7 +401,7 @@ BufferWriter Transport::write_e2e_crypto(const Storer &storer, const AuthKey &au //FIXME: rewrite without reinterpret cast auto &header = *reinterpret_cast(packet.as_mutable_slice().begin()); - header.auth_key_id = auth_key.id(); + header.auth_key_id = le_swap64(auth_key.id()); write_crypto_impl(packet_info->is_creator || packet_info->version == 1 ? 0 : 8, storer, auth_key, packet_info, &header, data_size, padded_size); @@ -422,7 +423,7 @@ Result Transport::read(MutableSlice message, const AuthKe << "]"); } - int32 code = as(message.begin()); + int32 code = le_load_int32(message.begin()); if (code == 0) { return ReadResult::make_nop(); } else if (code == -1 && message.size() >= 8) { diff --git td/telegram/Td.cpp td/telegram/Td.cpp index a313ce6..3edc385 100644 --- td/telegram/Td.cpp +++ td/telegram/Td.cpp @@ -427,10 +427,15 @@ void Td::on_result(NetQueryPtr query) { void Td::start_up() { uint64 check_endianness = 0x0706050403020100; auto check_endianness_raw = reinterpret_cast(&check_endianness); + bool little_endian = true; for (unsigned char c = 0; c < 8; c++) { auto symbol = check_endianness_raw[static_cast(c)]; - LOG_IF(FATAL, symbol != c) << "TDLib requires little-endian platform"; + if (symbol != c) { + little_endian = false; + break; + } } + LOG_IF(WARNING, !little_endian) << "Running TDLib on a big-endian platform; PPC compatibility mode enabled"; requests_ = make_unique(this); diff --git tdtl/td/tl/tl_simple_parser.h tdtl/td/tl/tl_simple_parser.h index d3c8edd..941f984 100644 --- tdtl/td/tl/tl_simple_parser.h +++ tdtl/td/tl/tl_simple_parser.h @@ -60,17 +60,28 @@ class tl_simple_parser { std::int32_t fetch_int() { check_len(sizeof(std::int32_t)); - std::int32_t result = *reinterpret_cast(data); + const unsigned char *p = reinterpret_cast(data); + std::uint32_t raw = static_cast(p[0]) | + (static_cast(p[1]) << 8) | + (static_cast(p[2]) << 16) | + (static_cast(p[3]) << 24); data += sizeof(std::int32_t); - return result; + return static_cast(raw); } std::int64_t fetch_long() { check_len(sizeof(std::int64_t)); - std::int64_t result; - std::memcpy(&result, data, sizeof(std::int64_t)); + const unsigned char *p = reinterpret_cast(data); + std::uint64_t raw = static_cast(p[0]) | + (static_cast(p[1]) << 8) | + (static_cast(p[2]) << 16) | + (static_cast(p[3]) << 24) | + (static_cast(p[4]) << 32) | + (static_cast(p[5]) << 40) | + (static_cast(p[6]) << 48) | + (static_cast(p[7]) << 56); data += sizeof(std::int64_t); - return result; + return static_cast(raw); } std::string fetch_string() { diff --git tdutils/td/utils/StringBuilder.cpp tdutils/td/utils/StringBuilder.cpp index aa39c62..c9e3c44 100644 --- tdutils/td/utils/StringBuilder.cpp +++ tdutils/td/utils/StringBuilder.cpp @@ -9,6 +9,7 @@ #include "td/utils/misc.h" #include "td/utils/port/thread_local.h" +#include #include #include #include @@ -192,28 +193,31 @@ StringBuilder &StringBuilder::operator<<(long long unsigned int x) { } StringBuilder &StringBuilder::operator<<(FixedDouble x) { - if (unlikely(!reserve(std::numeric_limits::max_exponent10 + x.precision + 4))) { - return on_error(); + // NB: the previous implementation used a std::stringstream and reserved + // max_exponent10 + precision + 4 bytes; when that large reservation could not + // be satisfied it emitted an EMPTY string, producing invalid JSON for some + // doubles (observed on big-endian PPC with out-of-range values). Format with + // snprintf and reserve exactly what is needed. Non-finite values have no JSON + // representation, so emit 0 to keep output valid. + if (unlikely(!std::isfinite(x.d))) { + if (unlikely(!reserve(1))) { + return on_error(); + } + *current_ptr_++ = '0'; + return *this; } - - static TD_THREAD_LOCAL std::stringstream *ss; - if (init_thread_local(ss)) { - auto previous_locale = ss->imbue(std::locale::classic()); - ss->setf(std::ios_base::fixed, std::ios_base::floatfield); - } else { - ss->str(std::string()); - ss->clear(); + int len = std::snprintf(nullptr, 0, "%.*f", x.precision, x.d); + if (unlikely(len <= 0)) { + if (unlikely(!reserve(1))) { + return on_error(); + } + *current_ptr_++ = '0'; + return *this; } - ss->precision(x.precision); - *ss << x.d; - - auto len = narrow_cast(static_cast(ss->tellp())); - auto left = end_ptr_ + RESERVED_SIZE - current_ptr_; - if (unlikely(len >= left)) { - error_flag_ = true; - len = left ? narrow_cast(left - 1) : 0; + if (unlikely(!reserve(static_cast(len) + 1))) { + return on_error(); } - ss->read(current_ptr_, len); + std::snprintf(current_ptr_, static_cast(len) + 1, "%.*f", x.precision, x.d); current_ptr_ += len; return *this; } diff --git tdutils/td/utils/bits.h tdutils/td/utils/bits.h index 2580543..1e5aa80 100644 --- tdutils/td/utils/bits.h +++ tdutils/td/utils/bits.h @@ -48,12 +48,18 @@ inline uint64 lower_bit64(uint64 x) { } inline uint64 host_to_big_endian64(uint64 x) { - // NB: works only for little-endian systems +#if defined(__BIG_ENDIAN__) || defined(__ppc__) || defined(__powerpc__) || defined(__POWERPC__) + return x; +#else return bswap64(x); +#endif } inline uint64 big_endian_to_host64(uint64 x) { - // NB: works only for little-endian systems +#if defined(__BIG_ENDIAN__) || defined(__ppc__) || defined(__powerpc__) || defined(__POWERPC__) + return x; +#else return bswap64(x); +#endif } //TODO: optimize diff --git tdutils/td/utils/crypto.cpp tdutils/td/utils/crypto.cpp index dee0e25..4492f15 100644 --- tdutils/td/utils/crypto.cpp +++ tdutils/td/utils/crypto.cpp @@ -6,6 +6,8 @@ // #include "td/utils/crypto.h" +#include + #include "td/utils/as.h" #include "td/utils/BigNum.h" #include "td/utils/bits.h" @@ -156,16 +158,18 @@ void init_crypto() { template static string as_big_endian_string(const FromT &from) { + // Endian-independent: emit the minimal big-endian byte representation. The + // previous implementation relied on little-endian memory layout via as<>. char res[sizeof(FromT)]; - as(res) = from; - - size_t i = sizeof(FromT); - while (i && res[i - 1] == 0) { - i--; + typename std::make_unsigned::type v = static_cast::type>(from); + for (size_t j = 0; j < sizeof(FromT); j++) { + res[sizeof(FromT) - 1 - j] = static_cast((v >> (8 * j)) & 0xff); } - - std::reverse(res, res + i); - return string(res, res + i); + size_t i = 0; + while (i < sizeof(FromT) && res[i] == 0) { + i++; + } + return string(res + i, res + sizeof(FromT)); } static int pq_factorize_big(Slice pq_str, string *p_str, string *q_str) { diff --git tdutils/td/utils/endian_load.h tdutils/td/utils/endian_load.h new file mode 100644 index 0000000..23a1d3e --- /dev/null +++ tdutils/td/utils/endian_load.h @@ -0,0 +1,44 @@ +// +// Endian-independent little-endian integer loads from a byte buffer. +// +// MTProto/TL stores integers little-endian on the wire and in several places +// reads them back with a native `as` cast. That cast is correct only on +// little-endian hosts; on big-endian PPC it byte-swaps. These helpers always +// read little-endian, so they match `as` on little-endian hosts and stay +// correct on big-endian ones. +// +#pragma once + +#include "td/utils/common.h" +#include "td/utils/bits.h" + +namespace td { + +inline int32 le_load_int32(const void *ptr) { + const unsigned char *p = static_cast(ptr); + uint32 res = static_cast(p[0]) | (static_cast(p[1]) << 8) | + (static_cast(p[2]) << 16) | (static_cast(p[3]) << 24); + return static_cast(res); +} + +inline int64 le_load_int64(const void *ptr) { + const unsigned char *p = static_cast(ptr); + uint64 res = 0; + for (int i = 0; i < 8; i++) { + res |= static_cast(p[i]) << (8 * i); + } + return static_cast(res); +} + +// Swap between host order and little-endian wire order for integers that are +// read/written through raw struct overlays (MTProto CryptoHeader/CryptoPrefix). +// Identity on little-endian hosts; byte-swap on big-endian (PPC). +#if defined(__BIG_ENDIAN__) || defined(__ppc__) || defined(__powerpc__) || defined(__POWERPC__) +inline uint32 le_swap32(uint32 x) { return bswap32(x); } +inline uint64 le_swap64(uint64 x) { return bswap64(x); } +#else +inline uint32 le_swap32(uint32 x) { return x; } +inline uint64 le_swap64(uint64 x) { return x; } +#endif + +} // namespace td diff --git tdutils/td/utils/port/path.cpp tdutils/td/utils/port/path.cpp index 4d94ac5..5b7b205 100644 --- tdutils/td/utils/port/path.cpp +++ tdutils/td/utils/port/path.cpp @@ -344,6 +344,10 @@ Result walk_path_dir(string &path, DIR *subdir, const WalkFunction &func) } Result walk_path_dir(string &path, FileFd fd, const WalkFunction &func) { +#if defined(__APPLE__) + (void)fd; + return walk_path_dir(path, func); +#else auto native_fd = fd.move_as_native_fd(); auto *subdir = fdopendir(native_fd.fd()); if (subdir == nullptr) { @@ -351,6 +355,7 @@ Result walk_path_dir(string &path, FileFd fd, const WalkFunction &func) { } native_fd.release(); return walk_path_dir(path, subdir, func); +#endif } Result walk_path_dir(string &path, const WalkFunction &func) { diff --git tdutils/td/utils/tl_parsers.h tdutils/td/utils/tl_parsers.h index f08009b..1d466d5 100644 --- tdutils/td/utils/tl_parsers.h +++ tdutils/td/utils/tl_parsers.h @@ -73,14 +73,16 @@ class TlParser { } int32 prefetch_int_unsafe() const { - int32 result; - std::memcpy(&result, data, sizeof(int32)); - return result; + const unsigned char *p = data; + uint32 raw = static_cast(p[0]) | + (static_cast(p[1]) << 8) | + (static_cast(p[2]) << 16) | + (static_cast(p[3]) << 24); + return static_cast(raw); } int32 fetch_int_unsafe() { - int32 result; - std::memcpy(&result, data, sizeof(int32)); + int32 result = prefetch_int_unsafe(); data += sizeof(int32); return result; } @@ -91,10 +93,17 @@ class TlParser { } int64 fetch_long_unsafe() { - int64 result; - std::memcpy(&result, data, sizeof(int64)); + const unsigned char *p = data; + uint64 raw = static_cast(p[0]) | + (static_cast(p[1]) << 8) | + (static_cast(p[2]) << 16) | + (static_cast(p[3]) << 24) | + (static_cast(p[4]) << 32) | + (static_cast(p[5]) << 40) | + (static_cast(p[6]) << 48) | + (static_cast(p[7]) << 56); data += sizeof(int64); - return result; + return static_cast(raw); } int64 fetch_long() { @@ -103,9 +112,9 @@ class TlParser { } double fetch_double_unsafe() { + uint64 raw = static_cast(fetch_long_unsafe()); double result; - std::memcpy(&result, data, sizeof(double)); - data += sizeof(double); + std::memcpy(&result, &raw, sizeof(double)); return result; } diff --git tdutils/td/utils/tl_storers.h tdutils/td/utils/tl_storers.h index 0ddecdd..5edd84e 100644 --- tdutils/td/utils/tl_storers.h +++ tdutils/td/utils/tl_storers.h @@ -12,6 +12,7 @@ #include "td/utils/StorerBase.h" #include +#include namespace td { @@ -27,8 +28,32 @@ class TlStorerUnsafe { template void store_binary(const T &x) { +#if defined(__BIG_ENDIAN__) || defined(__ppc__) || defined(__powerpc__) || defined(__POWERPC__) + // TDLib's wire/storage format is little-endian. On big-endian PPC, emit the + // bytes of arithmetic values in little-endian order. Aggregate raw types such + // as UInt128/UInt256 are byte arrays and must be copied verbatim. + if constexpr (std::is_integral::value) { + typename std::make_unsigned::type u = static_cast::type>(x); + for (size_t i = 0; i < sizeof(T); i++) { + buf_[i] = static_cast((u >> (8 * i)) & 0xff); + } + buf_ += sizeof(T); + } else if constexpr (std::is_floating_point::value) { + static_assert(sizeof(T) == 4 || sizeof(T) == 8, "unexpected floating-point size"); + typename std::conditional::type bits = 0; + std::memcpy(&bits, &x, sizeof(T)); + for (size_t i = 0; i < sizeof(T); i++) { + buf_[i] = static_cast((bits >> (8 * i)) & 0xff); + } + buf_ += sizeof(T); + } else { + std::memcpy(buf_, &x, sizeof(T)); + buf_ += sizeof(T); + } +#else std::memcpy(buf_, &x, sizeof(T)); buf_ += sizeof(T); +#endif } void store_int(int32 x) {