// This file is part of Desktop App Toolkit, // a set of libraries for developing nice desktop applications. // // For license and copyright information please follow this link: // https://github.com/desktop-app/legal/blob/master/LEGAL // #include "base/unixtime.h" #include #include #ifdef Q_OS_WIN #include #elif defined Q_OS_MAC #include #else #include #endif namespace base { namespace unixtime { namespace { constexpr auto kIgnoreTimeDifference = TimeId(3); std::atomic ValueUpdated/* = false*/; std::atomic ValueShift/* = 0*/; std::atomic HttpValueValid/* = false*/; std::atomic HttpValueShift/* = 0*/; class MsgIdManager { public: MsgIdManager(); void update(); [[nodiscard]] uint64 next(); private: void initialize(); QReadWriteLock _lock; uint64 _startId = 0; std::atomic _incrementedPart = 0; uint64 _startCounter = 0; const uint64 _randomPart = 0; const float64 _multiplier = 0.; }; MsgIdManager GlobalMsgIdManager; [[nodiscard]] float64 GetMultiplier() { // 0xFFFF0000 instead of 0x100000000 to make msgId grow slightly slower, // than unixtime and we had time to reconfigure. #ifdef Q_OS_WIN LARGE_INTEGER li; QueryPerformanceFrequency(&li); return float64(0xFFFF0000L) / float64(li.QuadPart); #elif defined Q_OS_MAC // Q_OS_WIN mach_timebase_info_data_t tb = { 0, 0 }; mach_timebase_info(&tb); const auto frequency = (float64(tb.numer) / tb.denom) / 1000000.; return frequency * (float64(0xFFFF0000L) / 1000.); #else // Q_OS_MAC || Q_OS_WIN return float64(0xFFFF0000L) / 1000000000.; #endif // Q_OS_MAC || Q_OS_WIN } [[nodiscard]] uint64 GetCounter() { #ifdef Q_OS_WIN LARGE_INTEGER li; QueryPerformanceCounter(&li); return li.QuadPart; #elif defined Q_OS_MAC // Q_OS_WIN return mach_absolute_time(); #else // Q_OS_MAC || Q_OS_WIN timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec); #endif // Q_OS_MAC || Q_OS_WIN } [[nodiscard]] uint32 PrepareRandomPart() { auto generator = std::mt19937(std::random_device()()); auto distribution = std::uniform_int_distribution(); return distribution(generator); } MsgIdManager::MsgIdManager() : _randomPart(PrepareRandomPart()) , _multiplier(GetMultiplier()) { initialize(); srand(uint32(_startCounter & 0xFFFFFFFFUL)); } void MsgIdManager::update() { QWriteLocker lock(&_lock); initialize(); } void MsgIdManager::initialize() { _startCounter = GetCounter(); _startId = ((uint64(uint32(now()))) << 32) | _randomPart; } uint64 MsgIdManager::next() { const auto counter = GetCounter(); QReadLocker lock(&_lock); const auto startCounter = _startCounter; const auto startId = _startId; lock.unlock(); const auto incrementedPart = (_incrementedPart += 4); const auto delta = (counter - startCounter); const auto result = startId + (uint64)floor(delta * _multiplier); return (result & ~0x03L) + incrementedPart; } TimeId local() { return (TimeId)time(nullptr); } rpl::event_stream<> &UpdatesStream() { static auto result = rpl::event_stream<>(); return result; } } // namespace TimeId now() { return local() + ValueShift.load(); } void update(TimeId now, bool force) { if (force) { ValueUpdated = true; } else if (ValueUpdated) { return; } const auto shift = now - local(); const auto ignore = !force && (std::abs(shift - ValueShift) < kIgnoreTimeDifference); if (ignore) { return; } else if (!force) { auto expected = false; if (!ValueUpdated.compare_exchange_strong(expected, true)) { return; } } const auto old = ValueShift.exchange(shift); HttpValueShift = 0; HttpValueValid = false; if (old != shift) { GlobalMsgIdManager.update(); crl::on_main([] { UpdatesStream().fire({}); }); } } rpl::producer<> updates() { return UpdatesStream().events(); } QDateTime parse(TimeId value) { return (value > 0) ? QDateTime::fromSecsSinceEpoch(value - ValueShift) : QDateTime(); } TimeId serialize(const QDateTime &date) { return date.isNull() ? TimeId(0) : date.toSecsSinceEpoch() + ValueShift; } bool http_valid() { return HttpValueValid; } TimeId http_now() { return now() + HttpValueShift; } void http_update(TimeId now) { HttpValueShift = now - base::unixtime::now(); HttpValueValid = true; } void http_invalidate() { HttpValueValid = false; } uint64 mtproto_msg_id() { return GlobalMsgIdManager.next(); } } // namespace unixtime } // namespace base